【備忘】Express.js + PostgreSql をdocker上に構築
Introduction
最近、Express.jsとPostgreSQLを用いてWEB APIを作って遊ぶことが多いのですが、毎回環境構築のためにWEBの海を彷徨う学習能力の無さ。。。
さすがに懲りたので、備忘のために手順を残そうと思います(限りなく自分用のメモなのでだいぶ端折っています)。
前置き
環境
Windows10のWSL2上にインストールしたUbuntu-20.04を使用。その上でdockerを起動し、各種アプリケーションを動かします。
WSL2、docker desktop等の基盤の環境設定手順は割愛。
ワークフォルダ
WSL2 Ubuntu上に作成した下記フォルダを使用。
- express_sandbox
- node
- app
- sandbox
- app
- postgresql
- psdata
- node
完成品
GitHub : express-postgresql-sandbox
dockerコンテナ作成
dockerfile作成
node.js環境用dockerfileを作成
express_sandbox/node/ に、node.js用のコンテナの元になるdockerfile作成します。今回はこのようにしました。
FROM node
ENV NODE_ENV=development
WORKDIR /app
RUN apt-get update && \
apt-get install git &&\
apt-get install -y nodejs &&\
npm install -g npm &&\
npm install -g express-generator
ENV HOST 0.0.0.0
EXPOSE 3000
PostgreSql環境用dockerfileを作成
同様に、PostgreSql用のdockerfileを、express_sandbox/node/ に作成。
FROM postgres
RUN localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8
ENV LANG ja_JP.utf8
docker-compose作成
docker-compose定義作成
node.jsとPostgreSqlのコンテナをまとめて管理するため、docker-compose定義を、express_sandbox/ に作成。サービスの名称として、node.jsコンテナを webserver、PostgreSqlコンテナを db とします。
またデータの永続化のため、nodeは、/app配下を最初に作成したホスト上の/node/appへ、PostgreSqlは、/var/lib/postgresql/data配下を/postgresql/psdata にマウントします。この設定をしておかないとコンテナを終了した際に、データが消えてしまうため。
version: '3'
services:
webserver:
build: node
tty: true
volumes:
- ./node/app:/app
ports:
- "3000:3000"
db:
build: postgresql
volumes:
- ./postgresql/psdata:/var/lib/postgresql/data
ports:
- 5432:5432
environment:
POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --locale=ja_JP.UTF-8"
POSTGRES_PASSWORD: 'password'
docker-composeからビルド・起動
作成したコンテナ定義から、ビルドする。WSL2のターミナル上から下記コマンド実行。
docker-compose build
ビルド完了後、コンテナを起動します。
docker-compose up -d
起動中のコンテナを確認し、2つのコンテナが起動していることを確認。
docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
express_sandbox-db-1 "docker-entrypoint.s…" db running 0.0.0.0:5432->5432/tcp
express_sandbox-webserver-1 "docker-entrypoint.s…" webserver running 0.0.0.0:3000->3000/tcp
Express.jsサンプルプロジェクト作成
事前準備
WSL2上から下記コマンドを実行することで、起動中のnode.jsコンテナに入ります。以降はコンテナ上での作業となります。
docker-compose exec webserver bash
コンテナに入り込むことができたら、あらかじめホスト側で作成・マウントしているsandboxフォルダに移動し、Express.jsプロジェクトの初期化実行します。
cd sandbox
npm init -y
プロジェクトの初期化が完了したら、続いて必要なパッケージの追加します。今回はExpress.jsの他、PostgreSqlを操作するためパッケージ、ORMとしてsequelizeを追加。
npm install express pg pg-hstore sequelize sequelize-cli
ここでできたら事前準備は完了。続いてExpress.jsの動作確認をするにあたり、ホスト(WSL2)側からファイル操作ができるようパーミッション設定を変更します。
chmod -R 777 .
これでホストからファイル操作が許可され、VScode等のエディタを使った編集も可能に。
動作確認
Express.jsからお決まりの "Hello World !" が帰ってくるようなコードを追加し、動作確認を。
1枚新しくWSL2ターミナル端末を開き、express_sandbox/node/app/sandbox に index.js を追加、下記コードを記述します。
const port = 3000;
const express = require("express");
const app = express();
app.get("/", (req, res) => {
res.json({title : "Hello World, express.js"})
})
app.listen(port, () => {
console.log("Server running on port:" + port);
})
index.jsの記述ができたら、再度node.jsのコンテナに戻り(WSL2ターミナルではない点に注意)、 /sandbox/ 上で下記コマンド実行作成したサンプルプログラムを起動します。
node index.js
ポート:3000 でプログラムが起動しているはずなので、WEBブラウザからhttp://localhost:3000/
にアクセス、Hello World とjson形式でお返事してくれていればOK。
PostgreSqlとの接続
続いて、PostgreSqlと接続し、DBから取得した内容をもとにお返事できるようにします。Express.jsから直接 生SQLを叩いてもよいのですが、開発効率を考え、ORM(Object-relational mapping)用パッケージ sequelizeを使用します。
sequelize ORM初期設定
node.jsコンテナの/sandbox/ 上でsequelizeの初期化コマンドを実行します。
npx sequelize-cli init
続いて、DBへの接続情報を記述します。/sandbox/config/config.json を開いて、記述内容を書き換えます。ホストから変更する場合は再度 chmod -R 777 .
を実行し、アクセス許可を与える必要があるかも。
今回、PostgreSqlコンテナ登録時に特に指定しなかったため、データベース名・ユーザー名は postgres、パスワードは password となっています(あくまでローカルでの検証目的なのでこれでよいかと)。
{
"development": {
"username": "postgres",
"password": "password",
"database": "postgres",
"host": "db",
"dialect": "postgres",
"operatorAliases":false
}
}
これでsequelizeの初期設定は完了。次にmigrationファイルを作成し、実際にデータモデル作成・データ投入を試してみます。
データモデルのmigration定義
DBへのテーブル登録と対応するプログラム側のデータモデル登録のためのマイグレーションスクリプトを生成します。
今回はお試しということで、姓・名・メールアドレスを管理するモデル:user を作成するため、/sandbox/上で下記コマンドを実行。
npx sequelize-cli model:generate --name user --attributes firstName:string,lastName:string,email:string
マイグレーションスクリプトが/sandbox/migrations上に作成されます。今回は動作確認目的なので、特に修正せずこのまま適用してしまいます。
下記マイグレーションコマンドを実行
npx sequelize-cli db:migrate
これでデータモデル登録、およびテーブル登録が完了しました。試しにDBの中を覗いて(私はクライアントソフトとしてA5:SQLを使用)、usersテーブルが作られていたらOK。
この後のテスト用に数件、適当なレコードを追加します。
データ取得プログラム動作確認
最後に、Express.jsが、PostgreSqlのDBから取得した内容をもとにお返事してくれるようプログラムを改修し、Express.js + PostgreSqlサンプルプログラムの動作を確認します。
先ほど/sandbox/に作成したindex.jsを次のように書き換えます。
const port = 3000;
const express = require("express");
const app = express();
const db = require("./models/index")
app.get("/", (req, res) => {
res.json({title : "Hello World, express.js"})
})
app.get("/users", (req, res) => {
db.user.findAll({}).then((users) => {
res.json(users)
})
})
app.listen(port, () => {
console.log("Server running on port:" + port);
})
再度、
node index.js
からプログラムを起動し、WEBブラウザからhttp://localhost:3000/users
へアクセス、DBのusersテーブルに登録した値がレスポンスとしてかえってきていれば成功です!!
改めてまとめてみて
dockerは、ホストを汚すことなく(既存の資源と競合することなく)様々な開発・検証環境を比較的簡単につくることができる、というメリットは大きい。ものの。。。
コンテナレイヤー・WSL2レイヤー・ホストOS(Windows)レイヤーと3つの異なる領域が存在し絡み合ってしまっているため、何かエラーが起きたときにどこに起因があるのか?(例えばデータアクセス権限がないエラーがでたとき、コンテナなのかWSL2上のLinuxなのか、Windowsなのか、誰が権限を抑えているのか?)探すのに一苦労という側面もあり、慣れないとなかなか辛くものという印象。
とはいえ、これまでなかなかハードルの高かったWindows上でのWEB開発環境の構築が、随分楽になったことは事実なので、良い時代になったものですね。
参考文献
Comments