こんにちはフロントエンドエンジニアのまさにょんです!
今回は、Docker・Docker Composeでよく使用するコマンドとオプションについて考え方と使い方をまとめていきます。
わかりやすさ重視で、すべてDockerの新型CLIの書き方で、統一してまとめているので、その点は注意してください。
新型CLI何それって方は、ぜひこちらを見てみてください。
また、今回の記事は、実践 Docker – ソフトウェアエンジニアの「Docker よくわからない」を終わりにする本 にて、
Dockerを学習(引用・参照)しながら、まとめた記事になります。
目次
Docker Image系のコマンド・オプション
所持しているイメージを一覧表示する
docker image ls
イメージを削除する(Containerが存在していない状態)
docker image rm < IMAGE ID >
# または、、、
docker image rm < REPOSITORY:TAG >
イメージを強制的に削除する(Containerも同時に削除する)
Containerが存在する状態で、イメージを削除しようとすると次のエラーが発生します。
Error response from daemon: conflict: unable to remove repository reference.
(翻訳: デーモンからのエラー応答: 競合: リポジトリ参照を削除できません)
上記のエラー対応としては、docker container rm
コマンドでコンテナを削除してから、docker image rm
を実行するか、
または、次のように-f
オプションを付与して、イメージを強制的に削除する方法があります。
-f
オプションを使用してのイメージ削除は、イメージに紐づいているContainerも同時に削除します。
docker image rm -f < REPOSITORY >
Docker Container系のコマンド・オプション
起動中のコンテナだけを一覧表示する
docker container ps
# または、、、
docker container ls
停止中も含めて、すべてのコンテナを一覧表示する
docker container ls
でも同じオプションを使用することができます。
docker container ps --all
# または、、、
docker container ps -a
イメージを使用して、コンテナを作成・起動する
docker container run
コマンドではIMAGE ID
またはREPOSITORY:TAG
を指定して、コンテナを新しく作成&起動することができます。
docker container run < IMAGE ID >
# または、、、
docker container run < REPOSITORY:TAG >
バックグラウンドで、コンテナを作成・起動する
-d
または、--detach
を使用することで、バックグラウンドでコンテナを新しく作成・起動することができます。
docker container run -d < IMAGE ID >
# または、、、
docker container run -d < REPOSITORY:TAG >
コンテナに名前を付与して、コンテナ内部のターミナルに接続する
docker container run --name app --rm -it docker-practice:app bash
停止中のコンテナを削除する
docker container rm < CONTAINER ID >
# または、、、
docker container rm < NAMES >
起動中のコンテナでも強制的に削除する
docker container rm -f < CONTAINER ID >
# または、、、
docker container rm -f < NAMES >
ボリュームを作成する
コンテナのファイルを保持するには?
コンテナ内で作成されたファイルはコンテナの削除とともに全て廃棄されます。
そうなると ログやデータを次起動するコンテナに引き継げない ので開発に不自由してしまいます。
そこで、Docker にはホストマシンとコンテナでファイルを共有する方法がいくつか用意されています。
1つの方法であるボリュームを使い、DB コンテナのデータベースのデータが消失しないようにします。
ボリュームとは?
ボリュームはコンテナ内のファイルをホストマシン上で Docker が管理してくれる仕組み です。
ホストマシン側のどこに保存されているかは関心がなくとにかくデータを永続化したい という場合に有用で、
たとえばデータベースのデータの永続化に活用できます。
コンテナの特定のディレクトリをボリュームとしてホストマシン側で管理すれば、
コンテナが削除されてもデータが消失しなくなります。
ボリュームの作成
docker volume create [option]
# db-volume という名前で、ボリュームを作成する
docker volume create --name db-volume
オプション | 意味 | 用途 |
---|---|---|
--name | ボリューム名を指定 | ID ではなく名前で扱えるようにする |
ボリュームの確認
ボリューム一覧を確認するには、 volume ls
を使います。
docker volume ls
# [ 実行結果 ]
DRIVER VOLUME NAME
local db-volume
DB コンテナにボリュームをマウントする
作成したボリュームは、コンテナ起動時にマウントして使います。
container run
には --volume
オプションと --mount
オプションというほぼ同じことができるオプションがあります。
–volume オプションによるマウント
docker container run \
--name db \
--rm \
--detach \
--platform linux/amd64 \
--env MYSQL_ROOT_PASSWORD=rootpassword \
--env MYSQL_USER=hoge \
--env MYSQL_PASSWORD=password \
--env MYSQL_DATABASE=event \
--volume docker-practice-db-volume:/var/lib/mysql \
–mount オプションによるマウント
--mount
オプションは key=val
形式で設定を列挙 して指定します。
主なキーは type
と source
と destination
で、ほかに readonly
のような任意オプションのキーもあります。
また、source
は src
など、destination
は dst
や target
などの略記も存在します。
ボリュームをマウントするには type
に volume
を指定します。
source
はボリューム名の docker-practice-db-volume
を指定します。
destination
は /var/lib/mysql
を指定します。
docker container run \
--name db \
--rm \
--detach \
--platform linux/amd64 \
--env MYSQL_ROOT_PASSWORD=rootpassword \
--env MYSQL_USER=hoge \
--env MYSQL_PASSWORD=password \
--env MYSQL_DATABASE=event \
--mount type=volume,src=docker-practice-db-volume,dst=/var/lib/mysql \
docker-practice:db
ボリュームの詳細を確認する
docker volume inspect docker-practice-db-volume
[
{
"CreatedAt": "2023-04-02T22:28:02Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/docker-practice-db-volume/_data",
"Name": "docker-practice-db-volume",
"Options": {},
"Scope": "local"
}
]
バインドマウントとは
バインドマウントは、ホストマシンの任意のディレクトリをコンテナにマウントする 仕組みです。
ホストマシンとコンテナ双方がファイルの変更に関心がある という場合に有用で、たとえばソースコードの共有などに活用できます。
ソースコードのディレクトリをコンテナにバインドマウントしてホストマシン側と共有すれば、
ホストマシンでコードを変更した際に同期や転送が不要 になります。
バインドマウントはボリュームと違い 既存のディレクトリをそのままマウントする ので、事前作成などはありません。
バインドマウントも --volume
オプションと --mount
オプションどちらでも行えます。
バインドマウントの実体と注意
バインドマウントの実体は そのままホストマシンのファイルシステム です。
つまりバインドマウントはボリュームと比べると 実体の面倒を見ているのが Docker ではなく自分 であり、それが ホストマシン上 であるという違いがあります。
「仮想環境だから」と安易に rm -rf *
でもして、もしそこにバインドマウントしたディレクトリが含まれていたら、削除はホストマシンに波及します。
Docker の公式も まずはボリュームを検討し、どうしてもだめならバインドマウントを使え と言っています。
COPY とバインドマウントの使い分け
COPY
とバインドマウントはどちらもホストマシンのファイルをコンテナで扱えるようにする機能ですが、
用途 と 反映タイミング を理解しておかないと扱いを間違えやすいので整理しましょう。
COPY
は image build
をするタイミングでイメージにファイルを含める ため、コンテナが起動すればファイルが存在 します。
また 元ファイルの変更を行ってもコンテナには反映されない ため、image build
の再実行が必要 になります。
COPY
の用途には次のようなものが挙げられるでしょう。
- 設定ファイルなど、コンテナによって変えない かつ 滅多に変更しない ものを配置する場合
- 本番デプロイ時のソースコードなど、即起動できる配布物を作る 場合
対してバインドマウントは イメージではなくコンテナに行う ため、
同じイメージを使ってもファイルの存在はコンテナ起動のオプションによって異なります。
また ホストマシンでファイルを変更するとコンテナに即時影響します。
バインドマウントの用途には次のようなものが挙げられるでしょう。
- 開発時のソースコードなど、ホストマシンで変更したいがコンテナに随時反映したい ものがある場合
- 初期化クエリなど、イメージを配布する時点では用意できない ものがある場合
2つは イメージに対して行っている か コンテナに対して行っている かが明確に違います。
その点をちゃんと理解しておけば、使い分け も 変更したいならどうすべきか も判断できます。
バインドマウントの要点
- バインドマウントは ホストマシンでもファイルに関心がある という用途に向いている
- コンテナ内でのファイル削除などが ホストマシンに影響する 可能性がある
- イメージに対して行う
COPY
とは コンテナに対して行う 点が大きく違う
ポートの公開
コンテナにホストマシンからアクセスするために、ポートの公開について学びます。
ポートの公開とは?
コンテナはホストマシンから隔離されており、コンテナ内で Web サーバや DB サーバを起動してもそのままではホストマシンからアクセスすることはできません。
Web サーバなどいわゆるパブリックネットワークに置くようなコンテナでは、
その問題を解決するために ホストマシンに対して ポートの公開を行うことになります。
ポートを公開して、コンテナを起動するオプション – container run
公開ポートは --publish host-machine:container
の形式でマッピングします。
オプション | 意味 | 用途 |
---|---|---|
-p, --publish | コンテナのポートを ホストマシンに公開する | ホストマシンからコンテナ内の サーバにアクセスする |
このとき host-machine
側のポートは起動する人が自由に決めます が、
1024 未満のポートは特権ポートと呼ばれるあらかじめ用途が決められているポートなるので避けておくのが無難です。
コンテナの 8000
ポートをホストマシンの 8000
ポートに割り当てることも可能ですが、
8000
ポートは使われていることが多いので今回は 18000
ポートにマッピングします。
$ docker container run \
--name app \
--rm \
--detach \
--interactive \
--tty \
--mount type=bind,src=$(pwd)/src,dst=/src \
--publish 18000:8000 \
docker-practice:app \
php -S 0.0.0.0:8000 -t /src
正しく表示されない場合は、--detach
オプションを外して出力をよく確認したり、
コンテナに配置されている設定ファイルを bash
を使って確認したりしてください。
ホストマシン – コンテナ の通信はできるようにはなりましたが、コンテナ – コンテナ の通信は ポートの公開では解決できません。
コンテナ – コンテナ の通信は、ネットワークで対応します。
ポート公開の要点まとめ
- ホストマシンからアクセス したい場合は、コンテナのポートを公開する
- コンテナ側のポートは、起動しているサービスに合わせる
- ホストマシン側のポートは、衝突しないように自分で決める
ネットワークを作成する – network create
docker network create [option] name
ネットワークを指定して、コンテナを起動するオプション – container run
オプション | 意味 | 用途 |
---|---|---|
--network | コンテナをネットワークに接続 | ほかのコンテナと通信できるようにする |
--network-alias | コンテナにネットワーク内での エイリアスを設定 | ほかのコンテナから指定しやすくする |
Docker のネットワークについて
Docker のコンテナはネットワークドライバというもので Docker ネットワークに接続されます。
ネットワークドライバはデフォルトでいくつか用意されており、
たとえばブリッジネットワークやオーバーレイネットワークというものがあります。
ブリッジネットワークにはこのような特徴があります。
- ネットワークドライバを特に指定しなかった場合の デフォルト である
- 同一の Docker Engine 上のコンテナ が互いに通信をする場合に利用する
オーバーレイネットワークにはこのような特徴があります。
- 異なる Docker Engine 上のコンテナ が互いに通信をする場合に利用する
ユーザ定義ブリッジネットワーク
コンテナを起動する際にネットワークドライバについて一切の指定を行わないと、
デフォルトブリッジネットワークが自動的に生成され、コンテナはこのネットワークに接続されます。
このデフォルトブリッジネットワークには次のような特徴があります。
- コンテナが通信するためには、全てのコンテナ間をリンクする操作が必要になる
- コンテナ間の通信は IP アドレスで行う
- Docker Engine 上の全てのコンテナ ( たとえば別プロジェクト ) に接続できてしまう
これに対し、自分でブリッジネットワーク(ユーザ定義ブリッジネットワーク)を作成すると、
デフォルトネットワークと比べて次のような利点のあるネットワークに接続できます。
- 相互通信をできるようにするには同じネットワークを割り当てるだけでよい
- コンテナ間で自動的に DNS 解決を行える
- 通信できるコンテナが同一ネットワーク上のコンテナに限られ、隔離度があがる
「コンパイルをしてほしい」「静的コンテンツをホスティングしてほしい」のように
コンテナ間通信を必要としない場合はデフォルトブリッジネットワークで十分 ですが、
「PHP から MySQL を使う」のように、コンテナ間通信を必要とする場合はユーザ定義ブリッジネットワークを使う と判断してよいでしょう。
ユーザ定義ネットワークを作成する
ネットワークの作成は、次のコマンドで行います。
docker network create [option] <name>
この本では特に [option]
は指定せず、docker-practice-network
という名前のネットワークを作ります。
docker network create docker-practice-network
これでコンテナをネットワークに接続する準備ができました。
ネットワーク一覧を確認する
ネットワーク一覧を確認するには、 network ls
を使います。
docker network ls
NETWORK ID NAME DRIVER SCOPE
6905ce02f3e1 bridge bridge local
7d0ac1503226 docker-practice-network bridge local
a99e9af11f39 host host local
2811948d2fd4 none null local
bridge
と host
と none
はデフォルトの必ず存在するネットワークで、
それに加えて1つの bridge
ドライバの docker-practice-network
があることを確認できます。
App コンテナをネットワークに接続する
ボリュームのときと同様に、作成したネットワークにコンテナを接続するには container run
のオプションを追加します。
--network
で作成した docker-practice-network
を指定するようにコマンドを修正します。
docker container run \
--name app \
--rm \
--detach \
--mount type=bind,src=$(pwd)/src,dst=/src \
--publish 18000:8000 \
--network docker-practice-network \
docker-practice:app \
php -S 0.0.0.0:8000 -t /src
これで App コンテナがほかのコンテナに接続する準備ができました。
コンテナがネットワークに接続できたことを確認する
コンテナがネットワークに接続できているか確認するには、
ネットワークを検査する network inspect
コマンドとコンテナを検査する container inspect
コマンドを使います。
docker network inspect docker-practice-network | jq '.[].IPAM.Config'
[
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
次にコンテナの検査をしてみると、Networks
に docker-practice-network
が設定されており、
IPAddress
が 172.18.0.2
になっています。
docker container inspect app | jq '.[].NetworkSettings.Networks'
{
"docker-practice-network": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"4938ec87cf68"
],
"NetworkID": "7d0ac150322661f9991bf2f55c6bfe335f2b5fb13fc555661a7840a95104666a",
"EndpointID": "01c1b854019725e62a6b370975d0afaaceb5276051851d507b9cb77526c6f2d0",
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:12:00:02",
"DriverOpts": null
}
}
ネットワークの Gateway
とコンテナの Gateway
が一致していて、
コンテナの IPAddress
がそれに続く値になっていれば、ちゃんと設定できています。
DB コンテナをネットワークに接続し、エイリアスを設定する
App コンテナだけネットワークに接続しても意味はないので、DB コンテナもネットワークに接続します。
当然接続するネットワークは App コンテナと同じ docker-practice-network
です。
また、App コンテナから DB コンテナにアクセスする時に使うホスト名を --network-alias
オプションを使って設定します。
docker container run \
--name db \
--rm \
--detach \
--platform linux/amd64 \
--env MYSQL_ROOT_PASSWORD=rootpassword \
--env MYSQL_USER=hoge \
--env MYSQL_PASSWORD=password \
--env MYSQL_DATABASE=event \
--mount type=volume,src=docker-practice-db-volume,dst=/var/lib/mysql \
--mount type=bind,src=$(pwd)/docker/db/init.sql,dst=/docker-entrypoint-initdb.d/init.sql \
--network docker-practice-network \
--network-alias db \
docker-practice:db
1216c252738eace7f9aabb7ef4015c0b35a26cdeb059d965df09c10d06b77ecb
これで DB コンテナがほかのコンテナから db
というホスト名で接続される準備ができました。
ネットワークとコンテナの Gateway
が一致しているか、コンテナの IPAddress
が妥当か、確認してみてください。
docker container inspect db | jq '.[].NetworkSettings.Networks'
{
"docker-practice-network": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"db",
"1216c252738e"
],
"NetworkID": "7d0ac150322661f9991bf2f55c6bfe335f2b5fb13fc555661a7840a95104666a",
"EndpointID": "0b3ee46fee9fc831607b0cc3e9cc50e0461985d04529d2d8170edde3d9e0edba",
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:12:00:03",
"DriverOpts": null
}
}
また、DB コンテナにはエイリアスを設定したので Networks
の Aliases
に db
が増えていることが確認できるはずです。
--network
オプションを正しく設定できたかは Gateway
と IPAddress
を、--network-alias
オプションを正しく設定できたかは Aliases
を、それぞれ確認できれば大丈夫です。
Docker のネットワークの要点
- コンテナ同士で通信したいときは 自分でネットワークを作る
- 同じネットワーク に接続したコンテナ同士なら通信できる
- エイリアス を設定すると ホスト名 で通信できる
Docker Compose系のCLI・コマンド操作
Docker ComposeでDockerfileから Docker Imageを作成(Build)する
docker-compose build
コマンドを使用すると、DockerfileからDocker Imageを再構築できます。
docker-compose build
コマンドは、Dockerfileを使用してイメージをビルドまたは再ビルドします。
docker-compose build
Docker Composeで Containerをバックグラウンドで起動する
次のコマンドで、定義したサービスをバックグラウンドで起動します。
docker-compose up -d
docker-compose up
コマンドは、docker-compose.yml
ファイルからサービスをビルドし、Containerを起動するためのコマンドです。
-d
フラグは、コンテナをデタッチドモードで実行することを指定しています。
Docker Composeで立ち上げた Container の 一括停止・一括削除
Docker Composeで立ち上げたDocker Containerたちは、次のコマンドで、一括停止 & 一括削除できます。
コマンドの実行は、プロジェクト・ルードで行います。
docker-compose down
ちなみに、docker-compose down
コマンドに引数は必要ありません。
ちなみに、docker-compose down
コマンドに引数は必要ありません。
引数を指定しなくても、このコマンドは、プロジェクト内のdocker-compose.yml
ファイルを自動的に探し、実行されます。
つまり、そのDirectory内のdocker-compose.yml
を自動で発見して、定義されている、すべてのサービスのコンテナを停止 & 削除してくれるわけです。
引数に指定したサービスのみを停止・削除するには、docker-compose stop
やdocker-compose rm
コマンドを使用する必要があります。