Dockerでnginx + Node.js + MongoDBの環境を用意する
Dockerでnginx + Node.js + MongoDBの環境を用意する
タイトルの通り、nginx(リバースプロキシ)、Node.js、MongoDBの環境をDocker + Docker Composeで用意します。
全て公式のイメージを使用し、基本的に公式のガイドに従って準備します。
バージョン
❯ docker -v
Docker version 20.10.10, build b485636
❯ docker-compose -v
docker-compose version 1.29.2, build 5becea4c
ディレクトリ構成
.
├── docker-compose.yml
├── mongo
│ ├── init.js # DB初期化用スクリプト
│ └── mongo-data # データ保存用ディレクトリ(バインドマウント)
├── nginx
│ ├── Dockerfile
│ └── conf
│ └── proxy.conf
└── server
├── Dockerfile
├── node_modules
├── package-lock.json
├── package.json
└── src # アプリケーションコード
MongoDB
公式イメージを利用します。
Dockerfileは用意せず、公式イメージをそのまま使います。
mongo:
image: mongo
container_name: mongo
environment:
MONGO_INITDB_DATABASE: admin
volumes:
- ./mongo/init.js:/docker-entrypoint-initdb.d/init.js:ro
- ./mongo/mongo-data:/data/db
# Start mongo with authentication enabled
command: [mongod, --auth]
MongoDBはデフォルトだと認証が無効になっているので、command: [mongod, --auth]
で認証を有効にして起動しています。
また、以下のような初期化用のスクリプトを用意します。
db.createUser({
user: 'echizen',
pwd: 'password',
roles: [
{
role: 'readWrite',
db: 'nginx-node-mongo-docker-example',
},
],
})
この初期化用のスクリプトをコンテナ内の/docker-entrypoint-initdb.d/
にマウントしています。
どういうことかというと、この公式イメージのコンテナは初めて立ち上がったとき、/docker-entrypoint-initdb.d/
以下にある.js
もしくは.sh
ファイルを実行してくれるようになっています。
また、スクリプトを実行する際に使用するデータベースをMONGODB_INITDB_DATABASE
という環境変数で指定できます。
ここではMONGODB_INITDB_DATABASE
にadmin
(デフォルトの認証情報用DB)を指定しているので、そこにechizen
というユーザーが作成されます。
また、MONGODB_INITDB_ROOT_USERNAME
とMONGODB_INITDB_ROOT_PASSWORD
という環境変数を指定することで、初期化時にroot権限のユーザーをadmin
データベースに作ることもできます。詳しくは公式イメージのページを参照。
Node.js
こちらも公式イメージを利用します。
アプリケーションコード
expressと、MongoDB公式のNode.jsドライバーを利用します。(APIドキュメント)
npm init -y
npm install express
npm install mongodb
ES Modules, Top level awaitを使いたいので、package.json
のtype
フィールドにmodule
を指定します。
{
"name": "server",
"version": "1.0.0",
"type": "module",
"dependencies": {
"express": "^4.17.3",
"mongodb": "^4.5.0"
}
}
アプリケーションコードを用意します。
import express from 'express'
import { MongoClient } from 'mongodb'
const PORT = 8080
const HOST = '0.0.0.0'
const app = express()
app.get('/', async (req, res) => {
const result = await collection.find({}).toArray()
res.json(result)
})
app.listen(8080, HOST, () => {
console.log(`Listening on port ${PORT}`)
})
const URL = 'mongodb://echizen:password@mongo/nginx-node-mongo-docker-example?authSource=admin'
const client = new MongoClient(URL)
try {
await client.connect()
console.log('Succesfully connected to mongo')
} catch (e) {
console.error(e)
}
const db = client.db()
// Prepare initial data
const doc1 = { name: 'Echizen', age: 24 }
const doc2 = { name: 'Bob', age: 32 }
const collection = db.collection('test-collection')
await collection.insertMany([doc1, doc2])
URLでauthSource=admin
を指定して、先ほどの初期化用スクリプトで作成されるユーザーで認証しています。
Docker化
次に、公式のガイドを参考にDocker化します。
FROM node:16
# アプリケーションディレクトリを作成する
WORKDIR /usr/src/app
# キャッシュを利用するために、package.jsonとpackage-lock.jsonのみをコピーし、
# 依存関係を先にインストール
COPY package*.json ./
RUN npm install
# アプリケーションコードをコンテナにコピー
COPY . .
EXPOSE 8080
CMD [ "node", "src/app.js" ]
node_modules
等を無視するために、上のDockerfile
と同じディレクトリに.dockerignore
を以下の内容で作成します。
node_modules
npm-debug.log
nginx
こちらも公式イメージを使って、nginx公式のガイドに従って準備します。
設定ファイル
デフォルトではコンテナ内に/etc/nginx/nginx.conf
と/etc/nginx/conf.d/default.conf
という二つが用意され、nginx.conf
からdefault.conf
を読み込むようになっていますが、default.conf
を削除してリバースプロキシ用の設定ファイルを代わりに用意します。
リクエストは全てserver
(Node.jsが動いているコンテナ)に飛ばします。
server {
listen 80;
location / {
proxy_pass http://server:8080/;
}
}
Dockerfile
FROM nginx:1.21
# デフォルトの設定を削除
RUN rm /etc/nginx/conf.d/default.conf
# 作成した設定ファイルをコピー
COPY conf/proxy.conf /etc/nginx/conf.d
docker-compose.yml
最後にdocker-compose.yml
を準備します。
services:
nginx:
build: ./nginx
container_name: nginx
depends_on:
- server
ports:
- 8080:80
server:
build: ./server
container_name: server
depends_on:
- mongo
mongo:
image: mongo
container_name: mongo
environment:
MONGO_INITDB_DATABASE: admin
volumes:
- ./mongo/init.js:/docker-entrypoint-initdb.d/init.js:ro
- ./mongo/mongo-data:/data/db
# Start mongo with authentication enabled
command: [mongod, --auth]
nginx以外は外にポートを公開していません。
docker-composeで作ったコンテナはデフォルトですべて<親ディレクトリ名>_default
というネットワークに所属し、コンテナ名で通信できます。
動かしてみる
❯ docker-compose up -d
❯ curl http://localhost:8080/ | jq .
[
{
"_id": "625103c4f9d2309deaad919e",
"name": "Echizen",
"age": 24
},
{
"_id": "625103c4f9d2309deaad919f",
"name": "Bob",
"age": 32
},
]
最後に
今回のソースコードは全てGitHubに置いてあります。
まだまだ勉強中なので、「こうした方がいい」等あったら是非教えてください。
この記事が何かの参考になったら幸いです。
Discussion