えっ、nginxって$HOGEで環境変数読めないの??
人のコード見てわかった気になっていた
社内の他のインフラエンジニアが作ったnginxのDockerfileなどを見て、環境変数を読み込んでるっぽいらしい記述を横目で見ていたのでnginx.conf上で$HOGE
とか書いておけばなんとなく環境変数って読めるんだろうって思ってました。
今回初めて一から作ることになってやっと気付きました。どうもゆとりエンジニアです。
まとめ
ランタイムで読む方法とスタートする直前で変換する方法があるけど、後者が良さそう。
以下はこれにたどり着く流れの話です。
今のご時世、環境変数読みたいよね
インスタンスにnginxインストールしてconf書き換えみたいな場合は、chefなどのプロビジョニングツールなどで変換すればよかったのです。 ただ、今のご時世、コンテナで動かしたいと思うのが正直なところ。コンテナだと一度作ってしまうと中身は基本書き換えられないし、書き換えて環境ごとにビルドするとか面倒でまぁやりたくない。 そんな訳で、The Twelve-Factor App的にも全部環境変数で外部から注入したい訳です。
やり方は大きく分けて2つある
- 実行時に環境変数の値を読みにいく方法
- 起動前にテンプレートなどで書き換える方法
1. 実行時に環境変数の値を読みに行く方法
アプリケーション書いてる人からすると普通にパッと思いつくのはこれで、$HOGEとか書けばまぁ読んでくれるだろと思ったけどそれができなかった。
nginxの場合は、要は何らかの他の言語の手助けを得てセットするしかないようです。 セットする方法は、perlかluaがあります。
perl_set
env SERVER_NAME; http { perl_set $server_name_from_env 'sub { return $ENV{"SERVER_NAME"}; }';
set_by_lua
env SERVER_NAME; http { set_by_lua $server_name_from_env 'return os.getenv("SERVER_NAME")';
なお、luaを使う場合はnginx-lua-moduleが必要。nginxの別バージョン的なOpenRestyというやつを使えば最初からこのモジュールが入ってるので使えます。 それか、普通にnginxをソースコードからビルドして自分で追加することもできます。けど、基本やらない方がいいよって書いてありました。
なんか思ったより大変だったという感じ。
2. 起動前にテンプレートなどで書き換える方法
すぐに思いつきそうでなぜか、敬遠してしまってました。敬遠してしまった理由としては、変換をビルド時にやるしかないと思い込んでたから。
どういうことかというと、DockerfileのRUN命令でやるしかないと思ってました。
FROM nginx # ↓ ビルド時に変換してる。example.comの部分を書き換えてビルドしないとダメ・・ RUN sed -i -e "s/SERVER_NAME/example.com/g" /etc/nginx/nginx.conf CMD ["nginx", "-g", "daemon off;"]
これじゃ結局ビルドしなきゃダメじゃんと固い頭になってました。
違う、そうじゃない
これも社内のコード見てやっと意味がわかった。ビルド時に、コンテナの実行時に書き換える。コンテナの実行時であれば環境変数も渡ってきてるので置換ができるのです。 要はCMD命令内でやるという訳です。
変換してからnginxを起動するスクリプトを用意してあげて、
#!/bin/sh sed -i -e "s/SERVER_NAME/$SERVER_NAME/g" /etc/nginx/nginx.conf nginx -g "daemon off;"
CMDでそのscriptを動かすようにしとく
FROM nginx COPY start.sh start.sh CMD ["start.sh"]
これで実行時に docker run -e SERVER_NAME=example.com
で渡すだけでnginx起動直前にconfを書き換えてくれる🎉
置換する方法あれこれ
変換の仕方は、sedでがんばってもいいし色々あるわけだけど、テンプレートで文字列埋め込み式が一番スマート!
moの例
bashが使えるならMo - Mustache Templates in Bashというやつが色々できる。
- 変換するコマンド
cat /nginx.conf.mustache | /mo/mo > /etc/nginx/nginx.conf
- 埋め込み側
$server_name_from_env {{SERVER_NAME}};
envsubstの例
alpineみたいにbashが使えないやつはenvsubstを使う。これは、gettextというリナックスのパッケージでインストールできるソフトウェアの中に入ってるコマンドらしいっす。
- 変換するコマンド
cat /etc/nginx/template/nginx.tpl.default.conf \ | envsubst '$SERVER_NAME' \ > /etc/nginx/conf.d/default.conf
envsubstに変換対象となる変数名を前もって渡さないといけないのがちょっとしたはまりポイントです
- 埋め込み側
$server_name_from_env ${SERVER_NAME};
まとめ
こんな感じでnginxをどんどんコンテナ化、環境変数化していきましょう!