Dockerでのコンテナはなるべく最小限にする。だから連携が必要
1つプロセスで1つのコンテナ
Dokcerではできるだけ1つプロセスで1つのコンテナという構成が推奨されるとのことです。
基本的にコンテナにはSSHなどでログインするのは推奨されていないようです。つまりサーバコンテナ側にsshdを立てるなということらしいです。
なのでいくつかのアプリケーションを連携させるには、コンテナの連携を学ぶ必要がありそうです。
コンテナをサーバみたく扱うことはできるのだけど、これだと後々なんらかのプロセスだけ差し替えたい、スケールしたいなどの場合に面倒になるようです。
@MINOには現時点でよくわかりませんが…。
ともかくもコンテナはなるべく最小限の状態であるべきと偉い人たちが言っているので素直に従いマス。
うむ。頑張ってみよう。
今回のテスト環境はGNU/Linux debian8です。
コンテナ連携
linkオプションの使用例 公式のMySQLイメージの使用
いろいろ試行錯誤しているうちにコンテナ連携にはlink
オプションを使うのがよいことを学びました。
よくあるケースとして、データベースコンテナとクライアントコンテナの連携を例に進めていきたいと思います。
今回の例では公式のMySQLイメージを使いたいと思います。
公式MySQLイメージはすぐにデータベースを使うことができるように設計されています。
docker run
時にいくつかの設定をくっつけてあげれば、パスワード設定、データベース作成等を簡単に行うことができます。
@MINOのような初心者にはありがたいイメージです。
MySQLサーバコンテナの作成・起動
まずは公式のMySQLイメージをpull
します。テストなので特にバージョンを指定していません。
$ dokcer pull mysql
これでローカルに公式のMySQLイメージがダウンロードされたはずです。
セットアップに使う環境変数の意味
pull
ができたらrun
をするのですが、その前に少し説明しておきたいことがあります。
公式MySQLイメージでは簡単にデータベースのセットアップをすることができます。
具体的には環境変数をつかったセットアップです。
環境変数の種類
公式MySQLイメージいくつかの環境変数が用意されており、run
のe
オプションでその変数を設定することができます。
環境変数の種類と意味は以下です。
環境変数 | 意味 |
---|---|
MYSQL_DATABASE | データベースの名前を指定する |
MYSQL_USER | ユーザを作成する |
MYSQL_PASSWORD | ユーザパスワードを設定する |
MYSQL_ROOT_PASSWORD | rootのパスワードを設定する |
MYSQL_ALLOW_EMPTY_PASSWORD | パスワードなしでログインできるようにする |
MYSQL_ROOT_PASSWORD
MySQLのrootのパスワードを指定します。この変数の設定は必須です。これを設定しないとエラーが出てrun
が成功しません。
MYSQL_DATABASE
イメージ起動時に作成されるデータベースの名前を指定することができます。
ユーザー/パスワードを設定した場合(下記参照)、そのユーザーはこのデータベースへのスーパーユーザになります。(GRANT ALL
に相当します)
MYSQL_USER・MYSQL_PASSWORD
新しいユーザーを作成・パスワード付与のオプションです。このユーザーはMYSQL_DATABASE
変数で指定されたデータベースのスーパーユーザになります。
ユーザーを作成するためにはMYSQL_USER
・MYSQL_PASSWORD
両方の変数が必要です。どちらかだけだとエラーになります。
MySQLサーバコンテナ起動
上記を踏まえて、MySQLサーバコンテナを起動させます。
docker run --name hogedb -e MYSQL_ROOT_PASSWORD=password -e MYSQL_USER=hoge -e MYSQL_PASSWORD=hogepass -e MYSQL_DATABASE=db -d mysql
コマンドの意味
コマンド | 意味 |
---|---|
docker run | コンテナの作成及び起動 |
–name hogedb | コンテナに名前をつけるオプション。例ではhogedb |
-e MYSQL_ROOT_PASSWORD=password | rootのパスワード設定。例ではpassword |
-e MYSQL_USER=hoge | ユーザの名前設定。例ではhoge |
-e MYSQL_PASSWORD=hogepass | ユーザパスワード設定。例ではhogepass |
-e MYSQL_DATABASE=db | 作成されるデータベースの名前。例ではdb |
-d | デタッチ状態(バックグラウンド動作)での起動オプション |
mysql | コンテナの元になるイメージ |
これでdb
というデータベースを持ち、hoge
というユーザがいるMySQLサーバコンテナがバックグラウンドで起動しました。
docker ps
コマンドで確認してみてください。NAMES
がdb
というコンテナが起動しているのがわかるかと思います。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5d7489f5d22c mysql "/entrypoint.sh mysql" 2 hours ago Up 2 hours 3306/tcp db
クライアントコンテナの作成・起動
上記の手順で今ホストPC上にはMySQLサーバコンテナがまるでデーモンのように起動しています。
このコンテナと連携するのにlink
オプションを使います。
link
オプションを使うには、link対象のコンテナが起動している必要があります。そのため、先ほど起動させたMySQLサーバコンテナは起動させたままにしてください。
さて今度はクライアントを起動させます。コマンドは以下です。
docker run --name dbclient --link db:mysql -it mysql /bin/bash
コマンドの意味
コマンド | 意味 |
---|---|
docker run | コンテナの作成及び起動 |
–name dbclient | コンテナに名前をつけるオプション。例ではdbclient |
–link db:mysql | –linkオプションで連携するコンテナを指定。指定はコンテナ名で行っている |
-it | iオプションはコンテナの標準入力へのアタッチ。tオプションはttyの割り当て。つまりコンテナの中に入れるようになる。 |
mysql | クライアントコンテナ構築に使うイメージ |
/bin/bash | コンテナ起動後にbashシェルを起動させる |
補足
クライアントコンテナのイメージもサーバコンテナでつかったイメージと同じものを使っています。 最初なんでかなと思いました。
クライントコンテナはデータベースサーバコンテナに命令を与えることが役目ですが、MySQLをコマンドラインで操作したいならクライアントコンテナにもコマンドを打つためにMySQLが導入されている必要があります。
なので同じイメージ(MySQLが入っていれば良い)を使います。
もちろんPHPなどからMySQLを使うならクライアントにMySQLのインストールは必要なく、PDOなどのエクステンションが導入されていればOKです。
クライアントからデータベースを見てみる
クライントコンテナを起動させた時点でプロンプトが以下のようにクライントに移行しているかと思います。
プロンプトの構成は若干違うかもしれません。また@の後ろはコンテナのホスト名なので環境ごとに異なる可能性があります。
root@a6e4178c5e6d:/#
早速DBサーバコンテナに繋げたいと思いますが、その前にDBサーバコンテナのアドレスを確認しておきます。
以下を実行するとずらずらと環境変数がでてくると思います。
root@a6e4178c5e6d:/# env
出力された環境変数
MYSQL_ENV_MYSQL_DATABASE=db
MYSQL_ENV_MYSQL_ROOT_PASSWORD=password
TERM=xterm
MYSQL_VERSION=5.7.10-1debian8
MYSQL_PORT_3306_TCP_PORT=3306
MYSQL_PORT_3306_TCP=tcp://172.17.0.2:3306
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MYSQL_ENV_MYSQL_USER=hoge
PWD=/
MYSQL_ENV_MYSQL_PASSWORD=hogepass
MYSQL_ENV_MYSQL_VERSION=5.7.10-1debian8
HOME=/root
SHLVL=1
MYSQL_PORT_3306_TCP_PROTO=tcp
MYSQL_NAME=/dbclient3/mysql
MYSQL_MAJOR=5.7
MYSQL_PORT_3306_TCP_ADDR=172.17.0.2
MYSQL_ENV_MYSQL_MAJOR=5.7
MYSQL_PORT=tcp://172.17.0.2:3306
すごく乱暴なことを言えば、linkオプションとはこれらの環境変数を吐き出すためのオプションと言えます
この中でMYSQL_PORT_3306_TCP_ADDR
として出力されている172.17.0.2
がDBサーバコンテナのアドレスになります。
実際は以下のように変数として指定してあげれば172.17.0.2
を指定したことになります。
root@a6e4178c5e6d:/# mysql -u hoge -p -h $MYSQL_PORT_3306_TCP_ADDR db
パスワードを入力してログインに成功すると、以下のようにMySQLのコマンドラインに移行します。
root@a6e4178c5e6d:/# mysql -u hoge -p -h $MYSQL_PORT_3306_TCP_ADDR db
Enter password:
Welcome to the MySQL monitor. Commands end with ; or g.
Your MySQL connection id is 4
Server version: 5.7.10 MySQL Community Server (GPL)
Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.
mysql>
あとはテーブルを作るとかユーザを作るとかすればいいよ。
補足・ハマりポイント
公式MySQLのデータ永続化について
公式MySQLではデータが永続的に使えるように設定されています。データベースだからデータはいちいちなくなったら面倒な場合もありますからね。
MySQLコンテナがデータを永続化するために、ホストのどこにディレクトリをマウントしているかについては以下のコマンドで調べることができます。
$ dokcer inspect コンテナID
どばっといろんな情報が出力されるますが、以下のようなMountsに関する出力があるかと思います。
"Mounts": [
{
"Name": "4b538bd7dec36757b9cd2133a5e66725812b07745eb324012156465f635c4047",
"Source": "/var/lib/docker/volumes/4b538bd7dec36757b9cd2133a5e66725812b07745eb324012156465f635c4047/_data",
"Destination": "/var/lib/mysql",
"Driver": "local",
"Mode": "",
"RW": true
}
],
Source
がホスト側のディレクトリ、Destination
がコンテナ側のディレクトリです。この場合/var/lib/mysql
には/var/lib/docker/volumes/4b538bd7dec36757b9cd2133a5e66725812b07745eb324012156465f635c4047/_data
がマウントされているということになります。
MySQLで書き込みしたデータは実質的にホストのディレクトリに保存されているということになります。
これは公式MySQLのDockerfile内で以下のようにVOLUME指定がされている為です。
VOLUME /var/lib/mysql
この方法ではホストにデータが依存する形になりますので、可搬性に問題がでてきます。
そのため、より自由度を高める為、データを格納することを専門とするボリュームコンテナをこしらえてマウントするの定石とされています(と、もっぱらの噂です)。
MySQLの設定の変更
my.cnf
にて設定を変えたい場合があるかもしれません。公式MySQLのDokcerfile
では以下の方法で設定を変更しています。
Docker公式MySQLのDockerfile(GitHub)
RUN sed -Ei 's/^(bind-address|log)/#&/' /etc/mysql/my.cnf
&& echo 'skip-host-cachenskip-name-resolve' | awk '{ print } $1 == "[mysqld]" && c == 0 { c = 1; system("cat") }' /etc/mysql/my.cnf > /tmp/my.cnf
&& mv /tmp/my.cnf /etc/mysql/my.cnf
コマンド | 意味 |
---|---|
sed -Ei ‘s/^(bind-address|log)/#&/’ /etc/mysql/my.cnf | 正規表現でbind-addressまたはlogとなっているところをコメントアウト |
&& echo 'skip-host-cache-name-resolve' |
skip-name-resolveは「DNS問い合わせを使用しない」、skip-host-cacheは「接続ごとにDNSサーバに問い合わせ」という意味 |
awk ‘{ print } $1 == "[mysqld]" && c == 0 { c = 1; system("cat") }’ /etc/mysql/my.cnf > /tmp/my.cnf | /etc/mysql/my.cnfの[mysqld]と記述されている次の行にskip-host-cache-name-resolveを挿入し/tmp/my.cnfに出力 |
&& mv /tmp/my.cnf /etc/mysql/my.cnf |
/etc/mysql/my.cnfにファイルを上書きする |
sed
とawk
を使ってワンライナーで設定を変更しています。@MINOはsed
とawk
はほとんど使ったことがないので勉強しなければなりません。
Dockerを使いこなすにはこれらのいわゆる「シェル芸」に慣れる必要があるかもしれません。
もちろん設定ファイルを別途マウントする方法はありますが、Dockerfile
内にワンライナーとして記述できたほうが一元的に設定を管理でき便利な気がします。
文字コードについて
Docker公式のMySQLはマルチバイト向けの設定になっていない(文字コードがUTF8じゃない)ので、設定を変える必要がありそうです。
マルチバイト語族をなめるなよ〜。
後記
コンテナの連携ができるとだんだんDockerの真価がわかってきたような気がします。開発環境がこれほど簡単に操れるのはすごいことですよね。そりゃ話題にもなりますよね。
もっと勉強していきたいと思います。