Docker入門

Kazuki Koide

February 5, 2020

社内でDocker入門講座を開催した時に作った資料。


公式ドキュメント

Dockerとはなんぞや

何が嬉しいの?

インストール

公式サイトでDocker Desktopのインストーラーをダウンロードしてインストールしてください。

WindowsにDocker toolboxをインストールしている記事を見かけることがありますが、2020年2月時点ではDocker toolboxは廃止されて非推奨となっているようです。

基本的なコマンド一覧

結構種類があって大変ですが、この後ひとつひとつ実行して動作確認していきます。

コンテナ操作系

コマンド 用途
docker run コンテナを生成して起動する
docker exec コンテナ内でコマンドを実行する
docker ps コンテナの状態を参照する
docker stop コンテナを停止する
docker start コンテナを起動する
docker rm コンテナを削除する
docker logs コンテナのログを参照する

Dockerイメージ操作系

コマンド 用途
docker images Dockerイメージの一覧を参照する
docker rmi Dockerイメージを削除する
docker commit コンテナに対して行った変更をDockerイメージとして保存する
docker build DockerfileからDockerイメージを作成する
docker login Dockerイメージのリポジトリサイトにログインする
docker push リポジトリにDockerイメージを格納する

他にも色々ありますので、気になる人はdocker --helpで確認してみてください。

チュートリアル1(コンテナを操る)

チュートリアル1ではdockerコマンドを実際に叩いてみて、Dockerに慣れていきましょう。

NginxをDocker上で起動する

以下の通りdocker runコマンドを実行してください。

$ docker run -d -p 8080:80 --name=nginx nginx:latest

Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
bc51dd8edc1b: Pull complete
66ba67045f57: Pull complete
bf317aa10aa5: Pull complete
Digest: sha256:ad5552c786f128e389a0263104ae39f3d3c7895579d45ae716f528185b36bc6f
Status: Downloaded newer image for nginx:latest
6a4692e3ff5f601b1c81c7a720888251b5b7ac3fe0d02bb90de736488a35f28b

docker runを実行すると、DockerHubからDockerイメージがダウンロードされ、それを使ってコンテナが生成されます(docker run のオプションについてはマニュアルを参考にしてください)。http://localhost:8080 にアクセスするとNginxが起動しているのが確認できるかと思います。

nginx

コマンド内のnginx:latestという部分がDockerイメージの識別子になります。Dockerイメージは{リポジトリ名}:{タグ}で一意に識別されるので覚えておいてください。latestタグは最新バージョンのDockerイメージに付与されるタグです。もしdocker run実行時にタグを省略した場合は、latestタグが使用されます。

コンテナの状態を確認してみる

docker imagesでDockerイメージの一覧を表示できます。

$ docker images

REPOSITORY   TAG       IMAGE ID          CREATED         SIZE
nginx        latest    2073e0bcb60e      44 hours ago    127MB

Dockerイメージの一覧にnginxが追加されているかと思います。これが先程docker run実行時にDockerHubからダウンロードされたDockerイメージです。

次にコンテナの状態をdocker psで確認しましょう。

$ docker ps

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
4e3aa2d731f3        nginx:latest        "nginx -g 'daemon of…"   3 seconds ago       Up 2 seconds        0.0.0.0:8080->80/tcp   nginx

Nginxのコンテナが作成されていて、STATUSがUp(稼働中)であることが分かります。

Nginxのコンテナ内でBashを実行する

コンテナ内で何かしらのコマンドを実行したいときは、docker execを使います。

$ docker exec -it nginx /bin/bash

root@4e3aa2d731f3:/#

Nginxのコンテナ内で実行されたBashに繋がったかと思います。ここでls -lコマンドでNginxのログフォルダを確認してみてください。

root@4e3aa2d731f3:/# ls -l /var/log/nginx

total 0
lrwxrwxrwx 1 root root 11 Feb  2 08:06 access.log -> /dev/stdout
lrwxrwxrwx 1 root root 11 Feb  2 08:06 error.log -> /dev/stderr

ログファイルではなく、標準出力、標準エラー出力に繋がれたシンボリックリンクがありました。基本的にコンテナの世界ではログは標準出力および標準エラー出力に出力するのが習わしとなっています。そうすると、出力した文字列がDockerによってホストOSに転送され、docker logsコマンドで参照することができるようになります。Nginxコンテナもこれに倣ってログを標準出力、標準エラー出力に吐くよう作られているわけです。では一旦exitでシェルを抜けて、docker logsコマンドを実行してみましょう。

Nginxのログを見てみる

$ docker logs nginx

172.17.0.1 - - [04/Feb/2020:03:48:15 +0000] "GET /robots.txt HTTP/1.1" 404 555 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36" "-"
2020/02/04 03:48:15 [error] 6#6: *1 open() "/usr/share/nginx/html/robots.txt" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /robots.txt HTTP/1.1", host: "localhost:8080"
172.17.0.1 - - [04/Feb/2020:03:48:15 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36" "-"
172.17.0.1 - - [04/Feb/2020:03:48:16 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://localhost:8080/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36" "-"
2020/02/04 03:48:16 [error] 6#6: *2 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "localhost:8080", referrer: "http://localhost:8080/"

先程アクセスしたログが確認できるかと思います。ちなみにdocker logs -fとしてやると、tail -fのようにログの追記を監視することができます。

コンテナを停止して起動する

コンテナを停止するときはdocker stopです。

$ docker stop nginx

nginx

コンテナが停止し、http://localhost:8080 にアクセスできなくなっているはずです。docker psで状態を確認します。

$ docker ps

docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

Nginxのコンテナが表示されなくなりました。ただしNginxのコンテナは停止しているだけで削除されたわけではありません。停止しているコンテナも含めて表示するにはdocker ps -aとやります。

$ docker ps -a

docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS               NAMES
4e3aa2d731f3        nginx:latest        "nginx -g 'daemon of…"   About an hour ago   Exited (0) 2 minutes ago                       nginx

このようにNginxのコンテナが表示され、STATUSがExitedであることが確認できます。このコンテナを再度起動するにはdocker startを実行します。

$ docker start

nginx

もう一度docker psコマンドを叩くと、またNginxコンテナが表示され、STATUSがUp(稼働中)であることが分かります。

$ docker ps

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
4e3aa2d731f3        nginx:latest        "nginx -g 'daemon of…"   About an hour ago   Up 23 seconds       0.0.0.0:8080->80/tcp   nginx

コンテナを削除する

コンテナの削除はdocker rm です。ただし、起動中のコンテナは削除できませんので、いったんdocker stopで停止してから削除してください。

$ docker stop nginx

nginx

$ docker rm nginx

nginx

これでコンテナが削除されるため、docker ps -aを叩いてもNginxは表示されません。

$ docker ps -a

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

Dockerイメージを削除する

先程コンテナは削除しましたが、Dockerイメージはまだ残っています。後片付けとしてDockerイメージも削除しましょう。docker rmiコマンドで削除できます。

docker rmi nginx:latest

Untagged: nginx:latest
Untagged: nginx@sha256:ad5552c786f128e389a0263104ae39f3d3c7895579d45ae716f528185b36bc6f
Deleted: sha256:2073e0bcb60ee98548d313ead5eacbfe16d9054f8800a32bedd859922a99a6e1
Deleted: sha256:a3136fbf38691346715cac8360bcdfca0fff812cede416469653670f04e2cab0
Deleted: sha256:99360ffcb2da18fd9ede194efaf5d4b90e7aee99f45737e918113e6833dcf278
Deleted: sha256:488dfecc21b1bc607e09368d2791cb784cf8c4ec5c05d2952b045b3e0f8cc01e

この状態でdocker imagesコマンドを叩くと、NginxのDockerイメージが削除されていることが確認できるかと思います。

$ docker images

REPOSITORY        TAG        IMAGE ID      CREATED       SIZE

以上でチュートリアル1は完了です。

ここまでの知識で、DockerHub上で公開されているDockerイメージが利用できるはずです。最近はOSSの配布をDockerで行うことが増えてきました。Dockerを使うと複雑なアプリケーション(3層アーキテクチャとか)でも簡単にインストールできます。また、コンテナはホストOSの環境と切り離されているため、ローカルPCの環境が汚れてしまう恐れもありません。コンテナを削除すると環境もろとも綺麗に削除されます。この気軽さがDockerを使う大きなメリットの一つです。DockerHubには様々なDockerイメージが公開されているので、ぜひ色々動かしてみてください。

チュートリアル2 (Dockerイメージをカスタマイズする)

dockerコマンドに慣れたら、次のステップとしてオリジナルのDockerイメージの作成に挑戦してみましょう。チュートリアル2ではPythonの開発環境用にDockerイメージを作成します。

Docker HubでPythonを検索

DockerHubにアクセスします。アカウントを持っていない人はサインアップしてください。Pythonで検索すると、PythonのDockerイメージが見つかるかと思います。

dockerhub1

DockerHubには、公式のイメージだけでなく個人が作った信頼性の低いDockerイメージも公開されています。DockerHubを使う際は、「VERIFIED PUBLISHER」や「OFFICIAL IMAGE」のラベルがあるかどうかや、ダウンロード数、スター数を参考にして信頼性を確認するようにしましょう。

ではPythonを選択して、Tagsをクリックしてタグを表示してください。

dockerhub2

様々なタグがあることが確認できるかと思います。Pythonの場合、Python自体のバージョンと、OSのバージョンでタグが付けられているようです。ここでは3.8.1-busterを使うことにします。busterというのはDebianというOSのバージョン10のコードネームです。つまり、Debian10にpythonがインストールされた状態のDockerイメージということになります。

コンテナを起動

docker runでコンテナを起動します。名前はmypythonにしました。Dockerイメージのサイズが大きいので割と時間がかかります。

$ docker run -d -it --name mypython python:3.8.1-buster

Unable to find image 'python:3.8.1-buster' locally
3.8.1-buster: Pulling from library/python
dc65f448a2e2: Pull complete
346ffb2b67d7: Pull complete
dea4ecac934f: Pull complete
8ac92ddf84b3: Pull complete
a3ca60abc08a: Pull complete
9253bd2ee3f6: Pull complete
fad96c8dce44: Pull complete
ec0f51d2752d: Pull complete
ff10001c6dc3: Pull complete
Digest: sha256:db86894e8fcdb6ca371e1143cbc646ef554ca83c5a85dc4a13f427e085153bb5
Status: Downloaded newer image for python:3.8.1-buster
39c933d7c9a31329b8be6e3aaba62f1454f7089844b2351da5c050a3ffff5e61

起動できたら、コンテナ内のBashに接続して、Pythonのバージョンを確認してみます。

docker exec -it mypython /bin/bash

root@35c8bacc3411:/# python --version
Python 3.8.1
root@35c8bacc3411:/# pip --version
pip 20.0.2 from /usr/local/lib/python3.8/site-packages/pip (python 3.8)

ちゃんとバージョン3.8.1がインストールされているのが確認できます。

コンテナをカスタマイズ

このコンテナを開発環境用にカスタマイズしていきます。なんでも良いですが、今回は開発にPandasが必要になったという想定で、Pandasをインストールしてみます。

root@35c8bacc3411:/# pip install pandas

Collecting pandas
  Downloading pandas-1.0.0-cp38-cp38-manylinux1_x86_64.whl (9.9 MB)
     |████████████████████████████████| 9.9 MB 10.1 MB/s
Collecting numpy>=1.13.3
  Downloading numpy-1.18.1-cp38-cp38-manylinux1_x86_64.whl (20.6 MB)
     |████████████████████████████████| 20.6 MB 591 kB/s
Collecting pytz>=2017.2
  Downloading pytz-2019.3-py2.py3-none-any.whl (509 kB)
     |████████████████████████████████| 509 kB 10.0 MB/s
Collecting python-dateutil>=2.6.1
  Downloading python_dateutil-2.8.1-py2.py3-none-any.whl (227 kB)
     |████████████████████████████████| 227 kB 11.2 MB/s
Collecting six>=1.5
  Downloading six-1.14.0-py2.py3-none-any.whl (10 kB)
Installing collected packages: numpy, pytz, six, python-dateutil, pandas
Successfully installed numpy-1.18.1 pandas-1.0.0 python-dateutil-2.8.1 pytz-2019.3 six-1.14.0

root@35c8bacc3411:/# python

Python 3.8.1 (default, Feb  2 2020, 08:37:37)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pandas
>>> exit()

root@35c8bacc3411:/#

インストールできました。次にこのコンテナを使って新しいmypythonという名前のDockerイメージを作成します。exitでシェルを抜けて、docker commitを実行します。

xxxxxの箇所はご自身のDockerHubのユーザー名に置き換えてください。

$ docker commit mypython xxxxx/mypython:3.8.1-buster

sha256:8e8066959b691698077fc78988dbc09d77a7e586b010e690f9e22ed093da9b80

$ docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
xxxxx/mypython              3.8.1-buster        80db86c8f1ae        5 seconds ago       1.1GB
python                      3.8.1-buster        eeadc22d21a9        47 hours ago        933MB

xxxxx/mypythonという名前のDockerイメージが作成されているのが確認できます。

コンテナをDockerHubで公開する

このmypythonをDockerHubで公開して、他の人とシェアできるようにしてみます。それほど難しい操作は必要なく、docker loginコマンドでDockerHubにログインし、docker pushするだけで簡単に公開できます。

$ docker login

(ご自身のID・パスワードでログインしてください)

$ docker push xxxxx/mypython:3.8.1-buster

これで、DockerHubのマイページにmypythonが表示されているはずです。DockerHubにアクセスして確認してみましょう。

dockerhub3

このようにDockerイメージ化することで、開発環境をチームで共有することが可能になります。Dockerが実行環境の差異を吸収してくれるので、もしチームの中にWindowsユーザーとMacユーザーとLinuxユーザーが混在していたとしても環境差異でハマることが殆どありません。環境差異に悩まなくて良いのもDockerの大きなメリットの一つです。

Dockerイメージを作成するもう一つの方法

先程行ったDockerイメージのカスタマイズでは、

  1. カスタマイズのベースとなるDockerイメージを使ってコンテナを起動
  2. コンテナ内のBashに接続
  3. 何かしらをインストール
  4. docker commitコマンドでオリジナルのDockerイメージを作成

という流れでDockerイメージを作成しましたが、それとは別にdocker buildコマンドを使う方法もあります。docker buildを使った方法では、まずDockerfileを作成します。(Dockerfileというファイル名です。拡張子はありません。)

以下がDockerfileの例です。上記の手順の1〜3をスクリプトで自動化するようなイメージです。

FROM python:3.8.1-buster

RUN pip install pandas

そして、Dockerfileのあるフォルダ上で、docker buildコマンドを実行すると、xxxxx/mypythonという名前のDockerイメージが自動的に作成されます。

$ docker build -t xxxxx/mypython:3.8.1-buster .

Sending build context to Docker daemon  300.6MB
Step 1/3 : FROM python:3.8.1-buster
 ---> eeadc22d21a9
Step 2/3 : RUN pip install pandas
 ---> Running in 290d7be35a90
Collecting pandas
  Downloading pandas-1.0.0-cp38-cp38-manylinux1_x86_64.whl (9.9 MB)
Collecting numpy>=1.13.3
  Downloading numpy-1.18.1-cp38-cp38-manylinux1_x86_64.whl (20.6 MB)
Collecting python-dateutil>=2.6.1
  Downloading python_dateutil-2.8.1-py2.py3-none-any.whl (227 kB)
Collecting pytz>=2017.2
  Downloading pytz-2019.3-py2.py3-none-any.whl (509 kB)
Collecting six>=1.5
  Downloading six-1.14.0-py2.py3-none-any.whl (10 kB)
Installing collected packages: numpy, six, python-dateutil, pytz, pandas
Successfully installed numpy-1.18.1 pandas-1.0.0 python-dateutil-2.8.1 pytz-2019.3 six-1.14.0
Removing intermediate container 290d7be35a90
 ---> e67c3fa0fb05
Step 3/3 : ENTRYPOINT /bin/bash
 ---> Running in 16b989c8b647
Removing intermediate container 16b989c8b647
 ---> f50c90e2b7df
Successfully built f50c90e2b7df
Successfully tagged xxxxx/mypython:3.8.1-buster

この方法はDockerイメージの作成を自動化できるため、何度もDockerイメージを作成する必要がある場合に向いています。一般的にCIツールと組み合わせて自動化することが多いです。Dockerfileの構文を覚える必要があるので今回は時間的に解説できませんが、興味がある方は、Dockerfile リファレンスBest practices for writing Dockerfiles などを読んでみてください。

以上でチュートリアル2は完了です。

Docker Tips

Dockerに関するTipsをいくつか紹介します。

docker runのオプション

以下のオプションを常に付けることをおすすめします。

オプション 意味
-d バックグラウンドで実行
–rm コンテナ停止時に自動的にコンテナを削除
–name {名前} コンテナに名前を付ける

-dを常に付ける理由は、Dockerコンテナを不用意に停止させないためです。例えば、-dを付けずに実行すると、

$ docker run -it --name mypython python:3.8.1-buster /bin/bash

root@da3508c54ee2:/# exit
exit

$ docker ps

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

というように、プロセスが終了するとコンテナが停止してしまいます。デタッチ/アタッチという仕組みもありますが、それをやるなら-dを使うほうが簡単です。

$ docker run -d -it --name mypython python:3.8.1-buster

13b59f84fc3d68f53c244e0801c336f6f439dce75a359ac1b5f05e96753c1bd9

$ docker exec -it mypython /bin/bash

root@13b59f84fc3d:/# exit
exit

$ docker ps

CONTAINER ID        IMAGE                 COMMAND             CREATED             STATUS              PORTS               NAMES
13b59f84fc3d        python:3.8.1-buster   "/bin/bash"         9 seconds ago       Up 7 seconds                            mypython

このように、コンテナのシェルにはdocker execで接続します。この方法だとシェルを抜けてもコンテナは停止しません。停止したいときはdocker stopで明示的に停止します。

--rmを付ける理由としては、コンテナを停止状態で置いておく必要がないからです。なのでコンテナを停止したら勝手に削除してくれたほうがコンテナを削除する手間が省けます。コンテナの中で作成したファイルを消したくないというケースはあるかと思いますが、それであればdocker run時にホストOSのフォルダをマウントしてホストOS側に保存するか、もしくはdocker commitでDockerイメージ化したほうが安全です。「コンテナを誤って削除してしまった」という事故を防げます。

--nameを付ける理由は、これをしないとDockerに割り当てられたランダムな単語を使ってコンテナの操作をしなければならなくなるからです。

$ docker run -d -it --rm python:3.8.1-buster
783aab3c9c46dda9ad44e8289871f1ac0594698ef85799b93da43587b357f336
d

$ docker ps

CONTAINER ID        IMAGE                 COMMAND             CREATED             STATUS                  PORTS               NAMES
783aab3c9c46        python:3.8.1-buster   "/bin/bash"         1 second ago        Up Less than a second                       romantic_rhodes

というように、この場合ではromantic_rhodesという名前か、もしくはCONTAINER IDを使わなければならず、操作性が低くなります。

docker runのコマンドが長くて見づらい

オプションが多くなった場合は、バックスラッシュで改行すると見やすいです。 (Windowsはキャレットで改行できる?)

$ docker run \
    -d \
    -it \
    --rm \
    --name mypython \
    python:3.8.1-buster

Dockerイメージのゴミを削除したい

Dockerを使っていると、Dockerイメージのゴミが溜まってきます。そんなときはdocker system pruneとやると掃除できます。たまに実行すると良いかと思います。

$ docker system prune

WARNING! This will remove:
  - all stopped containers
  - all networks not used by at least one container
  - all dangling images
  - all dangling build cache

Are you sure you want to continue? [y/N] y
Deleted Images:
untagged: koirand/mypython@sha256:09973f3e3a51d59d5d57306b928a3c24ee4cbd8baeda46ab60e3810e38c1d9d4
deleted: sha256:80db86c8f1ae595cedd7d909156bd0638e21ab7c6b6da48af57fff58bdd30e31
deleted: sha256:70eca39dc93885416684888ad63f2e68133c848e0708ffba0681d5938cfb6936
deleted: sha256:142edfeafc60e404b5038812ede95e8a5fb902b6e041231d9e0304eab7127a37
deleted: sha256:ef94e002c5727977623f12810fc8e8c45c8c89d80ec1d613c059e85b222bc61f
deleted: sha256:3f70246ee63ab87ba8717df29a81c7b5eb0812493da7e03b0c23363ec3513661
deleted: sha256:f5e1b6e88039e92cad924d47fafe4c0c533914cf2d8d136396f47e22e99a6f62
deleted: sha256:7d31605ba9a91ad8c70438b2dbb8fee6f599b165137f03a58b918274dbd106eb
deleted: sha256:930f81dcf389890495ca1103ab4361718b4dc51a1d149b88454424566e76293b
deleted: sha256:899090de47ef18ae38c91752ae5d3d91bcea683c0b7a1610d92a56a61f82e694
deleted: sha256:014bf9ec8a38021ca309be775d9bbd55be782417b03e79b53fda9e43dc1261d4
deleted: sha256:05e4bef6d74ab30ceda651deaf8cf5098493ae4b3637f80cc84d0d4d39e008a8
deleted: sha256:a2e778d0eb88ab6a02ff378eb3605d9bdf34fef5545427a22312bb0cb85729ef
deleted: sha256:f1c46cc442566e473088a0cd5ea74e6da8bcbe4456f041e461e721d7c2f29e8c
deleted: sha256:66e1bbe79ff4646f032b7aeaa8acda8bf28fbac489dc55a403cb2d025a56ee9a
deleted: sha256:96c1612de1b3dea0b223faafdbde1553e17697468b5951456d338105bf208de3

Total reclaimed space: 447.5MB

VSCode拡張機能

VSCodeの便利な拡張機能を紹介します。

Docker - Visual Studio Marketplace

これを入れると、ツールバーにDockerアイコンが表示され、コンテナの操作をGUIで行うことができます。(さっき紹介したゴミの削除もできます。)

vscode1

Remote - Containers - Visual Studio Marketplace

これはまだプレビューですが、VSCodeからコンテナに直接アクセスできる革命的な拡張機能です。

vscode2

エイリアス

毎回dockerを打つのがダルいという人はエイリアスを設定すると良いかと思います。

alias d="docker"

以上、Docker入門講座でした。お疲れさまでした。