NginxのリバースプロキシでのDNS名前解決における落とし穴

問題

Nginxのリバースプロキシでは、プロキシ先として(IPアドレスの他に)ホスト名を指定することができます。 その際、設定ファイルのlocationコンテキストは以下のようになると思います。

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

しかし、これには大きな落とし穴があります。DNSの名前解決がnginxの起動時にしか行われず、TTLは無視され、初回起動時に解決されたIPアドレスがずっと使われてしまうのです。 もちろん、ホスト名に対応するIPアドレスに変更があったときにエラーになってしまいます。

自分の場合では、プロキシ先としてAWSのELBのホスト名を指定しており、(ELBのアドレスは固定ではないので)この問題が原因で502エラーを吐き出してしまっていました。

解決策?

nginxにはresolverというディレクティブがあります(公式ドキュメント)。By default, nginx caches answers using the TTL value of a response とあるので、TTLが尊重されるみたいです。うまく行きそうな気がします。

試しにresolverにはGoogle Public DNS8.8.8.8を利用してみます。

location /hoge/ {
  resolver 8.8.8.8;
  proxy_pass http://example.com/;
}

しかし、これでもうまくいきません。もはやバグですね。

解決策

このようにして、一旦ホスト名を変数に格納して使うと、うまくいくようです。

location /hoge/ {
  set $target example.com;
  resolver 8.8.8.8;
  proxy_pass http://$target/;
}

なんだかなあって感じです。