この記事はiRidge Advent Calendar 2018の22日目の記事です。
はじめに
2018年8月に転職し、初めてDockerなるものを触ることになりました。
https://docker-curriculum.com/ に沿ってキャッチアップし、
その中で学んだ知識を整理しましたので共有したいと思います。私と同じようにDockerに入門する方の役に立てば幸いです。
用語
- イメージ - コンテナの基礎をなすアプリケーションの設計図。
- コンテナ - イメージから作成され、実際にアプリケーションが稼働する。
- Dockerデーモン - Dockerコンテナの構築、実行、及び配布を管理する、ホスト上で実行されるバックグラウンドサービス。デーモンはクライアントと通信するOSで実行されるプロセス。
- Dockerクライアント - ユーザがデーモンと対話できるようにするCLIツール。
- DockerHub - Dockerイメージのレジストリ。必要に応じて独自のDockerレジストリをホストし、イメージを取得するために使用できる。
コマンド
-
docker pull
コマンドでbusybox
という名称のimageをDockerレジストリから取得する。
$ docker pull busybox
-
docker image
コマンドで、取得したイメージをリスト表示する。 -
TAG
はイメージの特定のスナップショットを参照していて、IMAGE ID
はそのイメージのユニークな識別子となっている。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx alpine d3dcc25e0dc4 12 days ago 17.8MB
busybox latest 59788edf1f3e 6 weeks ago 1.15MB
hello-world latest 4ab4c602aa5e 2 months ago 1.84kB
ubuntu latest 735f80812f90 3 months ago 83.5MB
nginx latest c82521676580 3 months ago 109MB
python 2.7.14-slim a7804e68bf72 6 months ago 139MB
-
ubuntu
の特定バージョンをpullしたい場合は、以下のようにする。
$ docker pull ubuntu:12.04
12.04: Pulling from library/ubuntu
d8868e50ac4c: Pull complete
83251ac64627: Pull complete
589bba2f1b36: Pull complete
d62ecaceda39: Pull complete
6d93b41cfc6b: Pull complete
Digest: sha256:18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005
Status: Downloaded newer image for ubuntu:12.04
$ docker images ubuntu
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 735f80812f90 3 months ago 83.5MB
ubuntu 12.04 5b117edd0b76 19 months ago 104MB
-
docker run
コマンドでbusybox
イメージのDockerコンテナを起動する。
$ docker run busybox
$
このとき、Dockerクライアントはイメージを探し、コンテナ内でコマンドを実行する。
でも、上のコマンドでは実行するコマンドを与えていないので、コンテナが立ち上がったあと何もせずに終了した。
以下のコマンドは上と違って、echo
コマンドが実行された後に終了した。
$ docker run busybox echo "hello from busybox"
hello from busybox
$
-
docker ps
コマンドは現在起動中の全てのコンテナを表示する。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
-
-a
オプションを付けると実行済みのコンテナも含め全てを表示する。
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
43daaba225dd busybox "echo 'hello from bu…" 5 minutes ago Exited (0) 5 minutes ago quizzical_ptolemy
04c83ee35767 busybox "sh" 30 minutes ago Exited (0) 30 minutes ago clever_swirles
7952dfa41dc4 hello-world "/hello" 33 minutes ago Exited (0) 33 minutes ago reverent_euler
- コンテナ内で複数コマンドを実行したい場合は
-it
オプションを使用する。インタラクティブttyをコンテナ内でアタッチする。
$ docker run -it busybox sh
/ # ls
bin dev etc home proc root sys tmp usr var
/ # uptime
03:19:03 up 9:28, 0 users, load average: 0.02, 0.06, 0.02
/ #
-
docker run --help
で、全フラグの一覧を見ることが出来る。 -
docker run
を何度も実行してそのままにしておくと、ディスク領域を食いつぶしてしまうので、docker rm
コマンドを実行すればコンテナを削除できる。
$ docker rm b99636fc4c19
b99636fc4c19
- 終了済みのコンテナをいっぺんに削除したいなら、
docker rm $(docker ps -a -q -f status=exited)
とする。-
-q
はIDだけを返却し、-f
は指定した状態のみでフィルタリングしている。
-
$ docker rm $(docker ps -a -q -f status=exited)
f28e03403d45
2f12ba39987b
e942ae377ecf
43daaba225dd
04c83ee35767
34efcecec10d
7952dfa41dc4
8a911e50170a
14656bae8192
020dff00d96e
- 実は
docker container prune
コマンドで同様の結果が得られる。
$ docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
bb45579d3020722abc88fd570400c10641e3db0a51cad0a6afc34421af67963e
41c78c7ee3fcc88ec1cff64ca69e827b36fdeb433cc7832dea518dde5e8593d3
Total reclaimed space: 0B
-
また、
docker run
コマンドに--rm
フラグを付与して起動すると、終了時にコンテナが削除される。 -
もう必要ないimageは
docker rmi
で削除できる。
$ docker rmi hello-world
Untagged: hello-world:latest
Untagged: hello-world@sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788
Deleted: sha256:4ab4c602aa5eed5528a6620ff18a1dc4faef0e1ab3a5eddeddb410714478c67f
Deleted: sha256:428c97da766c4c13b19088a471de6b622b038f3ae8efa10ec5a37d6d31a2df0b
Webアプリケーションをデプロイしてみる
このデモ用のアプリケーションがレジストリにホストされているのでそれをダウンロードしてdocker run
してみる。
$ docker run --rm prakhar1989/static-site
イメージがローカルになければ、クライアントはまずレジストリからイメージをフェッチしてきて、それから起動する。
うまく行けばNginx is running...
とターミナルに表示される。
ただ、この場合、どのポートも公開されていないので、ポートを公開するために以下の通りdocker run
コマンドを再実行する必要がある。
$ docker run -d -P --name static-site prakhar1989/static-site
721f9bed9efbe569e17361fa327d2719ef01246de8f8894850896ff13ace34ff
-
-d
:ターミナルのデタッチ -
-P
はランダムなポートを公開し -
--name
は起動するコンテナに名前を付ける。 -
docker port [CONTAINER]
コマンドで、ポートを確認することが出来る。
$ docker port static-site
443/tcp -> 0.0.0.0:32768
80/tcp -> 0.0.0.0:32769
-
-p 8888:80
のようにすることで、クライアントが接続を転送するカスタムポートを指定することも出来る。
$ docker run -p 8888:80 prakhar1989/static-site
Nginx is running...
-
docker stop
コマンドにコンテナIDを与えて実行すると、デタッチしたコンテナの停止が出来る。このとき、起動時に指定した名前を使うことも出来る。
$ docker stop static-site
static-site
これを実サーバにデプロイするなら、単にDockerをインストールし、その上でDockerコマンドを実行すればよい。
Dockerイメージの作成
新しいDockerイメージを取得したい場合、DockerHubのようなレジストリから取得することも、自分で作成することも出来る。
DockerHub上には多数のイメージがある。また、イメージはCLIからdocker search
で検索することも出来る。
-
baseイメージとchildイメージの違い
- baseイメージは親イメージを持たないもので、ubuntu、busybox、debianなど。
- childイメージはbaseイメージを元に作成され、機能が追加されたもの。
-
officialイメージとuserイメージの違い
- officialイメージは公式にメンテとサポートされているもの。
- 通常は一語で、
python
、ubuntu
、busybox
、hello-world
などがそれにあたる。
- 通常は一語で、
- userイメージは一般ユーザにより作成されシェアされているもの。
- baseイメージに機能を追加している。
- 通常、
user/image-name
というフォーマットである。
- officialイメージは公式にメンテとサポートされているもの。
よりよく理解するために、簡単なFlaskアプリケーションのイメージを作成してみる。
ロード時に毎回ランダムな猫のgifをディスプレイに表示するFlaskアプリケーションが作成されているので、ローカルにリポジトリをクローンする。
$ git clone https://github.com/prakhar1989/docker-curriculum
$ cd docker-curriculum/flask-app
# この手順はローカルで実施すること。dockerコンテナ内ではない
そして、今クローンしたWebアプリケーションのイメージを作成するために、Dockerfileを作成する。
Dockerfileとは、シンプルなテキストファイルで、Dockerクライアントがイメージを作成する時にコールするコマンドが記述されている。
また、Dockerfileに記述するコマンドはほとんどLinuxコマンドと同等であり、これは新しい構文をDockerfile作成のために新たに覚える必要はないということになる。
まずはじめに、Flaskアプリケーションと同フォルダに、Dockerfile
という名前で空ファイルを作成する。
baseイメージとしてはpython3を使用する。特に、python:3-onbuild
のpythonイメージを使用する。
FROM python:3-onbuild
とする。次に公開するポート番号を指定する。Flaskアプリケーションは5000
ポートで動くので、それを指定する。
EXPOSE 5000
最後に、アプリケーションの実行のためのコマンドを指定する。単にpython ./app.py
と書く、CMD
コマンドを使うと
CMD["python", "./app/py"]
と書ける。CMD
で、コンテナに対して、起動時に実行すべきコマンドを指示している。
これで、イメージをビルド出来る。docker build
コマンドでDockerfile
からDockerイメージを作成する。
-t
オプションにタグ名を与え、Dockerfile
を含むディレクトリの場所を指定する。
$ docker build -t kamihara/catnip .
Sending build context to Docker daemon 8.704kB
Step 1/3 : FROM python:3-onbuild
3-onbuild: Pulling from library/python
1c7fe136a31e: Pull complete
ece825d3308b: Pull complete
06854774e2f3: Pull complete
f0db43b9b8da: Pull complete
2d21c767035c: Pull complete
e10b68fb77e6: Pull complete
e2c0fb34dff3: Pull complete
e96bc319bce0: Pull complete
490280dbcfb7: Pull complete
Digest: sha256:76df62c122c910751d8cd3101f8e3da39efd4ee828686b7ff0b5a5b1d967553f
Status: Downloaded newer image for python:3-onbuild
# Executing 3 build triggers
---> Running in 431f3c96d745
Collecting Flask==0.10.1 (from -r requirements.txt (line 1))
Downloading https://files.pythonhosted.org/packages/db/9c/149ba60c47d107f85fe52564133348458f093dd5e6b57a5b60ab9ac517bb/Flask-0.10.1.tar.gz (544kB)
Collecting Werkzeug>=0.7 (from Flask==0.10.1->-r requirements.txt (line 1))
Downloading https://files.pythonhosted.org/packages/20/c4/12e3e56473e52375aa29c4764e70d1b8f3efa6682bef8d0aae04fe335243/Werkzeug-0.14.1-py2.py3-none-any.whl (322kB)
Collecting Jinja2>=2.4 (from Flask==0.10.1->-r requirements.txt (line 1))
Downloading https://files.pythonhosted.org/packages/7f/ff/ae64bacdfc95f27a016a7bed8e8686763ba4d277a78ca76f32659220a731/Jinja2-2.10-py2.py3-none-any.whl (126kB)
Collecting itsdangerous>=0.21 (from Flask==0.10.1->-r requirements.txt (line 1))
Downloading https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl
Collecting MarkupSafe>=0.23 (from Jinja2>=2.4->Flask==0.10.1->-r requirements.txt (line 1))
Downloading https://files.pythonhosted.org/packages/08/04/f2191b50fb7f0712f03f064b71d8b4605190f2178ba02e975a87f7b89a0d/MarkupSafe-1.1.0-cp36-cp36m-manylinux1_x86_64.whl
Installing collected packages: Werkzeug, MarkupSafe, Jinja2, itsdangerous, Flask
Running setup.py install for Flask: started
Running setup.py install for Flask: finished with status 'done'
Successfully installed Flask-0.10.1 Jinja2-2.10 MarkupSafe-1.1.0 Werkzeug-0.14.1 itsdangerous-1.1.0
You are using pip version 10.0.1, however version 18.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
Removing intermediate container 431f3c96d745
---> c14f035b14b0
Step 2/3 : EXPOSE 5000
---> Running in 86d209c4dec8
Removing intermediate container 86d209c4dec8
---> 3e48f1f15bc9
Step 3/3 : CMD ["python", "./app.py"]
---> Running in 6afca14ae3fc
Removing intermediate container 6afca14ae3fc
---> 82068b34e99b
Successfully built 82068b34e99b
Successfully tagged kamihara/catnip:latest
もし、python:3-onbuild
イメージを持っていなければ、はじめにそのイメージをpullしてから、自分のイメージが作成される。
docker run
でイメージを実行して、実際の動作を確認できる。
以下のコマンドで、コンテナ内の5000ポートを使用し外部に8888ポートを割り当てた。ポート8888を使ったURLでアプリの動作が確認できる。
docker run -p 8888:5000 kamihara/catnip
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
AWSへのデプロイ
AWSへのデプロイをする前に、AWSがアクセス出来るレジストリにイメージを公開する必要がある。
以下のコマンドでDockerHubに公開できる。username/image_name
の形式が重要。
$ docker push kamihara/catnip
denied: requested access to the resource is denied
これが終われば、イメージをDockerHub上で見ることが出来る。
今イメージがオンライン上にあり、Dockerがインストール済みの人は誰でも、一コマンドでアプリを実行できる。
$ docker run -p 8888:5000 kamihara/catnip
Beanstalk
Elastic BeanstalkはAWSが提供しているPaaSで、もしHeroku、GAEなどを使っていたことがあれば馴染み深い。
EBは非常に直感的なCLIを備えているが、いくつかの設定が必要だ。簡単にするために、Web UIを使用してアプリケーションを起動する。
ここでのAWSコンソールの細かい操作説明は省略(※元サイトをご覧ください)するが、基本的にはいま作成したアプリケーションはコンテナ化されているので、ElasticBeanstalkに対してはコンテナの情報を伝えればよい。
Dockerrun.aws.json
を簡単に見てみよう。ここではEBが使用するイメージ名とコンテナが開くポート番号を提供している。
{
"AWSEBDockerrunVersion": "1",
"Image": {
"Name": "kamihara/catnip",
"Update": "true"
},
"Ports": [
{
"ContainerPort": "5000"
}
],
"Logging": "/var/log/nginx"
}
このファイルをアップロードすればアプリケーションが準備できているはずだ。
EBのページを見れば、アプリが起動している事を示す緑のチェックマークがついているはず。
まとめ
Dockerの基本コマンドから始まり、簡単なDockerfileの作成、AWSのElasticBeanstalkへのアプリケーションデプロイまでをやりました。軽い気持ちで和訳メモを作り始めて、ボリュームの多さに心折れかけてました。
https://docker-curriculum.com/ のすごいところは、これで終わりではなくマルチコンテナ環境もカバーしているところです。
というわけで、明日はその2としてマルチコンテナ環境への入門編を書きたいと思います。