Vagrant で Elixir/Phoenix 開発環境を構築する手順

Elixir/Phoenix の開発環境を構築するにはいくつかの方法がありますが、ある程度の規模の Phoenix アプリを開発するなら Docker Compose を利用したくなるでしょう。

しかし、実際に開発を始めると、悩ましい問題に遭遇します。それは、Docker Desktop for Mac あるいは Docker Desktop for Windows の元で動く Phoenix アプリがとても遅い、ということです。いや、Phoenix アプリに限りません。データベースを使用する Web アプリ全般の反応がとても悪いです。「Docker Mac 遅い」または「Docker Windows 遅い」で Google 検索すると、大量の怨嗟の声とさまざまな解決策がヒットします。

ひとつの解決策は、Docker Desktop for Mac/Windows の利用をあきらめて、Vagrant + VirtualBox を採用するというものです。VirtualBox の上に Linux を載せ、そこに Docker をインストールし、Linux 上に Elixir コンテナを構築するという方法です。

筆者が試してみた限りでは、この方法はとてもうまく行きます。しかし、VirtualBox 特有の罠がいくつかあるので、なにもないところから `Vagrantfile` と `Dockerfile` と `docker-compose.yml` を作るのは意外に難しいものです。

そこで、[phx-vagrant](https://github.com/oiax/phx-vagrant) という GitHub リポジトリに、これらの設定ファイルのセットを作りました。

以下、このリポジトリを clone して Elixir/Phoenix 開発環境を構築する手順を紹介します。

もしこの手順の通りにやってみて環境構築に失敗したら、GitHub で issue を立てて教えていただけると助かります。

## 動作確認済みのOS

* macOS Catalina (v10.15)
* macOS Mojave (v10.14)
* Windows 10 Pro
* Windows 10 Home

## 必要なソフトウェア

* Oracle VM VirtualBox 6.0/6.1
* Vagrant 2.2 以上
* Git 2.7 以上

### Git for Windows に関する注記

Git for Windows をインストールする過程で「Configuring the line ending conversions」について尋ねられたとき、デフォルトの「Checkout Windows-style, commit Unix-style line endings」ではなく、2番目の選択肢「Checkout as-is, commit Unix-style line endings」を選んでください。

すでに Git for Windows をインストール済みの場合は、次のコマンドで変更できます。

```
% git config --global core.autocrlf input
```

## 環境構築手順

```
% git clone https://github.com/oiax/phx-vagrant.git
% cd phx-vagrant
% export VAGRANT_DISABLE_STRICT_DEPENDENCY_ENFORCEMENT=1
% vagrant plugin install vagrant-disksize
% vagrant up
% vagrant ssh
$ bin/setup.sh
```

これで、Ubuntu 18.04 Server の上に Docker がインストールされ、Elixir 1.9.4 がインストールされた Debian の Docker コンテナができあがります。

ネットワーク接続状況によりますが、初めてこの環境を構築する場合、2時間以上かかることがあります。ほとんどの時間は Ubuntu 18.04 Server の ISO イメージのダウンロードに費やされます。

## Phoenix アプリケーション開発手順

### Phoenix アプリケーションの新規作成

```
$ bin/login.sh
> mix phx.new my_app
> exit
```

途中で「Y」または「n」の入力を求められたら、そのまま Enter キーを押してください。

### ソースコードの書き換え

ホストPC側の `projects` ディレクトリの下にソースコードツリーができます。ここから先、ここに生成された Phoenix アプリケーションのソースコードをテキストエディタで書き換えていきます。

#### データベース接続設定の変更

`projects/my_app/config/dev.exs` を開き、`hostname` の値を `"localhost"` から `"db"` に変更してください。

### データベースの作成

```
$ bin/login.sh
> cd my_app
> mix ecto.create
> exit
```

### Phoenix サーバーの起動

```
$ bin/start.sh
> cd my_app
> mix phx.server
```

### 動作確認

ブラウザで http://localhost:4000 を開けば、Phoenix アプリケーションにアクセスできます。

### ソースコードの同期について

ホストPC（Mac/Windows）側の `projects` ディレクトリ（以下、**A** と呼ぶ）と Docker コンテナ内の `/projects` ディレクトリ（以下、**B** と呼ぶ）は双方向に同期されています。

一方でファイルの内容が変更されると、ほぼ瞬時に他方に反映されます。

動作確認のため、ホストPC（Mac/Windows）側のテキストエディタで以下の2つのファイルを適宜書き換えてください。

* `projects/my_app/lib/my_app_web/templates/page/index.html.eex`
* `projects/my_app/assets/css/phoenix.css`

ファイルを保存してから数秒以内にブラウザが自動でリロードされ、表示が更新されることを確認してください。

新しく作成されたファイルやディレクトリの同期も自動で行われます。ただし、**B** から **A** への同期は、若干の遅れが生じます。

ファイルおよびディレクトリの削除はどちらの方向にも自動で同期されませんが、次のコマンドにより **A** から **B** へ同期できます。

```
$ bin/sync_to_container.sh
```

ファイルおよびディレクトリの移動（名前変更）は、ファイルおよびディレクトリの複製と削除の組み合わせであると考えてください。つまり、ファイルおよびディレクトリの複製は **A** から **B** へ自動で同期されますが、ファイルおよびディレクトリの削除は手動で同期する必要があります。

なお、**B** の中でファイルおよびディレクトリを削除することはできません。仮に削除しようとしても、すぐに **A** から同期されてしまいます。

### サーバーの停止

ターミナルで `Ctrl-C` を入力すると Phoenix サーバーが停止します。

### Docker コンテナの停止

```
$ bin/stop.sh
```

### VirtualBox ゲストマシンの停止

```
$ exit
% vagrant halt
```

## 備考

この記事の冒頭で「VirtualBox 特有の罠」と書きました。主な罠は、次の 2 点です。

1. VirtualBox の共有フォルダの中にデータベースのデータ領域を置いてはならない。
2. VirtualBox の共有フォルダの中に `node_modules` ディレクトリを置いてはならない。

これらの罠にはまると、Phoenix サーバーがうまく動きません。

本稿で紹介した [phx-vagrant](https://github.com/oiax/phx-vagrant) では、仮想マシン上の共有フォルダ `/vagrant` とは別に `/work` ディレクトリを用意し、`node_modules/` を含むパスを除外してこれらのディレクトリを rsync で双方向に同期することによりこの罠を回避しています。

本稿が直接的に対象とする Web 開発フレームワークは Phoenix のみですが、Node.js に依存するフレームワーク全般（Ruby on Rails、Laravel、等）にも応用可能です。ぜひ、上記リポジトリを fork して、特定のフレームワークに合わせた設定ファイル群を作成してください。