Nginxでproxy_passにホスト名を書いた時の名前解決のタイミング
Nginx 1.4.2で試しました。
ネームサーバーは、ローカルのunboundをlocal-zone, local-dataを使って簡易コンテンツサーバーにして試しました。
local-zone: "oreno." static local-data: "api.oreno. 30 IN A 192.0.2.11" # local-data: "api.oreno. 30 IN A 192.0.2.12"
proxy_passにホスト名を書くと→名前解決は一度だけ
このように Nginx の設定を書いた場合、
location /api { proxy_pass http://api.oreno:9999; }
- 「api.oreno」の名前解決は、nginxの起動時に行われます
という挙動になります。
つまり、ホスト名に紐づくIPアドレスがDNS的に変更されても、nginxは(HUPを受けるまでは)古いIPアドレスにreverse proxyし続けます。
定期的に名前解決させるにはどうすればよいか?
このように、setディレクティブで変数にホスト名をセットし、proxy_passではその変数を参照すれば、定期的に名前を引き直してくれるようになります。バッドな感じがしますが、公式ドキュメントにもこの挙動はちゃんと記されています。
location /api { resolver 127.0.0.1; set $api_backend "api.oreno"; proxy_pass http://$api_backend:9999; }
- http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
- 下の方の「A server name, its port and the passed URI can also be specified using variables:」のあたり
この時、resolverディレクティブもあわせて指定しないと、「no resolver defined to resolve api.oreno」とエラーログに記され、クライアントには 502 Bad Gateway が返されるので注意してください。
デフォルトではDNS応答のTTLが切れたら再度問い合わせに行きますが、
location /api { resolver 127.0.0.1 valid=2s; set $api_backend "api.oreno"; proxy_pass http://$api_backend:9999; }
と、resolverディレクティブにvalidオプションを指定することによって、TTLの値に関わらず、2秒経過したらまた再度DNS問い合わせするようにもできます。
upstreamディレクティブでserverを羅列してい場合に、定期的に名前解決させるには?
upstream api_upstream { resolver 127.0.0.1; set $api1 "api1.oreno"; server $api1; set $api2 "api2.oreno"; server $api2; } location /api { proxy_pass http://api_upstream:9999; }
と書けばできそうに思えますがダメです。なぜなら、setディレクティブはupstreamコンテキストでは指定できないからです。
upstreamディレクティブでserverを羅列してい場合に、定期的に名前解決させる方法はないように思えるのですが、なにかよい方法があったら教えてもらえるとうれしいです><
追記
1.5.12以降なら、serverディレクティブにresolveパラメータをつければhttpブロックのresolverの設定の通りに名前を引き直してくれるようです。
http { resolver 127.0.0.1; upstream api_upstream { server api.oreno resolve; } }
やったー!!!!と思って試してみたのですが「nginx: [emerg] invalid parameter "resolve" in ...」と怒られる。。と、よくドキュメント読んだら、
Additionally, the following parameters are available as part of our commercial subscription:
と書いてありました><
追記2
を使えばできました。nginx 1.8.0で確認。nginx 1.4.1ではビルドができませんでした。
これでTTL後に引き直し、
http { resolver 127.0.0.1; upstream api_upstream { dynamic_server api.oreno; } }
これでTTLに関係なく2秒後に引き直してくれました!
http { resolver 127.0.0.1 valid=2s; upstream api_upstream { dynamic_server api.oreno; } }
追記3
- https://www.nginx.com/resources/wiki/modules/
- https://www.nginx.com/resources/wiki/modules/domain_resolve/
でも紹介されている https://github.com/wdaike/ngx_upstream_jdomain/ を使ったほうがよさそうです。
Nginx 1.14ではコンパイルできなかったので fork の
https://github.com/mwhipple/ngx_upstream_jdomain を試しました。ディレクティブが jdomain から hostname に変わっている点に注意してください。
http { resolver 127.0.0.1 valid=2s; upstream api_upstream { hostname api.oreno; } }
追記4
ApacheだとProxyPassにdisablereuse=Onをつければいいっぽい。
なぞの「DNS error (32: Unknown error), query id:XXXXX」
まだ再現方法がわからないのですが、proxy_passで変数参照している場合にこのエラーが出て数秒〜十数秒間reverse proxyできなくなる現象が起きています。
こちら何か情報お持ちの方は教えていただけるとうれしいです><
おまけ: nginxのdebug log
configureするときに --with-debug した上で、
error_log /path/to/file debug;
とすると、nginxがdebug logを吐くようになります。resolverの挙動もこれで確認できます。ただ、かなりの量を吐くので注意してください。
あと、コンテキストによって吐かれるログが違うので、serverやhttpコンテキストだけでなく、一番外側のmainコンテキストにもerror_logディレクティブを書いた方がよいです。