今回はDocker Composeに関する内容をアウトプットしていきたいと思います。
以前の記事でDockerfileに関する記事を書きました。併せて読んでいただくと理解の手助けになるかと思います。
Dockerfileとも密接に関係のあるDocker Composeについて解説したいと思います。
参考資料はDocker/Kubernetes実践コンテナ開発入門 改訂新版 を参考にしています。
Docker Composeとは
Docker Composeとは一言でいうと、単一のマシン上で複数のコンテナをまとめて管理する機能です。
こんな絵見たことないでしょうか?
まぁ、これはComposerと言われるPHPのライブラリ管理ツールのアイコンなので別物なのですが、
Compose = 作曲する という意味になりますが、これだけだとよく意味がわかりませんね。
作曲するには楽譜にメロディーを書いて、最終的に出来上がった楽譜を見ながら楽器を使って音楽が完成します。
Docker Composeに話を置き換えてみましょう。
Docker Composeの絵は以下のようなものです。タコが足を使って複数のコンテナを持っていますね。
Docker Composeにもcompose.ymlと呼ばれる楽譜のようなものがあります。
このcompose.ymlファイルにどんなコンテナを定義したいのか?を書いていきます。(楽譜の場合はどんな曲を作りたいのか)
先に結論を言うと、このファイルが出来上がって$ docker compose up
とコマンドをたたくとタコが動き出して定義したコンテナが起動し始めます。
アーキテクチャ
comose.ymlファイルを使ってコンテナが作られるアーキテクチャは以下のようなものです。
compose.ymlのファイルの例は以下のようなものです。
version: '3.9' services: mysql: build: context: ./containers/mysql environment: MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password MYSQL_DATABASE: taskapp MYSQL_USER: taskapp_user MYSQL_PASSWORD_FILE: /run/secrets/mysql_user_password secrets: - mysql_root_password - mysql_user_password volumes: - mysql_data:/var/lib/mysql ports: - "3306:3306"
- docker compose upコマンドを実行すると、タコを召喚し、compose.ymlに書かれた内容を実行する
- compose.ymlには
build:
に書かれたコンテキストファイルを読み込む → ここでは/container/mysql
配下に置かれているDockerfileとその他設定ファイルを読み込み - DockerfileのFROMに記述されたイメージをもとにDocker Hubからイメージを取得
- DockerfileのFROM以下の内容を実行しimageをbuild
- imageからDockerコンテナが起動される
上記のような流れでDocker Composeが実行されます。
単一のコンテナを操作する場合と複数のコンテナを操作する際の違い
単一のコマンドを起動する際には、dockerコマンドでコンテナへの操作を提供しますが、docker composeコマンドを使うことで複数のコンテナに対して実行、停止などの操作を1つのコマンドで行うことができます。
※誤解無きように記載しておくと、もちろん単一のコンテナに対してもdocker composeコマンドは使用可能です。
単一のコンテナを起動する際には以下のようなコマンドを打って、一つのコンテナを立ち上げます。
docker container run -d --name [コンテナ名] [コンテナイメージ名]
しかし、docker composeを使うことでcompose.ymlに定義した複数のコンテナを立ち上げることができます。
docker compose up -d # compose.yml内で定義したsevicesのうち特定のコンテナのみ立ち上げたい場合はserivces名を指定する # 指定しない場合はcompose.ymlで定義したservicesがすべて起動する docker compose up -d [起動させたいコンテナサービス名1] [起動させたいコンテナサービス名2]
実際にcompose.ymlが何を書いているのかを説明していきましょう。
version: '3.9' services: # (1)-1 mysql: # (1)-2 build: # (2) context: ./containers/mysql environment: # (3) MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password MYSQL_DATABASE: taskapp MYSQL_USER: taskapp_user MYSQL_PASSWORD_FILE: /run/secrets/mysql_user_password secrets: # (4)-1 - mysql_root_password - mysql_user_password volumes: # (5)-1 - mysql_data:/var/lib/mysql ports: # (6) - "3306:3306" secrets: # (4)-2 mysql_root_password: file: ./secrets/mysql_root_password mysql_user_password: file: ./secrets/mysql_user_password volumes: # (5)-2 mysql_data:
(1)-1,2 services
servicesは作成するサービス(コンテナ)を定義しています。
今回の例ではmysqlという名前のコンテナを作りますよ、という定義をしています。
services: mysql:
(2) build
実際に./container/mysql
配下にはDockerfileやコンテナに必要なビルドコンテキスト(設定ファイル)などを配置することによって、Dockerfileをもとにしたイメージをbuildします。
ビルドコンテキストについては以下に解説していますので併せて参照してみてください。
(3) environment
環境変数を指定する箇所です。
environment: # (3) MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password MYSQL_DATABASE: taskapp MYSQL_USER: taskapp_user MYSQL_PASSWORD_FILE: /run/secrets/mysql_user_password
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
MYSQL_PASSWORD_FILE: /run/secrets/mysql_user_password
上記二つはMySQLのrootユーザパスワード, 一般ユーザ(MySQLユーザ)のパスワードが保存されているディレクトリを参照する指示になっています。
/run/secrets/mysql_root_password
にMySQLに接続する際のrootユーザのパスワードが記述されたファイルがあるのでそこを参照しなさい。
/run/secrets/mysql_user_password
に一般ユーザ(MySQLユーザ)のパスワードが記述されたファイルがあるのでそこを参照しなさい。と指示を出しています。
このディレクトリはコンテナ内のディレクトリを指しており、厳密には後述するsecretsで定義しているホスト側で生成したパスワードを参照する仕組みになっています。
MYSQL_DATABASE: taskapp
MYSQL_USER: taskapp_user
上記二つはMySQLに作成するデータベース名、ユーザ名を定義しています。
(4)-1,2 secrets
secretsは環境変数に渡す値を定義します。
secrets: # (4)-1 - mysql_root_password - mysql_user_password secrets: # (4)-2 mysql_root_password: file: ./secrets/mysql_root_password mysql_user_password: file: ./secrets/mysql_user_password
環境変数に渡す値を定義する理由としては、ymlファイルに直接パスワードなどのシークレットな値を記述すると、GitHub上に誤って公開してしまった場合に大事故になるからです。
GitHubは基本的プライベートにしていたとしても、誤ってパブリックで公開されてしまった場合のリスクもあるため、パスワードなどのシークレットな情報はymlファイルに直接記述することは推奨されません。
前提を理解できたところで、secretsをどう読み取っているのかを見てみましょう。
図で表すと以下です。
compose.ymlのsecretsディレクティブに記載されたmysql_root_password, mysql_user_passwordの箇所を参照しなさい、という指示で、ではそのsecretsの情報はどこのディレクトリに存在しますか?という記述が、file: 以降に記述のあるホスト内のデディレクトリにファイルが配置してあるので、そこを参照しなさい、というコードになっています。
/secrets/mysql_root_password
ファイルと/secrets/mysql_user_password
ファイルに記述があるため、そこを参照します。
さらにホストで定義したsecretsファイルをdocker compose up
を実行の際にコンテナ側のディレクトリへコピーすることで、コンテナがMySQL起動時に指定されたディレクトリを参照しMySQLへ接続できるわけです。
言葉だけで表現するとちょっとわかりづらいかと思いますが、図で表現すると理解がしやすいかと思います。
(5)-1,2 volumes
コンテナにマウントされるボリュームを定義します。
services: mysql: # services内で宣言されているvolumes volumes: # (5)-1 - mysql_data:/var/lib/mysql # 途中省略 # トップレベルのvolumes volumes: # (5)-2 mysql_data:
services内で宣言されているvolumesはmysql_data
というボリュームに対してコンテナ内の/var/lib/mysql/
がマウントしているという意味になります。
では、mysql_data
というボリュームはどこで作られているのでしょうか?
それがトップレベルで宣言されているvolumesのmysql_data:
がそれにあたります。
このトップレベルで宣言されたvolumesによってデータを永続化するボリュームを作成し、mysqlコンテナがマウントします。
volumesの補足
volumesがどういう仕組みかを一時的なコンテナを作成して確認してみましょう。
まず、以下のコマンドを実行してbusyboxという名前のコンテナを作成し、さらにmyvolというボリュームを作成し、busyboxの/dir → myvol にマウントします。
イメージは何でも良いですがubuntu:23.10を使用します。
$ docker container run --rm -it --name busybox -v myvol:/dir ubuntu:23.10 # busyboxに/dirディレクトリが作成されているためcdします root@283506ea2808:/# cd dir/ # /dir内にtest.txtファイルを作成し、exitします root@283506ea2808:/dir# touch test.txt root@283506ea2808:/dir# exit # --rm を指定しているため、exitした時点でbusyboxは破棄されます # dockerボリュームが作成されていることを確認します $ docker volume ls DRIVER VOLUME NAME local myvol # 新たなbusyboxを作成し、今度は/dir2としてmyvolにマウントします $ docker container run --rm -it --name busybox -v myvol:/dir2 ubuntu:23.10 # /dir2からmyvolに保存されているtest.txtが見えることが確認できます root@32a8631634c3:/# cd dir2/ root@32a8631634c3:/dir2# ll total 8 drwxr-xr-x 2 root root 4096 Mar 30 23:08 ./ drwxr-xr-x 1 root root 4096 Mar 30 23:37 ../ -rw-r--r-- 1 root root 0 Mar 30 23:08 test.txt
このボリュームについてですが、最初非常にややこしいですが、以下の記事が非常にわかりやすく書かれていました。
ぜひ参考に読んでいただくと良いかと思います。
Docker、ボリューム(Volume)について真面目に調べた – Qiita
(6) ports
最後にportsです。
ports: # (6) - "3306:3306"
ポートはホストマシンとDockerコンテナ間のネットワークポートマッピングを紐づけます。
上記の場合、コンテナ内で接続を待ち受けるポート3306番をホストのポート3306番に紐づけています。
今回はDocker Compseについて解説してみました。
参考資料: