BLOGサブスレッドの日常

2019.04.24

nginx の proxy_pass の闇

torikai

月曜日担当の鳥飼です。
2日連続です。よろしくお願いします。

nginx 使っていますか? 私はまだまだ使いこなせていないのですが…
先日遭遇した nginx のproxy_passの設定の小ネタを紹介します。

よくある設定

location /hoge/ {
    proxy_pass https://hoge.example.com/;
}

これは、/hoge/ 以下のアクセスを、https://hoge.example.com/ に転送する設定ですね。
例えば、/hoge/hige?food=curry にアクセスすると、https://hoge.example.com/hige?food=curry に転送します。

ただ、この書き方だと少し困ったことがありました。
それは、proxy_pass に指定した URL の IP が変わった時です。

nginx の proxy_pass に指定したURLの名前解決のタイミング

nginx のproxy_passに指定している URL の名前解決は nginx 起動時に行われ、以降はその IP を使い続けます。
ホストからIPを逆引きした時、得られる IP が固定の場合はこの挙動でも良いのですが…

ALB(ELB) や S3 などの DNS を CNAME に設定している場合、Auto Scaling が働くと IP が変わります。
先程触れたように、nginx では名前解決は起動時の一度のみ。
つまり、Auto Scaling などの理由でIPが変わった場合、nginx は起動時に名前解決した時の IP で転送処理を続けてしまいます

そうだ TTL のキャッシュ時間を短くしよう

そこで、resolver ディレクティブの valid オプションを使って、TTLのキャッシュ時間を短くしてみました。
ほぼ都度名前解決を行うので IP が変わったら新しい IP に転送するはず!
あんまり行儀が良いとは言えませんが、かと言って古い IP を持たれても困るので^^;

cf. resolver ディレクティブ

location /hoge/ {
    resolver 172.31.0.2 valid=5s;
    proxy_pass https://hoge.example.com/;
}

172.31.0.2は、AWSのVPC内DNSサーバのIPです)

これで解決する…
と思っていたのですが。

闇があった

実はこれでは設定が不充分であり、この設定では nginx はずっと古い IP を持ち続け、名前解決をしてくれませんでした。
(そんな馬鹿な)

泣きたい気持ちで proxy_pass ディレクティブのページをよくよく読んで見れば、こんなことが書かれていました。

Parameter value can contain variables. In this case, if an address is specified as a domain name, the name is searched among the described server groups, and, if not found, is determined using a resolver.

再度名前解決をする為には変数を指定しなければならない、というような断定的な書き方ではないものの、proxy_passに変数を指定すると上手く名前解決をしてくれそうです。

location /hoge/ {
    resolver 172.31.0.2 valid=5s;
    set $endpoint hoge.example.com;
    proxy_pass https://$endpoint/;
}

転送先ホストのIPを切り替えながらテストをしてみました。

  • proxy_passに直接URLを指定している場合、転送先ホストのIPが変わると古いIPに転送する
  • proxy_passに変数を指定している場合、転送先ホストのIPが変わると新しいIPに転送する

上記2点の確認を行い、想定の挙動を確認しまして、これで解決、とほっと胸をなでおろしました。
のですが…

闇はもう少し深かった

上記設定で/hoge/hige?food=curry にアクセスしてみると、https://hoge.example.com/ に転送されます。
期待する転送先は https://hoge.example.com/hige?food=curry です。
そう、/hoge/以下のパラメータがまるっと無視されています。
(そんな馬鹿な)

/の有無で転送の挙動が変わる、ということは知っていたのですが、まさか文字列指定を変数に変更しただけで挙動が変わるとは露程にも思いませんでした…
とは言え、そうなるものはしょうがない(´・ω・`)

最終的な設定内容

正規表現を使って、/hoge/以下のパスをproxy_passにくっつけつつ、クエリストリングがあれば追加するようにしました。

location ~ /hoge/(.*) {
    resolver 172.31.0.2 valid=5s;
    set $endpoint hoge.example.com;
    proxy_pass https://$endpoint/$1$is_args$args;
}

これにて I got Kotonaki.
想定した通りに転送され、解決しました。

余談

社内に共有したところ
「っていうかこれnginxのバグじゃない?
「旧バージョンではURLを直接指定したら構文エラーになっていたので、こんな怪しい動きをするならいっその事そのままで居てくれたほうがよかった
「nginx にかかわらず、リバースプロキシの設定はちょっと値が変わるだけでも動きが変わるので変更した場合は念入りにテストした方が良いね

…などなど、熱い議論が交わされました。

正直、まさかここまで振り回されるとは思いませんでした^^;
もし、nginx のproxy_passの設定で悩まれている方の助けになりましたら、幸いです。

今日のひとネタでした(・ω・)ノ

この記事を書いた人

torikai