アプリのコンテナ化
Part 1ではDockerについて簡単な説明と環境構築を行った.
Part 2では実際にアプリをコンテナ化してみる.
Get Started, Part 2: Containerizing an Application
https://docs.docker.com/get-started/part2/
もくじ
- Prerequisites
- Introduction
- Setting Up
- Build and Test Your Image
- Conclusion
Prerequisites
https://docs.docker.com/get-started/part2/#prerequisites
- このパートに入る前に, Part 1の内容を完了していること.
Introduction
https://docs.docker.com/get-started/part2/#introductionDocker Desktop
のおかげでコンテナオーケストレーターが既にセットアップされているので, コンテナアプリ開発の準備はできている.
コンテナアプリ開発は一般的に以下のフローに沿って行う.
- 作成したいアプリの各コンポーネントについて
image
を作成し, それを元にコンテナの作成とテストを行う. - コンテナとインフラの設定を組み合わせたアプリを
Docker Stack
ファイルまたはKubernetes
のYAMLファイルで定義する. - コンテナ化したアプリをテスト, 共有, デプロイする.
このパートでは上記フローの1つめに着目し, コンテナの元になるimage
の作成を行う.
Part 1で学んだようにimage
はコンテナ化されたプロセスを動かすためのプライベートなファイルシステムを提供するものであるから, 開発者はアプリケーションの動作に必要なものだけを内包するimage
を作成すれば良い.
コンテナ化された開発環境ではアプリの依存関係をすべてimage
に閉じ込めてしまうので, 開発マシンにDocker
以外のものをインストールする必要がない.
したがって, 同じマシン上で複数のアプリを開発する際に異なるアプリ間での依存関係の競合がなくなる.
そのため, image
の作成方法さえ取得してしまえばコンテナ化された開発環境は一般的な開発環境よりも構築が容易で使いやすい.
Setting Up
https://docs.docker.com/get-started/part2/#setting-up
1: GitHubからサンプルプロジェクトをcloneしてくる.
# リポジトリをclone
$ git clone -b v1 https://github.com/docker-training/node-bulletin-board
Cloning into 'node-bulletin-board'...
remote: Enumerating objects: 190, done.
remote: Counting objects: 100% (190/190), done.
remote: Compressing objects: 100% (132/132), done.
remote: Total 190 (delta 79), reused 158 (delta 55), pack-reused 0
Receiving objects: 100% (190/190), 194.66 KiB | 432.00 KiB/s, done.
Resolving deltas: 100% (79/79), done.
# cloneしてきたリポジトリのbulletin-board-appディレクトリに移動
$ cd node-bulletin-board/bulletin-board-app
# ファイルを確認
$ ls
Dockerfile app.js fonts package.json server.js
LICENSE backend index.html readme.md site.css
これはNode.jsで書かれた簡単な掲示板アプリケーションで, 今回はこのアプリをコンテナ化していく.
2: Dockerfile
というファイルを見てみる.Dockerfile
にはコンテナで使用するプライベートファイルシステム(image
)を構築するための手順と, このimage
を元にコンテナを起動する際の情報(メタデータ)が書かれている.
今回使用する掲示板アプリのDockerfile
は以下の内容になっている.
# node:6.11.5をベースimageとして使用
FROM node:6.11.5
# image内での作業ディレクトリを/usr/src/appに変更
WORKDIR /usr/src/app
# 開発マシンのpackage.jsonをimageの作業ディレクトリにコピー
COPY package.json .
# npm(Node.jsのパッケージマネージャ)によりアプリの依存パッケージをインストール
RUN npm install
# 開発マシンの現在のディレクトリの中身をimageの作業ディレクトリにコピー
COPY . .
# コンテナが起動した際に`npm start`コマンドを実行
CMD [ "npm", "start" ]
Dockerfile
を書くことはコンテナアプリ開発の第一歩である.Dockerfile
に記載された各行のコマンドは独自のimage
を作成するための操作を定義し, このDockerfile
の場合は次の内容を定義している:
FROM
で既に存在するimage
(node:6.11.5)をベースとして指定する.
node:6.11.5はNode.jsのベンダーによってビルド,Docker
チームによって検証された高品質なofficial image
であり, node 6.11.5のインタプリタと基本的な依存パッケージを内包している.WORKDIR
でimage
内の作業ディレクトリを**/usr/src/app**に指定する.
以降のコマンドは全てこのディレクトリで行われる. このディレクトリは開発マシンのディレクトリとは無関係である.
[追加]指定したディレクトリが存在しない場合は自動で生成される.COPY
で開発マシンのpackage.jsonをimage
の現在のディレクトリにコピーする.
今回の場合は**/usr/src/app/package.json**に配置される.RUN
でnpm install
コマンドをimage
内で実行する.
このコマンドは現在のディレクトリにあるpackage.json(依存関係が定義されたファイル)を読み込み, アプリの依存パッケージをインストールする.COPY
で開発マシンの残りのソースコードをimage
の現在のディレクトリにコピーする.
以上のコマンドは一般的な開発環境でNode.jsのアプリをインストールする際とほぼ同じである.
しかしDockerfile
でこれらを定義することで, 開発マシンの環境には一切影響を与えずに必要な操作を全てimage
に閉じ込めることができる.
[追加]この開発マシンにNode.jsがなくてもimage
さえあればアプリが動く. すごい.
上記手順はファイルシステムの構築方法を定義しているが, このDockerfile
にはもう1行コマンドが存在する.CMD
はこのimage
を元にコンテナが起動する際のメタデータを定義する.
この例では, このimage
を使用して実行するプロセスがnpm start
コマンドであることを示している.
[追加]npm start
はアプリを実行するコマンド.
上記はFROM
でベースとなるimage
を指定し, その後にファイルシステムの構築に必要なコマンドを1行ずつ記述, 最後にメタデータを定義するというシンプルなDockerfile
の良いお手本である.
また, この例で示したのはDockerfile
で使用できるコマンドのほんの一部であり, その他のコマンドについてはDockerfile referenceで説明されている.
Build and Test Your Image
https://docs.docker.com/get-started/part2/#build-and-test-your-image
ソースコードとDockerfile
が用意できたので, いよいよimage
をビルドして, それを元にコンテナが想定通り動作するか確認する.
1: node-bulletin-board/bulletin-board-app ディレクトリにいることを確認し, 掲示板アプリのimage
をビルドする.
# 現在のディレクトリを確認
$ pwd
/path/to/node-bulletin-board/bulletin-board-app
# 現在のディレクトリ(.)にあるDockerfileを使用してimage(bulletinboard:1.0)をビルド
$ docker image build -t bulletinboard:1.0 .
ビルド時のログを見るとDockerfile
に定義された操作を1行ずつ読み込んで実行し,
作成したimage
にタグ(bulletinboard:1.0)をつけていることがわかる.
[追加]ビルド時に-t
オプションでリポジトリ名:タグを指定できる.image
は基本的にリポジトリ名とタグで管理され, リポジトリ名はバージョンに依存しないそのimage
の名前, タグはバージョンなどの情報を表す.
(例: node:6.11.5の場合はnodeがリポジトリ名, 6.11.5がタグ)
ビルド時のログ(クリックで展開)
$ docker image build -t bulletinboard:1.0 .
Sending build context to Docker daemon 45.57kB
Step 1/6 : FROM node:6.11.5
6.11.5: Pulling from library/node
85b1f47fba49: Pull complete
ba6bd283713a: Pull complete
817c8cd48a09: Pull complete
47cc0ed96dc3: Pull complete
8888adcbd08b: Pull complete
6f2de60646b9: Pull complete
1666693bf996: Pull complete
2fe410df7942: Pull complete
Digest: sha256:fe109b92edafd9821fbc1c80fd7587a1b4e1ff76fec3af675869e23e50bbf45b
Status: Downloaded newer image for node:6.11.5
---> 852391892b9f
Step 2/6 : WORKDIR /usr/src/app
---> Running in 7d1414939508
Removing intermediate container 7d1414939508
---> 5d0751457bd4
Step 3/6 : COPY package.json .
---> 50081a240492
Step 4/6 : RUN npm install
---> Running in 3c9f159d46c7
vue-event-bulletin@1.0.0 /usr/src/app
+-- body-parser@1.19.0
| +-- bytes@3.1.0
| +-- content-type@1.0.4
| +-- debug@2.6.9
| | `-- ms@2.0.0
| +-- depd@1.1.2
| +-- http-errors@1.7.2
| | +-- inherits@2.0.3
| | `-- toidentifier@1.0.0
| +-- iconv-lite@0.4.24
| | `-- safer-buffer@2.1.2
| +-- on-finished@2.3.0
| | `-- ee-first@1.1.1
| +-- qs@6.7.0
| +-- raw-body@2.4.0
| | `-- unpipe@1.0.0
| `-- type-is@1.6.18
| +-- media-typer@0.3.0
| `-- mime-types@2.1.24
| `-- mime-db@1.40.0
+-- bootstrap@3.4.1
+-- ejs@2.7.1
+-- errorhandler@1.5.1
| +-- accepts@1.3.7
| | `-- negotiator@0.6.2
| `-- escape-html@1.0.3
+-- express@4.17.1
| +-- array-flatten@1.1.1
| +-- content-disposition@0.5.3
| +-- cookie@0.4.0
| +-- cookie-signature@1.0.6
| +-- encodeurl@1.0.2
| +-- etag@1.8.1
| +-- finalhandler@1.1.2
| +-- fresh@0.5.2
| +-- merge-descriptors@1.0.1
| +-- methods@1.1.2
| +-- parseurl@1.3.3
| +-- path-to-regexp@0.1.7
| +-- proxy-addr@2.0.5
| | +-- forwarded@0.1.2
| | `-- ipaddr.js@1.9.0
| +-- range-parser@1.2.1
| +-- safe-buffer@5.1.2
| +-- send@0.17.1
| | +-- destroy@1.0.4
| | +-- mime@1.6.0
| | `-- ms@2.1.1
| +-- serve-static@1.14.1
| +-- setprototypeof@1.1.1
| +-- statuses@1.5.0
| +-- utils-merge@1.0.1
| `-- vary@1.1.2
+-- method-override@2.3.10
+-- morgan@1.9.1
| +-- basic-auth@2.0.1
| `-- on-headers@1.0.2
+-- vue@1.0.28
| `-- envify@3.4.1
| +-- jstransform@11.0.3
| | +-- base62@1.2.8
| | +-- commoner@0.10.8
| | | +-- commander@2.20.3
| | | +-- detective@4.7.1
| | | | +-- acorn@5.7.3
| | | | `-- defined@1.0.0
| | | +-- glob@5.0.15
| | | | +-- inflight@1.0.6
| | | | | `-- wrappy@1.0.2
| | | | +-- minimatch@3.0.4
| | | | | `-- brace-expansion@1.1.11
| | | | | +-- balanced-match@1.0.0
| | | | | `-- concat-map@0.0.1
| | | | +-- once@1.4.0
| | | | `-- path-is-absolute@1.0.1
| | | +-- graceful-fs@4.2.2
| | | +-- mkdirp@0.5.1
| | | | `-- minimist@0.0.8
| | | +-- private@0.1.8
| | | +-- q@1.5.1
| | | `-- recast@0.11.23
| | | +-- ast-types@0.9.6
| | | +-- esprima@3.1.3
| | | `-- source-map@0.5.7
| | +-- esprima-fb@15001.1.0-dev-harmony-fb
| | +-- object-assign@2.1.1
| | `-- source-map@0.4.4
| | `-- amdefine@1.0.1
| `-- through@2.3.8
`-- vue-resource@0.1.17
npm WARN vue-event-bulletin@1.0.0 No repository field.
Removing intermediate container 3c9f159d46c7
---> 64d35ff348c3
Step 5/6 : COPY . .
---> c8fa377b131a
Step 6/6 : CMD [ "npm", "start" ]
---> Running in f0aeac57fc19
Removing intermediate container f0aeac57fc19
---> 06f7fe6f1ca0
Successfully built 06f7fe6f1ca0
Successfully tagged bulletinboard:1.0
[追加]ホスト(今回は開発マシン)にあるimage
の一覧はdocker image ls
で確認できる.
今回の場合はbulletinboard:1.0とそのベースに使用したnode:6.11.5が存在していることがわかる.
# imageの一覧を表示
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
bulletinboard 1.0 06f7fe6f1ca0 51 minutes ago 681MB
node 6.11.5 852391892b9f 23 months ago 662MB
2: 作成したimage
を使用してコンテナを起動する.
# ホストの8000番ポートをコンテナ(bb)の8000番ポートに割り当て,
# image(bulletinboard:1.0)を用いてコンテナ(bb)をデタッチドモードで起動
$ docker container run --publish 8000:8080 --detach --name bb bulletinboard:1.0
bee46e68b8cf8db5c946060f887a9a4f5bcca18e6ac6958c76d35f34d3f330b5
ここでは複数のフラグ(オプション)を組み合わせて使用している:
--publish
でホスト(この場合は開発マシン)の8000番ポートとコンテナの8000番ポートを疎通させるよう指定する.
コンテナにはそれぞれ独自のポートが複数あり, ネットワークからコンテナにアクセスするためには使用するポートをこのように指定する必要がある.
コンテナの指定していないポートを使う通信はデフォルトで設定されているファイアウォールによりすべて遮断される.
[追加]-p
オプションでも同じことができる.--detach
でこのコンテナをバックグラウンドで起動するよう指定する.
[追加]-d
オプションでも同じ. この起動方法をデタッチドモード
という.--name
でこのコンテナに名前(bb)をつける. この名前は以降のコマンドでこのコンテナを指定するときに使用できる.
また, 上記のコマンドでは実行した際にコンテナでどのプロセスを実行するかの指定がされていない.
これはDockerfile
のCMD
でコンテナ起動時に自動実行するプロセス(npm start
)が指定されていて, 改めて指定する必要が無いためである.
[追加]起動中のコンテナの一覧はdocker container ls
で確認できる.
# 起動しているコンテナ一覧を表示
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cc1a42172625 bulletinboard:1.0 "npm start" 5 seconds ago Up 4 seconds 0.0.0.0:8000->8080/tcp bb
3: http://localhost:8000 をブラウザで開き, 掲示板アプリが実際に起動していることを確認する.
ここまで来ればアプリの動作検証に必要な任意の作業(例: 単体テストの実行)を行うことができる.
4: 掲示板コンテナが問題なく動作することを確認したら, このコンテナを削除する.
# コンテナ(bb)を強制的に削除
$ docker container rm --force bb
bb
[追加]--force
を指定するとコンテナがプロセスを実行中でも強制的にコンテナを削除することができる.
また, コンテナを削除せず停止だけ行いたい場合はdocker container stop
を使用する. また, docker container start
で停止しているコンテナを起動できる.
# コンテナ(bb)を停止
$ docker container stop bb
bb
# 停止しているものも含め全てのコンテナを表示
$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cc1a42172625 bulletinboard:1.0 "npm start" 2 minutes ago Exited (0) 32 seconds ago bb
# 停止しているコンテナ(bb)を起動
$ docker container start bb
bb
# 起動しているコンテナ一覧を表示
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cc1a42172625 bulletinboard:1.0 "npm start" 6 minutes ago Up 12 seconds 0.0.0.0:8000->8080/tcp bb
Conclusion
https://docs.docker.com/get-started/part2/#conclusion
このパートでは簡単なアプリケーションのコンテナ化を行い, コンテナ化したアプリが正常に動作することを確認した.
次はKubernetes
形式のYAMLファイルでコンテナの起動/管理方法を定義してKubernetes
上で動作させるか(Part 3で説明), またはStack
ファイルを記述してこのパートと同じ内容をSwarm
上で行う(Part 4で説明).