🐾

docker composeでphoenixアプリを立ち上げるまで

2021/12/30に公開

表題の通り、docker composeを利用してphoenixアプリを立ち上げるまでの備忘録です。
立ち上げるまでの流れとか確実に忘れるだろうなあと思ったので記憶定着も兼ねて書いてみました。
最低限ではありますがvscodeのremote-containersでの設定ファイルも載せてあります。

「そこ違うよ」や「こうした方が良い」などあれば教えて頂けると幸いです。

環境

  • mac OS
  • Docker Desctop for Mac
  • vscode

作るコンテナ

  • phoenix
  • postgresql

ディレクトリ構成

├── .devcontainer
│   └── devcontainer.json  <- vscode用docker設定ファイル
│
├── .docker-config  // dockerfileを置くディレクトリ
│   ├── app
│   │   └── Dockerfile
│   └── db
│       └── Dockerfile
├── db
│   └── data  // DBマウント用のディレクトリ
│
├── .env  <- docker-composeで使う環境変数
│
└── docker-compose.yml

Dockerfileは.docker-configディレクトリに入れてまとめてあります。

.envにはdocker-compose.ymlで使用する環境変数などを書くんですが、vscodeで使う際には『COMPOSE_PROJECT_NAMEが書かれた.envファイルはプロジェクトのrootディレクトリ直下に配置してください』と言われているのでプロジェクトのrootディレクトリに置いています。
https://code.visualstudio.com/remote/advancedcontainers/set-docker-compose-project-name
その関係で、.envと同じ階層に置かないといけないdocker-compose.ymlもプロジェクトのrootディレクトリに置いています。

ファイル設定

phoenix用Dockerfile

.docker-config/app/Dockerfile
FROM elixir:1.13.1-slim

WORKDIR /workspace

RUN apt-get update && apt-get install -y \
    inotify-tools \
    git \
    npm \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

RUN mix do \
    local.hex --force, \
    local.rebar --force, \
    archive.install --force hex phx_new

RUN apt-get...で必要そうなものをインストール。
RUN mix do...でphoenix関連のライブラリの更新。

DBサーバー用Dockerfile

.docker-confifg/db/Dockerfile
FROM postgres:14.1-alpine

ENV LANG ja_JP.utf8

docker-compose.ymlと.envファイル

docker-compose.yml
version: "3"

services:
  app:
    build: ./.docker-config/app
    ports:
      - ${APP_PORT}:4000
    command: mix phx.server
    volumes:
      - .:/workspace
      - node_modules:/workspace/assets/node_modules

  db:
    build: ./.docker-config/db
    environment:
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    ports:
      - ${POSTGRES_PORT}:5432
    volumes:
      - ./db/data:/var/lib/postgresql/data

volumes:
  node_modules:

ざっくりとした説明
app, db: サービス名(任意の名前)
build : Dockerfileが置かれているディレクトリを指定(context)
ports : {ホスト側のポート}:{コンテナ側のポート}
command: コンテナを立ち上げた時のデフォルトのコマンド
volumes: 大体はホスト側とコンテナ側のディレクトリをマウントするときに使う

buildやvolumesで指定するパスはdocker-compose.ymlを基準とする相対パスです。

node_modulesのマウントを避けるためにvolumesを別途指定しています。(node_modules:/workspace/assets/node_modulesの部分)

.env
COMPOSE_PROJECT_NAME=phx_docker_sample

APP_PORT=4000

POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_PORT=5432

コンテナ名が分かりやすくなるのでCOMPOSE_PROJECT_NAMEをちゃんと定義しておきます。
(コンテナ名:{COMPOSE_PROJECT_NAME}{サービス名}{連番})

docker-compose.yml内でcontainer_nameを設定することで直接コンテナ名を指定することもできます。

devcontainer.json

.devcontainer/devcontainer.json
{
    // vscode上での表示名
    "name": "phoenix app",
    // devcontainer.jsonから見たときのdocker-compose.ymlの場所
    "dockerComposeFile": "../docker-compose.yml",
    // docker-compose.ymlで指定したサービス名
    "service": "app",
    // vs codeで開くディレクトリ
    "workspaceFolder": "/workspace",
}

実行の流れ

各種設定ファイルを書き終えたら
プロジェクト作成 -> ファイルを少し編集 -> DB作成 -> サーバー立ち上げ
の流れで進んでいきます。

0. 動作確認

とりあえず各コンテナが動作するか確認。

> docker compose run --rm app mix --version
...
Erlang/OTP 24 [erts-12.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit]

Mix 1.13.1 (compiled with Erlang/OTP 24)
> docker compose run --rm db psql --version
psql (PostgreSQL) 14.1

1. プロジェクト作成

確認ができたらプロジェクトを作成します。今回はmyappというアプリ名で作ります。(本当ならCOMPOSE_PROJECT_NAMEと同じ名前の方が良さそう)

> docker compose run --rm app mix phx.new . --app myapp

yes/noを聞かれるのでyesで答えていきます。

プロジェクト作成時のディレクトリ構成

.
├── .devcontainer
├── .docker-config
├── .env
├── .formatter.exs
├── .gitignore
├── README.md
├── _build
├── assets
├── config
├── db
├── deps
├── docker-compose.yml
├── lib
├── mix.exs
├── mix.lock
├── priv
└── test

2. ファイル編集

2.1. IPアドレスの設定

まずはconfig/dev.exsを編集します。
デフォルトだとhttp: [ip: {127, 0, 0, 1}, port: 4000]になっているので、docker経由で接続できるようにhttp: [ip: {0, 0, 0, 0}, port: 4000]に変更します。

config/dev.exs
...
config :mayapp, MyappWeb.Endpoint,
  # Binding to loopback ipv4 address prevents access from other machines.
  # Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
  http: [ip: {0, 0, 0, 0}, port: 4000],
  check_origin: false,
  code_reloader: true,
...

2.2. DBの設定

同じくconfig/dev.exs内のDBの接続情報を編集します。

config/dev.exs
...
config :myapp, Myapp.Repo,
  username: "postgres", # 編集 (POSTGRES_USER)
  password: "postgres", # 編集 (POSTGRES_PASSWORD)
  hostname: "phx_docker_sample_db_1", # 編集 (コンテナ名)
  database: "myapp_dev",
  show_sensitive_data_on_connection_error: true,
  pool_size: 10
  ...

usernameとpasswordはdocker-compose.ymlの部分で設定したものにします。
hostnameはdbのコンテナ名を設定してください。
上手いこと設定できているとサービス名("db")で大丈夫な気もするんですが、自分の環境だとコンテナ名にしないと繋がりませんでした。 ->サービス名でも繋がったので下で紹介します。

hostnameに関してはここで議論されています。
https://elixirforum.com/t/docker-container-cannot-access-postgres/34489

3. DB作成

hostnameをコンテナ名にした場合

> docker compose run --rm mix ecto.create

4. 立ち上げ

> docker compose up -d

よく見る画面が出てきたら成功です🎊

VSCodeで作業するとき

とりあえず起動してるコンテナを消去。

> docker compose down

プロジェクトをvscodeで開くとReopen in Container?と聞かれるので選択。もしくは左下にある><が重なったマークのボタンを押してReopen in Containerを選択。
そうするとdocker compose upを実行してくれた状態でappコンテナの中に入れます。

この時点では、devcontainer.jsonに設定をほとんど書いていないためコンテナ内にElixirLSなどの拡張機能が入っておらず、拡張機能の恩恵が得られない状態になっています。なので横のタブの拡張機能のところから自分の使いたい拡張機能をポチポチ選択して入れていきましょう。
詳しくは公式のリファレンスをば。
https://code.visualstudio.com/docs/remote/devcontainerjson-reference
extenstionsとsettingsあたりは設定しておくと便利そう。

ちなみに、vscodeのremote containers機能を使えるようになるタイミングですが、docker compose run mix phx.new ...でプロジェクトを作った後になります。(プロジェクトを作らないとdocker-compose.ymlで設定したコマンドmix phx.serverが実行できない)
プロジェクトを作った段階でremote containersを起動し、mix ecto.createなどの作業を進めた方が楽かもしれません。

追記1 hostnameをサービス名にした場合

docker compose upによってコンテナを立ち上げた状態であればlocalhostをサービス名("db")に設定した状態でも通りました。

config/dev.exs
...
config :myapp, Myapp.Repo,
  username: "postgres",
  password: "postgres",
  hostname: "db", # 変更
  database: "myapp_dev",
  ...
> docker compose up -d
> docker compose exec app bash -c "mix ecto.create"
Compiling 14 files (.ex)
Generated myapp app
The database for Myapp.Repo has already been created

追記2 docker composeコマンドを使いたくない人向け

おそらくこっちの方が楽なので書いておきます。
変更は以下の通りで、docker-compose.yml内のcommandを消してttyをtrueに設定します。

docker-compose.yml
version: "3"

services:
  app:
    build: ./.docker-config/app
    ports:
      - ${APP_PORT}:4000
-   command: mix phx.server
+   tty: true
    volumes:
      ...

これでphoenixのプロジェクトがない状態でもremote-containersを使えるようになります。

ディレクトリ構成は同じでDockerfile、docker-compose.yml、.env、devcontainer.jsonが書けたらvscodeでremote-containersを起動します。

あとはコンテナの中でmix phx.new ...やらmix create.ectoやらを実行できます。
config/dev.exsの編集は2.ファイル編集の項と同じように行なってください。
このときDBのhostnameはサービス名("db")で大丈夫でした。

最後にmix phx.serverでサーバーを立ててサイトが見られれば完了です。

雑感

docker composeについて少しだけ理解できたので、これからはもっと積極的に使っていきたい。

参考

GitHubで編集を提案

Discussion