Elixir/Phoenix+MySQL5.7環境をDocker/Docker Composeで整え開発する。

# Elixir/Phoenix+MySQL5.7環境をDocker/Docker Composeで整え開発する。

##この記事を書いた理由１
手元の開発コンピュータが何であっても良いように開発環境を、いまさら・・**Docker**の勉強も踏まえてみましたので、その共有です。

##この記事を書いた理由２
あー、これでApache、NginX、MySQLは、XAMPPやMAMPのやつ使って、足りないものはOS(MacOSX、Windows10などなど)にインストールして〜・・・ってのがなくなるし、新しいメンバーに開発環境整えてもらう時に、「**動かないんですけど・・**」で苦労することもなくなる〜。実際、社内でも、そうしてます。:grin:

##注意点
私が**MacOSX**を使用しているので、細かいターミナルでの操作などは、OSXベースになりますが、**Windows10**など、その他の方も、ほとんど同じなはずなので参考になると思います。

また今回はDockerを使用しています。[DockerDesktop](https://www.docker.com/products/docker-desktop)をインストールするなどしてください。Windows10では、Dockerは、確か、**Professional**じゃないと動かなかったような気もしていますので、そのあたりは・・ご了承ください。 :bow_tone2: 。

またDockerについては、**細かく説明**は、していません。


##キーワード
- Elixir
- Phoenix
- Docker
- Docker Compose

##アジェンダ
1. Elixirの環境をDockerで起動できるようになる。  
  - Docker初体験
2. 1.の環境（Dockerコンテナ）でPhoenixプロジェクトを作成して起動してみる。
  - Phoenixプロジェクト作成・初体験
3. MySQLもDockerで起動してPhoenixプロジェクトでDBも使えるようになる。
    - DockerComposeで、複数Dockerをまとめる。
    - プロジェクト一式をローカルPCにコピーする。
4. 日々の開発が出来るか？確認する。
    - 1日が終わってdocker-compose down。次の日に開発環境を起動する。

##1. Elixirの環境をDockerで起動できるようになる。
###1-1. 作業ディレクトリを作成する
ホームディレクトリ直下に、適当な作業ディレクトリを作成することにします。
とりあえず、ここでは、elixerというディレクトリを作成しました。

```
mkdir ~/elixir
cd elixir
```

###1-2. ElixirのDockerfileを作成する
テキストエディタで以下のように**Dockerfile**を作成します。

```Elixir:Dockerfile
FROM elixir:alpine
```
###1-3. DockerでElixir環境を起動してみる。
早速、Elixirの環境を起動？してみましょう。Docker初めてな人は、起動というと変な感じかもですが。
まず、Dockerfileで記述した内容のものを**build**します。**elixir_sample**という名前にしています。

```Elixir
docker build -t elixir_sample .
```

buildできたら、起動します。

```
docker run -p 4000:4000 -i -t elixir_sample /bin/ash
```
実行後、iexというコマンドを実行して、**ex(1)>** というのが出ていたら成功っす。

##2. 1.の環境（Dockerコンテナ）でPhoenixプロジェクトを作成して起動してみる。
###2-1. Phoenixフレームワークのインストール
1-3.のiex状態な人は、ctl+cを2回押してコマンド待ち状態になってください。そして以下のコマンドを実行します。

```elixir
mix archive.install hex phx_new 1.4.0
```
途中、[Yn]と聞かれますが、何も考えず**Y**で。

###2-2. プロジェクトを作成してみる。
このコマンドで**sample_db**というプロジェクトを作成します。Phoenixフレームワークは、postgresqlがデフォルトDBになっているんですが、**今回はMySQL**を使用したいので、--databaseオプションで指定しています。

あ、、この時点では、まだMySQLサーバ自体は、ローカルにインストールも起動もされていません。sample_dbプロジェクトで使用するデータベース・サーバの**指定のみ**です。

2-1.と同様に途中で[Yn]と聞かれますが**Y**で。

```elixir
mix phx.new sample_db --no-webpack --database=mysql
```

###2-3. プロジェクト起動してみる。
ここまでで、プロジェクトを起動してみましょう。
下のコマンドを実行します。

```eliir
cd sample_db
iex -S mix phx.server
```

そして、少々待つと、
>[info] Running SampleDbWeb.Endpoint with cowboy 2.6.1 at http://localhost:4000

と表示されます。

さらに、まだデータベースが用意されていないため、**接続エラー**が**ズラズラ**と表示されます。

>[error] Mariaex.Protocol (#PID<0.2550.0>) failed to connect: \** (Mariaex.Error) tcp connect: econnrefused

が、、そんなものは、無視して:ok_hand:、ブラウザに**localhost:4000**と入力して、
sample_dbプロジェクトが無事起動しているかを確認しましょう。

<img width="791" alt="スクリーンショット 2019-01-07 0.48.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/14363/b1c822d4-6fa0-89c4-1781-087672986630.png">
こんな風に表示されました? :lollipop:

###2-4. データベースの設定
接続先のデータベースは**未だ作ってない**のですが、
その前に、Phoenixプロジェクト内のデータベースに関する**設定**をやっちゃいます。

```elixir
cd config
vi dev.exs
```

として、設定ファイル**dev.exs**を開いて、
下のようにデータベースに関する部分を修正して保存します。

修正したのは、**password**の値と**hostname**です。

passwordは、あとで作成するデータベースsample_db_devへの**接続パスワード**なので、
自由に設定してもらってOKです。ここでは **root** としています。

また、**hostname**はデフォルトで**localhost**となっているのですが、 
**db** というものにしています。

localhostじゃないとダメなんじゃ・・？　って思う方もいらっしゃるかもですが、
ここは、後述の**DockerCompose**関連が理由で、そうやっています。

```elixir

config :sample_db, SampleDb.Repo,
  username: "root",
  password: "root",
  database: "sample_db_dev",
  hostname: "db",
  pool_size: 10
```

###2-5. プロジェクトファイル群をローカルPCにコピー
2-2.で作成したsample_dbプロジェクトは、あくまでDockerコンテナに存在するリソースになります。つまり、これらのファイルを参照するためにはDockerコンテナに入る必要があります。

ということは・・・**毎回**（毎日？）、dockerコンテナ内にコマンド打って入って、
さらに、そのコンテナ内からファイルをさわる・・必要がある？？

コンテナ内のファイルって、コンテナ外にあるテキストエディタで修正したり出来るのかな・・？
＊この方法をジックリ調べていないので、次の様にしています。:innocent:

私は、ローカルPCに、この**プロジェクト一式をコピー**し、Dockerコンテナ起動時にDockerコンテナに**マウント**する方法をとるようにしています。マウントについては、後述しますので、その準備として、ローカルPCにコピーします。

今、起動しているelixir_sampleコンテナを**終了せず**に、
新しいターミナルウィンドウを開いて、次のコマンドを打ちます。

```
docker ps
```

すると、次のように表示されるのでコンテナIDをコピーします。
ここでは、**fe7dd49baeb5** です。

```elixir

CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS
fe7dd49baeb5        elixir_sample       "/bin/ash"          About a minute ago   Up About a minute   0.0.0.0:4000->4000/tcp   
```

つづけてローカルPCにコピーしていきます。

```
docker cp fe7dd49baeb5:sample_db .
```

sample_dbディレクトリ一式がローカルの~/elixir/sample_dbとしてコピーされていますか？

##3. MySQLもDockerで起動してPhoenixプロジェクトでDBも使えるようになる。
###3-1. MySQL用のDockerfileを用意する
~/elixir/mysqlディレクトリを作成して以下のDockerfileを用意します。

```elixir:mysql/Dockerfile
FROM mysql:5.7
```

###3-2. Elixir用のDockerfileファイルの修正
以下のように2行目以降を追加しておいてください。

```Elixir:Dockerifle
FROM elixir:alpine

RUN mkdir /app
WORKDIR /app

RUN yes | mix local.hex
RUN yes | mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez
RUN mix local.rebar --force
RUN apk update && \
    apk upgrade && \
    apk add --update\
    bash \
    curl-dev \
    gcc \
    g++ \
    make \
    git \
    nodejs \
    nodejs-npm \
    && rm -rf /var/cache/apk/*
```
###3-3. docker-compose.ymlファイルの作成
3-1.で、MySQLサーバもDockerで起動することが出来るのですが、このままだとDocker runをElixirとMySQLを使えるようになるために**2回**打たないといけません。こんな感じだと、そのほかのミドルウェアが必要になるたびにDocker runコマンドを打つ**回数も増え**ます。

しかも、起動した後に、それぞれのサービスに対して毎回、、

**1. 決まったコマンドを実行する必要があったり、**
**2. ボリューム（ローカルPCのディレクトリ領域を各サービスのDocker領域から共有したい）のマウントが必要になったり、、**

すると、毎回コマンドを打つだけで**大変**ですし、その**管理**も大変になってしまいます。

そんなのを、まとめて管理してくれるのが**Docker Compose**です。
Docker Composeの詳しいことは、ネット上に詳しい方々がいらっしゃいますので、そちらなどを参照してください。:sunglasses:

```elixir:docker-compose.yml
version: '3'
services:

  db:
    build: mysql/
    volumes:
      - ./mysql_lib:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=root
    ports:
      - "3306:3306"

  app:
    build: .
    tty: true
    volumes:
      - ./sample_db:/app
    ports:
      - "4000:4000"
    command: ash -c "mix deps.get && mix phx.server"
    depends_on:
      - db

volumes:
  mysql:
    driver: local
```

ここで行われていることを、ちょっとだけ説明しておきます。

* db:
    * db: は、MySQLコンテナの参照名にもなります。そのため2-4.でプロジェクトからMySQLへの接続に関して**hostname**をdbにしています。
    * volumes:でやっているのは、 MySQLのDockerコンテナにある/var/lib/mysqlフォルダをローカルPC上の~/elixir/mysql_libにマウントしています。これは、データベース（のデータ）をローカルPC上に持っておきたい（**＝永続化**）ためです。そーじゃなかったら、やらなくてOKです。Dockerイメージを削除したりすると、**データベース自体が消えてしまう**ので、毎回データベース作る必要が出てきます。それでも大丈夫だったら、やんなくてOKです。
    * portsは、指定しなくても起動できるんですが、私は、**MySQLWorkbench**をMacOSXで使っていて、ポート指定をしていないと、DockerコンテナのMySQLに接続できなかった（正確には、出来そうなのですが、ややこしい？）ので、指定しています。

* app:
    * volumes: では、2.5でローカルにコピーしていたプロジェクトを**Dockerコンテナ内にマウント**しています。こーすることによって、プロジェクトファイルをローカルPC上から直接編集できるようになり、且つElixir/Phoenix環境が存在するDockerコンテナを利用して、このプロジェクトを実行することが出来ます。

###3-4. コンテナ（ElixirとMySQL）の同時起動
では、**DockerCompose**でElixir/Phoenix環境とMySQL環境を**同時**に起動します。
docker-composeコマンドを実行する前に、現在起動しているコンテナを**stop**しておきます。
そうしないと、既に4000ポートを、このコンテナ（elixir_sample）が使用しているので、起動できないっす。

ターミナルからdocker psコマンドを叩いて、コンテナIDを確認します。

```elixir
docker ps
```

コンテナIDが確認できたら、次のコマンドを実行します。

```
docker stop コンテナID
```

ここまで出来たら、docker-composeコマンドで一気に2つのコンテナを作成してみましょう。

```
docker-compose up
```

2-3.の時と同様に、まだデータベースが作成されていないので、次の用に接続エラーが表示されると思いますが、一旦OKです。
ブラウザにlocalhost:4000で同じくPhoenixフレームワークの画面が表示されるかを確認してください。

###3-5. データベースの作成
とりあえず、このままでも、この記事の目的としては、大丈夫なのですが、データベース接続エラーが、**気持ち悪い**ので、MySQLコンテナに入ってデータベースを作成してみましょう。**docker ps**コマンドでMySQLコンテナの**コンテナID**を確認しコピーします。

```elixir
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES
b07acadbaa95        elixir_db           "docker-entrypoint.s…"   14 minutes ago      Up 5 minutes        0.0.0.0:3306->3306/tcp, 33060/tcp   elixir_db_1
```

2つコンテナが確認できますが、データベースのコンテナは、このように**"_db"**という名前が付いていると思います。
では、続いてコンテナの中に入りましょう。

```elixir
docker exec -it b07acadbaa95 /bin/bash
```

コンテナに入れたら、mysqlサーバーに接続して、データベースを作成します。データベース名は、2-4.のconfig/dev.exsに書かれてあった**sample_db_dev**です。

```elixir
mysql -uroot -proot
create database sample_db_dev default character set utf8;
```

作成できたら、一旦コンテナを停止して、再度起動します。

```elixir
docker-compose down
docker-compose up
```

データベース接続エラーは、なくなりましたでしょうか？


##4. 日々の開発が出来るか？確認する。
###4-1. 1日の開始｜コンテナ起動
これまでの　おさらいになっちゃいますが、念のため書いておこうと思います。
作業を開始する時は、PCを起動して、以下のコマンドでコンテナを起動します。

```
cd ~/elixir
docker-compose up
```

###4-2. 作業
ソースコードは、~/elixir/sample_dbとしてプロジェクトファイルがありますので、それを修正していきます。
docker-compose.ymlで、Dockerコンテナ内にマウントしているので、ローカルPC上のソース・ファイルを直接、好きなエディタで編集可能です。

確認もブラウザなりから4000ポートを経由すれば出来ます。
あとは、gitにコミットしたりですね。

###4-3. 1日の終わり
dockerコンテナを停止して、PCの電源切りましょう。:tools:

```elixir
docker-compose down
```

##まとめ
Elixir/Phoenixの開発環境のイメージと、Docker,DockerComposeを未だ使っていない方の、**とっかかり**　や**参考**になれば嬉しいです。もっと、こんな方法があるよ！　や、間違っている箇所があれば教えてください。:confounded:　次回は、、**Distilleryのデプロイ手法**か、**datadog**で、Elixir/Phoenixの吐く**ログを見る設定**なんかを投稿しようかと思っています（予定は未定）。