前回の記事
Docker入門 ~Dockerfile編~
の続きです。
Docker-compose
Docker-composeは、複数のコンテナを簡単に操作できる仕組みです。
使用したいコンテナの数が増えるとdockerコマンドをうつのが大変ですが、docker-compose
コマンドで楽になります。
ファイル構成は以下とします。
.
├── docker-compose.yml
├── mysql
│ ├── Dockerfile
│ └── my.cnf
└── public
└── index.html
mysqlディレクトリの内容
FROM mysql:5.7.27
RUN apt-get update \
&& apt-get install -y locales \
&& rm -rf /var/lib/apt/lists/* \
&& echo 'ja_JP.UTF-8 UTF-8' >> /etc/locale.gen \
&& locale-gen ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
COPY ./my.cnf /etc/mysql/conf.d/my.cnf
[client]
default-character-set=utf8mb4
[mysqld]
character-set-server=utf8mb4
docker-compose.yml
docker-compose.ymlで、docker-composeコマンドでまとめて起動するコンテナの設定を記述します。
version: '3.7'
services:
php:
image: my-php-apache:7.3.10
ports:
- 80:80
- 443:443
networks:
- web
volumes:
- ./:/var/www
mysql:
build: ./dockerfiles/mysql
expose:
- 3306
networks:
- web
volumes:
- mysql:/var/lib/mysql
environment:
- MYSQL_DATABASE=laravel
- MYSQL_USER=laravel
- MYSQL_PASSWORD=secret
- MYSQL_ROOT_PASSWORD=secret
networks:
web:
volumes:
mysql:
上記では、phpコンテナとmysqlコンテナを設定しています。docker-compose up
コマンドで、コンテナをまとめて起動できます。
$ docker-compose up
Creating network "sample_web" with the default driver
Creating volume "sample_mysql" with default driver
Creating sample_php_1 ... done
Creating sample_mysql_1 ... done
Attaching to sample_php_1, sample_mysql_1
docker-compose.yml解説
上記ymlファイルの解説です。
version: '3.7'
Docker-compose.ymlの記述方法のバージョンを指定しています。古いバーションだと使用できないキーなどがあります。
services:
php:
...
mysql:
...
Docker-composeはコンテナをサービスとして管理します。ここではphpとmysqlのコンテナ設定を書いていきます。
services:
php:
image: my-php-apache:7.3.10
ports:
- 80:80
- 443:443
networks:
- web
volumes:
- ./:/var/www
- image - 使用するイメージ
- ports - ポートのバインディング。
ホスト:コンテナ
の順番でかく - networks - コンテナが所属するネットワーク(後述)
- volumes - マウントするボリューム(後述)
ボリュームは前回利用しましたが、データをコンテナとは別に管理して永続化する仕組みでした。後ほどネットワークと合わせて詳しく解説します。
mysql:
build: ./mysql
expose:
- 3306
networks:
- web
volumes:
- mysql:/var/lib/mysql
environment:
- MYSQL_DATABASE=laravel
- MYSQL_USER=laravel
- MYSQL_PASSWORD=secret
- MYSQL_ROOT_PASSWORD=secret
- build - Dockerfileのパス
イメージではなくDockerfileも指定できます。
- expose - 他のサービスに公開するポート(ホストからはアクセスできない)
- environment - コンテナ内で使用できる環境変数
特定の環境変数を設定しておくと楽にカスタマイズできるように、ベースイメージ側で準備してくれていることがあります。
MySQLイメージのDockerHubページを見ると、MYSQL_DATABASEでデータベース名を設定できると分かります。
ここでは上からデータベース名、データベースのユーザー名、ユーザーのパスワード、ルートユーザーのパスワードを設定しています。Laravelからデータベースに接続するときに、この情報で接続します。
networks:
web:
volumes:
mysql:
各サービスで使用するネットワーク・ボリュームを明示します。忘れると動作しません。
Dockerネットワーク
Docker-composeというよりDockerの仕組みですが、ここで説明します。
コンテナは同じ仮想ネットワークに配置されないと、相互にやりとりできません。なので、ApacheのコンテナとMySQLのコンテナは同じネットワークにしてあげます。
services:
php:
networks:
- web
mysql:
networks:
- web
networks:
web:
同じネットワークにすると、コンテナ内から他のコンテナにアクセスするときに、サービス名でアクセスできるようになります。
phpコンテナに配置したLaravelアプリケーションから、MySQLコンテナにアクセスするには.envファイルをこう書きます。
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=secret
DB_HOSTがmysql
とサービス名になっていて、これで繋がります。内部的にはDockerがDNSサーバーを用意して、名前解決してくれるようです。
Dockerボリューム
こちらもDocker-composeというよりDockerの仕組みですが、ここで説明します。
MySQLコンテナを立ち上げて、DBにデータを保存してもコンテナを削除するとそのデータは消えてしまいます。データを永続保存するための仕組みがボリュームです。
ボリュームはコンテナとは切り離して管理されるもの、ということです。
ボリューム一覧は以下のコマンドで確認できます。
$ docker volume ls
DRIVER VOLUME NAME
local tmp
...
ボリュームには以下の3種類があります。
- ホスト(host)
- 匿名(anonymous)
- 名前付き(named)
ホストボリューム
-v /host/path:/container/path
でホストのパスをコンテナのパスにマウントし、ボリュームとします。
$ docker container run -dit -v $HOME/tmp:/tmp alpine
上記の例ではコンテナを削除しても、/tmpの内容は$HOME/tmpに残ります。そしてホストボリュームはdocker volume ls
には表示されません。
匿名ボリューム
-v /container/path
で、匿名ボリュームです。
$ docker container run -dit -v /tmp alpine
Dockerが隠蔽して管理するボリュームで、ハッシュ値がボリューム名となります。
$ docker volume ls
DRIVER VOLUME NAME
local a1138c254e5df7d419191d29e9e7e5acaf6389d69521c09eb00abe05889151ad
名前付きボリュームがなかった時代に使われていたようで、現在は名前付きボリュームを使います。
docker container run --rm
で立ち上げたコンテナが削除される時、匿名ボリュームも削除されるという特徴があります。
名前付きボリューム
-v name:/container/path
で名前付きボリュームです。
$ docker container run -dit -v tmp:/tmp alpine
こちらもDockerが隠蔽して管理するボリュームですが、名前がありアクセスしやすいです。
$ docker volume ls
DRIVER VOLUME NAME
local tmp
またdocker container run --rm
で立ち上げたコンテナが削除されても名前付きボリュームは削除されません。
どの種類のボリュームにするか
最初のdocker-compose.ymlを見ると、phpコンテナはホストでファイルを編集したいので、ホストボリューム./:/var/www
にしています。
MySQLコンテナは、特にホストでMySQLが管理するファイルをいじることはないですから、名前付きボリュームmysql:/var/lib/mysql
です。ただホストボリューム./.db:/var/lib/mysql
などにしても問題はないと思います。
また、前回でやったcomposerの例で考えてみましょう。
composer install
するためのcomposer.jsonや、インストール結果のvendorディレクトリなどをコンテナとやりとりするだけであれば、ホストボリュームでいいです。
今後別のコンテナでcomposer install
するためのキャッシュは、名前付きボリュームでどこからでも参照できるようにしておきましょう。
$ docker container run --rm -it -v $PWD:/app -v composer_cache:/tmp composer install
上記なら、2回目以降は以下のようにキャッシュからインストールしてくれます。
$ docker container run --rm -it -v $PWD:/app -v composer_cache:/tmp composer install
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Package operations: 84 installs, 0 updates, 0 removals
- Installing doctrine/inflector (v1.3.0): Loading from cache
- Installing doctrine/lexer (1.1.0): Loading from cache
...
ボリュームにファイルがあると
-
ボリュームにファイルあり、コンテナになし
ボリュームのファイルがコンテナにコピーされる -
ボリュームにファイルなし、コンテナにあり
コンテナのファイルがボリュームにコピーされる -
両方にファイルあり
コンテナのファイルがなくなり、ボリュームのファイルがコンテナにコピーされる
実際にdocker-composeコマンドを使ってみよう
docker-compose.ymlがあれば、以下のコマンドでコンテナを一気に立ち上げることができます。
$ docker-compose up
起動したコンテナ、ネットワーク、ボリュームを確認してみましょう。
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
af5e814670de my-php-apache:7.3.10 "docker-php-entrypoi…" 5 minutes ago Up 5 minutes 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp sample_php_1
e4116e1a3bfa sample_mysql "docker-entrypoint.s…" 5 minutes ago Up 5 minutes 3306/tcp, 33060/tcp sample_mysql_1
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
81e33015a6f9 bridge bridge local
e2eab5ac1197 host host local
a8432cc388c6 none null local
37b562bfd9c1 sample_web bridge local
$ docker volume ls
DRIVER VOLUME NAME
local sample_mysql
ネットワークのbridge,host,noneは、デフォルトで用意されているネットワークなので気にしないでください。
dockerコマンドと同じように、exec <service> <command>
でコンテナ内でコマンドを実行できます。
$ docker-compose exec php bash
root@026c8f03e602:/var/www#
MySQLに接続するのであれば
$ docker-compose exec mysql mysql -u laravel -D laravel -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
コンテナ削除もdocker-compose down
でまとめてできます。コンテナとネットワークが削除され、ボリュームは残ります。
$ docker-compose down
Stopping sample_php_1 ... done
Stopping sample_mysql_1 ... done
Removing sample_php_1 ... done
Removing sample_mysql_1 ... done
Removing network sample_web
プロジェクト
コンテナ、ネットワーク、ボリューム名がsample_*
になっています(コンテナ名はls結果の一番右に書いてあります)。これはsampleディレクトリ下でdocker-compose up
したからです。
他のディレクトリでdocker-compose up
すると、そのディレクトリ名がプリフィックスになります。ディレクトリ名だとわかりにくい場合は、環境変数COMPOSE_PROJECT_NAMEや-p
オプションでプリフィックスを指定できます。
docker-composeコマンドは.envファイルをみてくれるので、以下のようにしましょう。
COMPOSE_PROJECT_NAME=プロジェクト名
プロジェクト固有のイメージ
docker image ls
コマンドで所持イメージを確認してみます。
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
sample_mysql latest c8914e3551fe 35 seconds ago 385MB
my-php-apache 7.3.10 ed7f24566ef1 About a minute ago 452MB
php 7.3.10-apache b83ebda0d7d4 7 weeks ago 410MB
mysql 5.7.27 383867b75fd2 3 months ago 373MB
phpとmysqlはベースにした公式イメージで、my-php-apacheは前回自分でビルドしたイメージでした。
sample_mysqlは、docker-compose up
した時にビルドされたイメージです。
docker-compose.ymlにbuildを指定すると、プロジェクトごとにイメージがビルドされます。今回は
mysql:
build: ./mysql
としていたので、sampleがプリフィックスのプロジェクト固有のmysqlイメージになりました。
プロジェクトごとにイメージを持ちたいならbuildを、使いまわしたいなら最初にdocker build
しておき、それをimageに指定すれば良いでしょう。
終わりに
Docker-composeを使うことができれば、開発環境は構築できると思います。
他のイメージを使えば、PostgreSQLやRedisサーバー、メール送信のテストを行えるMailHogなどを立ち上げてLaravelで使用できるので試してみてください。