Dockerコンテナ内にmysqlサーバを立てる


## 概要

Dockerコンテナ内にmysqlサーバを立てます。
mysqlアカウントを作成したり、mysqlサーバを外部に公開することも行います。

動作確認を行った環境は、ホストOS, コンテナOSともにCentOSです。


## そもそもDockerとは

[仮想環境構築に docker を使う - apatheia.info](http://apatheia.info/blog/2013/06/17/docker/) を読んでください！

## Dockerfile

さっそくですが、以下が Dockerfile です。
コンテナイメージを作成するために必要なファイルです。

```
# DOCKER-VERSION 0.3.4
FROM    centos:6.4

# ここは自由に変えてください
MAINTAINER Taro Tanaka

# パッケージインストール
RUN yum install -y mysql mysql-server

# mysqlサーバのセットアップ
RUN echo "NETWORKING=yes" > /etc/sysconfig/network
ADD ./setup.sql
RUN /usr/bin/mysqld_safe & \
        sleep 10s && \
        cat setup.sql | mysql

# 外部からmysqlサーバにアクセスできるように
RUN sed -i -e"s/^bind-address\s*=\s*127.0.0.1/bind-address = 0.0.0.0/" /etc/mysql/my.cnf

# ポート番号 3306 を外部に公開
EXPOSE 3306

CMD ["/usr/bin/mysqld_safe"]
```

### 構文解説

Dockerfile 内で書かれている <b>Instruction</b> について軽く解説します。

- FROM \<image>
 - コンテナで利用するOSイメージです
 - この例では「centos:6.4」
 - 「centos』とだけ書くと、最新版を利用するようです
- MAINTEINER \<name>
 - Dockerfile の作成者
- RUN \<コマンド>
 - コンテナ環境でコマンドを実行させます
- ADD \<src> \<dest>
 - ホスト環境のファイルをコンテナ環境へコピーします
- EXPOSE \<port>
 - コンテナ環境の外から参照させたいポート番号を指定します
 - mysqlサーバの場合は 3306. HTTPサーバだと通常 80 を指定します
- CMD
 - CMD はDockerfileに１つしか記述できません。
 - 引数なしでコンテナを起動したときの、挙動を書きます
 - コンテナ起動のセクションでまた触れます

また、docker でキャッシュを活用するためには、あまり変わらないInstructionをDockerfileの上の方に記述するのがいいそうです。

### MySQLセットアップ部分

MySQL固有の部分に関する部分を抜粋します。

```
RUN echo "NETWORKING=yes" > /etc/sysconfig/network

ADD ./setup.sql ./setup.sql

RUN /usr/bin/mysqld_safe & \
        sleep 10s && \
        cat setup.sql | mysql
```

まず１行目の ```/etc/sysconfig/network``` を生成している部分についてです。
この行がないとCentOSをコンテナOSとする場合に、mysqlが起動できないので、追加しています。

２行目はSQLスクリプトをコンテナで利用できるようにコピーしているだけです。

３行目は「mysqlサーバを起動させて、起動が完了するであろう10秒後に、セットアップ用のSQLを流し込む」という処理です。
```setup.sql```にアカウント追加やデータベース作成、テーブル作成など処理を書いていて、それを実行しています

ここでの処理を見て、「１行にごちゃごちゃ詰め込むのではなく、３行に分けてRUNを書けばいいのでは？」と思われるかもしれません。
実は、ここが今回の最大のポイントです。というのも、３行に分けてRUNを書くと、setup.sqlでの処理は正しく動きません。
なぜかというと、<b>RUNが別の行にまたぐと、ファイルシステム上での変化は記憶されるのですが、メモリやプロセスの状態変化は保持されない</b>からです。
そのため、アカウント作成、テーブル作成といった処理を行うためには、mysqlサーバが起動している状態である必要ので、１行のRUNで処理を完結させているというわけです。

#### MySQLサーバをコンテナ外部からもアクセスさせる

```
RUN sed -i -e"s/^bind-address\s*=\s*127.0.0.1/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
```

デフォルトだとmysqlサーバは localhost からの接続しか許さないようになっているので、my.cnfを書き換えてあげます。
このsedコマンドでは、my.cnfの 127.0.0.1 となっている行を 0.0.0.0 に置換しています。

## コンテナイメージの作成

Dockerfile の編集が完了したら、コンテナイメージを作成しましょう。

コンテナにタグをつけておくと、参照しやすいです(-t オプション).
また、タグ名は <b><自分のユーザ名>/<コンテナ名></b> とするのが良い習慣だそうです。


```sh
$ sudo docker build -t gologo13/mysql . 
```

## コンテナ起動

コンテナイメージの作成に成功すれば、次はコンテナの起動です。

以下のように引数なしで```docker run```を実行すれば、Dockerfileに書いた CMDの内容```/usr/bin/mysqld_safe```が実行されます。
docker run することで、mysqlサーバのデーモンプロセスの起動完了です！

```sh
$ sudo docker run -i -p 3306:3306 -t gologo13/mysql
```


また、コンテナ内でコマンドを実行したい場合は、シェルを立ち上げることもできます。この時、CMD で記述した内容は実行されません。

```sh
$ sudo docker run -i -p 3306:3306 -t gologo13/mysql /bin/bash
```

## MySQLサーバへのアクセス

最後に、dockerコンテナ内でmysqlサーバにアクセスします。

以下の様なスクリプトを作っておくと、アクセスが楽になります。
処理的には、コンテナIDを取得して、そのコンテナの詳細情報からIPアドレスを取得しています。

```sh
$ cat mysql_client.sh
#!/bin/sh

# docker build 時に指定したタグ名
TAG="gologo/mysql"

CONTAINER_ID=$(docker ps | grep $TAG | awk '{print $1}')

IP=$(docker inspect $CONTAINER_ID | python -c 'import json,sys;obj=json.load(sys.stdin);print obj[0]["NetworkSettings"]["IPAddress"]')

# アカウントとパスワードは適宜変更してください
mysql -u admin -ppassword -h $IP
```

あとは実行すればおしまいです。

```sh
$ chmod u+x mysql_client.sh
$ sudo mysql_client.sh
```


## 参考

- [Creating a MySQL Docker Container](http://txt.fliglio.com/2013/11/creating-a-mysql-docker-container/)
- [Dockerfile - Docker Documentation](http://docs.docker.io/reference/builder/#dockerfile-examples)
- [Dockerfile Best Practices](http://crosbymichael.com/dockerfile-best-practices.html)