DockerでMySQL+永続コンテナを構築する

## 概要

Docker上にMySQLコンテナとデータを保持するための永続コンテナを構築する。

```
 MySQL Container | Data Container <- Here
---------------------------------
              Docker
---------------------------------
         Vagrant (CentOS)
---------------------------------
             VirtualBox
---------------------------------
         LocalMachine (OS X)
```

## 関連ページ

* [VagrantにCentOS 7.0をインストールする](http://qiita.com/naomichi-y/items/9d776a7f2a9038ba57c2)

## 前提となる環境

* Host
  * OS: Mac OS X
  * VirtualBox 4.2.26
  * Vagrant 4.3.26
* [Guest](http://qiita.com/naomichi-y/items/9d776a7f2a9038ba57c2)
  * OS: CentOS 7.1
  * Docker 1.5.0-dev

## 目的

* Docker上で使い捨てのMySQLを構築する
* データは永続化させたいので、MySQLとは別のコンテナを用意
  * データコンテナは/opt/mysqlにマウント
* Dockerfileでイメージを作成
* `vagrant up`と同時にMySQLを自動起動させる

## 実行手順

### 永続コンテナの構築

先ずはDockerfileの作成。

```
$ mkdir -p ~/containers/storage
$ cd ~/containers/storage
```

```code:~/containers/storage/Dockerfile
FROM busybox
MAINTAINER Naomichi Yamakita <n.yamakita@gmail.com>

VOLUME /opt
CMD /bin/sh
```

続いてDockerfileをビルドする。

```
$ docker build -t storage .
$ docker run \
--name storage \
storage

$ イメージが作成されているか確認
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
storage             latest              e5530f217dc5        About an hour ago   2.43 MB
```

以上でデータコンテナの作成は完了となる。

### MySQLコンテナの構築

続いてMySQLのDockerfileを作成するが、ゲストOSにCentOSを利用している場合、`yum update`で"One of the configured repositories1 failed (Unknown), ..."といったエラーが発生する場合がある。これはSELinuxが原因となっている可能性が高いため、予めゲストOS上で設定を無効にしておいたほうが無難。

```
$ mkdir -p ~/containers/mysql
$ cd ~/containers/mysql
```

```code:~/containers/mysql/Dockerfile
FROM centos:latest
MAINTAINER Naomichi Yamakita <n.yamakita@gmail.com>

## system setting
RUN \cp -p /usr/share/zoneinfo/Japan /etc/localtime

## update system
RUN yum -y update
RUN yum -y install wget hostname

## add yum repository
RUN yum -y install epel-release
RUN rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm

## install mysql
RUN wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm
RUN rpm -Uvh mysql-community-release-el7-5.noarch.rpm
RUN yum -y --enablerepo=mysql56-community install mysql-server

RUN mkdir -p /opt/mysql
RUN chown mysql:mysql /opt/mysql

ADD build/my.cnf /etc/

ADD build/start /usr/local/bin/
RUN chmod +x /usr/local/bin/start

## start services
EXPOSE 3306
CMD ["/usr/local/bin/start"]
```

`CMD`には`docker run`実行時に起動するスクリプトを指定。ここではリモートユーザの初期ユーザID/パスワードを`webapp`とした。

```code:~/containers/mysql/build/start
#!/bin/bash
set -e

DB_REMOTE_ROOT_NAME="webapp"
DB_REMOTE_ROOT_PASS="webapp"
DB_REMOTE_ROOT_HOST="%"

# fix permissions and ownership of /var/lib/mysql
mkdir -p -m 700 /var/lib/mysql
chown -R mysql:mysql /var/lib/mysql

# fix permissions and ownership of /run/mysqld
mkdir -p -m 0755 /run/mysqld
chown -R mysql:root /run/mysqld

# initialize MySQL data directory
if [ ! -d /var/lib/mysql/mysql ]; then
  echo "Installing database..."
  mysql_install_db --user=mysql >/dev/null 2>&1

  # start mysql server
  echo "Starting MySQL server..."
  /usr/bin/mysqld_safe >/dev/null 2>&1 &

  # wait for mysql server to start (max 30 seconds)
  timeout=30
  echo -n "Waiting for database server to accept connections"
  while ! /usr/bin/mysqladmin -u root status >/dev/null 2>&1
  do
    timeout=$(($timeout - 1))
    if [ $timeout -eq 0 ]; then
      echo -e "\nCould not connect to database server. Aborting..."
      exit 1
    fi
    echo -n "."
    sleep 1
  done
  echo

  echo "Creating remote user \"${DB_REMOTE_ROOT_NAME}\" with root privileges..."
  mysql -uroot \
  -e "GRANT ALL PRIVILEGES ON *.* TO '${DB_REMOTE_ROOT_NAME}'@'${DB_REMOTE_ROOT_HOST}' IDENTIFIED BY '${DB_REMOTE_ROOT_PASS}' WITH GRANT OPTION; FLUSH PRIVILEGES;"

  /usr/bin/mysqladmin --defaults-file=/etc/my.cnf shutdown
fi

exec /usr/bin/mysqld_safe
```

続いてmy.cnfを作成。

```code:~/containers/mysql/build/my.cnf
[mysqld]
character-set-server=utf8
bind-address = 0.0.0.0
datadir=/opt/mysql
```

データコンテナ同様にDockerfileをビルド。

```
$ docker build -t mysql .
docker run \
-h dev-local-mysql \
-p 3306:3306 \
--name mysql \
-d \
--volumes-from=storage \
mysql
```

コンテナが作成できた場合、Dockerのプロセス一覧にmysqlが表示される。

```
$ docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                    NAMES
fec5fe7fed1e        mysql:latest        "/usr/local/bin/star   31 seconds ago      Up 30 seconds       0.0.0.0:3306->3306/tcp   mysql
```

ホスト (あるいはゲスト) OSからMySQLに接続できることを確認する。

```
# mysqlコンテナのIPを調べる
$ docker inspect --format '{{ .NetworkSettings.IPAddress }}' mysql
172.17.0.1

$ mysql -u webapp -pwebapp -h 172.17.0.1
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.6.23 MySQL Community Server (GPL)

Copyright (c) 2000, 2014, 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 |
| mysql              |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)
```

### 自動起動の設定

ホストOSのVagrantfileにプロビジョニングを追加。

```code:Vagrantfile
config.vm.provision "shell", path: "provision/always.sh", run: "always"
```

Vagrantfileのディレクトリ下にalways.shを作成する。

```
$ mkdir provision
```

```code:provision/always.sh
#!/usr/bin/bash

echo "start containers"
docker start mysql
```

```
$ chmod +x provision/always.sh
$ vagrant reload
...
==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> default: to force provisioning. Provisioners marked to run always will still run.
==> default: Running provisioner: shell...
    default: Running: /var/folders/nm/m391v5jn1wsc1hpltn2gq3r40000gn/T/vagrant-shell20150405-2988-1du2a2q.sh
==> default: start containers
==> default: mysql
...

$ vagrant ssh

# プロセスが動作しているか確認
$ ps ax|grep mysql
 4145 ?        Ss     0:00 /bin/sh /usr/bin/mysqld_safe
 4553 ?        Sl     0:00 /usr/sbin/mysqld --basedir=/usr --datadir=/opt/mysql --plugin-dir=/usr/lib64/mysql/plugin --user=mysql --log-error=/opt/mysql/dev-local-mysql.err --pid-file=/opt/mysql/dev-local-mysql.pid
 4603 pts/0    S+     0:00 grep --color=auto mysql
```

起動ログに"start containers"と出力されていることが分かる。