Vagrant + Docker（Provider）で複数の開発環境をOSX内に立ち上げる


今までMacのネイティブ環境でWordPress開発をしてきたけれど、

- クライアント環境に合わせてサーバー環境が切り替えられなかったので、開発内容によってクライアントサーバー上で動作しないこということが多発
- クライアントによっては入稿されたHTMLファイルがルート相対パスで書いてあったりして、そんなときにvhostsいじるのが嫌
- OSバージョンアップで環境が壊れたりする（実際にMarvericsからYosemiteにアップしたらhttpdが起動しなくなって大変だった）
- 追加のモジュールとかでどんどん環境が肥大していく

と諸々が面倒なので、Yosemiteをクリーンインストールしたタイミングで、流行のDockerを使った開発環境構築を試してみたメモ。

# 前提条件

- Mac OSX Yosemite 10.10.3
- Vagrant 1.7.2
	- [vagrant-hostsupdater][vagrant-hostsupdater]
- VirtualBox 4.3.26

# やりたいこと

ローカルのドメイン毎に環境の違う複数の開発環境を作りたい。

1. wordpress.local  
汎用的なWordPress案件開発用
2. static.local  
静的サイト案件開発用
 
# 構築方法

Webサーバーコンテナ毎にVagrantfileを作成してプロキシVMを立ち上げ、その内部に関連するコンテナだけを立ち上げる。

- プロキシVM（wordpress.local）
	- Webサーバーコンテナ
	- MySQLコンテナ
	- データコンテナ
- プロキシVM（static.local）
	- Webサーバーコンテナ

## ホストマシンからのアクセス

プロキシVMのIPアドレス（プライベートネットワーク）をVagrantfileから設定し、[vagrant-hostsupdater][vagrant-hostsupdater] を使ってローカルドメインを設定。

- プロキシVM（wordpress.local）  
192.168.33.20
- プロキシVM（static.local）  
192.168.33.30

## この方法のメリット

* Vagrantfileのみで全ての設定が簡潔できるため、`docker run` でコンテナを1つ1つ立ち上げるよりも楽。
* VMに対してプライベートネットワークでIPアドレスを別々に割り振れるので、ホストマシンからのポートフォワードなどを考える必要がない。（プロキシVMとコンテナのポートフォワードは必要）
* Vagrantのプラグインでホスト名の設定なども自動で行えるので `vagrant up` のみで簡単に仮想環境が立ち上げられる。

# 構築環境

## 開発環境1（wordpress.local）

### ディレクトリ構成

* srv/
	* wordpress.local
		* htdocs/
			* {案件毎ディレクトリ}
		* Vagrantfile
		* proxy.Vagrantfile
		* Dockerfile

### Vagrantfile

* MySQLコンテナはDocker公式の[MySQL](https://registry.hub.docker.com/_/mysql/)イメージを利用。
* Dataコンテナは軽さを追求して[busybox](https://registry.hub.docker.com/_/busybox/)イメージを利用。
* WebサーバーコンテナはDockerfileから生成。
* WebサーバーコンテナはSynced Folderでホストマシンの `htdocs/` を `/var/www/html` としてNFSマウント。  
参考：[Vagrantで複数環境のSynced FolderにNFSを利用する方法](http://qiita.com/jyokyoku/items/8c6921f741c8d9420e0f)
* Dockerfileのビルドディレクトリに `htdocs/` を含んでしまうとビルドにとんでもなく時間がかかるため、ビルドディレクトリの同期には `rsync` を使い `htdocs/` を除外するように。

```Vagrantfile
Vagrant.configure(2) do |config|
  # dockerプロバイダの初期設定
  config.vm.provider "docker" do |docker| 
    docker.vagrant_machine = "wordpress_proxy"
    docker.vagrant_vagrantfile = "proxy.Vagrantfile"
    docker.host_vm_build_dir_options = {
      :type => "rsync",
      :rsync__exclude => ["htdocs"]
    }
  end

  # データコンテナの立ち上げ
  config.vm.define "wordpress_data" do |node|
    node.vm.provider "docker" do |docker|
      docker.name = "data"
      docker.image = "busybox"
      docker.remains_running = false
      docker.volumes = %w(/var/lib/mysql)
    end
  end

  # mysqlコンテナの立ち上げ
  config.vm.define "wordpress_mysql" do |node|
    node.vm.provider "docker" do |docker|
      docker.name = "mysql"
      docker.image = "mysql:5.5"
      docker.create_args = %w(--volumes-from="data")
      docker.ports = ["3306:3306"]
      docker.env = {
        MYSQL_ROOT_PASSWORD: "root"
      }
    end
  end

  # Webサーバーコンテナの立ち上げ
  config.vm.define "wordpress_web" do |node|
    node.vm.synced_folder "htdocs", "/var/www/html", type: "nfs", nfs_export: false, mount_options: ["nolock", "vers=3", "udp"]
    node.vm.provider "docker" do |docker|
      docker.name = "web"
      docker.build_dir = "."
      docker.create_args = %w(--volumes-from="data")
      docker.ports = ["80:80"]
      docker.link "mysql:mysql"
    end
  end
end
```

### Webサーバーコンテナ用のDockerfile

* クライアントのサーバーにCentOSが多いため、公式の[CentOS](https://registry.hub.docker.com/_/centos/)イメージを利用。
* Apacheと、PHP5.4（とりあえず必要なモジュールをいくつか）をインストール。
* Apacheの設定ファイル `welcome.conf` は開発に邪魔なので削除。
* `httpd.conf` の公開ディレクトリに `AllowOverride All` を設定。
* `php.ini` を開発用にカスタマイズ。

```Dockerfile
FROM centos:6.6
MAINTAINER m.ietomi <jyokyoku@gmail.com>

# TimeZone設定
RUN echo 'ZONE"=Asia/Tokyo"' > /etc/sysconfig/clock

# httpdをインストール
RUN yum -y update && \
    yum install -y httpd && \
    rm -rf /etc/httpd/conf.d/welcome.conf

# httpd.confを編集
RUN sed -i -e '/<Directory "\/var\/www\/html">/,/<\/Directory>/s/    AllowOverride None/    AllowOverride All/' /etc/httpd/conf/httpd.conf

# phpをインストール
RUN rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm && \
    rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm && \
    yum install -y php php-mbstring php-mysql php-gd php-devel php-pecl-xdebug --enablerepo=remi

# php.iniを編集
RUN sed -i -e 's/;date.timezone =/date.timezone = Asia\/Tokyo/' /etc/php.ini && \
    sed -i -e 's/display_errors = Off/display_errors = On/' /etc/php.ini && \
    sed -i -e 's/post_max_size = 8M/post_max_size = 100M/' /etc/php.ini && \
    sed -i -e 's/upload_max_filesize = 2M/upload_max_filesize = 100M/' /etc/php.ini && \
    sed -i -e 's/;mbstring.language = Japanese/mbstring.language = Japanese/' /etc/php.ini && \
    sed -i -e 's/;mbstring.internal_encoding = EUC-JP/mbstring.internal_encoding = UTF-8/' /etc/php.ini && \
    sed -i -e 's/;mbstring.http_input = auto/mbstring.http_input = pass/' /etc/php.ini && \
    sed -i -e 's/;mbstring.http_output = SJIS/mbstring.http_output = pass/' /etc/php.ini && \
    sed -i -e 's/;mbstring.encoding_translation = Off/mbstring.encoding_translation = Off/' /etc/php.ini

# xdebugの設定を追加
RUN printf "xdebug.max_nesting_level = 1000\n\
xdebug.var_display_max_depth = 3\n" >> /etc/php.d/xdebug.ini

EXPOSE 80

ENTRYPOINT ["/usr/sbin/apachectl"]
CMD ["-DFOREGROUND"]
```

### プロキシVM用のVagrantfile

* boxはSynced FolderにNFSを使いたいのと、Dockerの最新版をサポートしているので[dduportal/boot2docker](https://github.com/dduportal/boot2docker-vagrant-box)を利用
* ホストマシンから直接Dockerを触ることはないので、Docker関係のポートフォワードは無効化。
* 今回使用したイメージだと `vm.config.hostname` が設定できなかったので省略したが、 `vm.hostsupdater.aliases` の設定だけで特に問題なくhostsが設定された。

```proxy.Vagrantfile
Vagrant.configure(2) do |config|
  config.vm.define "wordpress_proxy" do |node|
    node.vm.box = "dduportal/boot2docker"

    node.vm.network :private_network, ip: "192.168.33.20", id: "default-network"
    node.vm.network :forwarded_port, guest: 22, host: 2233, id: 'ssh'
    node.vm.network :forwarded_port, guest: 2375, host: 2375, id: 'docker', disabled: true
    node.vm.network :forwarded_port, guest: 2376, host: 2376, id: 'docker-ssl', disabled: true

    node.hostsupdater.aliases = ["wordpress.local"]
    node.ssh.insert_key = false
  end
end
```

## 開発環境2（static.local）

### ディレクトリ構成

* srv/
	* static.local
		* htdocs/
			* {案件毎ディレクトリ}
		* Vagrantfile
		* proxy.Vagrantfile
		* Dockerfile

### Vagrantfile

* DataコンテナとMySQLコンテナがなくなっただけで、開発環境1と同じ

```Vagrantfile
Vagrant.configure(2) do |config|
  # dockerプロバイダの初期設定
  config.vm.provider "docker" do |docker| 
    docker.vagrant_machine = "static_proxy"
    docker.vagrant_vagrantfile = "proxy.Vagrantfile"
    docker.host_vm_build_dir_options = {
      :type => "rsync",
      :rsync__exclude => ["htdocs"]
    }
  end

  # Webサーバーコンテナの立ち上げ
  config.vm.define "static_web" do |node|
    node.vm.synced_folder "htdocs", "/var/www/html", type: "nfs", nfs_export: false, mount_options: ["nolock", "vers=3", "udp"]
    node.vm.provider "docker" do |docker|
      docker.name = "web"
      docker.build_dir = "."
      docker.ports = ["80:80"]
    end
  end
end
```

### Webサーバーコンテナ用のDockerfile

* PHP（とその関連）を抜いた以外は開発環境1と同じ。

```Dockerfile
FROM centos:6.6
MAINTAINER m.ietomi <jyokyoku@gmail.com>

# TimeZone設定
RUN echo 'ZONE"=Asia/Tokyo"' > /etc/sysconfig/clock

# httpdをインストール
RUN yum -y update && \
    yum install -y httpd && \
    rm -rf /etc/httpd/conf.d/welcome.conf

# httpd.confを編集
RUN sed -i -e '/<Directory "\/var\/www\/html">/,/<\/Directory>/s/    AllowOverride None/    AllowOverride All/' /etc/httpd/conf/httpd.conf

EXPOSE 80

ENTRYPOINT ["/usr/sbin/apachectl"]
CMD ["-DFOREGROUND"]
```

### プロキシVM用のVagrantfile

* SSH用のポートとホスト名が違うだけで開発環境1とほぼ同じ。

```proxy.Vagrantfile
Vagrant.configure(2) do |config|
  config.vm.define "static_proxy" do |proxy|
    proxy.vm.box = "dduportal/boot2docker"
    
    proxy.vm.network :private_network, ip: "192.168.33.30", id: "default-network"
    proxy.vm.network :forwarded_port, guest: 22, host: 2232, id: 'ssh'
    proxy.vm.network :forwarded_port, guest: 2375, host: 2375, id: 'docker', disabled: true
    proxy.vm.network :forwarded_port, guest: 2376, host: 2376, id: 'docker-ssl', disabled: true

    proxy.hostsupdater.aliases = ["static.local"]
    proxy.ssh.insert_key = false
  end
end
```

# 課題

- NFSマウントがMacを再起動する毎に解除されてしまうので、いちいち `vagrant reload` してやる必要がある。（再Buildされてしまうので面倒）
- Vagrantfile毎にIPアドレスの設定やポートフォワードの設定をしなくてはならないので、環境が増えたときに管理する手間がかかる

[vagrant-hostsupdater]:https://github.com/cogitatio/vagrant-hostsupdater