- Overview
- Summary
- Docker Hub
- Dokcer Image とは
- Docker コンテナでのコマンドの実行
- Docker Imageの管理
- Web サーバーを Docker で構築する
- コンテナのライフサイクル
- コンテナのシェルに接続する
- Docker commit
- Docker link
Overview
Docker について今更ながら入門したのでその記録をつらつらと書いていく。
Summary
Docker について雰囲気でしか理解しておらず、実務でちゃんと使えるレベルになかったので Udemy の「ゼロから始める Docker によるアプリケーション実行環境構築」 を見ながら、今更 Docker の基礎中の基礎について再入門しました。
Docker Hub
Docker向けのコンテナ共有サービス「Docker Hub」
Dokcer Image とは
Docker コンテナの実行に必要なファイルをまとめたファイルシステム(AUFS) OSのライブラリ、アプリケーション、ミドルウェアがすでにインストールされている(RubyやPHPを含) Webサーバーを作る場合は nginx や apatch がすでにインストールされている、というイメージ。 イメージのデータはレイヤで構成され read only
Docker コンテナは軽量であることが求められる。 過去のレイヤーのファイルは消えない。削除を実行したコンテナレイヤー上でファイルは削除されるが、履歴は残り続けるのであるレイヤーでファイルを追加した処理は残り続ける。 1度大きなファイルを追加してイメージ化したものをあとのレイヤで削除したとしても、イメージにはその過去追加した大きなファイルは残り続けるのでイメージ全体が肥大化する。 Dockerコンテナはなるべく軽量であることが求められているので、なるべく無駄なファイルがイメージの中に存在しないようにイメージを作成することが大事。 Dockerイメージがデカイ -> イメージのDL, Docker Hub 等へのアップロードに時間がかかる、ということ。 イメージを作成するときはサイズに注意。
Docker コンテナでのコマンドの実行
docker run docker/whalesay cowsay Hello
を実行する。
※ docker/whalesay は Docker 社が公開してる公式のアスキーアートのイメージ。docker run でコマンドを実行する練習をすることができる。
docker run $ImageName {$command} {$args}
で docker コンテナ内部で指定したコマンドを実行できる。
$ docker run docker/whalesay cowsay Hello Unable to find image 'docker/whalesay:latest' locally latest: Pulling from docker/whalesay Image [docker.io/docker/whalesay:latest](http://docker.io/docker/whalesay:latest) uses outdated schema1 manifest format. Please upgrade to a schema2 image for better future compatibility. More information at [https://docs.docker.com/registry/spec/deprecated-schema-v1/](https://docs.docker.com/registry/spec/deprecated-schema-v1/) e190868d63f8: Pull complete 909cd34c6fd7: Pull complete 0b9bfabab7c1: Pull complete a3ed95caeb02: Pull complete 00bf65475aba: Pull complete c57b6bcc83e3: Pull complete 8978f6879e2f: Pull complete 8eed3712d2cf: Pull complete Digest: sha256:178598e51a26abbc958b8a2e48825c90bc22e641de3d31e18aaf55f3258ba93b Status: Downloaded newer image for docker/whalesay:latest _______ < Hello > ------- \ \ \ ## . ## ## ## == ## ## ## ## === /""""""""""""""""___/ === ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ / ===- ~~~ \______ o __/ \ \ __/ \____\______/
こんな感じ。
Docker Imageの管理
docker images
を叩く。
現在 local に存在する image の一覧を確認できる。
イメージのリポジトリ名を変更する
docker tag docker/whalesay emahiro_whalesay
で local のイメージに新しくタグを追加することができる。
$docker images emahiro_whalesay latest 6b362a9f73eb 4 years ago 247MB
こんな感じ。
タグを打つ
docker tag docker/whalesay emahiro_whalesay:emahiro
で元々のイメージにタグを打って分けることが可能になる。
$ docker images emahiro_whalesay emahiro 6b362a9f73eb 4 years ago 247MB emahiro_whalesay latest 6b362a9f73eb 4 years ago 247MB
こんな感じ。
イメージをビルドする
イメージを構築するには Dockerfile (イメージの定義ファイル)
Dockerfile からイメージを構築することを イメージビルド
と言う。
Dockerfile には 命令 引数
と言う形式で構築手順を記載していく。
ex. FROM docker/whalesay:latest
は新しいイメージをビルドするときの大元(タネ)になるイメージに docker/whalesay
イメージの latest
タグを使う、と言うことを表現しています。
ビルド時に指定する命令1つあたりが1レイヤーとして元となるベースイメージに追加される、と言う意味になる。
よくわかってなかったDocker build のあれこれ
CMD
命令 -> コンテナが作成された後に実行するコマンドを指定する命令。-t
optison: ビルドした Docker イメージに名前をつけるオプション。ex.docker build -t emahiro_whalesay .
- ビルドコンテキスト: イメージを作成する際にアクセスできるディレクトリやファイルの範囲を示す。
- 特に指定がなければ
.
でビルドしたい Dockerfile と同じ階層のカレントディレクトリを指定する。 - イメージ内に含めたいファイルやディレクトリを参照する場合は、ビルドコンテキスト内であればコピーすることができる。ビルドコンテキスト外のファイルはイメージ構築時に参照することができない。
- イメージビルド時にビルドコンテキストないのファイルやディレクトリはまとめて Docker deamon に送信される。大きなファイルがビルドコンテキストに含まれる場合に、ファイルの転送処理に時間がかかるので、不要なファイルはビルドコンテキストに含まない方が吉。
- ビルドコンテキストに含まれるファイルが全てイメージに取り込まれる、と言うわけではなく、イメージビルドした際に一時的に Docker deamon に転送されるだけで
COPY
やADD
と言った命令で明示的にイメージ内にファイルをコピーする命令を使わなければイメージ内にビルドコンテキスト上のファイルが保存されることはない。 - Docker build コマンドはデフォルトでビルドコンテキスト状にある Dockerfille が読み込まれる。
- 特に指定がなければ
サンプル
$ docker build -t emahiro-whale . Sending build context to Docker daemon 2.048kB
Sending build context to Docker daemon 2.048kB
はビルドコンテキストを Docker deamon に転送している処理。
ビルドキャッシュ
各ステップのビルドの結果をキャッシュとして保持したもの。 Dockerfile の内容が同じであればキャッシュが使用されて、実際の命令はスキップされる。
$ docker build -t emahiro-whale . Sending build context to Docker daemon 2.048kB Step 1/3 : FROM docker/whalesay:latest ---> 6b362a9f73eb Step 2/3 : RUN apt-get -y update && apt-get install -y fortunes ---> Using cache # キャッシュを使う ---> 2a92623b6954
※ 毎回動作が変わるようなコマンドには注意が必要 ( ex. apt-get -y update
など)
なぜならば、apt-get -y update
を記載した本人は毎回パッケージリストを更新することを期待しているかもしれないが、Dockerfile 上はコマンドが変わらないのでビルドキャッシュが利用されてしまい、実際の動作では package リストが更新されない、と言う問題が発生するからである。
--no-cache
を使うとキャッシュが使用されずに全てのステップが実行される。
Image をアップロードする
学習なので Docker Hub にアップロードする。このイメージのアップロード先については Google Cloud Registry や Amazon Elastic Container Registory などクラウドベンダーが用意してるコンテナイメージのホスティングサービスもある。 ローカルからイメージをビルドしてアップロードする場合、リモートのホスティング環境にすでに同名のイメージが存在するケースではタグを切らない場合にイメージが上書きされてしまうので注意が必要である。
- Docker image をホスティングしてるサーバーへのログインする。事前にログインをしておく必要がある。
docker login
-> デフォルトで Docker Hub へのログインをする。- タグ付のルール ->
$DockerID/$Image:$Tag
ex.docker tag emahiro-whale emahiro/emahiro-whalesay:v1
- Docker Hub へ Push する。
docker push emahiro/emahiro-whalesay:v1
※ Docker Hub では事前にリポジトリを作成しておかなくても、$DockerID/$Image:$Tag
のフォーマットで push
すれば自動でリポジトリが作成されます。
Web サーバーを Docker で構築する
Nginx) のイメージを使用して Web サーバーを立ててみる。
Nginx イメージについて
使用するのは Exposing external port
に記載されてる docker run
コマンド。
$ docker run --name $ContainerName -d \ -p $HostPortNum:$ContainerPortNum \ $ImageName
$ContainerName で指定したコンテナ名はコンテナを識別したり、コンテナ操作を行う上で必要になるのでできるだけ設定するようにしておくのが吉。
-p
はコンテナのポートをコンテナ外に公開する設定。前が外部に公開する番号で、後ろがコンテナにマッピングされているポート番号。
docker run --name emahiro-nginx -d -p 8080:80 nginx Unable to find image 'nginx:latest' locally latest: Pulling from library/nginx afb6ec6fdc1c: Pull complete b90c53a0b692: Pull complete 11fa52a0fdc0: Pull complete Digest: sha256:30dfa439718a17baafefadf16c5e7c9d0a1cde97b4fd84f63b69e13513be7097 Status: Downloaded newer image for nginx:latest 24e6863779c15bcd1580808b9efcd02ac8c871827de1f0fae12455a6bb840c06 # 起動したコンテナのID
コンテナを起動・停止・削除をする時には末尾のコンテナIDかコンテナ名を使用する。
-d
はデタッチモード。デタッチモードの場合に foreground でコンテナが動作するわけではないので、コンテナを立ち上げたまま他の操作をすることが可能。
デタッチモードを指定しないと foreground で nginx のコンテナが動作しているので Ctnl + C でコンテナを停止すると nginx の動作も停止します。
ホスト上の HTML を Nginx コンテナで公開する(バインドマウント)
Docker Hub の Nginx) に記載されている Hosting some simple static content
に習って進める。
Sample.
$ docker run --name some-nginx -v /some/content:/usr/share/nginx/html:ro -d nginx
-v
: Volume のv
を意味している。- 静的なHTMLファイルを Nginx で公開するものを想定している。
$ docker run --name $ConteinarName -d \ -v $HostDir:$ContainerMountedPoint:$Option \ -p $HostPortNum:$ContainerPortNum \ $ImageName
-v
の引数がコロン区切りで3つに分かれている。
$HostDir
: コンテナにマウントするホスト側のディレクトリ$ContainerMountPoint
:コンテナ側のディレクトリ。Nginx コンテナにおいては/usr/share/nginx/html
が公開対象のドキュメントルートになる。$Options
: 権限を指定できる。 ex.ro
マウント先のコンテナに読み取り専用でマウントする指定。なくても動作が可能。
※ 引数の指定は 絶対path
で指定することが必要。
pwd
コマンドを使えば現在いるディレクトリの絶対パスを表示することが可能。(だからよくサンプルとかで $PWD
と言う環境変数がよく出てきたのか、と言うことを今更理解した。)
Sample
docker run -it --name emahiro-nginx \ -v $PWD/assets/html:/usr/share/nginx/html:ro \ -d -p 8080:80 \ nginx 6af418943c7300bb2ac9e03f47aab91eb6cf3dbbc0fe87c0796ffa408e10ab43
COPY, ADD 命令
ホストマシン上のファイルをイメージ内にコピーする命令。
サンプルとして、 Nginx の修正した設定ファイルをコンテナ内にコピーしてみる。
編集する設定ファイルを取り出す
docker run --name emahiro-nginx --rm -d nginx
--rm
: コンテナを停止した時点で自動でコンテナの削除まで行うオプション。コンテナは通常停止しても停止状態で残り続けてしまう。これにより、同名のコンテナを作成するときなどに名前がバッティングして起動できなくないので、すでに同名のコンテナが存在する場合は削除する必要がある。毎回削除するのは手間なので --rm
オプションをつけることで、停止した時にコンテナ自体も削除することで名前がバッティングしたりするケースを避けることができる。
Nginx のコンテナを起動し、設定ファイルを取り出すことのみをするので、デタッチモードで起動する。
デタッチモードで起動したら Nginx の設定ファイルを取得する。
docker cp
ホストマシン → コンテナ へのコピー
$ docker cp $FilePathOnHost \ $ContainerName(or $ContainerID):$PathToFileOnContainer
コンテナ内のファイル → ホストマシン へのコピー
$ docker cp $ContainerName(or $ContainerID):$PathToFileOnContainer \ $PathToCopyOnHost
設定ファイルをコンテナにCOPYする
Docker ファイルで以下のようにコンテナに転送するファイルを指定します。
FROM nginx:latest COPY nginx_conf/default.conf /etc/nginx/conf.d/default.conf
そしてビルドコンテキストを .
に指定してイメージをビルドします。
$ docker build -t emahiro-nginx:v1 .
ADD 命令と COPY コマンドの違い
COPY コマンドは単純にファイルをコピーする。
ADD 命令は COPY 命令の機能に加えて tar でアーカイブされたファイルをコピー時に自動で展開したり、 COPY 元にURLを指定した場合は、URLからダウンロードしてコピー先に転送するといった動作を行う。
ADD 命令の動作は一見すると名前から類推しづらい動作をするため、ベストプラクティスとしては通常は COPY 命令を使い、ADD 命令に備わった機能が必要な場合のみ ADD 命令を使うことが推奨されている。
コンテナのライフサイクル
前提
- コンテナとは Docker Image のファイルシステムを元にして作られる一種の仮想環境。
- コンテナが起動してる間は、コンテナはホストマシンの1プロセスとして動作する。
- コンテナが作成されてから削除されるまでにいくつものステータスがある。
Created Status
コンテナが作成されてスタートされる前の状態。
docker create
を使ってコンテナを作成した状態がこのステータス。
docker create --name status-test -it alpine /bin/sh
-i
: コンテナの標準入力を取得して、双方向に接続できるようにするオプション。
-t
: コンテナ内にtty を割り当てる。コンテナでシェルを実行して、foreground で実行状態のままにしておきたい時によく使う。
-it
の組み合わせてよく使われる。
docker create
をしただけでは実行中ではないので docker ps
コマンドでは出力されない。 docker ps -a コマンドを利用する。
docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d1ed34aa3314 alpine "/bin/sh" 3 minutes ago Created status-test
Running Status
コンテナが作成され、Created 状態になった後に Start されると Runninng ステータスになる。
docker run
コマンドは create → start がセットになったもの。docker ps
で表示さえる Status には起動時間が表示される(=起動時間が表示されているのは Running status)
docker inspect
でも起動状態を確認できる。
$ docker inspect status-test [ { "Id": "abf9fb1cecaa8bdd08e643a41065c2048c072e401823a962997a2fdbedbdf3b0", "Created": "2020-05-25T10:20:21.487691823Z", "Path": "/bin/sh", "Args": [], "State": { "Status": "running", # これ。 "Running": true, "Paused": false, "Restarting": false, "OOMKilled": false, "Dead": false, "Pid": 2629, "ExitCode": 0, "Error": "", "StartedAt": "2020-05-25T10:23:29.494591462Z", "FinishedAt": "0001-01-01T00:00:00Z" }, #略
Paused Status
docker pause
コマンドで一時停止させた時にこのステータスになる。
docker pause status-test status-test ➜ docker git:(il/docker) docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES abf9fb1cecaa alpine "/bin/sh" 5 minutes ago Up 2 minutes (Paused) status-test
docker inspect
でも確認可能。
$ docker inspect status-test [ { "Id": "abf9fb1cecaa8bdd08e643a41065c2048c072e401823a962997a2fdbedbdf3b0", "Created": "2020-05-25T10:20:21.487691823Z", "Path": "/bin/sh", "Args": [], "State": { "Status": "paused", #これ "Running": true, "Paused": true, "Restarting": false, "OOMKilled": false, "Dead": false, "Pid": 2629, "ExitCode": 0, "Error": "", "StartedAt": "2020-05-25T10:23:29.494591462Z", "FinishedAt": "0001-01-01T00:00:00Z" }, #略
Paused Status のときは起動中のコンテナにリクエストを投げたりしても応答ができない場合がある。
(ってことはクライアントのタイムアウトのテストの時とかに使える?)
docker unpause
で解除できる。
Restarting Status
コンテナの再起動中のステータス。
通常はすぐに起動しちゃうのでこのステータスを見ることは稀。
Exited Status
コンテナは終了したのに、コンテナが残り続けてるとこのステータスになる。
$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 892fdf2e3fb8 hello-world "/hello" 15 seconds ago Exited (0) 15 seconds ago recursing_williamson
foreground で動作が終了したコンテナや、 docker stop
で停止させたコマンドがこの Exited ステータスになります。
Removing Status
コンテナ削除中のステータス。すぐに削除されちゃうのでこのステータスをみることは稀。
Dead Status
正常に終了できずにゾンビ化したコンテナがこのステータスになる。この状態のコンテナは削除するしかない。通常の docker rm
で削除できないので docker rm -f
で削除する。
コンテナのシェルに接続する
docker attach
を使用する
docker attach $ContainerName(or $ContainerID)
コンテナの pid:1 の標準入出力に接続する。コンテナ起動時のコマンドでシェルを起動した場合は attach
するとそのシェルに接続することができる。
ただし起動時にシェルでなく、デーモンを起動していた場合はデーモンの標準入出力に接続してしまう。
起動時に --it
オプションを使うことがセットになる。
docker exec
を使用する
docker exec
は起動してるコンテナ内で任意のコマンドを実行するためのコマンド。
シェルに接続するには -it
オプションつきでシェルを実行する。接続したシェルを抜けるには exit
コマンドを使います。
$ docker exec -it $ContainerName(or $ContainerID) /bin/bash
docker attach
と違い、 exit
で抜けてもコンテナが停止することはない。誤ってコンテナを停止させてしまうことを考えると docker exec
コマンドの方が安全。
Example
$ docker run --name connect-test -it -d ubuntu /bin/bash f612509fd241330357446a8d3a275a09c73968ac3fbd3743dee282305b4c9791 $ docker attach connect-test exit $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 104fb2fb5779 ubuntu "/bin/bash" 18 seconds ago Exited (127) 1 second ago connect-test # docker exec のケース $ docker exec -it connect-test /bin/bash root@42933e32d725:/# exit exit $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 42933e32d725 ubuntu "/bin/bash" About a minute ago Up About a minute connect-test
exec
コマンドのケースではコンテナが停止していないことが確認できます。
Docker commit
docker commit
: 存在するコンテナを対象に docker commit を実行した新しく保存するイメージ名、タグ名を指定する → docker commit $ContainerName $ImageName:$Tag
コンテナの状態をイメージとして保存することができる。
docker commit
コマンドで何かしら操作をしたコンテナからイメージを作成した場合、コンテナ内で行われた作業はどこにも明確な記録として残らなくなってしまうこと。
通常、Docker Image を作成する際に Dockerfile で実行された各コマンドはイメージの各レイヤーとして何のコマンドが実行されたかが記録されています。それを docker history
で確認することが可能ですが、コンテナ内部で操作したコマンドは docker history
コマンドの結果には出力されません。
Dockerfile で Image をビルドした場合は Dockerfile からもどういったコマンドを実行して作成したイメージなのかをあとから確認することもできる。
どのような操作で作成されたイメージなのかわからなくなってしまうと、何が含まれていて、どのような変更が加えられているのか把握しづらくなる。つまり使いづらい Docker Image になってしまう。
通常は Dockerfile を使うのが吉。
# ubuntuをベースに tempfile を加えたイメージを作成する。 $ docker run --name commit-test -it --rm -d ubuntu /bin/bash e61b85a3f4cd6cbf457e0b4bd25d40e7ffe1cbd430f25c8a4b4dd811b22ac585 $ docker exec -it commit-test /bin/bash root@e61b85a3f4cd:/# cd tmp root@e61b85a3f4cd:/tmp# dd if=/dev/zero of=tempfile bs=1M count=10 10+0 records in 10+0 records out 10485760 bytes (10 MB, 10 MiB) copied, 0.00829059 s, 1.3 GB/s root@e61b85a3f4cd:/tmp# ll total 10248 drwxrwxrwt 1 root root 4096 May 25 16:42 ./ drwxr-xr-x 1 root root 4096 May 25 16:40 ../ -rw-r--r-- 1 root root 10485760 May 25 16:42 tempfile root@e61b85a3f4cd:/tmp# exit # 一旦コンテナを抜ける。 $ docker commit commit-test commit-test:v1 # tempfile を作成した状態でイメージを作成する。
docker history
コンテナでどんな処理を行ったのかイメージのレイヤーが順に出力される。上から順に新しく追加されたレイヤーになっている。
docker history commit-test:v1 IMAGE CREATED CREATED BY SIZE COMMENT af6d001d6055 2 minutes ago /bin/bash 10.5MB # 10M を追加したレイヤー 1d622ef86b13 4 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B <missing> 4 weeks ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B <missing> 4 weeks ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 811B <missing> 4 weeks ago /bin/sh -c [ -z "$(apt-get indextargets)" ] 1.01MB <missing> 4 weeks ago /bin/sh -c #(nop) ADD file:a58c8b447951f9e30… 72.8MB
CREATED BY には bin/bash
の情報しか載っておらず、10Mを増やすのにどんな処理をしたのかは、このレイヤーを追加した人にしかわからない。
Infrastructure as code の観点からも、Docker においては Dockerfile を使うことが重要とされている。
Docker link
about link option
--link
オプションはいずれ廃止される可能性があるので詳細は割愛。
-e
: 引数の環境変数をコンテナに設定するオプション。
$ docker run --name $ContainterName -e ENV_VALUE="Hoge" -d $ImageName
$ContainerName で作成してコンテナ内で ENV_VALUE
に Hoge
という値がわかっていることになる。
$ docker run --name emahiro-ubuntu -it --rm -e EMAHIRO="emahiro" -d ubuntu /bin/bash 1e5d343ce111d9c5e26cfab553faa5d91c95a66fc8cfe9592581cd5f52f05b3b $ docker exec -it emahiro-ubuntu /bin/bash root@1e5d343ce111:/# echo $EMAHIRO emahiro
-e
オプションで指定した環境変数がコンテナで設定されてることがわかる。