今回は Docker Compose を使って複数のコンテナをまとめて管理する方法について。
docker run
コマンドを使ってチマチマとやるよりもぐっと楽にできる。
コンテナオーケストレータを使うほどでもないけど複数台コンテナを扱いたい…っていうシチュエーションかな?
今回使った環境は次の通り。 Docker のディストリビューションとしては Docker for Mac を使う。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.13.3 BuildVersion: 17D102
インストール
Docker Compose は Docker for Mac をインストールすれば一緒についてくる。
まずは Homebrew Cask を使って Docker をインストールする。
$ brew cask install docker
初期設定とサービスの起動をするために、インストールした Docker アプリケーションを実行する。
$ open /Applications/Docker.app
しばらくして Docker のサービスが立ち上がったら、次のように docker version
コマンドを実行する。
クライアントとサーバがエラーなく表示されれば上手くいっている。
$ docker version Client: Version: 18.03.0-ce API version: 1.37 Go version: go1.9.4 Git commit: 0520e24 Built: Wed Mar 21 23:06:22 2018 OS/Arch: darwin/amd64 Experimental: false Orchestrator: swarm Server: Engine: Version: 18.03.0-ce API version: 1.37 (minimum version 1.12) Go version: go1.9.4 Git commit: 0520e24 Built: Wed Mar 21 23:14:32 2018 OS/Arch: linux/amd64 Experimental: true
Docker Compose を使わずに複数のコンテナを管理する場合
まずは、もし Docker Compose を使わないで複数のコンテナを管理しようとした場合について考えてみる。 ここでは二つのコンテナを用意して、ネットワーク的な疎通があるようにしたい状況を考えてみよう。
まずは一つ目のコンテナを docker run
コマンドで起動する。
$ docker run --name container1 -it centos:7 /bin/bash
続いて二つ目のコンテナを起動する。
その際に --link
オプションを使って、先ほど起動したコンテナの名前を解決できるようにする。
ここでは一つ目のコンテナを c1
という名前で名前解決できるようにしている。
$ docker run --name container2 --link container1:c1 -it centos:7 /bin/bash
二つ目のコンテナのシェルから、一つ目のコンテナに向けて ping を打ってみよう。
# ping -c 3 c1 PING c1 (172.17.0.2) 56(84) bytes of data. 64 bytes from c1 (172.17.0.2): icmp_seq=1 ttl=64 time=0.120 ms 64 bytes from c1 (172.17.0.2): icmp_seq=2 ttl=64 time=0.096 ms 64 bytes from c1 (172.17.0.2): icmp_seq=3 ttl=64 time=0.098 ms --- c1 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2090ms rtt min/avg/max/mdev = 0.096/0.104/0.120/0.016 ms
ちゃんと疎通がとれた。
とはいえ、このやり方には色々と問題がある。 例を挙げると、それぞれのコンテナを起動するときのオプションを毎回正しく入力しなければ上手く動作しない。 正直そんなもの覚えていられないので、きっとそのうちシェルスクリプトを書き始めることになるだろう。
Docker Compose を使って複数のコンテナを管理する場合
先ほどは Docker Compose を使わずに複数のコンテナを協調させていた。 続いては Docker Compose を使った場合について書く。
Docker Compose では docker-compose.yml
という設定ファイルを基本にしてコンテナを管理する。
これで、先ほどの問題点だったオプションを毎回覚えておくような必要はなくなる。
$ cat << 'EOF' > docker-compose.yml version: '3' services: c1: image: centos:7 command: /usr/sbin/init c2: image: centos:7 command: /usr/sbin/init EOF
起動するコマンドとして /usr/sbin/init
を指定しているのは、コンテナをすぐに終了させないようにするため。
設定ファイルの書式については次の公式サイトに詳しく記載されている。
設定ファイルができたら docker-compose up
コマンドを使ってコンテナを起動する。
$ docker-compose up Creating network "compose_default" with the default driver Creating compose_c2_1 ... done Creating compose_c1_1 ... done Attaching to compose_c1_1, compose_c2_1
別のターミナルから docker ps
コマンドを使うと、それぞれのコンテナが起動していることが分かる。
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a434cf41dc96 centos:7 "/usr/sbin/init" 20 seconds ago Up 19 seconds compose_c2_1 a946853ecf00 centos:7 "/usr/sbin/init" 20 seconds ago Up 20 seconds compose_c1_1
ちなみに docker-compose ps
コマンドを使えば設定ファイルに含まれるコンテナだけの状態を確認できる。
これは docker-compose.yml
ファイルがあるディレクトリで実行する。
$ docker-compose ps Name Command State Ports --------------------------------------------- compose_c1_1 /usr/sbin/init Up compose_c2_1 /usr/sbin/init Up
同じく、コンテナでコマンドを実行したいときは docker-compose exec
コマンドを使う。
この場合 docker-compose.yml
ファイルにある名称でコンテナを指定できる。
$ docker-compose exec c1 /bin/bash
もちろん docker exec
コマンドを使っても構わない。
$ docker exec -it compose_c1_1 /bin/bash
シェルで ping コマンドを使ってコンテナ間で名前が解決できることを確認しよう。
[root@a946853ecf00 /]# ping -c 3 c2 PING c2 (172.18.0.3) 56(84) bytes of data. 64 bytes from compose_c2_1.compose_default (172.18.0.3): icmp_seq=1 ttl=64 time=0.184 ms 64 bytes from compose_c2_1.compose_default (172.18.0.3): icmp_seq=2 ttl=64 time=0.101 ms 64 bytes from compose_c2_1.compose_default (172.18.0.3): icmp_seq=3 ttl=64 time=0.110 ms --- c2 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2107ms rtt min/avg/max/mdev = 0.101/0.131/0.184/0.039 ms
ばっちり。
コンテナの作成順序を指定する
先ほどの例ではコンテナを作成する順序関係は関係なかった。 とはいえ、データベースなどを扱う場合には特定のコンテナを先に作って欲しいといったことがある。 次はそのような場合について扱う。
今度はデータベースを扱うシステムを想定してコンテナの一つを mysql
イメージにしてみた。
コンテナの作成に関係する記述は client
の depends_on
になる。
これを記述しておくと、そのコンテナは依存するコンテナよりも後に作られることになる。
また、ports
では外部に公開するポートを指定している。
$ cat << 'EOF' > docker-compose.yml version: '3' services: db: image: mysql environment: - MYSQL_ROOT_PASSWORD ports: - 3306:3306 client: image: centos:7 depends_on: - db command: /usr/sbin/init EOF
上記の設定ファイルでは db
コンテナに environment
も指定していた。
これはコンテナに渡す環境変数を指定する書式になっている。
一般的には <key>=<value>
という形で書くものの、上記では <key>
のみになっている。
これは Docker ホストで定義されている環境変数をそのまま渡すことを意味している。
パスワードなど設定ファイルに書けない秘密情報は、このようにしておくと良い。
$ export MYSQL_ROOT_PASSWORD=rootpasswd
docker-compose up
コマンドを使ってコンテナを起動すると、作成順序が必ず client -> db
の順になっているはず。
$ docker-compose up
ただし、この機能には注意すべき点が一つある。 それは、作成順序自体は指定できるもののコンテナの起動までは待ってくれないというところ。 実際のところはコンテナが起動するまで待ってほしいというニーズの方が大きいと思う。 なので、おそらくはシェルスクリプトなどを使ってサービスが起動するまで待つ (リトライする) ようなコードが必要になるだろう。
コンテナイメージも一緒に管理する
続いてはカスタマイズした Docker イメージを Docker Compose で管理する方法について。 Docker Compose ではコンテナ自体だけでなくコンテナイメージまで設定ファイルで管理できる。
先ほどの例ではデータベースのクライアントに相当するコンテナは作ったものの、実際に操作することはできなかった。 これは MySQL クライアントがインストールされていなかったため。 そこで、試しに MySQL クライアントをインストールした Docker イメージに変更してみよう。
まずは Docker イメージをビルドするのに必要な Dockerfile を用意する。
場所は docker-compose.yml
の下に client
というディレクトリを作って、そこに配置した。
$ mkdir client $ cat << 'EOF' > client/Dockerfile FROM centos:7 RUN yum -y update \ && yum -y install mysql EOF
ひとまず単体でイメージがビルドできることを確認しておこう。
単体で成功しないと docker-compose.yml
に組み込む以前の問題になってしまうため。
$ docker build -t example/client client ... Successfully tagged example/client:latest
イメージからコンテナを起動して mysql
コマンドが実行できることも確認しておく。
$ docker run -it example/client mysql --help mysql Ver 15.1 Distrib 5.5.56-MariaDB, for Linux (x86_64) using readline 5.1 Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others. Usage: mysql [OPTIONS] [database] ...
よさそうだ。
確認できたので、次は上記のコンテナイメージの情報を docker-compose.yml
に組み込む。
具体的には、次のように image
をビルド後のビルドイメージの名前にする。
その上で build
を指定して Dockerfile
のある client
ディレクトリを指定すれば良い。
$ cat << 'EOF' > docker-compose.yml version: '3' services: db: image: mysql environment: - MYSQL_ROOT_PASSWORD ports: - 3306:3306 client: image: example/client build: context: client depends_on: - db command: /usr/sbin/init EOF
こうしておけば docker-compose build
コマンドで設定ファイルに含まれるコンテナイメージが一気にビルドできる。
$ docker-compose build db uses an image, skipping Building client Step 1/2 : FROM centos:7 ---> e934aafc2206 Step 2/2 : RUN yum -y update && yum -y install mysql ---> Using cache ---> f82342f62e31 Successfully built f82342f62e31 Successfully tagged example/client:latest
あとは先ほどと同じように docker-compose up
でコンテナたちを起動する。
$ docker-compose up
クライアントに対応するコンテナのシェルをつかもう。
$ docker-compose exec client /bin/bash
データベースのコンテナに MySQL クライアントで接続してみる。
# mysql -u root -prootpasswd -h db Welcome to the MariaDB monitor. Commands end with ; or \g. Your MySQL connection id is 2 Server version: 5.7.21 MySQL Community Server (GPL) Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MySQL [(none)]>
ちゃんと接続できた!
ディスクを永続化する
先ほどの設定では MySQL がデータベースの情報を保存する先がコンテナのファイルシステム上になっていた。 これではコンテナが起動するごとにデータが消えてしまう。 そこで、次は MySQL のデータディレクトリとして Docker ホストのディレクトリをマウントさせることにしよう。
やることは単純で volumes
を指定するだけ。
あとはマウントする対応関係を <host-dir>:<container-dir>
という形で記述する。
$ cat << 'EOF' > docker-compose.yml version: '3' services: db: image: mysql environment: - MYSQL_ROOT_PASSWORD ports: - 3306:3306 volumes: - ./db-data:/var/lib/mysql client: image: example/client build: context: client depends_on: - db command: /usr/sbin/init EOF
ディレクトリのマウント周りは前のコンテナの情報が残っているといまいち上手くいかない感じみたい。 なので、一旦先ほどのコンテナを終了した上で全て削除しておこう。
$ docker system prune -f
そして設定ファイルを元にコンテナを起動する。
$ docker-compose up
Docker ホスト側でディレクトリを確認すると MySQL のデータディレクトリが指定した名前で作成されていることが分かる。
$ ls db-data auto.cnf ib_logfile0 private_key.pem ca-key.pem ib_logfile1 public_key.pem ca.pem ibdata1 server-cert.pem client-cert.pem ibtmp1 server-key.pem client-key.pem mysql sys ib_buffer_pool performance_schema
ちゃんとデータが永続化されているか確認しておこう。 まずはクライアントのシェルをつかむ。
$ docker-compose exec client /bin/bash
適当にデータを投入しておく。
# mysql -u root -prootpasswd -h db Welcome to the MariaDB monitor. Commands end with ; or \g. Your MySQL connection id is 2 Server version: 5.7.21 MySQL Community Server (GPL) Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MySQL [(none)]> CREATE DATABASE mydb; Query OK, 1 row affected (0.01 sec) MySQL [(none)]> USE mydb; Database changed MySQL [mydb]> CREATE TABLE users ( -> name TEXT, -> age INTEGER -> ); Query OK, 0 rows affected (0.03 sec) MySQL [mydb]> INSERT INTO users VALUES -> ('Alice', 20), -> ('Bob', 25), -> ('Carol', 30); Query OK, 3 rows affected (0.01 sec) Records: 3 Duplicates: 0 Warnings: 0
一旦コンテナを全て終了してから立ち上げ直す。
$ docker-compose down $ docker-compose up
もう一度クライアントのシェルをつかむ。
$ docker-compose exec client /bin/bash
確認すると、前回の内容が残っていることが分かる。 これで、コンテナを終了してもデータは Docker ホスト側に残り続けるので消えなくなった。
# mysql -u root -prootpasswd -h db # mysql -u root -prootpasswd -h db Welcome to the MariaDB monitor. Commands end with ; or \g. Your MySQL connection id is 2 Server version: 5.7.21 MySQL Community Server (GPL) Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MySQL [(none)]> SHOW DATABASES; +--------------------+ | Database | +--------------------+ | information_schema | | mydb | | mysql | | performance_schema | | sys | +--------------------+ 5 rows in set (0.03 sec) MySQL [(none)]> USE mydb; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed MySQL [mydb]> SHOW TABLES; +----------------+ | Tables_in_mydb | +----------------+ | users | +----------------+ 1 row in set (0.01 sec) MySQL [mydb]> SELECT * FROM users; +-------+------+ | name | age | +-------+------+ | Alice | 20 | | Bob | 25 | | Carol | 30 | +-------+------+ 3 rows in set (0.02 sec)
とりあえず、これくらい覚えておけば大丈夫そうかな。
スマートPythonプログラミング: Pythonのより良い書き方を学ぶ
- 作者: もみじあめ
- 発売日: 2016/03/12
- メディア: Kindle版
- この商品を含むブログ (1件) を見る