開発向けコンテナーの利用
読む時間の目安: 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 はイメージをコンパイルした上で起動を行います。
正常に処理されれば、以下のような出力が得られるはずです。
では API エンドポイントを確認します。 以下の curl コマンドを実行してみましょう。
$ curl --request GET --url http://localhost:8000/notes
以下のようなレスポンスが返ってくるはずです。
{"code":"success","meta":{"total":0,"count":0},"payload":[]}
デバッガーの接続
ここでは Chrome ブラウザーに付属するデバッガーを利用することにします。 マシン上の Chrome を起動して、アドレスバーに以下を入力します。
about:inspect
これによって以下の画面が表示されます。
Open dedicated DevTools for Node リンクをクリックします。 DevTools が開いて、コンテナー内部で稼働する Node.js プロセスに接続されます。
ソースコードに修正を加えて、ブレークポイント設定を行います。
すでにあるserver.use()
行の上に以下のコードを追加して、ファイルを保存します。
以下に示すように、return
文は単独の行であることに注目してください。
こうしておけばわかりやすくブレークポイントを設定することができます。
server.use( '/foo', (req, res) => {
return res.json({ "foo": "bar" })
})
Compose アプリケーションを実行したターミナルを見てみると、Nodemon が変更を検知してアプリケーションのリロードを行っている様子を見ることができます。
Chrome の DevTools 画面に戻り、return res.json({ "foo": "bar" })
がある行にブレークポイントを設定します。
そして以下の curl コマンドを実行してブレークポイントまで実行されるようにします。
$ curl --request GET --url http://localhost:8000/foo
コード実行がブレークポイント行において停止するので、ふだん利用している際と同じようにしてデバッガーを利用することができます。 変数内容の確認、条件つきブレークポイントの設定、スタックトレースの参照、などなどです。
次のステップ
本節では汎用的な開発イメージを生成しました。 このイメージは、ふだんのコマンドラインツールと何ら変わらずに実行できました。 また Compose ファイルを生成してソースコードを実行コンテナー内にマッピングし、デバッグ用のポートを公開しました。
次節では Docker においてユニットテストの実行方法を見ていきます。 以下を参照してください。
フィードバック
本トピック改善のためにフィードバックをお寄せください。 お気づきの点があれば Docker Docs の GitHub リポジトリに issue をあげてください。 あるいは PR の生成 により変更の提案を行ってください。