Docker Hubのオフィシャルイメージを使ったLAMP環境(Apache+PHP+MySQL)構築

# やること

* Docker Hubのオフィシャルイメージを一行で立ち上げる
* データの置き場所を考える
* Dockerfileによるイメージのカスタマイズ
* Dockerfileを使わないカスタマイズ
* イメージの連携

# ホスト側の環境

今回はUbuntu 16.04の64it版ISOをインストールしました。Dockerは32bitのOSでは動かないのでご注意。

仮想環境で使う場合は、

```bash
vagrant init ubuntu/xenial64
vagrant up
```

こんな感じで。

## Dockerのインストール

http://docs.docker.com/linux/step_one/
こちらを参考に。

```bash
wget -qO- https://get.docker.com/ | sh
```

これで最新のDockerパッケージを導入できます。

# PHP+Apacheのイメージを使ってみる

[Docker Hub](https://hub.docker.com/) のオフィシャルリポジトリを使います。

## とりあえず起動してみる

```bash
docker run -d php:5.6-apache
```

PHP+ApacheのイメージをDocker Hubから取得し、コンテナを起動します。

```bash
docker images
```

取得したイメージ一覧を表示します。

```bash
docker ps
```

起動しているコンテナ一覧を表示します。STATUSの欄がUpになっていれば起動しています。

## ブラウザで確認してみる

上の方法では、起動しているコンテナのポートが内部で閉じているのでブラウザで確認することはできません。そこでコンテナのポートを公開してみます。

```bash
docker run -p 80:80 -d php:5.6-apache
```

`-p`オプションでホストのポートとコンテナのポートを結びつけます。コロンの左側がホスト側、右側がコンテナ側のポート番号です。

ブラウザでサーバーのURLを開き「Forbidden」のメッセージが出れば成功です。

## コンテナを消す

次に進む前に、使ったコンテナを消しておきます。

```bash
docker rm -f コンテナID
```

これで削除できます。コンテナIDは

```bash
docker ps -a
```

で調べることができます。

面倒なら、

```bash
docker rm -f $(docker ps -a -q)
```

これでコンテナを一括ですべて削除することもできます。

## コンテンツを作成する

お次はHTMLなどのウェブコンテンツを入れてみます。コンテナ内にファイルを導入する方法はいくつかありますが、今回は2つの方法を試してみます。

### 1. コンテナに入ってファイルを作成する

```bash
docker run -p 80:80 --name php -d php:5.6-apache
```

先ほどと同じ起動方法ですが、分かりやすいように`--name`オプションでコンテナに`php`という名前を付けています。

```bash
docker exec -ti php bash
```

これでphpコンテナ内に入ることができます。`-ti`を付けないとターミナルを開くことができません。
コンテナ内にいるときはプロンプトにコンテナIDが付きます。

ドキュメントルートの`/var/www/html`にいるはずなので、この場でHTMLファイルを作成すれば良いのですが、テキストエディタがインストールされていません。インストールしても構いませんが、とりあえずechoで書きます。

```bash
echo '<?php phpinfo();' > index.php
```

ブラウザでサーバーのURLを開きphpinfoの画面が出てくれば成功です。

### 2. コンテナのディレクトリとホストのディレクトリを結びつける

コンテナは永続化されないので、コンテナを削除するとコンテナ内で作成したファイルも消えてしまいます。そこでホスト側にファイルを置き、そのディレクトリをコンテナ側から参照する手法を取ってみます。

ホスト側のファイルを置くディレクトリは今回`/docker/www`にしました。が、別にどこでも構いません。
まず /docker/www/index.php を作成します。

```php:index.php
<?php phpinfo();
```

以前のコンテナを削除してから以下を実行します。

```bash
docker run -p 80:80 -v /docker/www:/var/www/html --name php -d php:5.6-apache
```

`-v`オプションでホストのディレクトリとコンテナのディレクトリを結びつけます。コロンの左側がホスト側、右側がコンテナ側のディレクトリです。

ブラウザでサーバーのURLを開きphpinfoの画面が出てくれば成功です。

# MySQLのイメージを使ってみる

こちらも[Docker Hub](https://hub.docker.com/) のオフィシャルリポジトリを使います。

## 起動してコマンドラインから操作してみる

```bash
docker run --name mysql -e MYSQL_ROOT_PASSWORD=pass -d mysql:5.7
```

MySQL 5.7のイメージをDocker Hubから取得し、起動します。
`-e`オプションは環境変数を設定するもので、`root`のパスワードを`pass`に設定しています。

```bash
docker exec -ti mysql bash
```

これでコンテナ内に入り、

```bash
mysql -ppass
```

MySQLの操作ができます。

# PHPとMySQLの連携

次にPHPとMySQLの2つのコンテナを連携してみます。
先に今まで作成したコンテナを削除しておいてください。

## DockerfileによるPHPイメージのカスタマイズ

オフィシャルのPHPコンテナは最小限のオプションでコンパイルされているので、そのままではMySQLと連動するのが難しいです。そこでDockerfileを使うと、既にあるイメージをカスタマイズして新しいイメージを作ることができます。

Dockerfileの場所はどこでもよいのですが、今回は /docker/php/ に作成します。
ホスト側で /docker/php/Dockerfile を以下の内容で作成します。

```
FROM php:5.6-apache
RUN apt-get update && \
  docker-php-ext-install pdo_mysql mysqli mbstring
```

FROMは元となるイメージを指定します。
RUNでコマンドを指定します。docker-php-ext-installは`php:5.6-apache`イメージに含まれるユーティリティで、オプションを追加してPHPをリコンパイルするものらしいです。

Dockerfileが用意できたら、新しいイメージをビルドします。

```bash
docker build -t php:custom /docker/php
```

これで /docker/php にあるDockerfileを元に`php:custom`というイメージを作成します。`docker images`で確認してみてください。

## MySQLイメージの設定を変更する

先に今まで作成したコンテナを削除しておいてください。

Docker HubのMySQLイメージはcharacter setがlatin1になっている部分がありますので、設定ファイルを追加してみます。
/docker/mysql/custom.cnf （場所とファイル名は任意です）を以下の内容で作成します。

```
[mysqld]
character-set-server=utf8
```

この設定ファイルを読み込んでMySQLコンテナを起動します。

```bash
docker run --name mysql -v /docker/mysql:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=pass -d mysql:5.7
```

`-v`オプションでホスト側の /docker/mysql ディレクトリとコンテナ側の /etc/mysql/conf.d ディレクトリを結びつけています。
MySQLコンテナの設定ファイルは /etc/mysql/my.cnf ですが、その中で /etc/mysql/conf.d にある設定ファイルをすべて読み込むようになっているので、作成した custom.cnf が読み込まれる仕組みです。

## PHPコンテナをMySQLコンテナとリンクして起動する

いよいよPHPコンテナとMySQLコンテナを連携します。

```bash
docker run -p 80:80 -v /docker/www:/var/www/html --link mysql:mysql --name php -d php:custom
```

`--link`オプションでコンテナmysqlを連携します。コロンの右側で連携する名前を指定します。

連携が取れているか確認してみましょう。

```bash
docker exec -ti php bash
```

でPHPコンテナに入り、envコマンドや /etc/hosts を開きMySQLコンテナの情報が入っているか確認します。

```bash:/etc/hosts
172.17.0.36     mysql
```

このように先ほど`--link`で指定した名前でホスト名にアクセスできます。

## phpMyAdminを導入して確認する

連携の確認用にphpMyAdminをホスト側に導入します。

```bash
cd /docker/www
wget https://files.phpmyadmin.net/phpMyAdmin/4.4.13.1/phpMyAdmin-4.4.13.1-all-languages.tar.gz
tar zxf phpMyAdmin-4.4.13.1-all-languages.tar.gz
rm phpMyAdmin-4.4.13.1-all-languages.tar.gz
mv phpMyAdmin-4.4.13.1-all-languages myadmin
cd myadmin/
cp config.sample.inc.php config.inc.php
```

```php:config.inc.php
$cfg['Servers'][$i]['host'] = 'mysql';
```

hostを`localhost`から`--link`オプションにより設定されたホスト名`mysql`に変更します。

ブラウザで開いてみて、ID:root、PW:passでログインできれば成功です。

## PDOのサンプル

最後にひとつPDOのサンプルを作成します。

まずphpMyAdminでデータベースとテーブルを適当にひとつ作成します。

```
CREATE DATABASE test;
USE test;
CREATE TABLE test(name VARCHAR(100));
```

次に /docker/www/test.php を以下の内容で作成します。

```php:test.php
<meta charset="UTF-8">
<title>テスト</title>
<?php
$db = new PDO('mysql:host=mysql;dbname=test', 'root', 'pass');
$db->query("INSERT INTO test VALUES('テスト太郎')");
$st = $db->query("SELECT * FROM test");
var_dump($st->fetchAll());
```

ブラウザでtest.phpを開いてみて、データの挿入と表示が出来ていることを確認します。

# 感想

イメージやコンテナの作成と破棄がすごくお手軽なので、今までのやり直しが効かないサーバー管理から解放される感がありました。