要約
配布方法
Dockerfileをdocker-composeでビルドして, nginx由来とnode.js由来の2つのimageを作り, Docker Hubにプッシュする. docker-composeでビルドするときに, buildの中でcontextを使うと便利.
アプリケーションを作るdocker-compose.yamlの例
https://github.com/Toyoharu-Nishikawa/jsnote/tree/docker
git clone -b docker https://github.com/Toyoharu-Nishikawa/jsnote.git
docker-compose up -d
利用方法
利用者がアプリケーションを使うときは, docker-compose.yamlでDocker Hubに登録したimageをpullして使う.
アプリケーションを使うdocker-compose.yamlの例
https://github.com/Toyoharu-Nishikawa/jsnote/tree/app
git clone -b app https://github.com/Toyoharu-Nishikawa/jsnote.git
docker-compose up -d
背景
jsnoteというブラウザで動くJavaScriptのエディタを作っていた. 静的ファイルの配布はnginxで行い, データの保存はnode.jsで行うことにした.
docker-composeでnginxとnode.jsのイメージを立ち上げて, それぞれに必要なソースコードをvolumeでマウントすることでコンテナとファイルを共有し, コーディングをしていた.
Docker Hubにイメージを登録して, 誰でも簡単に使えるようにしようと思ったけれど, Docker Hubに置くimageをどう作成するべきかわからなかった.
jsnoteについてはこちらの記事
問題
下記のようなディレクトリ構成の下, public/でフロント側の開発を行い, src/でサーバ側の開発を行っていた場合, gitで管理したまま, public/とsrcを入れたdocker imageを如何に作成するか.
(例: jsnoteの階層の下に全ファイルをがあるとする.)
./jsnote
|-- docker-compose.yaml
|-- conf.d
| |-- server.conf
|
|-- public
| |-- index.html
| |-- scripts
| |-- styles
|
|-- src
| |-- index.js
| |-- node_modules
| |-- package.json
| |-- package-lock.json
開発中のdocker-compose.yamlの内容は下記.
version: '2'
services:
node:
image: node
restart: always
volumes:
- ./src:/usr/src
- ./public/sample:/usr/share/nginx/html/sample
- /var/log/jsnote:/var/log/node
working_dir: '/usr/src'
command: npm start
nginx:
image: nginx
restart: always
volumes:
- ./public:/usr/share/nginx/html
- ./conf.d:/etc/nginx/conf.d
- /var/log/jsnote:/var/log/nginx
ports:
- "2555:80"
試行
3つの方法を思いついた順に試行した.
方法1: nginxとnode.jsをひとつのdockerのimageに入れるDockerfileを作成, build.shでビルドする.
nginxもしくはnode.jsのイメージを基に, もう一つを入れる. 例えば, node.jsを基に作るとしたら次のようになる.
FROM node:alpine
以下nginx:alipineのDocker HubのDockerfileの中身をコピペ
.
.
.
RUN rm /etc/nginx/conf.d/*
ADD conf.d /etc/nginx/conf.d
ADD public /usr/share/nginx/html
ADD src/ /usr/src
CMD nginxとnode.js起動するコマンド
nginxとnode.jsの2つを起動するコマンドをどう書くべきかで悩んだ. Dockerの公式サイト Dockerのベストプラクティスを見て, 方法1を中止した.
コンテナ毎に1つのプロセスだけ実行
ほとんどの場合、1つのコンテナの中で1つのプロセスだけ実行すべきです。アプリケーションを複数のコンテナに分離することは、水平スケールやコンテナの再利用を簡単にします。サービスとサービスに依存関係がある場合は、 コンテナのリンク を使います。
方法2: nginxとnode.jsのimageからから別々のimageを作成するようにDockerfileを作成し, 双方をbuild.shでビルドする.
nginxとnode.jsを基にimageを作るDockerfileをそれぞれ用意する.
FROM nginx:alpine
RUN rm /etc/nginx/conf.d/*
ADD conf.d /etc/nginx/conf.d
ADD public /usr/share/nginx/html
build -t jsnote_web .
FROM node:alpine
ADD src/ /usr/src
WORKDIR /usr/src
ENTRYPOINT ["npm","start"]
build -t jsnote_api .
docker-composeでまとめているメリットを活かせないことに気づき, 方法2を中止した.
方法3: 方法2をdocker-composeでまとめてビルドする.
docker-composeの中でbuildを使うことで, (2)のDockerfileをまとめてbuildできる. しかし, Dockerfileと同じディレクトリにADDするファイルを置かないといけない....
⇒対処法参照
解決法
buildの中で, contextを指定することで, 親ディレクトリのファイルをDockerfileでADDできる. ことを利用すると, 開発中のdirectory階層のままでDockerfileをビルドすることができる.
step1: webとapiのディレクトリを追加し, その中にDockerfileを置く.
./jsnote
|-- docker-compose.yaml
|-- web ← 追加
| |-- Dockerfile ← 追加
|
|-- api ← 追加
| |-- Dockerfile ← 追加
|
|-- conf.d
| |-- server.conf
|
|-- public
| |-- index.html
| |-- scripts
| |-- styles
|
|-- src
| |-- index.js
| |-- node_modules
| |-- package.json
| |-- package-lock.json
step2: docker-compose.yamlを下記のようにする.
version: '2'
services:
api:
build:
context: ./
dockerfile: ./api/Dockerfile
web:
build:
context: ./
dockerfile: ./web/Dockerfile
ports:
- "2555:80"
step3: docker-composeでビルドする.
docker-compose build
これでdockerのimageが以下のように2つできる.
- jsnote_web
- jsnote_api
Docker Hubにpush
docker tagでimage名をDocker Hubに書き換えて, pushする.
step1: docker login
予めDocker Hubに作って置いたアカウントにログインする.
docker login
step2: docker tag
docker tagでDocker Hubに登録するimageの名前に変更する. この時, ユーザ名/を頭に付ける必要がある.
docker tag jsnote_web ユーザ名/jsnote_web:latest
docker tag jsnote_api ユーザ名/jsnote_api:latest
step3: docker push
docker push ユーザ名/jsnote_web:latest
docker push ユーザ名/jsnote_api:latest
以上で, Docker Hubでimageが公開され, 配布できるようになる.
以降は, 誰でもdocker pullでimageをダウンロードできる.
自分で試す場合は, docker rmiでimageを削除してから, pullできるか試す必要がある.
docker rmi ユーザ名/jsnote_web:latest
docker rmi ユーザ名/jsnote_api:latest
docker pull ユーザ名/jsnote_web_web:latest
docker pull ユーザ名/jsnote_api:latest
最後に(アプリケーションの使い方)
nginx+node.jsのアプリケーションとして使うには, docker-composeを使って, 2つをリンクさせる必要がある.
./jsnote
|-- docker-compose.yaml
version: '2'
services:
api:
image: ユーザ名/jsnote_api
web:
image: ユーザ名/jsnote_web
ports:
- "80:80"
(注意)
本例のdocker-compose.yamlの場合, nginxのリバースプロキシproxy_pass(ロードバランサ―の設定はupstream)はserverとして, jsnote_api_1もしくは単にapiを参照しなくてはならない. nginxのリバースプロキシの設定はnginxの.confに書かれている. 上記 解決法 で作成したnginxのimageの中に適切なリバースプロキシの設定が書かれていない場合, 作者がconf.d/default.confの中の設定を書き直し, もう一度imageを作り直してDocker Hubにアップロードするか, アプリケーションの利用者がdocker-composeの中でvolumesで設定を上書きする必要がある.