vagrant up 一発で立ち上がるウェブアプリケーション開発環境作り　第五回：DBサーバー編

VagrantとDocker（とDocker Compose）を使って開発環境を一発で立ち上げられるようすることを目標とした連載、第四回です。

[第一回：Vagrant編](http://qiita.com/marrontan619/items/01cf11fa74af5bbdd96b)
[第二回：Docker編](http://qiita.com/marrontan619/items/6ed07ad391884bfa1a22)
[第三回：プロキシサーバー編](http://qiita.com/marrontan619/items/1974753500a7e6956318)
[第四回：アプリケーションサーバー編](http://qiita.com/marrontan619/items/f2f2c796f4d039598101)

第二回で記事にしたdocker-compose.ymlのこの部分について、詳しく見ていきます。

```yaml:docker-compose.yml
version: "2"
services:
  # mysql
  mysql:
    image: mysql:latest
    volumes:
      - /vagrant/docker/mysql/volume/sql:/docker-entrypoint-initdb.d:rw
      - /vagrant/docker/mysql/volume/conf:/etc/mysql/conf.d:rw
      - /vagrant/docker/mysql/volume/data:/var/lib/mysql:rw
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: password
  # php
  app:
    build: app/
    volumes:
      - /vagrant/docker/app/volume/web-root:/var/www/html:rw
    environment:
      XDEBUG_CONFIG: remote_host=10.0.2.2
    links: # 追加
      - mysql:mysql
```

今回使用する部分のディレクトリ構成はこうです。

```
<vagrant-root>
├── docker
│   ├── docker-compose.yml
│   ├── app # アプリコンテナ関連
│   │   ├── Dockerfile
│   │   └── volume
│   │       └── web-root
│   │           ├── index.php
│   │           └── show-hoge.php
│   └── mysql # DBコンテナ関連
│       └── volume
│           ├── conf
│           │   └── my.conf
│           └── sql
│               ├── create_database.sql
│               ├── create_table.sql
│               └── insert_test.sql
├── package.box
├── provision.sh #第一回参照
└── Vagrantfile
```


## MySQLが動いているコンテナを用意する

ここまでくれば慣れたものだと信じて、イメージのビルドと起動についてはあまり触れません。Docker Hubの公式イメージをそのまま使っています。

### MySQLの設定を変更する

やっていることは[第三回](http://qiita.com/marrontan619/items/1974753500a7e6956318)の「設定をカスタマイズする」と同じです。

### 初期データを投入する

/docker-entrypoint-initdb.d/
にある、*.sqlと*.shファイルを辞書順に実行してくれます。
とりあえず検証用に簡単なデータベース、テーブル作成、データ投入を行います。
例によってvolumes:でsqlファイルが配置されるように設定します。

```sql:create_database.sql
CREATE DATABASE test;
```

```sql:create_table.sql
CREATE TABLE test.hoge (
  id int not null primary key auto_increment,
  name varchar(64) not null
);
```

```sql:insert_test.sql
insert into test.hoge (name) values
   ('abc'),
   ('def'),
   ('hij');
```

## ホスト（Windows）から接続してみる

docker execでmysqlコンテナに入ってコマンドラインからDBの操作することも可能ですが、開発をする上では不便なので、ホストのツール（MySQL Workbenchなど）から接続できるようにします。

* docker-compose.ymlのports:で、Vagrantの3306ポートからmysqlコンテナの3306ポートにフォワーディングする設定。
* ユーザーはroot（ユーザーを作成したい場合は、DockerfileのRUNや、/docker-entrypoint-initdb.d/*.sqlで設定しましょう）
* パスワードは環境変数"MYSQL_ROOT_PASSWORD"（今回は"password"）

接続情報：
Host:192.168.33.10
User:root
Password:password
Default Schema（任意）:test

接続に成功したら

```sql
select * from test;
```

を実行してみましょう。データが3件入っていれば成功です。

## アプリケーションサーバーから接続してみる

[第四回](http://qiita.com/marrontan619/items/f2f2c796f4d039598101)で作成したコンテナから接続して、PHPで中身を表示できるようにします。

### PDOをインストール

appコンテナのDockerfileに以下を追記して、イメージをビルド時にPDOをインストールするようにします。

```dockerfile
RUN docker-php-ext-install pdo_mysql mysqli mbstring
```

docker-php-ext-installとは、公式のイメージに入っているPHPの拡張機能をインストールするためのユーティリティです。
他にもdocker-php-ext-enable、docker-php-ext-configureなどが用意されています。
[公式ドキュメント](https://hub.docker.com/_/php/)

何がインストールできるか手っ取り早く知りたければ、docker-php-ext-install --helpを実行すればわかります。

```bash
vagrant@vagrant-ubuntu-trusty-64 /vagrant/docker (compose-web *)
$ docker exec -it docker_app_1 bash
root@fd0d74f1337f:/var/www/html# docker-php-ext-install --help
usage: /usr/local/bin/docker-php-ext-install [-jN] ext-name [ext-name ...]
   ie: /usr/local/bin/docker-php-ext-install gd mysqli
       /usr/local/bin/docker-php-ext-install pdo pdo_mysql
       /usr/local/bin/docker-php-ext-install -j5 gd mbstring mysqli pdo pdo_mysql shmop

if custom ./configure arguments are necessary, see docker-php-ext-configure

Possible values for ext-name:
bcmath bz2 calendar ctype curl dba dom enchant exif fileinfo filter ftp gd gettext gmp hash iconv imap interbase intl json ldap mbstring mcrypt mysqli oci8 odbc opcache pcntl pdo pdo_dblib pdo_firebird pdo_mysql pdo_oci pdo_odbc pdo_pgsql pdo_sqlite pgsql phar posix pspell readline recode reflection session shmop simplexml snmp soap sockets spl standard sysvmsg sysvsem sysvshm tidy tokenizer wddx xml xmlreader xmlrpc xmlwriter xsl zip
```

### DBの中身を表示するプログラムを作る

appサーバーのweb-root直下に、以下の2ファイルを作成して、DBの中身を表示してみます。

```php:db-config.php
<?php
ini_set('display_errors', true);
define('DB_DSN', 'mysql:host=mysql;dbname=test');
define('DB_USERNAME', 'root');
define('DB_PASSWORD', 'password');
define('DB_CHARACSET', 'utf8');
```

```php:show-hoge.php
<?php
include 'db-config.php';

$db = new PDO(DB_DSN, DB_USERNAME, DB_PASSWORD, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES '" . DB_CHARACSET . "';"));
$stmt = $db->prepare('select * from hoge');
$stmt->execute();

while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
  echo $row[id] . ' : ' . $row[name] . '<br />';
}
```

docker-compose.ymlのlinksで設定しているので、appコンテナから
mysqlのコンテナは"mysql"で参照できます。（db-config.phpの3行目）

以上が完了したら、 http://192.168.33.10/show-hoge.php にブラウザからアクセスして、投入したデータが表示されたら成功です。
![image](https://qiita-image-store.s3.amazonaws.com/0/48832/1525eb54-c08b-3730-c706-fd4bf82d2fae.png)

## データの永続化について

初期投入した以外のデータは、コンテナの削除で消えます。
（正確には初期投入したデータも消えるのですが、再作成の時にまた投入されるので）

| 操作 | データ消える？ |
|------|--------------|
| コンテナ停止（docker stop） | 消えない |
| コンテナ削除（docker rm） | 消える |
| コンテナのホストシャットダウン（vagrant halt） | 消えない |
| ホストマシン（Windows）のシャットダウン | 消えない |

データが消えるのが不安であれば、コンテナの/var/lib/mysqlをホストのどこかにマウントすると、データがホスト側に残ります。
Data Volume Containerパターンを思いっきり無視していますが、勉強中です。（いつか書くかも）

## 結び

長くなりましたが、これでvagrant up一発で、DBの中身まで参照できる環境が完成しました。一旦連載は終了です。
全コンテナに最低限の設定しかしておらず、実際にプロダクトを作ろうとなるとまだまだ問題があるとは思いますが、参考にしていただけると幸いです。