Elixir/Phoenix 環境を Docker for Windows で構築してみる

Phoenix 環境をDockerで構築してみるメモ。

## 前提

Windows10 PowerShell上でDocker for Windowsを使います。

Bash on Windowsやboot2dockerは使いません。

```
PS > [System.Environment]::OSVersion

Platform ServicePack Version      VersionString
-------- ----------- -------      -------------
 Win32NT             10.0.14393.0 Microsoft Windows NT 10.0.14393.0

PS > docker --version
Docker version 1.12.1, build 23cf638

PS > docker-compose --version
docker-compose version 1.8.0, build d988a55
```

なおデータベースにはデフォルトのPostgreSQLではなくMySQLを用います。

### Docker for Windowsの導入

http://www.atmarkit.co.jp/ait/articles/1609/01/news053.html
を参考にDocker for Windowsを準備しておきます。

インストール自体はGUIアプリケーションなので特に悩むポイントはありませんが、インストール後にDocker for Windowsアプリの設定でShared Devicesに必要なドライブを追加しておきます。
これが無いとファイルマウントがうまく行きません。

## Phoenixプロジェクト作成 (phoenix.new)

まずは phoenix.new でプロジェクトファイルを生成するための専用のDockerコンテナを用意します。

```
PS > mkdir project_name
PS > cd project_name
```

http://qiita.com/xtity/items/969eefb83fcac9023542
を参考にDockerfileを作成します。

```Dockerfile
#----------------
# OS
#----------------
FROM centos:centos7

RUN yum update -y && yum clean all
RUN yum reinstall -y glibc-common

#----------------
# ENV
#----------------
# Set the locale(ja_JP.UTF-8)
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8

# Set app env
ENV HOME /root

#----------------
# ミドルウェアインストール
#----------------
WORKDIR /usr/local/src
RUN yum install -y gcc gcc-c++ make openssl-devel ncurses-devel && yum clean all
RUN yum install -y epel-release.noarch && yum clean all
RUN yum install -y http://packages.erlang-solutions.com/site/esl/esl-erlang/FLAVOUR_1_general/esl-erlang_18.1-1~centos~7_amd64.rpm && yum clean all
RUN yum install -y sudo wget git tar bzip2 incron vim nodejs npm && yum clean all
RUN yum install -y mysql && yum clean all

#----------------
# Elixirインストール
#----------------
ENV ELIXIR_VERSION 1.3.2

# Build Elixir
RUN git clone https://github.com/elixir-lang/elixir.git
WORKDIR /usr/local/src/elixir
RUN git checkout refs/tags/v${ELIXIR_VERSION}
RUN make clean install

# Build Mix Tasks to use Dialyxir
WORKDIR /usr/local/src
RUN git clone https://github.com/jeremyjh/dialyxir.git
WORKDIR /usr/local/src/dialyxir
RUN mix archive.build
RUN yes | mix archive.install && mix dialyzer.plt

#----------------
# Phoenixインストール
#----------------
RUN mkdir -p /usr/local/src/phoenix/origin
WORKDIR /usr/local/src/phoenix/origin
RUN git clone https://github.com/phoenixframework/phoenix.git
WORKDIR /usr/local/src/phoenix/origin/phoenix/installer
RUN MIX_ENV=prod mix archive.build
RUN yes | mix archive.install
RUN mkdir -p /usr/local/src/phoenix/app
WORKDIR /usr/local/src/phoenix/app
```

いちどビルドして、プロジェクトファイルを作成します。

```
PS > docker build -t your_name/your_elixir_phoenix:0.1 .
PS > docker run --rm -it -v C:\path\to\current\dir\project_name:/usr/local/src/phoenix/app your_name/your_elixir_phoenix:0.1  mix phoenix.new . /usr/local/src/phoenix/app/project_name --database mysql
```

PhoenixのデフォルトのDBはPostgreSQLですが、 `mix phoenix.new project_name --database mysql` でMySQL(MariaDB)に変更できます。
(本当は、まずは手軽にSQLiteで作成したかったのですが、SQLite用O/Rマッパーがうまく動かせずPhoenixアプリが起動できませんでした...)

ここまでで、作成したファイルを `git init` 等で管理しておくと良いと思います。

## Phoenix起動 (phoenix.server)

先程のDockerfileに下記追記し、生成されたPhoenixプロジェクトを動かすためのDockerコンテナを作成します。

```Dockerfile
#----------------
# Setup Phoenix Project
#----------------
ENV PHOENIX_APP_NAME project_name
ENV PHOENIX_APP_PORT 4000
ENV PHOENIX_APP_ROOT /usr/local/src/phoenix/app/${PHOENIX_APP_NAME}

WORKDIR $PHOENIX_APP_ROOT

EXPOSE ${PHOENIX_APP_PORT}
COPY . $PHOENIX_APP_ROOT
WORKDIR $PHOENIX_APP_ROOT

# Compile phoenix(FOR dev)
RUN yes | mix local.hex && yes | mix local.rebar && mix do deps.get, compile

# npm install
RUN npm install
RUN npm install -g brunch

# Run Phoenix
CMD ["/bin/bash", "-c", "mix phoenix.server"]
```

続いて、Phoenixを動かすアプリケーションサーバとMySQLを動かすデータベースサーバを同時に管理するためのdocker-composeファイルを作成します。

```docker-compose.yml
version: '2'
services:
  app:
    build: .
    environment:
      MYSQL_ROOT_USERNAME: 'root'
      MYSQL_ROOT_PASSWORD: 'pass'
      MYSQL_HOSTNAME: 'mysql'
      MYSQL_PORT: '3306'
    ports:
      - '4000:4000'
    volumes:
      - .:/usr/local/src/phoenix/app/project_name
    links:
      - mysql
  mysql:
    image: mysql:5.7.10
    environment:
      MYSQL_ROOT_PASSWORD: 'pass'
    ports:
      - '3306:3306'
    volumes:
      - mysql-data:/var/lib/mysql
volumes:
  mysql-data:
    driver: local
```

dev.exsのMySQL設定箇所に接続設定を追加します。

```dev.exs
# Configure your database
config :app, App.Repo,
  adapter: Ecto.Adapters.MySQL,
  username: System.get_env("MYSQL_ROOT_USERNAME"),
  password: System.get_env("MYSQL_ROOT_PASSWORD"),
  hostname: System.get_env("MYSQL_HOSTNAME"),

  database: "app_dev",
  pool_size: 10
```

### 起動

```
PS > docker-compose build
PS > docker-compose up -d
```

localhost:4000 にアクセスして起動すれば成功です。

### メモ

- うまく表示されないときは `$ docker exec -it projectname_app_1 bash` で中に入って確認。
- うまく起動できないときは `$ docker run --rm -it projectname_app bash` で中に入って確認。
  - `RUN mix phoenix.server` が成功しないとコンテナのSTATUSがExitedになってしまい、`docker exec`もできない。
- `docker-compose up`や`docker run`で立ち上がったかどうかは `$ docker ps` で確認。`-a` オプションで、実行中でないコンテナについても確認。
- `docker-compose build`や`docker build`でビルドできたかどうかは `$ docker images`で確認。
  - 不要なコンテナは `$ docker rm` で、 不要なイメージは `$ docker rmi` で削除。
- 作成した _build/ ディレクトリをコンテナをまたいでマウントで持っていくと上手くいかない事があるので、適宜消す必要がある。デバッグ中にハマった。
- Docker for Windowsは意外と情報が少ない。

## 参考

- [CentOS \+ Erlang \+ Elixir \+ PhoenixのDockerイメージで今日から始めるElixir on Phoenix入門 \- Qiita](http://qiita.com/xtity/items/969eefb83fcac9023542)
- [RailsアプリをDockerで開発するための手順 \- Qiita](http://qiita.com/togana/items/30b22fc39fe6f7a188ec)