まーぽんって誰がつけたの?

iOS→Scala→インフラなおじさん技術メモ

えっ、nginxって$HOGEで環境変数読めないの??

人のコード見てわかった気になっていた

社内の他のインフラエンジニアが作ったnginxのDockerfileなどを見て、環境変数を読み込んでるっぽいらしい記述を横目で見ていたのでnginx.conf上で$HOGEとか書いておけばなんとなく環境変数って読めるんだろうって思ってました。 今回初めて一から作ることになってやっと気付きました。どうもゆとりエンジニアです。

f:id:masato47744:20180125005441p:plain

まとめ

ランタイムで読む方法とスタートする直前で変換する方法があるけど、後者が良さそう。

以下はこれにたどり着く流れの話です。

今のご時世、環境変数読みたいよね

インスタンスにnginxインストールしてconf書き換えみたいな場合は、chefなどのプロビジョニングツールなどで変換すればよかったのです。 ただ、今のご時世、コンテナで動かしたいと思うのが正直なところ。コンテナだと一度作ってしまうと中身は基本書き換えられないし、書き換えて環境ごとにビルドするとか面倒でまぁやりたくない。 そんな訳で、The Twelve-Factor App的にも全部環境変数で外部から注入したい訳です。

やり方は大きく分けて2つある

  1. 実行時に環境変数の値を読みにいく方法
  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をどんどんコンテナ化、環境変数化していきましょう!