Vagrant + Chef-Zero Provisioner で環境を作り、Docker 入門ハンズオンを実施する #getchef #vagrant #docker
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
「Docker 入門ハンズオン資料」をやってみました。本記事は Amazon EC2 Container Service (ECS) ではなく、Vagrant + VirtualBox で環境を作成し、資料に従ってハンズオンを実施した結果です。また、独自に資料の補足も行っています。
- Debian GNU/Linux 8.1
- VirtualBox 4.3.28
- Vagrant 1.7.2
で環境を作成しました。その際の Vagrantfile や Cookbook は https://github.com/cl-lab-k/docker-test-vagrant にあります。これを git clone し、rake コマンドを実行してください。自動的に Vagrant で VirtualBox 仮想マシンを作成し、docker のインストールなどの設定を行います。
% rake
berks vendor cookbooks
Resolving cookbook dependencies...
Fetching 'docker-test-vagrant' from source at .
Fetching cookbook index from https://supermarket.chef.io...
Using build-essential (2.2.3)
Using dmg (2.2.2)
Using docker (0.40.3)
Using docker-test-vagrant (0.1.0) from source at .
Using git (4.2.2)
Using windows (1.37.0)
Using chef_handler (1.2.0)
Using yum (3.6.3)
Using yum-epel (0.6.2)
:
:
:
vagrant up
==> default: Importing base box 'chef/ubuntu-14.04'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'chef/ubuntu-14.04' is up to date...
==> default: Setting the name of the VM: docker-test-vagrant_default_1436938954313_78895
:
:
:
==> default: Installing Ohai plugin
==> default: Running provisioner: chef_zero...
default: Installing Chef (latest)...
Generating chef JSON and uploading...
==> default: Running chef-zero...
==> default: stdin: is not a tty
==> default: [2015-07-15T05:43:49+00:00] INFO: Started chef-zero at chefzero://localhost:8889 with repository at /tmp/vagrant-chef/d5bd62bdfc377f8351aa10d1e78ba278
==> default: One version per cookbook
==> default: [2015-07-15T05:43:49+00:00] INFO: Forking chef instance to converge...
==> default: [2015-07-15T05:43:49+00:00] INFO: *** Chef 12.4.1 ***
:
:
:
==> default: [2015-07-15T05:44:07+00:00] INFO: Chef Run complete in 16.835821862 seconds
==> default: [2015-07-15T05:44:07+00:00] INFO: Skipping removal of unused files from the cache
==> default: [2015-07-15T05:44:07+00:00] INFO: Running report handlers
==> default: [2015-07-15T05:44:07+00:00] INFO: Report handlers complete
%
vagrant ssh コマンドで VM にログインします。
% vagrant ssh
Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-24-generic x86_64)
* Documentation: https://help.ubuntu.com/
Last login: Tue Oct 21 14:52:42 2014 from 10.0.2.2
vagrant@vagrant:~$
インストールされている docker は 1.6.2 となっています。
vagrant@vagrant:~$ docker version
Client version: 1.6.2
Client API version: 1.18
Go version (client): go1.4.2
Git commit (client): 7c8fca2
OS/Arch (client): linux/amd64
Server version: 1.6.2
Server API version: 1.18
Go version (server): go1.4.2
Git commit (server): 7c8fca2
OS/Arch (server): linux/amd64
vagrant@vagrant:~$
ここから資料に従ってハンズオンを行っていきます。
A-1. コンテナ・イメージの確認と取得
docker images コマンドで、ローカルにある Docker イメージを確認します。まったく新規の VM 内でハンズオンを行っているため、ローカルに Docker イメージがないので、何も表示されません。
vagrant@vagrant:~$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
vagrant@vagrant:~$
docker pull コマンドで、公開レポジトリ(Docker Hub Registry)から CentOS のイメージをダウンロードします。
vagrant@vagrant:~$ docker pull centos
latest: Pulling from centos
f1b10cd84249: Pull complete
c852f6d61e65: Pull complete
7322fbe74aa5: Already exists
centos:latest: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.
Digest: sha256:57554136c655abb33ecb7bb790b1db0279668d3763c3b81f31bc6c4e60e4a1f3
Status: Downloaded newer image for centos:latest
vagrant@vagrant:~$
ローカルにダウンロードしたイメージを確認します。イメージを取得する際は「イメージ名/タグ」という形で指定し、タグを省略した場合は「latest」(最新)を指定したことになります。ここでは CentOS の最新、すなわち CentOS 7 のイメージとなっています。
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
centos latest 7322fbe74aa5 3 weeks ago 172.2 MB
vagrant@vagrant:~$
タグで「6」を指定して、CentOS 6 のイメージをローカルにダウンロードしてみます。
vagrant@vagrant:~$ docker pull centos:6
6: Pulling from centos
fb9cc58bde0c: Pull complete
a005304e4e74: Already exists
a005304e4e74: Pulling fs layer
centos:6: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.
Digest: sha256:c20d775e1d35de76d59cec13335f3bd07b0b6e73c6b1901f14f1f008d4776aeb
Status: Downloaded newer image for centos:6
vagrant@vagrant:~$
ローカルのイメージを確認すると、名前は「centos」と同じで、タグが異なっていることがわかります。
vagrant@vagrant:~$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
centos 6 a005304e4e74 3 weeks ago 203.1 MB
centos latest 7322fbe74aa5 3 weeks ago 172.2 MB
vagrant@vagrant:~$
docker history コマンドでイメージ ID を指定すると、そのイメージがどのようにビルドされたかを確認することができます。「MAINTAINER The CentOS Proje」から、途中で切れているものの CentOS Project が作成したイメージに、変更を加えてできたイメージだとわかります。
vagrant@vagrant:~$ docker history a005304e4e74
IMAGE CREATED CREATED BY SIZE
a005304e4e74 3 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B
fb9cc58bde0c 3 weeks ago /bin/sh -c #(nop) ADD file:36d5dedfe60e45e7be 203.1 MB
f1b10cd84249 11 weeks ago /bin/sh -c #(nop) MAINTAINER The CentOS Proje 0 B
vagrant@vagrant:~$
A-2. コンテナの起動と停止
echo コマンドで「Hello World」と標準出力に表示するだけのコンテナを実行します。「docker run イメージ名:タグ コマンド 引数」という形になります。
vagrant@vagrant:~$ docker run centos echo "Hello World"
Hello World
vagrant@vagrant:~$
コマンドラインで「echo "Hello World"」と実行したものと見分けがつかないかもしれません。これは centos:latest イメージからコンテナを作成し、コンテナ内で「echo "Hello World"」を実行した結果です。コマンドが終了したら、同時にコンテナも終了しています。
次はコンテナ内で「ps ax」コマンドを実行し、コンテナ内で動作しているプロセスを確認してみます。
vagrant@vagrant:~$ docker run centos ps ax
PID TTY STAT TIME COMMAND
1 ? Rs 0:00 ps ax
vagrant@vagrant:~$
コンテナ内では PID 1 で ps ax コマンドが実行されていることがわかります。
ホスト上で動作しているコンテナを確認するには「docker ps」コマンドを使います。今はコンテナを一つも実行していないので、何も表示されません。
vagrant@vagrant:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
vagrant@vagrant:~$
「-a」オプションをつけて実行すると、終了したコンテナも表示できます。
vagrant@vagrant:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d02d860e8ea9 centos:latest "ps ax" 38 seconds ago Exited (0) 38 seconds ago berserk_meitner
7bfe4305f8c3 centos:latest "echo 'Hello World'" 54 seconds ago Exited (0) 53 seconds ago distracted_swartz
vagrant@vagrant:~$
これまではコンテナ内でコマンドを実行して即時コンテナごと終了させていました。次はコンテナ内でシェル (bash) を実行し、コンテナ内で任意の操作を行えるようにしてみます。「docker run -i -t イメージ名:タグ コマンド 引数」とします。「-i」オプションは標準入力を受け付ける、「-t」オプションは仮想端末を割り当てる、という意味です。
vagrant@vagrant:~$ docker run -i -t ubuntu bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from ubuntu
83e4dde6b9cf: Pull complete
b670fb0c7ecd: Pull complete
29460ac93442: Pull complete
d2a0ecffe6fa: Already exists
ubuntu:latest: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.
Digest: sha256:cb90b1a107073ab3e17271e45a6177b23f548b44157d88d955b7e8bcdbcfd14a
Status: Downloaded newer image for ubuntu:latest
root@51d127ffa61b:/#
Ubuntu のイメージ をダウンロードしてコンテナを起動し、bash を実行しています。次のように、任意のコマンドを実行できています。
root@51d127ffa61b:/# cat /etc/issue
Ubuntu 14.04.2 LTS \n \l
root@51d127ffa61b:/#
exit と入力すると、bash が終了すると同時にコンテナも終了してしまいます。
root@51d127ffa61b:/# exit
exit
vagrant@vagrant:~$
vagrant@vagrant:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
vagrant@vagrant:~$
コンテナを終了せずに元のコンソールに戻るには、Ctrl-p + Ctrl-q と入力します。この操作をデタッチと呼びます。
root@51d127ffa61b:/# vagrant@vagrant:~$
vagrant@vagrant:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
51d127ffa61b ubuntu:latest "/bin/bash" 3 minutes ago Up 3 minutes trusting_elion
vagrant@vagrant:~$
資料には記述がありませんが、デタッチがあるならアタッチもあります。「docker attach コンテナID」と実行すると、デタッチしたコンテナに接続できます。
vagrant@vagrant:~$ docker attach 51d127ffa61b
root@51d127ffa61b:/#
コンテナをバックグラウンドでデーモンとして動作させるには「-d」オプションをつけて最初からデタッチしておきます。120回パケットを送る ping をバックグラウンドで実行してみます。
vagrant@vagrant:~$ docker run -d ubuntu ping 127.0.0.1 -c 120
1c9647ddc5e1a0836a729ae60b06dd9276b80a67b63a057f9975a09dfa3e413b
vagrant@vagrant:~$
これまでのようにコマンドの結果ではなく、単にコンテナ ID のみが表示されています。「docker ps」でも確認してみます。
vagrant@vagrant:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c9647ddc5e1 ubuntu:latest "ping 127.0.0.1 -c 1 3 seconds ago Up 2 seconds kickass_franklin
vagrant@vagrant:~$
このコンテナ ID は繰り返し利用するので、環境変数に格納して使い回せるようにします。
vagrant@vagrant:~$ export CONTAINER=1c9647ddc5e1
vagrant@vagrant:~$
docker logs コマンドを使うと、指定したコンテナの出力が確認できます。
vagrant@vagrant:~$ docker logs $CONTAINER
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.027 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.114 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.038 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.038 ms
:
:
:
「-f」オプションをつけると、リアルタイムでコンテナの出力が流れていきます。
vagrant@vagrant:~$ docker logs -f $CONTAINER
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.027 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.114 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.038 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.038 ms
:
:
:
「--tail N」オプションで、末尾から N 行を表示できます。
vagrant@vagrant:~$ docker logs --tail 5 $CONTAINER
64 bytes from 127.0.0.1: icmp_seq=90 ttl=64 time=0.036 ms
64 bytes from 127.0.0.1: icmp_seq=91 ttl=64 time=0.032 ms
64 bytes from 127.0.0.1: icmp_seq=92 ttl=64 time=0.033 ms
64 bytes from 127.0.0.1: icmp_seq=93 ttl=64 time=0.041 ms
64 bytes from 127.0.0.1: icmp_seq=94 ttl=64 time=0.039 ms
vagrant@vagrant:~$
「--tail N -f」と組み合わせると、末尾から N 行を表示して、そこからリアルタイムにコンテナの出力が流れていきます。
vagrant@vagrant:~$ docker logs --tail 5 -f $CONTAINER
64 bytes from 127.0.0.1: icmp_seq=98 ttl=64 time=0.046 ms
64 bytes from 127.0.0.1: icmp_seq=99 ttl=64 time=0.047 ms
64 bytes from 127.0.0.1: icmp_seq=100 ttl=64 time=0.041 ms
64 bytes from 127.0.0.1: icmp_seq=101 ttl=64 time=0.037 ms
64 bytes from 127.0.0.1: icmp_seq=102 ttl=64 time=0.031 ms
64 bytes from 127.0.0.1: icmp_seq=103 ttl=64 time=0.050 ms
:
:
:
動作しているコンテナを停止するには、docker stop コマンドで SIGTERM シグナルで停止するか、docker kill コマンドで SIGKILL シグナルで強制停止します。
vagrant@vagrant:~$ docker stop $CONTAINER
1c9647ddc5e1
vagrant@vagrant:~$
A-3. コンテナの詳細情報を確認
docker inspect コマンドを使うと、コンテナの詳細な情報を JSON 形式で取得できます。
vagrant@vagrant:~$ docker inspect $CONTAINER
[{
"AppArmorProfile": "",
"Args": [
"127.0.0.1",
"-c",
"120"
],
"Config": {
"AttachStderr": false,
"AttachStdin": false,
"AttachStdout": false,
"Cmd": [
"ping",
"127.0.0.1",
"-c",
"120"
],
"CpuShares": 0,
"Cpuset": "",
"Domainname": "",
"Entrypoint": null,
"Env": null,
"ExposedPorts": null,
"Hostname": "1c9647ddc5e1",
"Image": "ubuntu",
"Labels": {},
"MacAddress": "",
"Memory": 0,
"MemorySwap": 0,
"NetworkDisabled": false,
"OnBuild": null,
"OpenStdin": false,
"PortSpecs": null,
"StdinOnce": false,
"Tty": false,
"User": "",
"Volumes": null,
"WorkingDir": ""
},
"Created": "2015-07-15T02:29:15.562242259Z",
"Driver": "aufs",
"ExecDriver": "native-0.2",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"CapAdd": null,
"CapDrop": null,
"CgroupParent": "",
"ContainerIDFile": "",
"CpuShares": 0,
"CpusetCpus": "",
"Devices": [],
"Dns": null,
"DnsSearch": null,
"ExtraHosts": null,
"IpcMode": "",
"Links": null,
"LogConfig": {
"Config": null,
"Type": "json-file"
},
"LxcConf": [],
"Memory": 0,
"MemorySwap": 0,
"NetworkMode": "bridge",
"PidMode": "",
"PortBindings": {},
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"RestartPolicy": {
"MaximumRetryCount": 0,
"Name": "no"
},
"SecurityOpt": null,
"Ulimits": null,
"VolumesFrom": null
},
"HostnamePath": "/var/lib/docker/containers/1c9647ddc5e1a0836a729ae60b06dd9276b80a67b63a057f9975a09dfa3e413b/hostname",
"HostsPath": "/var/lib/docker/containers/1c9647ddc5e1a0836a729ae60b06dd9276b80a67b63a057f9975a09dfa3e413b/hosts",
"Id": "1c9647ddc5e1a0836a729ae60b06dd9276b80a67b63a057f9975a09dfa3e413b",
"Image": "d2a0ecffe6fa4ef3de9646a75cc629bbd9da7eead7f767cb810f9808d6b3ecb6",
"LogPath": "/var/lib/docker/containers/1c9647ddc5e1a0836a729ae60b06dd9276b80a67b63a057f9975a09dfa3e413b/1c9647ddc5e1a0836a729ae60b06dd9276b80a67b63a057f9975a09dfa3e413b-json.log",
"MountLabel": "",
"Name": "/kickass_franklin",
"NetworkSettings": {
"Bridge": "",
"Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"MacAddress": "",
"PortMapping": null,
"Ports": null
},
"Path": "ping",
"ProcessLabel": "",
"ResolvConfPath": "/var/lib/docker/containers/1c9647ddc5e1a0836a729ae60b06dd9276b80a67b63a057f9975a09dfa3e413b/resolv.conf",
"RestartCount": 0,
"State": {
"Dead": false,
"Error": "",
"ExitCode": 0,
"FinishedAt": "2015-07-15T02:31:14.675859632Z",
"OOMKilled": false,
"Paused": false,
"Pid": 0,
"Restarting": false,
"Running": false,
"StartedAt": "2015-07-15T02:29:15.653140071Z"
},
"Volumes": {},
"VolumesRW": {}
}
]
vagrant@vagrant:~$
必要な情報のみ取得したい場合は --format オプションで指定します。書式は Go 言語の Package template に従います。
vagrant@vagrant:~$ docker inspect --format='{{.Config.Cmd}}' $CONTAINER
[ping 127.0.0.1 -c 120]
vagrant@vagrant:~$
次は JSON 形式で表示するための指定方法です。
vagrant@vagrant:~$ docker inspect --format='{{json .Config.Cmd}}' $CONTAINER
["ping","127.0.0.1","-c","120"]
vagrant@vagrant:~$
A-5. 不要なコンテナの削除
コンテナを停止しても、コンテナのデータ自体は残り続けます。まず、停止しているコンテナを docker ps で見てみましょう。
vagrant@vagrant:~$ docker ps -aq --filter='status=exited'
1c9647ddc5e1
51d127ffa61b
d02d860e8ea9
7bfe4305f8c3
vagrant@vagrant:~$
停止したコンテナを削除するには docker rm コマンドを使います。停止しているコンテナをまとめて削除するには、次のようにシェルを組み合わせて実行します。もちろん、コンテナ ID を 1つ 1つ指定してもかまいません。
vagrant@vagrant:~$ docker rm $(docker ps -aq --filter='status=exited')
1c9647ddc5e1
51d127ffa61b
d02d860e8ea9
7bfe4305f8c3
vagrant@vagrant:~$
停止したコンテナがすべて削除されていることを確認します。
vagrant@vagrant:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
vagrant@vagrant:~$
A-6. イメージの内容確定(コミット)や変更差分
コンテナを起動し、内容に変更を加えた後に停止し、もう一度コンテナを起動すると、変更した内容は反映されていません。それを確認してみます。
まず、ubuntu イメージからコンテナを起動し、/hello.txt ファイルを作成し、exit でコンテナを終了します。
vagrant@vagrant:~$ docker run -i -t ubuntu bash
root@19a2efc75949:/# echo "hello world" > /hello.txt
root@19a2efc75949:/# ls -la /hello.txt
-rw-r--r-- 1 root root 12 Jul 15 03:06 /hello.txt
root@19a2efc75949:/# exit
exit
vagrant@vagrant:~$
再度 ubuntu イメージからコンテナを起動すると、/hello.txt ファイルは存在していません。
vagrant@vagrant:~$ docker run -i -t ubuntu bash
root@14f3c0bda510:/# ls -la /hello.txt
ls: cannot access /hello.txt: No such file or directory
root@14f3c0bda510:/#
もう一度 /hello.txt ファイルを作成し、コンテナを停止します。
root@14f3c0bda510:/# echo "hello world" > /hello.txt
root@14f3c0bda510:/# ls -la /hello.txt
-rw-r--r-- 1 root root 12 Jul 15 03:07 /hello.txt
root@14f3c0bda510:/# exit
exit
vagrant@vagrant:~$
もっとも新しく作成したコンテナを docker ps -l コマンドで表示します。
vagrant@vagrant:~$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
14f3c0bda510 ubuntu:latest "bash" About a minute ago Exited (0) 24 seconds ago furious_wright
vagrant@vagrant:~$
docker diff コマンドを使うと、イメージに加えられた変更点がわかります。A は追加、C は変更、ここでは対象がありませんが D が削除になります。
vagrant@vagrant:~$ docker diff 14f3c0bda510
A /hello.txt
C /root
A /root/.bash_history
vagrant@vagrant:~$
このコンテナ ID は繰り返し利用するので、環境変数に格納して使い回せるようにします。
vagrant@vagrant:~$ export CONTAINER=14f3c0bda510
vagrant@vagrant:~$
変更点を保存して新たなイメージを作る(コミットする)には、「docker commit コンテナ ID レポジトリ名:タグ」としてコマンドを実行します。レポジトリ名:タグは任意の文字列が利用できます。
vagrant@vagrant:~$ docker commit $CONTAINER vagdocker/myapp:1.0
d674643f50918a5c977ebb046c4b135307fc811e236bf31f6782eb7ff4c77fbf
vagrant@vagrant:~$
コミットしたイメージは docker images コマンドで確認できます。
vagrant@vagrant:~$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
vagdocker/myapp 1.0 d674643f5091 12 seconds ago 188.4 MB
ubuntu latest d2a0ecffe6fa 5 days ago 188.4 MB
centos 6 a005304e4e74 3 weeks ago 203.1 MB
centos latest 7322fbe74aa5 3 weeks ago 172.2 MB
vagrant@vagrant:~$
作成したイメージの ID は繰り返し利用するので、環境変数に格納して使い回せるようにします。
vagrant@vagrant:~$ export IMAGEID=d674643f5091
vagrant@vagrant:~$
このイメージから作成したコンテナならば、先程作成した /hello.txt ファイルを保持しています。
vagrant@vagrant:~$ docker run vagdocker/myapp:1.0 cat /hello.txt
hello world
vagrant@vagrant:~$
docker history コマンドで、このイメージの変更履歴をたどっていくことができます。
vagrant@vagrant:~$ docker history $IMAGEID
IMAGE CREATED CREATED BY SIZE
d674643f5091 About a minute ago bash 85 B
d2a0ecffe6fa 5 days ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B
29460ac93442 5 days ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/ 1.895 kB
b670fb0c7ecd 5 days ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic 194.5 kB
83e4dde6b9cf 5 days ago /bin/sh -c #(nop) ADD file:c8f078961a543cdefa 188.2 MB
vagrant@vagrant:~$
Dockerfile で wget・ping のコンテナを構築
Dockerfile とは、Docker イメージを構築するための設定ファイルです。前項で行ったように、いちいち bash を起動して内容を変更してコミットという手順を踏まずにイメージを作成できます。
作業用ディレクトリを作成し、以降はこのディレクトリ内で作業を行います。1ディレクトリに1 Dockerfile とします。
vagrant@vagrant:~$ mkdir dev
vagrant@vagrant:~$ cd dev
vagrant@vagrant:~/dev$
Ubuntu の wget をインストールして実行するコンテナイメージを作成してみます。
vagrant@vagrant:~/dev$ vi Dockerfile
FROM ubuntu:14.04
RUN apt-get install -y wget
CMD /usr/bin/wget
vagrant@vagrant:~/dev$
FROM は元となるイメージ名を指定します。コメント行を除いて、Dockerfile の先頭になければいけません。
RUN はコンテナ内で指定のコマンドを実行し、その結果をコミットします。その性質上、コンテナ内で起動するデーモンを指定するような使い方はできません。
CMD はコンテナ内で最終的に実行するコマンドを指定します。このコマンドがコンテナ内での PID 1 となります。また複数の CMD を指定しても最後の指定しか効果を持ちません。
Dockerfile が完成したら、docker build コマンドに「-t」オプションでイメージの名前を指定し、イメージを作成します。
vagrant@vagrant:~/dev$ docker build -t vagdocker/wget .
Sending build context to Docker daemon 2.048 kB
Sending build context to Docker daemon
Step 0 : FROM ubuntu:14.04
14.04: Pulling from ubuntu
83e4dde6b9cf: Already exists
b670fb0c7ecd: Already exists
d2a0ecffe6fa: Already exists
ubuntu:14.04: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.
Digest: sha256:36de65e14e47b2a9a3d91c9a832899d4c432a00295bd77ced8d5bb6dc2764318
Status: Downloaded newer image for ubuntu:14.04
---> d2a0ecffe6fa
Step 1 : RUN apt-get install -y wget
---> Running in 374ebeb8150b
Reading package lists...
Building dependency tree...
Reading state information...
The following extra packages will be installed:
ca-certificates libidn11 openssl
The following NEW packages will be installed:
ca-certificates libidn11 openssl wget
0 upgraded, 4 newly installed, 0 to remove and 0 not upgraded.
Need to get 1026 kB of archives.
After this operation, 2397 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu/ trusty/main libidn11 amd64 1.28-1ubuntu2 [93.0 kB]
Get:2 http://archive.ubuntu.com/ubuntu/ trusty/main openssl amd64 1.0.1f-1ubuntu2 [489 kB]
Get:3 http://archive.ubuntu.com/ubuntu/ trusty/main ca-certificates all 20130906ubuntu2 [175 kB]
Get:4 http://archive.ubuntu.com/ubuntu/ trusty/main wget amd64 1.15-1ubuntu1 [270 kB]
debconf: unable to initialize frontend: Dialog
debconf: (TERM is not set, so the dialog frontend is not usable.)
debconf: falling back to frontend: Readline
debconf: unable to initialize frontend: Readline
debconf: (This frontend requires a controlling tty.)
debconf: falling back to frontend: Teletype
dpkg-preconfigure: unable to re-open stdin:
Fetched 1026 kB in 1s (597 kB/s)
Selecting previously unselected package libidn11:amd64.
(Reading database ... 11541 files and directories currently installed.)
Preparing to unpack .../libidn11_1.28-1ubuntu2_amd64.deb ...
Unpacking libidn11:amd64 (1.28-1ubuntu2) ...
Selecting previously unselected package openssl.
Preparing to unpack .../openssl_1.0.1f-1ubuntu2_amd64.deb ...
Unpacking openssl (1.0.1f-1ubuntu2) ...
Selecting previously unselected package ca-certificates.
Preparing to unpack .../ca-certificates_20130906ubuntu2_all.deb ...
Unpacking ca-certificates (20130906ubuntu2) ...
Selecting previously unselected package wget.
Preparing to unpack .../wget_1.15-1ubuntu1_amd64.deb ...
Unpacking wget (1.15-1ubuntu1) ...
Setting up libidn11:amd64 (1.28-1ubuntu2) ...
Setting up openssl (1.0.1f-1ubuntu2) ...
Setting up ca-certificates (20130906ubuntu2) ...
debconf: unable to initialize frontend: Dialog
debconf: (TERM is not set, so the dialog frontend is not usable.)
debconf: falling back to frontend: Readline
debconf: unable to initialize frontend: Readline
debconf: (This frontend requires a controlling tty.)
debconf: falling back to frontend: Teletype
Setting up wget (1.15-1ubuntu1) ...
Processing triggers for libc-bin (2.19-0ubuntu6.6) ...
Processing triggers for ca-certificates (20130906ubuntu2) ...
Updating certificates in /etc/ssl/certs... 164 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....done.
---> 9bdd7cd26ea3
Removing intermediate container 374ebeb8150b
Step 2 : CMD /usr/bin/wget
---> Running in ad80d7a4df9b
---> 3fc626799b4d
Removing intermediate container ad80d7a4df9b
Successfully built 3fc626799b4d
vagrant@vagrant:~/dev$
無事イメージが完成しました。
早速このイメージを使ってコンテナを起動します。
vagrant@vagrant:~/dev$ docker run -ti vagdocker/wget
wget: missing URL
Usage: wget [OPTION]... [URL]...
Try `wget --help' for more options.
vagrant@vagrant:~/dev$
Dockerfile の CMD では特に引数を与えずに wget を指定していたので、使い方を表示してコンテナは終了します。
CMD に似た文として ENTRYPOINT があります。ENTRYPOINT でも実行するコマンドを次のように指定できます。CMD と ENTRYPOINT の使い分けは Best practices for writing Dockerfiles を参照してください。
vagrant@vagrant:~/dev$ vi Dockerfile
FROM ubuntu:14.04
ENTRYPOINT ["ping", "-c", "60"]
vagrant@vagrant:~/dev$
イメージをビルドします。
vagrant@vagrant:~/dev$ docker build -t vagdocker/ping .
Sending build context to Docker daemon 2.048 kB
Sending build context to Docker daemon
Step 0 : FROM ubuntu:14.04
---> d2a0ecffe6fa
Step 1 : ENTRYPOINT ping -c 60
---> Running in 669a9e848da1
---> f35a0f7fdcd0
Removing intermediate container 669a9e848da1
Successfully built f35a0f7fdcd0
vagrant@vagrant:~/dev$
引数に「127.0.0.1」を与えてコンテナを起動します。これは ENTRYPOINT で指定していた「-c 60」オプションを付与して、「ping -c 60 127.0.0.1」としてコマンドを実行しています。
vagrant@vagrant:~/dev$ docker run vagdocker/ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.034 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.035 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.035 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.040 ms
64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.034 ms
:
:
:
ENTRYPOINT と同じオプションを指定すると、値を上書きすることができます。
vagrant@vagrant:~/dev$ docker run vagdocker/ping -c 1 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.031 ms
--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.031/0.031/0.031/0.000 ms
vagrant@vagrant:~/dev$
「--entrypoint」オプションを使うと、ENTRYPOINT で指定したコマンドそのものを上書きできます。次は ping コマンドを上書きして bash コマンドを実行する例です。
vagrant@vagrant:~/dev$ docker run -t -i --entrypoint bash vagdocker/ping
root@555c08683983:/#
A-8. Dockerfile で Nginx のコンテナを構築
ローカルに用意してある静的ファイルをコンテナ内に追加するには Dockerfile で ADD を使います。似たような文として COPY があります。ADD と COPY の使い分けは Best practices for writing Dockerfiles を参照してください。
まず、コンテナ内にコピーするためのファイル index.html を contents ディレクトリ内に準備します。
vagrant@vagrant:~/dev$ mkdir contents
vagrant@vagrant:~/dev$ echo "hello world" > contents/index.html
vagrant@vagrant:~/dev$
Dockerfile を作成します。nginx イメージをベースに、contents ディレクトリの内容をコンテナ内の /usr/share/nginx/html ディレクトリにコピーします。このディレクトリは Nginx のデフォルトのドキュメントルートです。
vagrant@vagrant:~/dev$ vi Dockerfile
FROM nginx
ADD ./contents /usr/share/nginx/html
vagrant@vagrant:~/dev$
イメージをビルドします。
vagrant@vagrant:~/dev$ docker build -t vagdocker/web .
Sending build context to Docker daemon 3.584 kB
Sending build context to Docker daemon
Step 0 : FROM nginx
latest: Pulling from nginx
902b87aaaec9: Pull complete
9a61b6b1315e: Pull complete
aface2a79f55: Pull complete
5dd2638d10a1: Pull complete
97df1ddba09e: Pull complete
438d75921414: Pull complete
7340fb61bdef: Pull complete
51ed40dd3671: Pull complete
2b12d66de277: Pull complete
4b1798a9cafa: Pull complete
61f2d0dc0350: Pull complete
ce293cb4a3c9: Already exists
nginx:latest: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.
Digest: sha256:90c72cfd3fabbc56971ea1cf04575b11dce4b3eb35a7363202c31052b5652a76
Status: Downloaded newer image for nginx:latest
---> ce293cb4a3c9
Step 1 : ADD ./contents /usr/share/nginx/html
---> d911784cd5e5
Removing intermediate container 2f7bb5376697
Successfully built d911784cd5e5
vagrant@vagrant:~/dev$
コンテナを起動します。「-p ホスト側のポート:コンテナ内のポート」とすると、ホスト側のポートに来たアクセスをコンテナ内のポートへと結びつけるようになります。
vagrant@vagrant:~/dev$ docker run -d -p 80:80 vagdocker/web
f2b16d21e8f826d38014bac97bedfe6aba4cf5c508b4b1c96dbc25db46fd00b9
vagrant@vagrant:~/dev$
vagrant@vagrant:~/dev$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f2b16d21e8f8 vagdocker/web:latest "nginx -g 'daemon of 9 seconds ago Up 9 seconds 0.0.0.0:80->80/tcp, 443/tcp pensive_pike
vagrant@vagrant:~/dev$
ホスト側の 80 番ポートにアクセスすると、コンテナ内に配置した index.html ファイルの内容が表示されることがわかります。
vagrant@vagrant:~/dev$ curl http://127.0.0.1/
hello world
vagrant@vagrant:~/dev$
一旦コンテナを停止します。
vagrant@vagrant:~/dev$ docker stop f2b16d21e8f8
f2b16d21e8f8
vagrant@vagrant:~/dev$
Dockerfile でコンテンツのコピーを行わずに、「-v ホスト側のディレクトリパス:コンテナ内のディレクトリパス」とすることで、ホストとコンテナのディレクトリを共有することができます。コンテナ内で動的に生成されるログなどをホスト側のディレクトリに保管するといった使い方ができるでしょう。
vagrant@vagrant:~/dev$ docker run -d -p 80:80 -v /home/vagrant/dev/contents:/usr/share/nginx/html vagdocker/web
e16181233c319caddbf836249723a8c377a234bfea170ccce7e5c9dd27d9a819
vagrant@vagrant:~/dev$
このようにディレクトリを共有したコンテナを起動して、ホスト側のディレクトリにファイルを作成してみます。
vagrant@vagrant:~/dev$ date > contents/date.txt
vagrant@vagrant:~/dev$
curl コマンドでコンテナにアクセスすると、ホスト側で作成したファイルが見えています。
vagrant@vagrant:~/dev$ curl http://127.0.0.1/date.txt
Wed Jul 15 03:41:59 UTC 2015
vagrant@vagrant:~/dev$
ホスト側のファイルを削除します。
vagrant@vagrant:~/dev$ rm contents/date.txt
vagrant@vagrant:~/dev$
curl コマンドでコンテナにアクセスすると、ファイルがなくなっていることがわかります。
vagrant@vagrant:~/dev$ curl http://127.0.0.1/date.txt
404 Not Found
vagrant@vagrant:~/dev$
まとめ
資料に従い、Docker 入門ハンズオンを実施してみました。Docker の使い方や動作について、さわりの部分がおおよそ体験できたと思います。また、Docker Docs は非常に充実した公式ドキュメントなので、一通り目を通しておくとよいでしょう。さらに理解が深まることと思います。