Laravelで最適なDocker環境を作る

この記事は[こちら](http://qiita.com/kotamat/items/1f3f12a1c78612065e47) の構成の具体的な説明となります

## ディレクトリ、ファイル構成

```
├── docker
│   ├── docker-compose.yml
│   ├── composer
│   │   └── Dockerfile
│   ├── data
│   │   └── Dockerfile
│   ├── fpm
│   │   ├── Dockerfile
│   │   └── php-fpm.d
│   │       ├── docker.conf
│   │       ├── www.conf
│   │       ├── www.conf.default
│   │       └── zz-docker.conf
│   ├── gulp
│   │   └── Dockerfile
│   ├── nginx
│   │   ├── Dockerfile
│   │   └── config
│   │       ├── laravel.conf
│   │       ├── nginx.conf
│   │       └── nginx_run.sh
│   └── npm
│       └── Dockerfile
├── logs
└── www
```

## 各種説明

こちらは各プロセスごとにディレクトリを分割し、それ専用のDockerfileを作成、docker-compose.ymlでそれらのビルド設定を記載し、一括ですべての環境を開始できるようにしています。

### docker-compose.yml

```yaml
version: "2" # docker-composeのversion2を使用
services: # 各種サービスの設定
  data: # データボリューム用コンテナ、ソースやDBのデータ等開発で生成するものはここにマウント
    build: ./data
    volumes:
      - ../:/data
      - /var/lib/mysql
  db: # DBストア
    image: mariadb
    volumes_from:
        - data
    environment:
      MYSQL_ROOT_PASSWORD: pass
  fpm: # php-fpm: PHPプロセスを実行するコンテナ
    build: ./fpm
    volumes_from:
      - data
  nginx: # php-fpmにリバースプロキシする
    build: ./nginx
    volumes_from:
      - data
    links:
      - fpm:fpm
    ports:
      - "80:80"
  composer: # composerコマンドを実行するときに都度実行
    build: ./composer
    volumes_from:
      - data
  gulp: # gulpコマンドを実行するときに都度実行
    build: ./gulp
    volumes_from:
      - data
  npm: # npmコマンドを実行するときに都度実行
    build: ./npm
    volumes_from:
      - data
```

### db

```dockerfile
FROM debian:jessie

MAINTAINER "Kota Matsumoto" <kota1861@gmail.com>

RUN mkdir -p /data
VOLUME ["/data"]
CMD ["true"]
```

このように`/data`ディレクトリを作成し、そこにボリュームマウントできるようにしているだけです。
使用しているimageはdebian:jessieを使っていますが、これは他のサービスもこれを使っているからで、他のimadeでも問題ありません。

### fpm

```dockerfile
FROM php:7.0.12-fpm

MAINTAINER "Kota Matsumoto" <kota1861@gmail.com>

RUN apt-get update -y

# clean fpm conf
# RUN rm /usr/local/etc/php-fpm.d/*
COPY php-fpm.d /usr/local/etc/php-fpm.d

RUN mkdir -p /data
VOLUME ["/data"]
```

[phpのDockerhub](https://hub.docker.com/_/php/)で公式にfpm用のimageを提供しているので、そちらを使っています。
ちなみに[github上](https://github.com/docker-library/php/tree/173945670390f6595da8f93ae46b442167ff05be)に元のDockerfileがあります。

なるべく最新のpackageを使いたいので、`apt-get update`を行い、php-fpm.dに設置してあるfpm用の設定ファイルをコピーしています。

### nginx

```dockerfile
FROM nginx

MAINTAINER "Kota Matsumoto" <kota1861@gmail.com>

COPY config/nginx.conf /etc/nginx/nginx.conf
COPY config/laravel.conf /etc/nginx/conf.d/laravel.conf
RUN rm /etc/nginx/conf.d/default.conf

RUN mkdir -p /data
VOLUME ["/data"]
```

nginxではphp-fpmを使用するため若干トリッキーなことをしています。
というのも、nginxからfpmのコンテナに向けてリバースプロキシをするために、サーバーのホスト名・ポート番号を下記のように指定する必要があるのですが

```conf
http {
    upstream phpfpm_backend {
        server fpm:9000;
    }
    ...
}
```

このfpmというのが、fpmサービスが提供しているもので、同じdocker-compose内だとこのサービス名がホスト名の名前解決に使われます。

またlogの出力先として`/data/logs`を指定しているので、実行後のログはホストのlogsディレクトリに逐一反映されます

```conf:nginx.conf
    access_log /data/logs/access.log;
    error_log /data/logs/error.log warn;
```

### gulp

```dockerfile
FROM debian:jessie

MAINTAINER "Kota Matsumoto" <kota1861@gmail.com>

RUN apt-get update -y
RUN apt-get install -y curl git
RUN curl -sL https://deb.nodesource.com/setup | bash --
RUN apt-get install -y nodejs
RUN npm update -g npm
RUN npm cache clean
RUN npm i -g n
RUN n stable
RUN apt-get purge -y nodejs npm
RUN npm i -g gulp-cli

RUN mkdir -p /data/www
VOLUME ["/data"]
WORKDIR /data/www

ENTRYPOINT ["gulp"]
CMD ["-h"]
```

最新のnodejsをinstallし、npmを最新にしています。
nパッケージを使用しているのは、apt-getに乗っかっているnodeだとgulpをインストールするバージョンに達しないため、nパッケージを使ってnodejsのバージョンを上げています。
ただ、nパッケージをインストールするにはnodejsとnpmが必要なため、一時的にインストールし、あとでpurgeしています。
ENTRYPOINTをgulp、CMDを-hとすることによって
`docker-compose run gulp`
と実行すれば`gulp -h`が実行され、
`docker-compose run gulp <command>`
とすれば`gulp <command>`が実行されます。

### npm

```dockerfile
FROM debian:jessie

MAINTAINER "Kota Matsumoto" <kota1861@gmail.com>

RUN apt-get update -y
RUN apt-get install -y curl git
RUN curl -sL https://deb.nodesource.com/setup | bash --
RUN apt-get install -y nodejs
RUN npm update -g npm
RUN npm cache clean
RUN npm i -g n
RUN n stable
RUN apt-get purge -y nodejs npm

RUN mkdir -p /data/www
VOLUME ["/data"]
WORKDIR /data/www

ENTRYPOINT ["npm"]
CMD ["-h"]
```

構造としてはgulpとほぼ同じです。
完全に別にしていますが、このnpmのdockerfileをビルドしたものをどこかのレジストリにpushしておき、npmとgulpで同じimageをFROMに設定することでDRYにすることが可能です。

### composer

```dockerfile
FROM php:7.0.12

MAINTAINER "Kota Matsumoto" <kota1861@gmail.com>

WORKDIR /tmp

RUN apt-get update -y
RUN apt-get install -y git unzip
RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer
RUN composer self-update
RUN composer global require hirak/prestissimo

RUN mkdir -p /data/www
VOLUME ["/data"]
WORKDIR /data/www

ENTRYPOINT ["composer"]
CMD ["--help"]
```

こちらはcomposerを使用するだけなので、使用するimageは純正のimage(fpmとか入っていない)を使っています。
composerをcurlコマンドで取得後、実行可能な場所に設置するのですが、このディレクトリはこのコンテナだけが参照できるので、他のサービスではcomposerを使用することはできません。
composerの各パッケージを並列にインストールできるよう`hirak/prestissimo`をインストールしています。
composerの実行対象ディレクトリは`/data/www`ですので、ここをworkdirとしています。
composerはキャッシュ機構があるので、複数人で開発をする場合は、都度composerの実行後コンテナをimageに固め、レジストリにpushすることで、各パッケージのインストール時間を軽減できます。

gulpと同様
ENTRYPOINTをcomposer、CMDを-hとすることによって
`docker-compose run composer`
と実行すれば`composer -h`が実行され、
`docker-compose run composer <command>`
とすれば`composer <command>`が実行されます。

## 使用方法

dockerディレクトリでdocker-composeコマンドを使用します。

```bash

# 各デーモン系コンテナの起動
docker-compose up -d

# laravelのインストール
docker-compose run --rm composer create-project laravel/laravel

# npmコマンドの実行(elixir等のインストール)
docker-compose run --rm npm install

# gulpでライブコーディング
docker-compose run --rm gulp watch

# composerパッケージのアップデート
docker-compose run --rm composer update
```

## まとめ

このように各サービス、プロセスごとに分割することで、それぞれのサービスを独立して管理することができ、開発に集中することができます。
LaravelではhomesteadというVagrantで動くものを提供していますが、Docker for Mac等Dockerをローカルで使用できる環境が整ってきたので、起動スピードの早いDockerを使うのが何かと便利かとおもいます。