コンテナーを利用した Node.js 開発
前提条件
Node.js アプリケーションのコンテナー化 を完了していること。
概要
本節ではコンテナー化したアプリケーションに対する開発環境の構築方法を学びます。 ここでは以下を行います。
- ローカルデータベースを追加して、そのデータを維持します。
- 開発環境として実行するためにコンテナー設定を行います。
- コンテナー化したアプリケーションのデバッグを行います。
ローカルデータベースの追加とデータ維持
コンテナーでは、ローカルサービスとしてたとえばデータベースを構築することができます。
本節では compose.yaml
ファイルを編集して、データベースサービスを追加してデータ保存のためのボリュームを定義します。
IDE またはテキストエディターを使って
compose.yaml
ファイルを開きます。データベースに関する命令部分のコメントを解除します。 書き換えた
compose.yaml
ファイルは以下のようになります。重要
本節においては、指示があるまで
docker compose up
は実行しないでください。 作業の途中においてコマンド実行してしまうと、データベースが誤って初期化されてしまう場合があります。compose.yaml# Comments are provided throughout this file to help you get started. # If you need more help, visit the Docker Compose reference guide at # https://docs.docker.com/go/compose-spec-reference/ # Here the instructions define your application as a service called "server". # This service is built from the Dockerfile in the current directory. # You can add other services your application may depend on here, such as a # database or a cache. For examples, see the Awesome Compose repository: # https://github.com/docker/awesome-compose services: server: build: context: . environment: NODE_ENV: production ports: - 3000:3000 # The commented out section below is an example of how to define a PostgreSQL # database that your application can use. `depends_on` tells Docker Compose to # start the database before your application. The `db-data` volume persists the # database data between container restarts. The `db-password` secret is used # to set the database password. You must create `db/password.txt` and add # a password of your choosing to it before running `docker-compose up`. depends_on: db: condition: service_healthy db: image: postgres restart: always user: postgres secrets: - db-password volumes: - db-data:/var/lib/postgresql/data environment: - POSTGRES_DB=example - POSTGRES_PASSWORD_FILE=/run/secrets/db-password expose: - 5432 healthcheck: test: ["CMD", "pg_isready"] interval: 10s timeout: 5s retries: 5 volumes: db-data: secrets: db-password: file: db/password.txt
メモ
Compose ファイル内の命令について詳しく学ぶには Compose ファイルリファレンス を参照してください。
IDE またはテキストエディターを使って
src/persistence/postgres.js
を開きます。 中を覗いてみると、このアプリケーションは Postgres データベースを利用しており、データベースへの接続に環境変数を利用していることがすぐにわかります。compose.yaml
ファイルでは、まだそれらの環境変数を定義していません。データベースの設定を行う環境変数を追加します。 書き換えた
compose.yaml
ファイルは以下のようになります。compose.yaml# Comments are provided throughout this file to help you get started. # If you need more help, visit the Docker Compose reference guide at # https://docs.docker.com/go/compose-spec-reference/ # Here the instructions define your application as a service called "server". # This service is built from the Dockerfile in the current directory. # You can add other services your application may depend on here, such as a # database or a cache. For examples, see the Awesome Compose repository: # https://github.com/docker/awesome-compose services: server: build: context: . environment: NODE_ENV: production POSTGRES_HOST: db POSTGRES_USER: postgres POSTGRES_PASSWORD_FILE: /run/secrets/db-password POSTGRES_DB: example ports: - 3000:3000 # The commented out section below is an example of how to define a PostgreSQL # database that your application can use. `depends_on` tells Docker Compose to # start the database before your application. The `db-data` volume persists the # database data between container restarts. The `db-password` secret is used # to set the database password. You must create `db/password.txt` and add # a password of your choosing to it before running `docker-compose up`. depends_on: db: condition: service_healthy db: image: postgres restart: always user: postgres secrets: - db-password volumes: - db-data:/var/lib/postgresql/data environment: - POSTGRES_DB=example - POSTGRES_PASSWORD_FILE=/run/secrets/db-password expose: - 5432 healthcheck: test: ["CMD", "pg_isready"] interval: 10s timeout: 5s retries: 5 volumes: db-data: secrets: db-password: file: db/password.txt
server
サービス配下にsecrets
セクションを追加します。 これにより、アプリケーションが利用するデータベースパスワードを安全に取り扱います。 書き換えたcompose.yaml
ファイルは以下のようになります。compose.yaml# Comments are provided throughout this file to help you get started. # If you need more help, visit the Docker Compose reference guide at # https://docs.docker.com/go/compose-spec-reference/ # Here the instructions define your application as a service called "server". # This service is built from the Dockerfile in the current directory. # You can add other services your application may depend on here, such as a # database or a cache. For examples, see the Awesome Compose repository: # https://github.com/docker/awesome-compose services: server: build: context: . environment: NODE_ENV: production POSTGRES_HOST: db POSTGRES_USER: postgres POSTGRES_PASSWORD_FILE: /run/secrets/db-password POSTGRES_DB: example ports: - 3000:3000 # The commented out section below is an example of how to define a PostgreSQL # database that your application can use. `depends_on` tells Docker Compose to # start the database before your application. The `db-data` volume persists the # database data between container restarts. The `db-password` secret is used # to set the database password. You must create `db/password.txt` and add # a password of your choosing to it before running `docker-compose up`. depends_on: db: condition: service_healthy secrets: - db-password db: image: postgres restart: always user: postgres secrets: - db-password volumes: - db-data:/var/lib/postgresql/data environment: - POSTGRES_DB=example - POSTGRES_PASSWORD_FILE=/run/secrets/db-password expose: - 5432 healthcheck: test: ["CMD", "pg_isready"] interval: 10s timeout: 5s retries: 5 volumes: db-data: secrets: db-password: file: db/password.txt
docker-nodejs-sample
ディレクトリ内にdb
という名前のディレクトリを生成します。db
ディレクトリ内にpassword.txt
という名前のファイルを生成します。 このファイルにデータベースパスワードを記述します。ここまでにより
docker-nodejs-sample
ディレクトリ内は少なくとも以下のようになっているはずです。├── docker-nodejs-sample/ │ ├── db/ │ │ └── password.txt │ ├── spec/ │ ├── src/ │ ├── .dockerignore │ ├── .gitignore │ ├── compose.yaml │ ├── Dockerfile │ ├── package-lock.json │ ├── package.json │ └── README.md
IDE またはテキストエディターを使って
password.txt
ファイルを開きます。 そしてパスワードを任意に取り決めて記述します。 このパスワードは 1 行内に記述し、これ以外の行を記述してはなりません。 改行文字や隠し文字などは一切含めないようにしてください。ここまでに修正したファイルは、すべて適切に保存したことを確認します。
以下のコマンドを実行してアプリケーションを起動します。
$ docker compose up --build
ブラウザーを開きます。 アプリケーションが起動していることを確認するため http://localhost:3000 にアクセスします。
データが保持されることを確認するため、todo リストにアイテムをいくつか追加します。
todo リストへの追加をある程度行ったら、端末上において
ctrl+c
を入力してアプリケーションを停止します。端末上において
docker compose rm
を実行してコンテナーを削除します。$ docker compose rm
docker compose up
を実行してアプリケーションを再度起動します。$ docker compose up --build
ブラウザーにおいて http://localhost:3000 の表示を更新します。 todo リスト内のアイテムが保持されていることを確認します。 コンテナーを削除した後の再起動であってもデータが保持されているはずです。
開発用コンテナーの設定と起動
バインドマウントを利用すれば、ソースコードをコンテナー内にマウントすることができます。 コードに対して変更を行って保存すれば、即座にコンテナー側でそれを利用することができます。 これはつまり、ファイルシステム内の変更を監視しそれに反応する処理を行う、nodemon のようなプロセスでも実行できるということです。 バインドマウントに関する詳細は ストレージ概要 を参照してください。
バインドマウントの追加に加えて、Dockerfile と compose.yaml
ファイルにおいて、開発のための依存パッケージのインストール設定を行い、また開発ツールを実行します。
開発向けの Dockerfile 修正
IDE またはテキストエディターを使って Dockerfile を開きます。 Dockerfile にはこの時点で開発用の依存パッケージをインストールするものでなく、nodemon も実行していません。 ここからは Dockerfile を修正して、開発用依存パッケージのインストールと nodemon の実行を行うようにします。
ここでは本番環境向けの Dockerfile、開発向けの Dockerfile をそれぞれ作成するのではなく、1 つの Dockerfile をマルチステージにより両方に利用するものとします。
Dockerfile を以下のように修正して、以下のようなマルチステージの Dockerfile とします。
# syntax=docker/dockerfile:1
ARG NODE_VERSION=18.0.0
FROM node:${NODE_VERSION}-alpine as base
WORKDIR /usr/src/app
EXPOSE 3000
FROM base as dev
RUN --mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=package-lock.json,target=package-lock.json \
--mount=type=cache,target=/root/.npm \
npm ci --include=dev
USER node
COPY . .
CMD npm run dev
FROM base as prod
RUN --mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=package-lock.json,target=package-lock.json \
--mount=type=cache,target=/root/.npm \
npm ci --omit=dev
USER node
COPY . .
CMD node src/index.js
この Dockerfile においては FROM node:${NODE_VERSION}-alpine
ステートメントに as base
としてラベルをつけています。
こうすることで、このビルドステージを別のビルドステージにて参照できるようになります。
そして新たなビルドステージに対しては dev
というラベルをつけて、開発向けの依存パッケージのインストールを行い、npm run dev
を使ってコンテナーを起動させます。
最終的に prod
というラベルのステージを作り出し、開発用依存パッケージは持たずに、node src/index.js
を使ってアプリケーションを起動します。
マルチビルドステージの詳細については
マルチステージビルド を参照してください。
次は Compose ファイルを修正して、新たなビルドステージを用いるようにします。
開発向けの Compose ファイル修正
dev
ステージを Compose から実行するには compose.yaml
ファイルの変更が必要です。
IDE またはテキストエディターを使って compose.yaml
ファイルを開きます。
そして target: dev
命令を加えることで、マルチステージ Dockerfile 内の dev
ステージを用いるように指定します。
また server サービスへのバインドマウントを行うためのボリュームを新たに追加します。
このアプリケーションでは、ローカルマシンの ./src
を、コンテナー内の /usr/src/app/src
にマウントします。
そしてデバッグ用にポート 9229
を開放します。
以下が修正を行った Compose jファイルです。 コメントはすべて解除されています。
services:
server:
build:
context: .
target: dev
ports:
- 3000:3000
- 9229:9229
environment:
NODE_ENV: production
POSTGRES_HOST: db
POSTGRES_USER: postgres
POSTGRES_PASSWORD_FILE: /run/secrets/db-password
POSTGRES_DB: example
depends_on:
db:
condition: service_healthy
secrets:
- db-password
volumes:
- ./src:/usr/src/app/src
db:
image: postgres
restart: always
user: postgres
secrets:
- db-password
volumes:
- db-data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=example
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
expose:
- 5432
healthcheck:
test: ["CMD", "pg_isready"]
interval: 10s
timeout: 5s
retries: 5
volumes:
db-data:
secrets:
db-password:
file: db/password.txt
開発用コンテナーの実行とアプリケーションのデバッグ
以下のコマンドを実行して、修正した Dockerfile
と compose.yaml
ファイルを使ったアプリケーションを実行します。
$ docker compose up --build
ブラウザーを開きます。 アプリケーションが起動していることを確認するため http://localhost:3000 にアクセスします。
ローカルマシン内にあるアプリケーションのソースファイルは、変更すると同時に実行コンテナー内にも即座に反映されることになります。
IDE またはテキストエディターを使って docker-nodejs-sample/src/static/js/app.js
を開きます。
そして 109 行めにあるボタンテキストを Add Item
から Add
に変更します。
+ {submitting ? 'Adding...' : 'Add'}
- {submitting ? 'Adding...' : 'Add Item'}
ブラウザーにおいて http://localhost:3000 の表示を更新します。 更新したボタンテキストが変更されていることを確認します。
アプリケーションに対してインスペクタークライアントを接続して、デバッグを行うことができます。 インスペクタークライアントの詳細については Node.js ドキュメント を参照してください。
まとめ
本節では Compose ファイルの設定を通じて、簡単なデータベースを追加しデータの保持を行う方法を見てきました。 またマルチステージ Dockerfile の生成方法と、開発向けのバインドマウントの設定方法について学びました。
関連情報
次のステップ
次の節では、Docker を用いたユニットテストの実行方法について学びます。