Docker Composeで手軽に開発環境を構築   (PHP+MySQL+Elasticsearch+Memcached）

やっとDockerに入門しました。

社内のシステムは古典的なLAMP環境で動いているので、その開発環境をDocker Composeで作りなおしたメモ。

作成したファイルは、下記に置いてあります。

https://github.com/acro5piano/docker-lamp-elasticsearch

設定ファイルを作ってしまえば、下記のコマンドですぐに開発を開始することができ、楽です。

```
git clone https://github.com/acro5piano/docker-lamp-elasticsearch.git
cd docker-lamp-elasticsearch
docker-compose up
```

[追記]

今だったらこうする。DockerHubフル活用して、できるだけ楽に構築する。

https://qiita.com/acro5piano/items/e834f44259c607d93086

[/追記]

# Dockerについて

Dockerは、開発環境を手軽に構築するツールです。

ApacheやMySQLなどのミドルウェアをコンテナと呼ばれる仮想環境上に構築し、それらを連携させて動作させます。

VagrantやXenなどの仮想化ツールと異なるのは、このコンテナそれぞれが一つのミドルウェアとして機能することです。

Vagrantだと、1つのOSの上に各ミドルウェアインストールしておき、それをBoxとしてイメージ化しますよね。

Dockerの場合、例えば

| コンテナ              | ミドルウェア            | 
|-------------------|-------------------| 
| Webコンテナ           | Apache2.2、 PHP5.5 | 
| DBコンテナ            | MySQL5.6          | 
| Elasticsearchコンテナ | Elasticsearch1.3  | 
| Memcachedコンテナ     | Memcached         | 


といった具合に、各コンテナを別々に構築していき、それらをリンクさせるという形で開発環境を作ります。

今回はこの古典的なLAMP+Elasticsearchという構成を作っていきます。

# Docker Composeについて

従来はDockerのコンテナはそれぞれ別々に起動・管理する必要があったのですが、Docker Composeというツールによってコンテナ群をひとまとめにして管理する手法ができました。

Docker Composeの公式ドキュメントはこちらです。インストールなどはこちらで。

<https://docs.docker.com/compose/>

# ファイル構成

今回作成するファイル群は下記のようになります。

```txt:tree
.
|-- db
|   |-- init.d
|   |   `-- setup.sql
|   |-- Dockerfile
|   `-- my.cnf
|-- src
|   `-- yourgreatapp
|       `-- index.php
|-- web
|    |-- Dockerfile
|    `-- httpd.conf
`-- docker-compose.yml
```

※追記

すいません、この記事は少し古いです。
現在自分が考える`docker-compose.yml`、`Dockerfile`、これらのディレクトリ構成のベストプラクティスは、こちらで紹介しています。

http://qiita.com/acro5piano/items/ac3bef8a9f8bb9fdbdd5


# docker-compose.yml

まず、全体の構成を定義する`docker-compose.yml`を書きます。

プロジェクト名は`yourgreatapp`という名前にしていますので、適宜置き換えて下さい。

```yaml:docker-compose.yml
version: '2'
services:
  web:
    build: ./web
    ports:
      - "8000:80"
      - "3000:3000"
    links:
      - db
      - memcached
      - elasticsearch
    volumes:
      - ./src/yourgreatapp/public:/var/www/yourgreatapp/public

  db:
    build: ./db
    environment:
      MYSQL_ROOT_PASSWORD: ROOT_PASSWORD
      MYSQL_DATABASE: yourgreatapp
      MYSQL_USER: yourgreatapp
      MYSQL_PASSWORD: yourgreatapp
      MYSQL_HOST: ""

    mem_limit: 1000000000
    volumes:
      - ./db/init.d:/docker-entrypoint-initdb.d
    ports:
      - "33333:3306"

  elasticsearch:
    image: "elasticsearch:1.3"

  memcached:
    image: memcached
```


### 各項目の説明

ポートやディレクトリをマッピングする際は、「ホスト側:ゲスト側」という書き方をします。

| 項目          | 説明                                               | 
|-------------|--------------------------------------------------| 
| build       | コンテナの作成をするワークスペースの指定。Dockerfileがある場所             | 
| ports       | ホスト側のポートとコンテナ側のポートのマッピング                         | 
| links       | コンテナ間の通信を定義。実際は/etc/hostsにコンテナ名とIPアドレスの名前解決が書かれる | 
| volumes     | ホストとコンテナで共有するディレクトリ                              | 
| environment | 環境変数                                             | 

### 注
- DBのMYSQL_HOSTは必要か分かりませんが、無いとエラーになる場合があるらしいので付けておきます。
- `build`や`volumes`は`docker-compose.yml`からの相対パスなので気を付けて下さい。

ここからは、各コンテナの作成方法を書きます。

# web

ApacheやPHPが乗っているサーバーです。

```txt:tree
|-- src
|   `-- yourgreatapp
|       `-- index.php
`-- web
    |-- init.d
    |    `-- setup.sh
    |-- Dockerfile
    `-- httpd.conf
```

### web/Dockerfile

イメージの展開からインストールまで、このファイルがやってくれます。

基本的にコマンドの羅列なので分かりやすいですね。

COPYコマンドは、`Dockerfile`からの相対パスなので気を付けて下さい。私は`docker-compose.yml`からの相対パスだと思ってはまりました。

```sh:web/Dockerfile
FROM centos:6

RUN rpm -ivh http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
RUN rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
RUN yum install -y httpd
RUN yum install --enablerepo=epel,remi-php55,remi -y \
                              php \
                              php-cli \
                              php-gd \
                              php-mbstring \
                              php-mcrypt \
                              php-mysqlnd \
                              php-pdo \
                              php-xml \
                              php-xdebug \
                              php-memcached
RUN sed -i -e "s|^;date.timezone =.*$|date.timezone = Asia/Tokyo|" /etc/php.ini

COPY httpd.conf /etc/httpd/conf.d/yourgreatapp.conf

EXPOSE 80
CMD ["/usr/sbin/apachectl", "-D", "FOREGROUND"]
```

### web/httpd.conf

普通のApacheの設定です。会社でFuelPHPというフレームワークを使っているので、SetEnvでDocker用の環境変数を与えています。

```xml:web/httpd.conf
NameVirtualHost *:80

<VirtualHost *:80>
    ServerName docker-compose-lamp.dev
    DocumentRoot /var/www/yourgreatapp/public
    <Directory /var/www/yourgreatapp/public>
        Options Indexes FollowSymLinks MultiViews
        AllowOverride all
        Order allow,deny
        allow from all
    </Directory>
    SetEnv FUEL_ENV development_docker
</VirtualHost>
```

### src/yourgreatapp

開発対象のプログラムを置く場所です。index.phpの中身は`<?php phpinfo();`で構いません。

```php:src/yourgreatapp/public/index.php
<?php phpinfo();
```

※Gitのリポジトリ直下に`docker-compose.yml`を置いて、リポジトリ全部をDockerにマウントする方がスマートな気がしてきた

```txt:tree（模索中）
hogehoge/fugafuga.git
|-- public/index.php
|-- docker-compose.yml
`-- docker
   |-- web
   |  `-- Dockerfile
...
```

# db

```
|-- db
   |-- init.d
   |   `-- setup.sql
   |-- Dockerfile
   `-- my.cnf
```

### db/Dockerfile

MySQL用の環境変数をセットすることで、コンテナ作成時に自動的にデータベースやユーザーが作成されます。便利。

```sh:db/Dockerfile
FROM mysql:5.6

COPY my.cnf /etc/mysql
```

※最初、ここでMYSQL_DATABASEなどの設定をしていましたが、見通しを良くするため`docker-compose.yml`の方に移管しました。

### db/my.cnf

普通のMySQLの設定です。現在稼働している開発サーバーから取ってくれば良いでしょう。

```sh:db/my.cnf
[mysqld]
default-storage-engine=InnoDB
character-set-server=utf8
```

### db/init.d/setup.sql

webの時と同様に、`/docker-entrypoint-initdb.d`というディレクトリにシェルスクリプトやSQLを置いておくと、コンテナ起動時に実行してくれます。

```sql:db/init.d/setup.sql
GRANT ALL PRIVILEGES ON `yourgreatapp`.* TO 'yourgreatapp'@'%';
```

# elasticsearch, memcached

開発用であれば、ElasticsearchとMemcachedは特に設定しなくても動くでしょう。

なので、docker-compose.ymlにイメージ使うよって書いておけば、あとはDockerがよしなにやってくれます。


```yaml:docker-compose.yml(抜粋)
elasticsearch:
  image: "elasticsearch:1.3"

memcached:
  image: memcached
```

# 起動

ここまできたら、あとは起動するだけです。

docker-compose.ymlがあるディレクトリで

`docker-compose up`

というコマンドを入力して下さい。

各コンテナのビルドが始まり、数分後には環境が立ち上がっているはずです。

うまくいけば、localhost:8000をブラウザで開けば、Docker内部で動いているPHPにアクセスできるはずです。

何か設定ファイルが間違っていて、やり直したい場合は

`docker-compose build`

でコンテナが再度作りなおされます。

コンテナ内のデータは基本的にコンテナを終了させると消えますが、mysqlのコンテナは予め永続化の処理がされているので消えません。

データを一から作り直したい場合は、

`docker-compose down`

とすれば消えます（多分）。

# 便利コマンド

### コンテナの稼働状況を確認

こんな感じで、各コンテナの状態が分かります。

`docker-compose ps`

```
         Name                       Command               State                      Ports                     
--------------------------------------------------------------------------------------------------------------
docker_db_1              docker-entrypoint.sh mysqld      Up      0.0.0.0:33333->3306/tcp                      
docker_elasticsearch_1   /docker-entrypoint.sh elas ...   Up      9200/tcp, 9300/tcp                           
docker_memcached_1       docker-entrypoint.sh memcached   Up      11211/tcp                                    
docker_web_1             /usr/sbin/apachectl -D FOR ...   Up      0.0.0.0:3000->3000/tcp, 0.0.0.0:8000->80/tcp
```

### コンテナの稼働状況を確認（全部）

複数サービスを`docker-compose`で動かしている場合、`docker ps`で全サービスのコンテナを確認できます。

### コンテナの内部でコマンドを実行

`docker exec -it docker_web_1 bash`

ここで指定しているdocker_web_1というのは、`docker-compose ps`で調べた名前（コンテナ名）と同一です。

bashを実行すると、SSHのようにシェルに入ることができます。

docker_web_1という名前が分かりにくければ、`docker-compose.yml`の中に、`container_name`を指定することで、分かりやすくコンテナ名を付けることができます。

```yaml:docker-compose.yml
web:
  container_name: some-web
```

こうすると、`docker exec -it some-web bash`として実行できます。

※下記の記事で、`docker-compose exec web <command>`という方法が紹介されていました。Docker Composeに限ってはこっちの方がいいですね。

http://qiita.com/skyriser/items/2cf98b747ed6577cc5ee

### MySQLのコンソールに接続

`mysql -u yourgreatapp -p yourgreatapp -D yourgreatapp -h 127.0.0.1`

ホストに127.0.0.1を指定するのがポイントで、指定しないとlocalhostにUnixソケット経由で接続しに行ってしまいます。これも少しはまりました。

※MySQLコンテナから接続する方が良いですね。ホスト側にMySQLクライアントを入れる必要が無いですし、ポートも塞ぎません。

`docker-compose run db mysql -u root -pROOT_PASSWORD -D yourgreatapp


### エイリアス

`docker-compose`は頻繁に叩くので、

```
alias dc='docker-compose'
```

とすると幸せになれます。これにより、

```
dc run web php /var/www/app/composer.phar update
```

と、ホスト環境で実行するのと同じような感覚でコマンドを実行できます。Dockerはデータが永続化せず、Historyが残らないので、できるだけホスト側でコマンドを実行したほうが良いです。

# まとめ

なんだか長くなってしまいましたが、これらのファイルを作っておけば

- 一瞬で開発環境作れる
- 起動早い、動作軽い
- 開発環境を変更したら、それをGitにコミットするだけでチーム全員に共有できる
- ~~どうせ誰にも読まれない~~導入手順書を書く手間が無い

などのメリットがあります。

PHPのビルトインサーバーとか使えば、`docker-compose.yml`単体で全部構築できそうな気がしてきました。今度それ記事にします。

# 参考

PHPのコンテナを構築する部分は、下記の記事をほとんど丸パクリです。ありがとうございましたm(_ _)m

http://qiita.com/tkyk@github/items/2f3ec0bb1ebe398af75f