🌀

nginx 1.27.3でupstreamにresolve指定が可能に

2024/11/30に公開

個人的にはビッグニュースなのに、あまり話題にしている人がいなかったのでメモ的に書いておきます。

nginxは最近開発がGitHubに移ってから、開発が活発になっており、以前から期待されていたupstreamでresolveを指定する機能がOSS版でも使えるようになりました(有料版では以前から使えていた)。

https://github.com/nginx/nginx/pull/208

今までの問題

nginxのproxy_passにドメインを指定すると、起動時に名前解決が走り、その後はずっとそのIPアドレスに対してproxy_passされます。よってDNSが更新されても古いIPアドレスにリクエストが行く問題がありました。

その状態で直したい場合はreloadすればまた名前解決されるので、reloadするくらいしかありません。

reloadをせずにDNS更新を反映させる方法としては、以下のように一度変数にsetするという荒技もありました。

resolver 8.8.8.8 valid=30s;
server {
  set $backend_host "backend.example.com:8080";

  location / {
    proxy_pass http://$backend_host;
  }
}

しかしこの方法だと、upstreamとのkeepaliveが有効にならないという問題があり、パフォーマンス面で難がありました。

この辺の問題を解決する方法だと、nginxにパッチを当てつつ、3rd party moduleを使うというのが現実的な方法だったと思います。

https://kazeburo.hatenablog.com/entry/2020/08/08/010457

それはさすがにつらいです。ということで新機能の紹介です。

upstreamのresolveを使う

DNSを解決する必要があるので、resolverオプションでDNSキャッシュサーバーを指定します。ここでは8.8.8.8にしますが、実際にはサーバーで利用しているDNSキャッシュサーバーと同じ設定にすると良いはずです。

resolver 8.8.8.8 valid=30s;

upstream app {
  zone upstream_dynamic 64k;

  server backend.example.com resolve;
}

server {
...

  location / {
    proxy_pass http://app;
  }
}

upstreamの中にzoneとそのサイズを指定する必要があります。固定値なので、サーバー台数に応じた適切なサイズを設定する必要があります。

resolverのデフォルトだとTTLの間はキャッシュを持つのと、DNSキャッシュサーバーもTTLの間はキャッシュを持つ可能性が高いので、validで少し短めの値を指定したり、DNSの設定もTTLを短めにしておくと安心できると思います。

最後に

昔からこの挙動を理由にnginxにパッチを当てたり、本来不要なミドルウェアを経由したり、Goでproxy serverを自作したりする人生を歩んできましたが、これからは不要になりそうでうれしいです。

ちなみにnginxの奇数のバージョンはmainlineなので開発版に近い位置づけです。stableには1.28がリリースされたら入ると思うので、stable版を使っている人は1.28のリリースを待ちましょう。

nginxは一時期開発が停滞していた時期もありましたが、GitHubに移ってからは、勢いを取り戻しつつあるように見えます。これからも新機能が楽しみですね。

Discussion