Money Forward Developers Blog

株式会社マネーフォワード公式開発者向けブログです。技術や開発手法、イベント登壇などを発信します。サービスに関するご質問は、各サービス窓口までご連絡ください。

20230215130734

Docker で起動した Phoenix アプリケーションでも IEx.pry や dbg でデバッグしたい!

こんにちは クラウド経費開発チームクラウド債務支払開発チーム の 宮村(みやむー) @miyamura.koyo です。

本業で Rails エンジニアをやる傍ら、コミュニティ の方で Elixir (フロントエンドも含め)で Web 開発を行っています。

最近では入門向けの書籍も充実してきたので、ご興味あればぜひ動かしてみてください! gihyo.jp

今回は Elixir で Web 開発している最中に得た知見をご紹介します。

対象バージョン

Elixir 1.16.2 OTP 26

IEx.pry

Elixir には標準で IEx.pry/0 という関数が提供されています。

これは Ruby on Rails の binding.pry や binding.irb と同じようなもので、動作中のアプリケーションを一時停止して、そのスコープで iex を操作できます。

defmodule Adder do
  def add(a, b) do
    c = a + b
    # 追加
    require IEx
    IEx.pry()
  end
end
$ iex adder.exs
Erlang/OTP 25 [erts-13.0.4] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit:ns]

Interactive Elixir (1.14.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Adder.add(1,2)
Break reached: Adder.add/2 (adder.exs:6)

    3:     c = a + b
    4:     # 追加
    5:     require IEx
    6:     IEx.pry()
    7:   end
    8: end

pry(1)> a
1
pry(2)> b
2
pry(3)> c
3

hexdocs.pm

dbg

また最近では dbg コマンドというデバッグにより特化した関数もリリースされています。 以下ドキュメントで動画付きで解説されているのでご覧ください。

hexdocs.pm

pry や dbg していると Docker 環境で起動した elixir アプリケーションで動作しない

以前、Qiitaで執筆した記事では Docker で Phoenix のローカル環境構築を行う方法を紹介しました。

qiita.com

しかしこれだとうまく pry や dbg が動作しませんでした。

動作させるには一工夫必要です。

Docker 環境でも動作させる方法 docker attach

IEx.pry を動作させる方法

まず先ほど紹介した Qiita 記事から以下のようにして Phoenix アプリケーションを起動しているとします。

services:
  web:
    ...
    command: "mix phx.server"

これを以下のように書き換えます。iex コマンドで起動させます。

※ Elixir 1.16.2 だと iex コマンドに変更しなくても動作しました。ただし後述する dbg コマンドを動かすには結局 iex コマンドで起動させる必要があるため変更することをオススメします。

services:
  web:
    tty: true
    stdin_open: true
    ...
    command: "iex -S mix phx.server"

そしてコンテナを起動します。 起動後に docker attach コマンドでコンテナにアタッチします。 これで準備完了です。あとは任意のコード位置に require IEx; IEx.pry() を記述することで自由にデバッグできます。

$ docker compose up -d
$ docker attach oauth_sample-web-1
[info] GET /users/log_in
[debug] Processing with OauthSampleWeb.UserLoginLive.new/2
  Parameters: %{}
  Pipelines: [:browser, :redirect_if_user_is_authenticated]
Request to pry #PID<0.520.0> at OauthSampleWeb.UserLoginLive.mount/3 (lib/oauth_sample_web/live/user_login_live.ex:41)

   38:   def mount(_params, _session, socket) do
   39:     # dbg()
   40:     require IEx
   41:     IEx.pry()
   42:     email = live_flash(socket.assigns.flash, :email)
   43:     form = to_form(%{"email" => email}, as: "user")
   44:     {:ok, assign(socket, form: form), temporary_assigns: [form: form]}

Allow? [Yn] Y
Erlang/OTP 26 [erts-14.2.4] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit]

Interactive Elixir (1.16.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> socket.id
"phx-F8xZO_VOo0B_cQVC

ハッカーっぽいですね! result

Docker でも IEx.pry でデバッグできるようになりました!

dbg を動作させる方法

dbg を動作させるにはもう一工夫必要です。 --dbg pry オプションを渡してみましょう。

services:
  web:
    tty: true
    stdin_open: true
    ...
    command: "iex -S --dbg pry mix phx.server"

Elixir は 1.16.2 だと dbg コマンドがデフォルトでは pry コンソールを起動させないようになっています。

そのため iex --dbg pry で起動させることで pry コンソールを起動させるようにする必要があります。

(古いバージョンだとオプションが違ったりするので、お使いの Elixir バージョンに合わせてドキュメントを探してみてください)

hexdocs.pm

おまけ --sname をつけてアプリケーションを起動させる方法

ちなみに Elixir が提供する Node の仕組み(分散システム機能)を用いることで、別の方法でも実現できます。 実際にはあまり使う機会はないかもしれませんが、docket attach コマンドを用いた手法がうまくいかなかったときには試してみてください。

まず先ほど紹介した Qiita 記事から以下のようにして Phoenix アプリケーションを起動しているとします。

services:
  web:
    ...
    command: "mix phx.server"

これを以下のように書き換えます。

services:
  web:
    ...
    command: "elixir --sname sample@web -S mix phx.server"

そしてコンテナを起動します。 この時、 iex セッションを先ほど命名した --sname に向けて接続します。 これで動作中の Phoenix アプリケーションに後付けで iex のセッションを接続することが出来ました。

docker compose up -d
docker compose exec web iex --sname "pry@web" --remsh sample@web

※ コマンドの意味は ChatGPT くんに聞いてみるとわかりやすいです。

gpt

こんな感じで止まってくれます!

$ docker compose exec web iex --sname "pry@web" --cookie sample --remsh sample@web
Erlang/OTP 26 [erts-14.2.4] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit]

Interactive Elixir (1.16.2) - press Ctrl+C to exit (type h() ENTER for help)
Request to pry #PID<0.544.0> at OauthSampleWeb.UserLoginLive.mount/3 (lib/oauth_sample_web/live/user_login_live.ex:41)

Request to pry #PID<0.544.0> at OauthSampleWeb.UserLoginLive.mount/3 (lib/oauth_sample_web/live/user_login_live.ex:41)
   38:   def mount(_params, _session, socket) do
   39:     # dbg()
   40:     require IEx
   41:     IEx.pry()
   42:     ...
   43:     ...
   44:     ...
Allow? [Yn] Y

Interactive Elixir (1.16.2) - press Ctrl+C to exit (type h() ENTER for help)

iex(sample@web)1> socket.id
"phx-F8xXV_t49kiTNwbB"

まとめ

Docker で起動した Phoenix アプリケーションで IEx.pry や dbg を用いてデバッグする方法を紹介しました。

ちなみに筆者は Elxiir 1.14.5 環境でも動作させてみたのですが、微妙に挙動が怪しいので、Elixir 1.16.2 など現時点の最新で試すことをお勧めします!

また、プロジェクトの docker-compose.yml を修正するのはちょっと...という人も docker-compose.override.yml を使うことで、自分の手元だけ動作を変えることができるので、そちらを使ってみてください。

とっても便利なのでぜひ使ってみてくださいね。よい Elixir ライフを。


マネーフォワード福岡拠点では、エンジニアを募集しています!

色んな言語を経験しているエンジニアと働きたい方はぜひ!

hrmos.co

福岡開発拠点のサイトもあるのでぜひみてください!

fukuoka.moneyforward.com