開発向けコンテナーの利用

読む時間の目安: 4 分

前提条件

コンテナーとしてのイメージ実行 において、イメージビルドを行いコンテナー化アプリケーションを実行していること。

はじめに

本節では前節においてビルドしたアプリケーション用に、ローカルの開発環境を構築していきます。 イメージのビルドには Docker を利用し、全体を取り扱いやすくするために Docker Compose を利用していきます。

ローカルデータベースとコンテナー

まずはコンテナー内でデータベースを動作させます。 ボリュームやネットワークを使ってデータの保存を行い、アプリケーションとデータベースのやりとりを実現します。 そしてこのすべてをとりまとめて Compose ファイルに収めます。 こうすることで、たった 1 つのコマンド実行によってローカル開発環境を構築して実行できるようにします。 最後に、コンテナー内で動作しているアプリケーションに対してデバッガー接続を行ってみます。

MongoDB をダウンロード、インストールして、Mongo データベースをサービスとして起動するようなことはしません。 そうではなく Docker の公式イメージの中から MongoDB 用のイメージを利用し、コンテナーとしてこれを実行します。

MongoDB をコンテナーとして実行するにあたって、いくつかボリュームを生成して Docker がデータや設定の保存ができるようにします。 バインドマウントは利用せず、管理されたボリューム機能を利用します。 詳しくは ボリュームの利用 を参照してください。

それではここでボリュームを生成します。 作り出すボリュームは 1 つは MongoDB のデータ用、そしてもう 1 つは MongoDB の設定用です。

$ docker volume create mongodb
$ docker volume create mongodb_config

またここでネットワークを生成して、アプリケーションとデータベースが互いにやりとりできるようにします。 ネットワークはユーザー定義によるブリッジネットワークであり、DNS 検索サービスが提供されるため、接続文字列を使った設定が利用可能になります。

$ docker network create mongodb

コンテナーとして MongoDB を実行します。 そして上で生成したボリュームとネットワークをこれに結びつけます。 Docker はイメージを Docker Hub からプルして、ローカル環境において実行します。

$ docker run -it --rm -d -v mongodb:/data/db \
  -v mongodb_config:/data/configdb -p 27017:27017 \
  --network mongodb \
  --name mongodb \
  mongo

MongoDB が実行できました。 そこでserver.jsを更新して、メモリ上へのデータ保存ではなく MongoDB を利用するように書き換えます。

const ronin     = require( 'ronin-server' )
const mocks     = require( 'ronin-mocks' )
const database  = require( 'ronin-database' )
const server = ronin.server()

database.connect( process.env.CONNECTIONSTRING )
server.use( '/', mocks.server( server.Router(), false, false ) )
server.start()

ここではronin-databaseモジュールを追加して、データベースへの接続を行うようにコード変更を行いました。 メモリ保存フラグは false に設定しています。 変更を行ったのでイメージを再ビルドする必要があります。

まずは npm を使ってアプリケーションにronin-databaseモジュールを追加します。

$ npm install ronin-database

そしてイメージをビルドします。

$ docker build --tag node-docker .

ではコンテナーを実行してみます。 ただし今回の実行では環境変数CONNECTIONSTRINGの設定が必要です。 つまりデータベースにアクセスする際の接続文字列をアプリケーションに伝える必要があります。 これをdocker runコマンドにおいて実現します。

$ docker run \
  -it --rm -d \
  --network mongodb \
  --name rest-server \
  -p 8000:8000 \
  -e CONNECTIONSTRING=mongodb://mongodb:27017/notes \
  node-docker

接続文字列の最後にあるnotesは、ここでのデータベースに必要となる名前です。

アプリケーションがデータベースに接続されていて、メモ書き(note)が追加できることを確認します。

$ curl --request POST \
  --url http://localhost:8000/notes \
  --header 'content-type: application/json' \
  --data '{"name": "this is a note", "text": "this is a note that I wanted to take while I was working on writing a blog post.", "owner": "peter"}'

サービスからは以下のような JSON データが返ってくるはずです。

{"code":"success","payload":{"_id":"5efd0a1552cd422b59d4f994","name":"this is a note","text":"this is a note that I wanted to take while I was working on writing a blog post.","owner":"peter","createDate":"2020-07-01T22:11:33.256Z"}}

Compose を使ったローカル開発環境

ここでは Compose ファイルを生成して、1 つのコマンド実行から node-docker と MongoDB を起動できるようにします。 なお note-docker はデバッグモードで起動するような Compose ファイルとして、稼働するノードプロセスに対してデバッガーを接続できるようにしておきます。

IDE あるいはテキストエディターにおいて notes-service を開いて、docker-compose.dev.ymlというファイルを新規生成します。 そのファイルに以下の記述をコピーおよび貼りつけます。

version: '3.8'

services:
 notes:
  build:
   context: .
  ports:
   - 8000:8000
   - 9229:9229
  environment:
   - SERVER_PORT=8000
   - CONNECTIONSTRING=mongodb://mongo:27017/notes
  volumes:
   - ./:/app
  command: npm run debug

 mongo:
  image: mongo:4.2.8
  ports:
   - 27017:27017
  volumes:
   - mongodb:/data/db
   - mongodb_config:/data/configdb
volumes:
 mongodb:
 mongodb_config:

この Compose ファイルは非常に便利なものです。 docker runコマンドの実行にあたって、パラメーターすべてを記述する必要はありません。 それは Compose ファイル内に宣言という形で行います。

ここではport 9229によってポート公開を行います。 デバッガーを接続できるようにするためです。 またローカルにあるそーそコードを実行中のコンテナー内にマッピングして、テキストエディターでのソース変更がコンテナー内からも取得可能なようにします。

Compose ファイルを利用する理由となるもう一つ優れているのは、サービス名を使ってサービス設定の解決を行うことができる点です。 だから接続文字列の中で“mongo”という名称を用いることができるのです。 mongo という名称をなぜ用いるかと言えば、Compose ファイル内にて MongoDB サービスに対してそのような名前づけを行ったからです。

アプリケーションをデバッグモードで起動するにはpackage.jsonファイルに記述追加を行う必要があります。 npm に対してアプリケーションをどのように起動するかを指示するためです。

package.jsonファイルを開いて、scripts セクションに以下の行を追加します。

  "debug": "nodemon --inspect=0.0.0.0:9229 server.js"

上からわかるように、ここでは Nodemon を利用します。 Nodemon はサーバーをデバッグモードで起動した上で、変更されているファイルを確認しサーバーを再起動します。 ではターミナルから以下のコマンドを実行して Nodemon をプロジェクトディレクトリ内にインストールします。

$ npm install nodemon

アプリケーションを起動して、正常に動作していることを確認します。

$ docker-compose -f docker-compose.dev.yml up --build

ここでは--buildフラグを指定しました。 これによって Docker はイメージをコンパイルした上で起動を行います。

正常に処理されれば、以下のような出力が得られるはずです。

node-compile

では API エンドポイントを確認します。 以下の curl コマンドを実行してみましょう。

$ curl --request GET --url http://localhost:8000/notes

以下のようなレスポンスが返ってくるはずです。

{"code":"success","meta":{"total":0,"count":0},"payload":[]}

デバッガーの接続

ここでは Chrome ブラウザーに付属するデバッガーを利用することにします。 マシン上の Chrome を起動して、アドレスバーに以下を入力します。

about:inspect

これによって以下の画面が表示されます。

Chrome-inspect

Open dedicated DevTools for Node リンクをクリックします。 DevTools が開いて、コンテナー内部で稼働する Node.js プロセスに接続されます。

ソースコードに修正を加えて、ブレークポイント設定を行います。

すでにあるserver.use()行の上に以下のコードを追加して、ファイルを保存します。 以下に示すように、return文は単独の行であることに注目してください。 こうしておけばわかりやすくブレークポイントを設定することができます。

 server.use( '/foo', (req, res) => {
   return res.json({ "foo": "bar" })
 })

Compose アプリケーションを実行したターミナルを見てみると、Nodemon が変更を検知してアプリケーションのリロードを行っている様子を見ることができます。

nodemon

Chrome の DevTools 画面に戻り、return res.json({ "foo": "bar" })がある行にブレークポイントを設定します。 そして以下の curl コマンドを実行してブレークポイントまで実行されるようにします。

$ curl --request GET --url http://localhost:8000/foo

コード実行がブレークポイント行において停止するので、ふだん利用している際と同じようにしてデバッガーを利用することができます。 変数内容の確認、条件つきブレークポイントの設定、スタックトレースの参照、などなどです。

次のステップ

本節では汎用的な開発イメージを生成しました。 このイメージは、ふだんのコマンドラインツールと何ら変わらずに実行できました。 また Compose ファイルを生成してソースコードを実行コンテナー内にマッピングし、デバッグ用のポートを公開しました。

次節では Docker においてユニットテストの実行方法を見ていきます。 以下を参照してください。

テストの実行

フィードバック

本トピック改善のためにフィードバックをお寄せください。 お気づきの点があれば Docker Docs の GitHub リポジトリに issue をあげてください。 あるいは PR の生成 により変更の提案を行ってください。


get started, NodeJS, local, development