Dockerコンテナグレートジャーニー
Dockerコンテナを0から理解する旅、Dockerコンテナ・グレートジャーニー第5回です。
これまで生Dockerの基礎を学んできました。今回はdocker-composeを用いて複数のコンテナを管理する意味と方法について解説します。
特に、前回までの記事も読まれた方にとっては、復習とDockerコマンドの応用にもなるはずです。
対象
- Dockerコマンドの基礎を学び終えた人
- docker-composeについて、概念レベルでの理解をしたい人
- docker-composeを実際に使いたい人
旅路(インデックス)
長いので記事を分割しています。
- そもそも仮想化とは? 仮想化ではないシステムとは?
- Dockerコマンド基礎(環境構築~ubuntu/httpdコンテナでのコマンド実行)
- Dockerのストレージについて
- Dockerのネットワークについて
- Docker-compose でコンテナをまとめる【⇦本記事】
- Docker image と Dockerfile
- AWS ECS で Dockerコンテナを走らせる(Comming soon)
【主題】docker-composeを、Dockerコマンドの集合体として理解する
こんなことはありませんか?
- dockerコンテナを使おうとググった時、Webの記事ではなぜか
docker-compose.yaml
を使って解説されていた。 - そこに書かれていたことをそのままコピペして使っている。
- 結局何をやっているのかよくわからない。
Dockerコマンドの基礎を学ばずにdocker-composeに手を出すと、間違いなくこのような「よくわかんないけど動く」状態に陥ってしまうでしょう。
しかし、Dockerコンテナグレートジャーニーの第五回までご一緒いただいた皆さんは問題なく理解できるはずです。なぜなら、docker-composeは実質的にDockerコマンドの集合体であり、Dockerコマンドを理解していれば難しくはないからです。
さて、これまでに基礎は学び終えたので、今こそ応用となるdocker-composeの理解を深める時です(まだ以前の記事を読んでいない、またはDockerコマンドの理解が不十分な場合は、まずは前記事を参照してください)
docker-compose とは何か?
docker-composeを一言で言うと、「複数のdockerコンテナをまとめて管理するためのもの」それに尽きます。
例えば下図のような、外部からのリクエストをNginxで負荷分散して2台のサーバーで応答する構成を、コンテナで実装する場合を考えます。
docker-composeを使用しない場合は、
- docker networkを作成する
- docker volume を作成する
- Webサーバーを2つ立ち上げる
- nginxを立ち上げる
という手順を踏むことになります。ユーザーが1つ1つのコマンドを打っていく必要があって面倒です。
コンテナが3つだけならまだいいでしょう。しかしこれが10,20…と増えていくと、まともにコマンドを打つのが馬鹿らしくなってきます。
そこでdocker-compose を使用すると、docker-compose up -d
と1コマンド打つだけですべてのコンテナが起動してくれます。
もちろん起動時だけではなく、コンテナを停止、破棄するときも1コマンドだけで済みます。
同時に管理するという特性上、それ以外にも数多くのメリットがあります。
なんとなくのイメージがつかめたところで、その威力を体感してみましょう。
docker-composeを使わずに、複数コンテナの連携を行う
まずはこれまでの記事で学んだことを応用して、docker-composeを使わずに下図の構成を実現してみましょう。
以前の記事の内容を思い出しながら実行していきましょう。
理解には実際にコマンドを打ってみるのが良いですが、時間が無い方は流し読みしてください。
1. ネットワークを作る
docker-networkを定義してそこにコンテナをアタッチすれば、IPアドレスだけでなくコンテナ名で通信ができるのでした。
通常、複数のコンテナを連携させる際には定義します。
# ネットワークを作成する
docker network create mynetwork
2. ストレージを作る
第3回で学んだ通り、Dockerコンテナのメモリは揮発性でしたね。コンテナを破棄した後もデータが残るように、Volumeを作成しておきましょう。
# ストレージを作成する
docker volume create vol_html1
docker volume create vol_html2
3. nginxコンテナを立ち上げる(失敗する)
負荷分散にも使われるwebサーバーであるnginxを立ち上げます。
なお、nginxには設定ファイルが必要です。今回はhost/1
のディレクトリのリクエストはweb1に、host/2
のディレクトリへのリクエストはweb2のサーバーに振り分けるようにします。
ワークフォルダ配下に、下記のようにファイルを配置してください。
work
|--nginx
|--default.conf
upstream backend {
server web1:80;
server web2:80;
}
server {
listen 80;
location /1 {
proxy_pass http://web1/; # 最後のスラッシュを付けて明示的にパスを指定しないと、http://web1/1 にアクセスされて通信ができないので注意
}
location /2 {
proxy_pass http://web2/; # 同上
}
}
ここではnginxについては解説しません。よくわからない方は「2つのサーバーに処理を振り分けてくれるもの」と考えていただければ大丈夫です。
ではnginxコンテナを立ち上げていきます。
docker container run -dit --name nginx_1 --network mynetwork -p 8080:80 --mount type=bind,src="$(pwd)"/nginx/,dst=/etc/nginx/conf.d nginx:latest
これでnginxコンテナが立ち上がります。が、1つ問題が発生します。コンテナを立ち上げて20秒待つと、コンテナが停止してしまいます。
PS C:\Users\******\qiita_docker_compose> docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8ed4fd0fe032 nginx:latest "/docker-entrypoint.…" 18 seconds ago Exited (1) 7 seconds ago nginx_1
このようになってしまう理由は、nginxが通信すべきweb1およびweb2のコンテナが見つからないからです。nginxはこのような場合にエラーとなってプロセスを終了させます。メインプロセスが終了するということは、コンテナが終了するということでした。
こんな経緯で、コンテナが終了してしまいます。
つまるところ、nginxのコンテナより先に、web1, web2のコンテナを立ち上げなければならない。つまり依存関係が存在することになります。
4. httpdコンテナを2つ立ち上げる
というわけで、先にweb1, web2コンテナを立ち上げていきましょう。
使用するイメージは、以前の記事から引き続きhttpd:2.4。コンテナ名を「web1」「web2」としましょう。
docker container run -dit --name web1 --network mynetwork --mount type=volume,src=vol_html1,dst=/usr/local/apache2/htdocs httpd:2.4
docker container run -dit --name web2 --network mynetwork --mount type=volume,src=vol_html2,dst=/usr/local/apache2/htdocs httpd:2.4
ストレージには最初に作成したvolumeを使用しています。
下記コマンドで起動したことを確認しましょう。
docker container ls -a
なお今回は、視覚的にweb1とweb2の違いを区別するため、下記のコマンドで応答するHTMLデータを書き換えておきます。
docker exec web1 /bin/bash -c "echo '<html><body><h1>It works on web1!</h1></body></html>' > htdocs/index.html"
docker exec web2 /bin/bash -c "echo '<html><body><h1>It works on web2!</h1></body></html>' > htdocs/index.html"
5. nginxコンテナを再起動する
web1, web2コンテナがしっかりと立ち上がっていれば、nginxはきちんと動作するはずです。
先ほど終了してしまったコンテナを再度起動します。
docker container start nginx_1
20秒ほど待って起動の有無を確認します。
docker container ls -a
6. 疎通テストを行う
これで準備が整ったので、きちんとサーバーにアクセスできるか確認してみます。
Webブラウザを開いてhttp://localhost:8080/にアクセスしましょう。下の画像のようになっていれば成功です。
7. コンテナを破棄する
- docker container ls
- docker network ls
- どのコンテナおよびネットワークを起動したか調べる
- docker container stop nginx_1 web1 web2
- docker container rm nginx_1 web1 web2
- docker network rm mynetwork
- Volumeももう使わないので削除する
- docker volume ls
- docker volume rm vol_html1 vol_html2
これでスッキリしました。面倒ですね。
ここまでは、docker-composeを使わずDockerコマンドのみでWebサーバーを立ち上げてきました。
Dockerコンテナを扱うにあたって、この感覚はとても重要です。最小単位でコンテナがどう動くかを知っていれば、応用する技術への解像度が大きく違ってきます。
ここまでのことがよくわからない場合は、まずは前回までの記事を復習してみてください。
「コンテナがどう動くのかなんとなく理解できた」という方は、次にdocker-composeを使ってみましょう。
docker-compose を使った場合
準備として、フォルダの下記の場所にdocker-compose.yaml
ファイルを作成してください。
work
|--nginx
|--default.conf
|--docker-compose.yaml
ファイルの内容は以下をコピペしてください。docker-compose.yamlの説明は後で行うので、ひとまずは体感してみましょう。
version: '3.8'
services:
nginx:
image: nginx:latest
ports:
- 8080:80
volumes:
- type: bind
source: ./nginx
target: /etc/nginx/conf.d
depends_on:
- web1
- web2
web1:
image: httpd:2.4
volumes:
- type: volume
source: html1
target: /usr/local/apache2/htdocs/
web2:
image: httpd:2.4
volumes:
- type: volume
source: html2
target: /usr/local/apache2/htdocs/
volumes:
html1:
html2:
では起動します。
docker-compose up -d
以上です。これで3つのDockerコンテナと、ネットワークと、ストレージが起動しました。煩わしい操作はなく、個別にコンテナを立ち上げるのは面倒でしたが、docker-composeを使えばこれだけです。
なお今回は、視覚的にweb1とweb2の違いを区別するため、下記コマンドでコンテナが応答するHTMLデータを書き換えておきます。
docker-compose ps
# 注意:docker-compose で立ち上げたコンテナは、名前にプレフィックスが付くので、`docker exec <コンテナ名>`のコンテナ名の部分は適宜書き換えてください
docker exec qiita_docker_compose-web1-1 /bin/bash -c "echo '<html><body><h1>It works on web1!</h1></body></html>' > htdocs/index.html"
docker exec qiita_docker_compose-web2-1 /bin/bash -c "echo '<html><body><h1>It works on web2!</h1></body></html>' > htdocs/index.html"
疎通確認
先ほどと同じように、localhostにアクセスしてコンテナが正しく動いているか確認しましょう。
下図のようになっていればOKです!
コンテナを破棄する
検証が終わったのでコンテナを破棄します。
破棄するコマンドは、
docker-compose down
以上です。とても簡単ですね。
コンテナやネットワーク、それぞれの動作状況はdocker-composeが裏側でしっかり管理しているので、このコマンドだけでdocker-compose up -d
で起動したものをきれいに削除してくれます。(※Volumeは削除されていません)
これで、docker-composeによるコンテナ立ち上げから破棄までを体験できました。
では次に、docker-composeの動作を定義していたdocker-compose.yamlファイルについて見ていきます。
docker-compose.yaml解説
ここからは、docker-compose.yaml内の1つ1つの記述について解説していきます。
yamlファイルは、「:
」とインデントによって分割、親子構造を表現するファイル形式です。
# docker-composeのバージョン指定です。
version: '3.8'
# コンテナの親部分
services:
# コンテナ名。
# 外から見るとプレフィックスがついた名前になりますが、同一docker-compose内のコンテナからは`nginx`として認識されます
nginx:
#Dockerイメージを指定しています。
image: nginx:latest
# docker コマンドの -p 8080:80 と同じです。ホストの8080とコンテナの80ポートを接続します
ports:
- 8080:80
# dockerコマンドの --mount type=bind,src=***,dst=*** と同じです。
volumes:
- type: bind
source: ./nginx
target: /etc/nginx/conf.d
# 依存関係。
# これを書くことで、コンテナが[web1, web2 -> nginx]の順に起動することができます。
# これにより、nginxが接続先を見つけられないエラーを回避できます。
# 加えて、docker-composeを停止するとき、[nginx -> web1, web2] の順番で停止することができます。
# 注) コンテナの起動順序を制御するだけで、コンテナ内部のサービス起動まではチェックできません
depends_on:
- web1
- web2
ここから下はweb1コンテナ
# コンテナ名
web1:
# Dokcerイメージの指定
image: httpd:2.4
# docker コマンドの`--mount type=volume,src=html1,dst=/usr/local/apache2/htdocs/`と同じです
volumes:
- type: volume
source: html1
target: /usr/local/apache2/htdocs/
(※web2についてはweb1と全く同じなので省略します)
いかがでしょうか?
コマンドを細かく見ていくと、dockerコマンドで実行していたことと同じことをやっています。というより、dockerコマンドでのコンテナ起動が元になっているので当然ですね(dockerコマンドを忘れている場合は、本記事の第2~4回目を再度参照ください!)。
また、docker-compose内ではネットワークを明示的に定義をしていません。しかしdocker-composeを使用して作られたコンテナは、起動時に自動的に作られたネットワークに参加しています! Dockerコマンドのみで作業をしていた時と比べて、ネットワークを定義する手間が省けるのです。
なお、自動作成されたネットワークはdocker-compose down
を実行したときに自動で削除されているので、我々はそもそもネットワークの存在を気にしなくても通信ができます。
まとめ
- docker-composeは、複数のコンテナをまとめて管理できる
- yamlファイルによって記述されるが、実態は、Dockerコマンドの集合体
- ネットワーク設定をしなくても、自動で定義してくれる
-
depends_on
で、コンテナの依存(起動/停止の順番)を定義できる
いかがでしたでしょうか?
docker-composeをDockerコマンドレベルから理解することで、そのメリットと利点をより享受できるようになるはずです。
いいことずくめなツールですので、ぜひ使っていきましょう!
参考:その他docker-compose のコマンド
docker-compose ~~
のように以下のコマンドを使えます。
- stop / start
-
docker-compose -down
はコンテナを終了&破棄までしてしまうのに対して、stop
破棄しません。 -
start
は、stop
したコンテナ群を再開できます。
-
- ps
- 該当docker-composeで起動中のコンテナ一覧を確認できます。
- exec
- Dockerコマンドと同様、任意のコマンドを実行できます
- logs
- それぞれのコンテナに対してログを確認することができます。
- top
- プロセスの確認
- --scale
- コンテナの起動数を指定できる。例えば、web1コンテナの性能が足りないので、3台に増やしたい! という設定もできます。
-
--
を付けないscale
は非推奨になっているので注意
参考資料
最初はこちらの本で勉強しました
depends_on について載っている公式ドキュメントです。