概要
DockerComposeを使用して、Phoenixプロジェクトの開発環境を構築します。ローカル環境で開発する場合にも、Dockerコンテナ内で開発する場合にも対応可能な設定を目指します。
この記事は、開発環境を構築する学習過程をまとめたものです。今回は、dev環境で正常に動作することを目標としています。
リポジトリは以下になります
環境
この記事では、以下の環境で開発を行っています。
- PC: MacBook
- OS: macOS 15.2
またElixir(Erlang)はasdf上で管理しています。
それぞれのバージョンは以下になります。
% asdf current
Name Version Source Installed
elixir 1.18.2-otp-27 /Users/linsei/Docker/Qiita/.tool-versions true
erlang 27.2.2 /Users/linsei/Docker/Qiita/.tool-versions true
nodejs 19.3.0 /Users/linsei/.tool-versions true
手順
DockerComposeを使用してPhoenixの開発環境を構築するために、以下の手順を進めます。
Phoenixプロジェクトを作成し、Dockerファイルを準備する
まず、Phoenixプロジェクトを作成します。データベースは特に指定していないので、デフォルトでPostgreSQLが使用されます。
$ mix phx.new tuto_docker
この時点で、プロジェクトのディレクトリ構成は以下のようになっています。
tuto_docker % tree -L 1
.
├── README.md
├── _build
├── assets
├── config
├── deps
├── lib
├── mix.exs
├── mix.lock
├── priv
└── test
Dockerfileを作成する
次に、Phoenixを起動するためのDockerfileを作成します。
下記コマンドを実行すると、リリース用のDockerfileが自動生成されます。
以下のコマンドを実行します
mix phx.gen.release --docker
これによりphoenixを起動するためのDockerfileが作成されます。
詳しくはこちらをご覧ください。
生成後、プロジェクトのディレクトリ構成は以下のようになります。
tuto_docker % tree -L 1
.
├── (省略)
├── Dockerfile
└── rel
Dockerfile.devの作成・編集
次に、生成されたDockerfileを編集し、開発用のDockerfile.devを作成します。
まず、元のDockerfileをコピーしてリネームしましょう。
そしたら、以下のようにファイルを編集します。
ARG ELIXIR_VERSION=1.18.2
ARG OTP_VERSION=27.2.2
ARG DEBIAN_VERSION=bullseye-20250203-slim
ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}"
FROM ${BUILDER_IMAGE} as builder
- RUN apt-get update -y && apt-get install -y build-essential git \
- && apt-get clean && rm -f /var/lib/apt/lists/*_*
+ RUN apt-get update -y && apt-get install -y build-essential npm inotify-tools git \
+ && apt-get clean && rm -f /var/lib/apt/lists/*_*
- WORKDIR /app これを削除
RUN mix local.hex --force && \
mix local.rebar --force
# これより下を削除
この編集により、Phoenixを起動するために必要なパッケージインストールのみに内容を絞っています。
このDockerfile.devは、後ほど作成するdocker-compose.ymlで使用します。
postgres
次に、PostgreSQLをサービスとして起動するためのイメージを探します。
PostgreSQLのDockerイメージは以下から確認できます。
現在の最新イメージは 15.10-alpine3.21 です。
これを後ほどdocker-compose.ymlで指定します。
docker-compose.ymlを作成する
Phoenixプロジェクト内にdocker-compose.ymlを作成します。
以下のように基本構成を記述しておきます。
services:
web:
(後で記述)
db:
(後で記述)
volumes:
dbdata:
ここで定義しているvolumesは、データ永続化のために使用する名前付きボリュームです。dbdataという名前で作成します。
webサービスの設定
webはPhoenixを起動するサービスです。次のように記述します。
web:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- .:/work
working_dir: /work
links:
- db
environment:
DB_HOST: db
ports:
- 4000:4000
command: tail -f /dev/null
各セクションの説明
- build: Dockerfile.devをDockerfileに指定します
-
volumes: 現在のディレクトリをコンテナ内の/workにマウントします
- コンテナ内でローカルと同じディレクトリを使用するために共有が必要です
- working_dir: コンテナ内での作業ディレクトリを/workに設定します
- links: dbサービスと通信できるようにリンクを設定しています
- environment: 環境変数DB_HOSTにdbサービスのホスト名を設定します
-
ports: ホストのポート4000をコンテナ内の4000番ポートにマッピングします
-
config/dev.exs
でサーバーに使用するポートと一致するようにしてください
-
- command: コンテナが即時終了しないようにするコマンドを実行します
dbサービスの設定
dbサービスはPostgreSQLを起動するための設定です。以下のように記述します。
db:
image: postgres:15.10-alpine3.21
volumes:
- dbdata:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
各セクションの説明
- volumes: コンテナ内の/var/lib/postgresql/dataをdbdataボリュームにマウントしています。これにより、データがコンテナを削除しても永続化されます。
-
environment: PostgreSQLの起動時に必要な環境変数を設定します
- POSTGRES_PASSWORDは必須項目です
- このパスワードは
config/dev.exs
の設定と一致させてください
-
ports: ホストのポート5432をコンテナ内の5432にマッピングしています
- PostgreSQLのデフォルトポート番号は5432です
docker-compose.ymlの全文
最終的に記述を終えたdocker-compose.ymlの全体は、以下のリポジトリから確認できます。
dev.exsの設定を変更する
dev.exsファイルを編集し、データベースのホスト名とサーバーのIPアドレスを設定します。
import Config
# データベース設定
config :tuto_docker, TutoDocker.Repo,
username: "postgres",
password: "postgres",
- hostname: "localhost",
+ hostname: System.get_env("DB_HOST") || "localhost",
database: "tuto_docker_dev",
stacktrace: true,
show_sensitive_data_on_connection_error: true,
pool_size: 10
# サーバー設定
config :tuto_docker, TutoDockerWeb.Endpoint,
- http: [ip: {127, 0, 0, 1}, port: 4000],
+ http: [ip: {0, 0, 0, 0}, port: 4000],
# 略
dockerコマンドでサービスを起動する
必要な設定が完了したら、以下のコマンドを実行してサービスを起動します。
% docker compose up -d
% docker compose exec web /bin/bash
root@コンテナID:/work# iex -S mix phx.server
最後のコマンドを実行すると、通常通りPhoenixサーバーのページを表示できるはずです。
コンテナから起動するメリット
コンテナからPhoenixアプリケーションを起動するメリットを説明します。
コンテナ内で環境変数を設定することで、ローカル環境に余計な環境変数を残さずに済みます。例えば、何のために設定したかわからない環境変数がローカルに残っていると管理が煩雑になります。
一方、コンテナでは環境変数をコンテナ内だけで適用し、不要なときは一括で削除できるため、開発環境をクリーンに保つことができます。
環境変数を使ったアプリケーション設定の例
例として、環境変数MYNAMEを使用する設定を例を示します。
import Config
# 略
config :tuto_docker,
myname: System.get_env("MYNAME") || "たぬき"
# 略
environment:
DB_HOST: db
MYNAME: "ブラックたぬたぬ"
環境に応じてhttp://localhost:4000/
の表示は以下のように変化します。
-
ローカルで起動:
I am たぬき.
-
コンテナで起動:
I am ブラックたぬたぬ.
まとめ
ここまでの設定を行うことで、Phoenixプロジェクトをローカル環境でもコンテナ環境でも開発できるようになります。
おまけ
データベースをコンテナ内で作成することで、ローカル環境に不要なデータベースを作成せずに済むというメリットがあります。
curdブランチを用意しています。
以下の手順でローカルとコンテナ環境の違いを確認してみてください。
- コンテナでサーバーを起動し、CRUDページにデータを入力します
- サービスを停止し、ローカルでサーバーを起動します
- コンテナ内のデータがローカル環境には反映されていないことが確認できます