エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

Elixirのreleases.exsの使いどころ

リリースするアプリケーションでは、実行環境に依存する情報をコード内に抱え込まないように、環境変数から設定を取得することが多いと思います。 Elixir では設定情報を config/config.exs に書くことが多いのですが、このファイルはビルド時に読み込まれるため、この中で環境変数の値を取り込んでも実行時に反映されません。

Elixir 1.9 では config の扱いが変わりました。

1.8 までは Mix.Config という Mix のモジュールを利用していましたが、1.9 からは Elixir が直接持つ Config というモジュールを利用するように変更されています。

また、実行環境に合わせた設定をアプリケーションが起動するタイミングでおこないたい、という動機で config/releases.exs が導入されたようです。

アプリケーションをリリースする段になって混乱しないように Elixr 1.9 の config の使い方を確認してみました。

config/config.exs

まず従来からある config/config.exs の挙動を確認してみます。

挙動を確認するためのプロジェクトを用意する

適当なプロジェクトを用意します。

$ mix new hoge
$ cd hoge

1.9 からは config の扱いが変更になったのにともない、 mix new コマンドはディレクトconfig および config ファイルを作成しなくなりました。

config が必要なばあいは自分でディレクトconfig を作成します。

$ mkdir config

config.exs を記述する

config/config.exs ファイルを作成して設定を記述します。

import Config

config :hoge,
  foo: System.get_env("FOO"),
  bar: System.get_env("BAR")

挙動を確認するための関数を用意する

lib/hoge.ex にアプリケーションの設定を表示する関数を追加します。

defmodule Hoge
  def show_env do
    IO.write("foo: ")
    IO.inspect(Application.fetch_env(:hoge, :foo))

    IO.write("bar: ")
    IO.inspect(Application.fetch_env(:hoge, :bar))
  end
end

またリリース版を作成するために lib/hoge/application.ex を用意します。

defmodule Hoge.Application do
  use Application

  def start(_, _) do
    # 環境変数を表示する関数を呼び出す
    Hoge.show_env()

    # 今回は実行を継続する必要がないので環境変数を表示したら終了する
    System.halt()
  end
end

Hoge.Application を callback module として設定します。

(最初の投稿時にこの記述が抜けていました。失礼しました)

defmodule Hoge.MixProject do

  # ...

  def application do
    [
      extra_applications: [:logger],
      mod: {Hoge.Application, []}
    ]
  end

  # ...

end

挙動を確認する

リリース版をビルドします。

$ mix release

実行します。

$ _build/dev/rel/hoge/bin/hoge start
foo: {:ok, nil}
bar: {:ok, nil}

環境変数を設定して実行します。

$ FOO=foo BAR=bar _build/dev/rel/hoge/bin/hoge start
foo: {:ok, nil}
bar: {:ok, nil}

実行時に環境変数を指定しても反映されません。

リリース版をビルドするときに環境変数を設定してみます。

$ FOO=foo BAR=bar mix release

実行します。

$ _build/dev/rel/hoge/bin/hoge start
foo: {:ok, "foo"}
bar: {:ok, "bar"}

環境変数を設定して実行します。

hoge $ FOO=hoge BAR=fuga _build/dev/rel/hoge/bin/hoge start
foo: {:ok, "foo"}
bar: {:ok, "bar"}

実行時の環境変数は影響を与えず、ビルド時に設定した値が表示されることがわかります。

config/releases.exs

次に config/releases.exs を作成して設定を記述します。

releases.exs を記述する

import Config

config :hoge,
  bar: System.get_env("BAR")

挙動を確認する

リリース版をビルドします。

$ mix release

実行します。

$ _build/dev/rel/hoge/bin/hoge start
foo: {:ok, nil}
bar: {:ok, nil}

環境変数を設定して実行します。

$ FOO=hoge BAR=fuga _build/dev/rel/hoge/bin/hoge start
foo: {:ok, nil}
bar: {:ok, "fuga"}

config/releases.exs環境変数を利用するようにした bar は実行時の環境変数の値が反映されることがわかります。

次にビルド時に環境変数を設定します。

$ FOO=foo BAR=bar mix release

実行します。

$ _build/dev/rel/hoge/bin/hoge start
foo: {:ok, "foo"}
bar: {:ok, nil}

ビルド時に環境変数 BAR にも値を設定しましたが、実行時に設定していないため bar の値は nil になっています。 またここから、 config.exsreleases.exs に同じ設定を記述したばあい、releases.exs の設定が優先されることがわかります。

環境変数を指定して実行します。

$ FOO=hoge BAR=fuga _build/dev/rel/hoge/bin/hoge start
foo: {:ok, "foo"}
bar: {:ok, "fuga"}

config/release.exs で設定していない foo はビルド時の環境変数の値になっています。 一方 config/releases.exs で設定した bar は慈光寺の環境変数の値になっています。

releases.exs の在り処

最後に。 releases.exs がどこで利用されているか確認します。

$ find . -name releases.exs
./config/releases.exs
./_build/dev/rel/hoge/releases/0.1.0/releases.exs

config/releases.exs はリリース版に同梱されていることがわかります。

またアプリケーションを起動するスクリプト _build/dev/rel/hoge/bin/hoge を読んでみると、_build/dev/rel/hoge/releases/0.1.0/sys.config のコピーを読み込んでいることがわかります。

このファイルの内容を確認すると次のようになっています。 実際のファイルではコメント行以外は一行で記述されていますが、ここでは読みやすいように改行とインデント、およびバイナリ部分にコメントを追加しました。

Erlang の内容に踏み込んでしまうので深追いはしませんが、同梱された releases.exsConfig.Reader で利用するように設定されていることがわかります。

%% coding: utf-8
%% RUNTIME_CONFIG=true
[
  {
    hoge,
    [
      {foo, nil},
      {bar, nil}
    ]
  },
  {
    elixir,
    [
      {
        config_providers,
        #{
          '__struct__' => 'Elixir.Config.Provider',
          config_path => {
            system,
            % "RELEASE_SYS_CONFIG"
            <<82,69,76,69,65,83,69,95,83,89,83,95,67,79,78,70,73,71>>,
            % ".config"
            <<46,99,111,110,102,105,103>>
          },
          extra_config => [
            {
              kernel,
              [
                {start_distribution, true}
              ]
            }
          ],
          providers => [
            {
              'Elixir.Config.Reader',
              {
                system,
                % "RELEASE_ROOT"
                <<82,69,76,69,65,83,69,95,82,79,79,84>>,
                % "/releases/0.1.0/releases.exs"
                <<47,114,101,108,101,97,115,101,115,47,48,46,49,46,48,47,114,101,108,101,97,115,101,115,46,101,120,115>>
              }
            }
          ],
          prune_after_boot => false
        }
      }
    ]
  },
  {
    kernel,
    [
      {start_distribution, false}
    ]
  }
].

いつか読むはずっと読まない:mismatch

昨年まで 3 年近く関わったプロジェクトではバックエンドの部分を担ったため、エンドユーザはあまり意識せずに開発をしていました。

今年から再びフロントエンドを含むウェブサービスの開発に参加することになりましたが、一番に戸惑ったのがフロントエンドの書き方、特にテストの書き方を結構忘れてしまっていたこと。 幸いすぐに勘を取り戻すことができましたが、そういったものに気を取られることがなくなるとアプリケーションが提供している操作そのものに意識が向くようになります。

ミスマッチ 見えないユーザーを排除しない「インクルーシブ」なデザインへ

ミスマッチ 見えないユーザーを排除しない「インクルーシブ」なデザインへ

  • 作者: キャット・ホームズ,ジョン・マエダ,大野千鶴
  • 出版社/メーカー: ビー・エヌ・エヌ新社
  • 発売日: 2019/03/15
  • メディア: 単行本
  • この商品を含むブログを見る