Windows10上のdocker環境の拡張（CakePHPの導入、DBの初期化・永続化）

# 概要
[以前、Docker Toolboxを利用し、Webアプリのローカル開発環境(Apach+PHP, MySQL)を構築](http://qiita.com/takeyoshi-k/items/ea043c6325ea2c030e5e)しました。
以前は最小限の構成だったため今回は追加設定を行います。


# 拡張したこと
## Webアプリ（PHP）周り
- Webアプリケーションフレームワーク（CakePHP）の導入

## DB（MySQL）周り
- DBの初期化
- DB（データ）の永続化


## 最終的なフォルダ・ファイル構成
```
./docker/
|-- docker-compose.yml
|-- web
|   |-- Dockerfile
|-- db
|   |-- Dockerfile
|   |-- initdb.d
|   |   |-- init_db.sql
|   |-- data
|-- html
    |-- composer.json
    |-- app
    |   |-- CakePHPプロジェクト
    |-- vendor
    |   |-- composerインストールしたバンドル
```


# フレームワークの導入

## docker-compose.ymlの編集
docker-compose.ymlを編集し、コンテナをリビルド・(再)起動します。
まずは一度コンテナを停止・削除します。

```
$ docker-compose down
Stopping docker_web_1 ... done
Stopping docker_db_1 ... done
Removing docker_web_1 ... done
Removing docker_db_1 ... done
Removing network docker_default
```

完了後、docker-compose.ymlを以下のように編集します。

```
$ cat docker-compose.yml
version: "2"

services:

  web:
    build: ./web
    links:
      - db
    ports:
      - "80:80"
    volumes:
      - ./html/:/var/www/html
    working_dir: /var/www/html
    #    command: app/bin/cake server -H webhost -p 80
    extra_hosts:
      - "webhost:192.168.99.100"
    hostname: webhost

  db:
    build: ./db
    ports:
      - "30000:3306"
    environment:
      MYSQL_ROOT_PASSWORD: password
```

前回からの追記内容は以下の通りです。

| ファイル名 | 説明 |
|:----------|:-----------|
| working_dir | webコンテナへログインしたときのホーム（作業）ディレクトリ |
| command | デフォルトで実行するコマンド（この時点ではコメントアウト、[後述](http://qiita.com/takeyoshi-k/items/de2071ea3442e2d575f6#%E3%83%93%E3%83%AB%E3%83%88%E3%82%A4%E3%83%B3%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%AE%E8%B5%B7%E5%8B%95)します） |
| extra_hosts | ホスト名とIPアドレスの関連付け（/etc/hostsに設定されます） |
| hostname | ホスト名の割り当て（/etc/hostnameに設定されます） |


## Dockerfileの編集

フレームワークの導入には、composer（パッケージ管理ツール）を利用します。
現状のWebコンテナには、composerはインストールされていないため[CakePHPシステム要件](https://book.cakephp.org/3.0/ja/installation.html)を参考にDockerfileを以下のように編集します。

```
$ vim web/Dockerfile
FROM php:5.6.30-cli

RUN ["apt-get", "update"]
RUN ["apt-get", "install", "-y", "vim"]
RUN ["apt-get", "install", "-y", "git"]
RUN ["apt-get", "install", "-y", "unzip"]
RUN ["apt-get", "install", "-y", "libicu-dev"]
RUN docker-php-ext-install mbstring intl pdo_mysql
RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer

EXPOSE 80
```

今回、CakePHPのビルトインサーバーを利用し動作確認するためApacheの入っていないイメージを指定しています。
`FROM php:5.6.30-cli`

git、unzipもインストールします。
gitは、composerを利用してバンドル（フレームワークのパッケージ群）をダウンロードするとき、unzipはダウンロードしたバンドルの解凍に利用されます。
また、CakePHPの導入に必要なintlもPHPの拡張モジュールとして追加しました。なおlibicu-devはintlインストールに必要なモジュールです。

composerはダウンロード後、`composer`コマンドとして利用するため環境パスを通しています。


## composer.jsonの作成・編集
composerを利用し、CakePHPをPHPのWebアプリケーションフレームワークとして導入します。
導入するバンドルを以下のようにcomposer.jsonに定義します。
composer.jsonの定義内容はこちら[CakePHP](https://book.cakephp.org/3.0/ja/installation.html)をご参考ください。
[後述](http://qiita.com/takeyoshi-k/items/de2071ea3442e2d575f6#composer%E3%81%AB%E3%82%88%E3%82%8B%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)しますが、`composer install`コマンドを実行すると、実行したディレクトリに存在するcomposer.jsonの定義をもとにフレームワークを導入します。

```
$ vim html/composer.json
{
    "require": {
        "cakephp/cakephp": "dev-master"
    }
}
```


## コンテナのリビルド・(再)起動
更新内容をコンテナに反映します。
コンテナをリビルドし、リビルド完了後、コンテナを起動します。

```
$ docker-compose build
..... 
Successfully built

$ docker-compose up -d
Creating network "docker_default" with the default driver
Creating docker_db_1
Creating docker_web_1
```


## composerによるフレームワークのインストール
コンテナ起動後、`composer install`コマンドを実行し、composer.jsonに定義したバンドルをインストールします。

```
$ docker-compose run web composer install
Loading composer repositories with package information
Updating dependencies (including require-dev)
..... 
Generating autoload files
```

正常にフレームワークがインストールされると、'html/vendor/'以下にフレームワークのバンドルが確認できます。


## CakePHPプロジェクトの作成
[CakePHP公式ドキュメント](https://book.cakephp.org/3.0/ja/installation.html)を参考に、CakePHPのプロジェクトを作成します。

```
$ docker-compose run web composer create-project --prefer-dist -n cakephp/app
Installing cakephp/app (3.4.3)
  - Installing cakephp/app (3.4.3): Downloading (100%)
Created project in app
..... 
Updated Security.salt value in config/app.php
```

`html/app`以下に、CakePHPのプロジェクトが作成できました。

## ビルトインサーバーの起動

`app/bin/cake server -H webhost -p 80`で、ホストとポートを指定して、ビルトインサーバーを起動します。
docker-compose.ymlのコメントアウトを外します。これで次回コンテナ起動時から、ビルトインサーバーが起動します。

```
    command: app/bin/cake server -H webhost -p 80
```

`webhost`は、webコンテナのホスト名（docker-compose.ymlに設定）です。
そして`192.168.99.100`は、dockerマシンのIPです。

```
$ docker-machine ip
192.168.99.100
```

この後、コンテナを停止・削除->リビルド->起動します。
（コンテナは停止・削除しなくても大丈夫かと思いましたが、しなかった場合、正常にビルトインサーバーが起動しなかったため停止・削除しています。おそらくネットワークの問題？）

```
$ docker-compose down
Stopping docker_web_1 ... done
Stopping docker_db_1 ... done
Removing docker_web_1 ... done
Removing docker_db_1 ... done
Removing network docker_default

$ docker-compose build
.....
Successfully built 62e7ad53eb28

$ docker-compose up -d
Creating network "docker_default" with the default driver
Creating docker_db_1
Creating docker_web_1

$ docker-compose ps
    Name                  Command               State            Ports
-------------------------------------------------------------------------------
docker_db_1    docker-entrypoint.sh mysqld      Up      0.0.0.0:30000->3306/tcp
docker_web_1   docker-php-entrypoint app/ ...   Up      0.0.0.0:80->80/tcp
```


## 動作確認
ブラウザで、`http://192.168.99.100/`にアクセスします。
CakePHPのデフォルト画面が表示されていれば動作確認の完了です。
![sc_cakephp.png](https://qiita-image-store.s3.amazonaws.com/0/128800/9de46855-a553-02eb-c10f-dca275a2895a.png)


# DB（MySQL）周りの拡張設定

## DBの初期化
[前回](http://qiita.com/takeyoshi-k/items/ea043c6325ea2c030e5e#pdo%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%9Fmysql%E3%81%B8%E3%81%AE%E6%8E%A5%E7%B6%9A%E7%A2%BA%E8%AA%8D)、dbコンテナにログインし、手動でDB・テーブル・データを作成しました。
これをコンテナ作成時に自動で作成するよう設定を追加します。

```
$ vim db/Dockerfile
FROM mysql:5.7

COPY ./initdb.d/init_db.sql /docker-entrypoint-initdb.d/
```

`/docker-entrypoint-initdb.d`ディレクトリに配置したシェルやSQLファイルはMySQLインスタンス起動時に実行されます。
`./initdb.d`は、マウントするホスト側のディレクトリです。
以下のディレクトリ・SQLファイルを作成します。

```
$ mkdir db/initdb.d
$ vim db/initdb.d/init_db.sql
CREATE DATABASE IF NOT EXISTS sample;

CREATE TABLE IF NOT EXISTS sample.user (
   `id` INT AUTO_INCREMENT,
   `name` VARCHAR(20) NOT NULL,
   PRIMARY KEY (`id`)
) ENGINE = InnoDB;

INSERT INTO sample.user(id, name) VALUES(1, 'Test1'), (2, 'Test2');
```

コンテナを停止・削除後、リビルド・起動します。

```
$ docker-compose down
Stopping docker_web_1 ... done
Stopping docker_db_1 ... done
Removing docker_web_1 ... done
Removing docker_db_1 ... done
Removing network docker_default

$ docker-compose build
.....
Successfully built 62e7ad53eb28

$ docker-compose up -d
Creating network "docker_default" with the default driver
Creating docker_db_1
Creating docker_web_1
```

DBの初期化に成功できていることを確認します。

```
$ docker-compose run db mysql -h db --port 3306 --protocol tcp -u root -ppassword

mysql> SELECT * FROM sample.user;
+----+-------+
| id | name  |
+----+-------+
|  1 | Test1 |
|  2 | Test2 |
+----+-------+
2 rows in set (0.01 sec)
```


## DB（データ）の永続化
DB（データ）の永続化は2種類の方法があります。

- ホストのディレクトリをマウント
- ボリューム用のコンテナを作成

今回は、ホストのディレクトリをマウントすることでデータを永続化します。
まずはdocker-compose.ymlを編集します。

```
  db:
    build: ./db
    ports:
      - "30000:3306"
    volumes:
      - ./db/data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: password
```

ホスト側のディレクトリは任意（`./db/data`ディレクトリは作成してください）で、dbコンテナ側は`/var/lib/mysql`をマウント指定します。[詳細](https://hub.docker.com/_/mysql/)はこちらを参照ください。

コンテナを停止・削除後、リビルド・起動し、DBにデータを追加します。

```
$ docker-compose down
Stopping docker_web_1 ... done
Stopping docker_db_1 ... done
Removing docker_web_1 ... done
Removing docker_db_1 ... done
Removing network docker_default

$ docker-compose build
.....
Successfully built 62e7ad53eb28

$ docker-compose up -d
Creating network "docker_default" with the default driver
Creating docker_db_1
Creating docker_web_1

$ docker-compose run db mysql -h db --port 3306 --protocol tcp -u root -ppassword

mysql> INSERT INTO sample.user(id, name) VALUES(3, 'Test3');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM sample.user;
+----+-------+
| id | name  |
+----+-------+
|  1 | Test1 |
|  2 | Test2 |
|  3 | Test3 |
+----+-------+
3 rows in set (0.00 sec)
```

そして、コンテナを停止・削除・起動します。

```
$ docker-compose down
Stopping docker_web_1 ... done
Stopping docker_db_1 ... done
Removing docker_db_run_1 ... done
Removing docker_web_1 ... done
Removing docker_db_1 ... done
Removing network docker_default

$ docker-compose up -d
Creating network "docker_default" with the default driver
Creating docker_db_1
Creating docker_web_1
```

永続化前は、コンテナを削除した時点でDBに追加したデータは消えてしまいます。
再度DBにアクセスし、さきほど登録されたデータが残っていることが確認できます。

```
$ docker-compose run db mysql -h db --port 3306 --protocol tcp -u root -ppassword

mysql> SELECT * FROM sample.user;
+----+-------+
| id | name  |
+----+-------+
|  1 | Test1 |
|  2 | Test2 |
|  3 | Test3 |
+----+-------+
3 rows in set (0.00 sec)
```

追加したデータが確認できれば完了です。