Nginxの設定ファイルに環境変数を埋め込むための2つの方法【envsubstで動的生成するか、Luaで変数を定義するか】


最近はNginxの設定ファイルを書く機会が多く、その中で環境変数をconfファイルに埋め込みたい場面に遭遇しました。

Nginxはデフォルトで環境変数の埋め込みをサポートしておらず、例えば開発環境時と本番稼働時でプロキシの向き先を変更したい場合などは一工夫が必要になります。

大きく分けて2つの方法からこれを実現できるので、その対策法を紹介します。

どんなアプローチになるのか

方針としては以下の2つになります。

  1. envsubstコマンドを使って、confファイルを動的に生成する
  2. LuaやPerlの組み込みモジュールを使って動的に値を設定する

結論からいうと、環境変数を埋め込むニーズだけを達成するのならば「1」のenvsubstを使う方がシンプルですが、既にLuaやPerlのモジュールを導入している場合は一つのconfファイルで完結するのでこちらを採用するのもアリかもしれません。

envsubstコマンドを使って、confファイルを動的に生成する

envsubstはgettextに付随したライブラリで、削りに削られたAlpineでも利用できます。(ただしMacの場合はbrew install gettextでインストールする必要があります)

envsubstを利用すると、以下のようなコマンドから変数を環境変数に置き換えてくれます。

envsubst < nginx.conf.template > nginx.conf

<によって読み込まれたテンプレートファイルの中にある$がついた変数を環境変数に置き換えて、>で指定されたファイルに出力します。

通常のファイルならばこれだけでも十分ですが、nginxの場合はconfファイルに$uriといった$マークがついた変数を利用していることが多く、envsubstに全ての置き換えを任せてしまうと、置換してほしくないところまで書き換えられてしまいます。

そんな時は置き換えを行う変数名だけを指定すると良いでしょう。

envsubst '$$variable_name1 $$variable_name2'< nginx.conf.template > nginx.conf

変数名の指定は「'$$~'」の箇所でここにはスペース区切りで複数の変数を指定することができます。
それでは実際にenvsubstを使った置き換えを見てみましょう。

nginx.conf.template
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    server {
        server_name localhost;
        listen 80;

        location / {
          rewrite ^(.*)$ http://test.com$uri redirect;
        }

        location ^~ / {
          proxy_pass $API_ROOT;
        }
    }
}

export API_ROOT=my-api.com
envsubst '$$API_ROOT'< nginx.conf.template > nginx.conf

これを実行すると、以下のようなnginx.confが出力されます。

nginx.conf
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    server {
        server_name localhost;
        listen 80;

        location / {
          rewrite ^(.*)$ http://test.com$uri redirect;
        }

        location ^~ / {
          proxy_pass my-api.com;
        }
    }
}

$API_ROOTが環境変数で指定された「my-api.com」に変わっており、指定されていない$uriの箇所はそのままになっていますね👍

例えばdocker buildを行う前にenvsubstを用いてconfファイルを作成してから、Dockerfile内で作成したconfファイルをコピーすれば、擬似的に環境変数の動的な埋め込みが実現されるでしょう。(もちろんDockerの使用に限定されるわけではありません)

LuaやPerlの組み込みモジュールを使って動的に値を設定する

LuaやPerlから値を動的に定義しますが、これを実現するにはnginxのモジュールを入れなければなりません。

Luaの場合はOpenRestyというNginxの拡張版を用いることが多く、こちらのDockerイメージを使えば通常のNginxと(ほとんど)同様のものに加えて、Lua Moduleが標準で入っているので組み込み機能をすぐに利用できます。

(そもそもLuaとは組み込みに特化した軽量な言語のことで、動的型付けにも関わらずJavaと同等程度の速度を誇ります)

Perlも同様にモジュールを通じて利用しますが、nginxの公式イメージの中にperlタグがついたものがあるので、こちらを利用することになります。

ただしPerlの場合はAlpineではないので容量がかなり大きくなり、OpenRestyはAlpineだと十分軽量なので、容量を気にされる場合はOpenRestyを使うのがオススメです。

イメージ 容量
nginx:alpine 約16MB
nginx:perl 約148MB
openresty:alpine 約23MB

なおLuaやPerlを使って変数を設定するのはとても簡単です。

Luaを使うパターン

nginx.conf
env SERVER_NAME;

http {
  set_by_lua $API_ROOT 'return os.getenv("API_ROOT")';
}

Perlを使うパターン

nginx.conf
env SERVER_NAME;

http {
  perl_set $API_ROOT 'sub { return $ENV{"API_ROOT"}; }';
}

こうして一度セットされた変数は「$API_ROOT」のような形で参照できるので、後は通常のconfファイルと同様に記述すれば問題ありません。

ちなみにenvはmainコンテキスト、set_by_luaperl_setはhttpコンテキスト内にのみ定義できます。

最後に

Dockerコンテナ内にNginxを配置する場合、Dockerfileで処理を分岐できないために、Develop用・Production用と複数のconfファイルを用意するケースを見かけますが、以上で紹介した方法を活用することで動的に値を埋め込むことができます。

envsubstはLinux系ならばほぼ全てに付属しているパッケージなので、ぜひ活用してみてください。