成果物
githubに作業したものをおいておきます。
https://github.com/togana/sample-express
ゴール
- Dockerfileを使い、親イメージからExpressアプリイメージを構築できる
- docker-composeを用いて複数コンテナの管理ができる
- Expressアプリが開発できる
ツールのバージョン
- docker-engine 1.11.2
- docker-machine 0.7.0
- docker-compose 1.7.1
- Virtualbox 5.1.2
Dockerホストの準備
実行環境を用意して環境変数までセットします。
$ docker-machine create -d virtualbox sample-express
$ docker-machine env sample-express
$ eval $(docker-machine env sample-express)
Expressアプリの作成
プロジェクトディレクトリを用意します。すでにあるアプリに適応する場合はDockerfileでwebサーバーコンテナを作成するからどうぞ。
$ mkdir sample-express && cd sample-express
Expressアプリを作成するにはexpress-generate
を用いて雛形を作成しなくてはなりません(ジェネレーター使うよね?使ってください)。
しかしローカルの環境にNode.jsを導入したくないのです(nodenvとかから開放されたい)。なので express .
すらDockerを利用します。dockerhubからNode.jsの公式イメージを取得して実行します。今回は6.3.1を使用しますが、他のバージョンが使用したい場合はTagsを参照してください。
イメージの選択にはDockerでnode.jsアプリを作るときにイメージ選択で悩んだ話でまとめましたので気になる方は参照ください。
$ docker pull node:6.3.1-slim
そしてpackage.json
を生成するために下記のコマンドを実行します。
$ docker run --rm -v "$PWD":/usr/src/sample-express -w /usr/src/sample-express node:6.3.1-slim npm init -y
オプションに関する補足
docker run オプション image名:タグ名 実行コマンド
--rm: 実行後のコンテナを削除します。指定しない場合はゴミが残り続けます。
-v: ホストのディレクトリをコンテナ内のディレクトリにマウントします。"$PWD"はカレントディレクトリを意味します。
-w: ワーキングディレクトリを指定します。
下記のように package.json
を編集します。
$ docker run --rm -v "$PWD":/usr/src/sample-express -w /usr/src/sample-express node:6.3.1-slim npm install -D express-generator
package.json
のscriptにexpressコマンドを使うために追加します。
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "express": "express"
},
そして、雛形を生成します。
$ docker run --rm -v "$PWD":/usr/src/sample-express -w /usr/src/sample-express node:6.3.1-slim yes | npm run express . -f
開発環境と本番環境のモジュールのバージョンを揃えるためにnpm-shrinkwrap.json
を作成します
先にこの先使わないexpress-generator
を削除します。
$ docker run --rm -v "$PWD":/usr/src/sample-express -w /usr/src/sample-express node:6.3.1-slim npm rm express-generator
--dev
をつけることでdevDependenciesのバージョンも固定できます。
$ docker run --rm -v "$PWD":/usr/src/sample-express -w /usr/src/sample-express node:6.3.1-slim npm shrinkwrap --dev
雛形ができバージョン固定もできたので.gitignore
を生成して置くと良いです。
下記のサイトでNodeを選択して生成したものを使用しています。(テンプレートを生成できるので迷ったら参考にするといいです)
Dockerfileでwebサーバーコンテナを作成する
とりあえずDockerfile
を作成します。
FROM node:6.3.1-slim
ENV APP_ROOT /usr/src/sample-express
WORKDIR $APP_ROOT
COPY package.json $APP_ROOT
COPY npm-shrinkwrap.json $APP_ROOT
RUN npm install && npm cache clean
COPY . $APP_ROOT
EXPOSE 3000
CMD ["npm", "start"]
ここで紹介できるテクニックはAPP_ROOTをコピーするより前にpackage.json
及びnpm-shrinkwrap.json
をコピーすることでこのファイルに変更がない限りnpm install
をキャッシュを使ってビルドされるところです。
ちなみに僕はここでビルドしてイメージを再作成しても良いんですが-vオプションつけたりだとか-pオプションつけたりだとかがどうせ発生するのでdocker-composeでビルドすることにしてます。
docker-compose.ymlによる管理
さくっとdocker-compose.yml
を作成します。
version: '2'
services:
app:
build: .
environment:
NODE_ENV: development
ports:
- '3000:3000'
volumes:
- .:/usr/src/sample-express
- /usr/src/sample-express/node_modules
NODE_ENVをdevelopmentにしているので環境ごとに変更してください。
開発時のファイル変更をすぐ反映できるようにvolumesに.:/usr/src/sample-express
を追加しています。
Node.jsのDockerでの開発での罠の対策としてvolumesに/usr/src/sample-express/node_modules
を追加しています。
罠の内容はコンテナの/usr/src/sample-express
をホストのアプリケーションディレクトリにマウント(.:/usr/src/sample-express)しているため、開発中にはまります(しかし無いとコードを変更するたびにビルドし直す必要があります)。node_modulesフォルダがホスト上に存在しないためコンテナにマウントされたタイミングでnode_modulesが消失します。ここまでの手順を上から順番に行って来た場合は問題ないのですがgit pullして開発を始める場合はアプリケーションディレクトリにnode_modulesが存在しないディレクトリをマウントしてしまうためイメージに焼き付けたnode_modulesが消失してしまうのです。そこでイメージのnode_modulesをアプリケーションディレクトリをマウントした後に更にマウントする形をとっています。
下記のコマンドでビルドして実行してみる。
$ docker-compose build
$ docker-compose up -d
ブラウザからアクセスできるか確認するためにipを確認する(雑なやり方)
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
sample-express * virtualbox Running tcp://192.168.99.100:2376 v1.11.2
ブラウザからアクセスしてみる。
下記の画面が出れば問題ないです!後はコード書いていきましょ!