Phoenixコンテナと共にLivebookコンテナを起動して開発時に利用する

2022/12/14に公開
2

はじめに

PhoenixでWebアプリケーションを開発する際に、開発メンバー間で開発環境を揃えるためにdockerを利用するケースがあると思います。そのような場合に、Phoenixのコンテナと一緒にLivebookのコンテナを立てておくと色々と捗ります。

この記事ではPhoenixと一緒にLivebookのコンテナを用意して開発時に利用する流れについて解説します。

構成

雑な絵ですが、このような構成を作ります。

phoenix_docker

この記事の検証に作ったリポジトリも公開しています。参考にしてください。

https://github.com/koga1020/phoenix-docker-with-livebook

docker準備

docker for mac利用時の一例を紹介します。以下などを参考にdocker環境は適宜読み替えていただいてOKです。

https://qiita.com/koyo-miyamura/items/a609de2e9fadaf198243

Dockerfileを作成する

次のようなdockerfileを作成します。

FROM hexpm/elixir:1.14.2-erlang-25.1.2-alpine-3.16.2

RUN apk add --no-cache --update \
  bash \
  inotify-tools \
  git \
  build-base \
  nodejs \
  npm

RUN mix local.hex --force && \
  mix local.rebar --force

WORKDIR /app

docker-compose.ymlを作成する

docker-compose.yml を次のように作成します。

version: "3"
services:
  app:
    build: .
    volumes:
      - .:/app
    ports:
      - 4000:4000
    depends_on:
      - db
    # 1. ノード名とcookieをセットしてphoenixサーバーを起動する
    command: "elixir --sname sample@app --cookie sample -S mix phx.server"
  db:
    image: postgres:14-alpine
    ports:
      - 5432:5432
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    volumes:
      - dbdata:/var/lib/postgresql/data
  livebook:
    image: ghcr.io/livebook-dev/livebook
    ports:
      - 8080:8080
    environment:
      # 2. appコンテナのnode起動時の `--sname` と合わせる
      - LIVEBOOK_COOKIE=sample
      # 3. defaultでappコンテナ内のnodeにattachedされた状態にする
      - LIVEBOOK_DEFAULT_RUNTIME=attached:sample@app:sample
      # 4. 開発環境なのでtokenによる認証は不要なため無効化する
      - LIVEBOOK_TOKEN_ENABLED=false

volumes:
  dbdata: {}

ポイントは次のとおりです。

  1. Phoenix起動時に --sname--cookie を設定してlivebookからノード名とcookieを指定して接続できるようにします。
  2. LIVEBOOK_COOKIE という環境変数でLivebook起動時のcookieを設定します。これを 1.--cookie で指定した値と同じ値を設定します。
  3. Livebook起動時に最初からappコンテナ内のノードに接続した状態にします。これを設定することで、Livebookでnotebook作成後に毎回Runtime Settingsからの接続が不要になります。
  4. Livebookのデフォルトで有効になっているtokenベースの認証を無効化します。これはおまけです。

Livebook起動時の環境変数についてはREADMEにまとまっているため、詳細はそちらを参照してください。

https://github.com/livebook-dev/livebook

プロジェクトの作成

コンテナを起動して、プロジェクトの雛形を作成します。

$ docker compose run --rm --no-deps app /bin/bash

# コンテナ内での作業

# archiveのインストール
bash-5.1# mix archive.install --force hex phx_new

# phoenixプロジェクトをカレントディレクトリに生成する
bash-5.1# mix phx.new . --app sample

The directory /app already exists. Are you sure you want to continue? [Yn] Y

# ... (略)

Fetch and install dependencies? [Yn] Y
* running mix deps.get
* running mix deps.compile

# ... (略)

dev.exsの修正

dev.exs を次のように修正して、appコンテナへの接続と、appコンテナからdbコンテナへの接続が通るようにします。

# Configure your database
config :sample, Sample.Repo,
  username: "postgres",
  password: "postgres",
- hostname: "localhost",
+ hostname: "db",
  database: "sample_dev",
config :sample, SampleWeb.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: {127, 0, 0, 1}, port: 4000],
+ http: [ip: {0, 0, 0, 0}, port: 4000],
  check_origin: false,

dev.exs を修正したら、コンテナ内で mix ecto.setup を実行します。エラーなくMigrationのログが出力されればOKです。

bash-5.1# mix ecto.setup
The database for Sample.Repo has been created

06:13:10.550 [info] Migrations already up

コンテナの起動

コンテナを抜けて、すべてのコンテナを起動します。

$ docker compose up -d

http://localhost:4000 にアクセスしてPhoenixのウェルカムページに、http://localhost:8080 にアクセスしてLivebookの画面にそれぞれアクセスできていればOKです。

Livebookの操作

続いて、Livebookの操作について試してみましょう。

新規ノートブックの作成

http://localhost:8080 からLivebookにアクセスします。右上の「New notebook」から新規ノートブックを作成します。

image

前述の通り、LIVEBOOK_DEFAULT_RUNTIME を設定しているため、すでにappコンテナ内のノードへの接続設定が済んでいます。

問題なく接続できていれば、試しに次のコードを実行してみると "/" が返ってくるはずです。このように、Livebookからappコンテナ内のノードに接続して、自由に関数を試すことができます。

SampleWeb.Router.Helpers.page_path(SampleWeb.Endpoint, :index)

image

DB操作を試す

Ectoの操作もできることを確認します。試しに Accounts.User スキーマを生成してmigrationまで行います。

$ docker compose exec app mix phx.gen.schema Accounts.User users name:string email:string
$ docker compose exec app mix ecto.migrate

その後、Livebookからrepo操作をしてみると、クエリが発行されているのが確認できると思います。

Sample.Repo.all(Sample.Accounts.User)

ecto operation on livebook

recompile

livebookからMixプロジェクトをrecompileする場合は、IEx.Helpers.recompile/0を実行すればOKです。

IEx.Helpers.recompile()

ノートブックを永続化する

docker-compose.yml を修正してlivebookに特定のディレクトリをmountすれば、ホストとコンテナ間で .livemd ファイルを読み書きできます。

試しに、次のように docs ディレクトリをmountして、livebook起動時にマウントしたディレクトリをデフォルトで開くように設定します。

  livebook:
    image: ghcr.io/livebook-dev/livebook
+   volumes:
+     - ./docs:/data
    ports:
      - 8080:8080
    environment:
      - LIVEBOOK_COOKIE=sample
      - LIVEBOOK_DEFAULT_RUNTIME=attached:sample@app:sample
      - LIVEBOOK_TOKEN_ENABLED=false
+     - LIVEBOOK_HOME=/data

コンテナを立ち上げ直します。

$ docker compose down
$ docker compose up -d

コンテナ再起動後、http://localhost:8080 にアクセスすると、/data/ を開いた状態でLivebookにアクセスできていればOKです。

change home to /data

再度「New notebook」から新規ノートブックを作成後、右下の保存ボタンから以下の手順でファイルを保存します。

  • 「Choose α file」をクリック
  • ファイル名を入力
  • 「Choose」をクリック
  • 「Save now」をクリック

save livemd file

実行したノートブックをファイルとして保存してcommitしておき、別の開発者が同じノートブックを同様に開く といったワークフローが可能になります。

まとめ

Phoenixコンテナと合わせてLivebookコンテナを立ち上げて運用する手順について解説しました。作業ログをmarkdownとして残せるリッチなIExとして使ったり、開発のオンボーディングのドキュメントとして整備したり、いろいろな活用方法ができます。ぜひ活用してみてください。

Discussion

tatotato

新しいLivebookでは、いつからかattachedにsnameが使えなくなっていました。
(本記事が結構前のものであるのは承知です。共有のためコメントします)

https://github.com/livebook-dev/livebook/pull/2646

    # 1. ノード名とcookieをセットしてphoenixサーバーを起動する
    command: "elixir --sname sample@app --cookie sample -S mix phx.server"

...

      # 3. defaultでappコンテナ内のnodeにattachedされた状態にする
      - LIVEBOOK_DEFAULT_RUNTIME=attached:sample@app:sample

--nameにしてFQDN形にすれば良いみたいなので、

LIVEBOOK_DEFAULT_RUNTIME=attached:sample@app.<network name>:sample

みたいな形にする必要がありました。
(<network name>はデフォルトでは、フォルダ名+_default