Nginxで、アクセスしたらちゃんと返ってきているのにログを見ると connect() failed (111:Connection refused)
が頻発に発生している謎を調査したら、
いままで知らなかったNginxの挙動にたどり着いたのでメモ。
(ただしよく見るとドキュメントにちゃんと書いてあったというオチ)
問題
upstream rails-unicorn {
server localhost:3000;
}
こんな風に書いていたら
[error] 11#11: *21735 connect() failed (111: Connection refused) while connecting to upstream, client: xx.xx.x.x, server: example.com, request: "GET /hogehoge HTTP/2.0", upstream: "http://[::1]:3000/hogehoge", host: "xx.xx.x.x", referrer: "http://xx.xx.x.x:80/hogehoge"
[warn] 11#11: *21735 upstream server temporarily disabled while connecting to upstream, client: xx.xx.x.x, server: example.com, request: "GET /hogehoge HTTP/2.0", upstream: "http://[::1]:3000/hogehoge", host: "xx.xx.x.x", referrer: "http://xx.xx.x.x:80/hogehoge"
ログに↑のメッセージがしょっちゅうでていた。
解決方法
localhost
ではなく 127.0.0.1
とすると解消する
upstream rails-unicorn {
server 127.0.0.1:3000;
}
[Sy] nginx(リバースプロキシ)+node.jsでconnect() failedが頻発する場合の対処 | Syntax Error.
何故なのか
nginxのupstream serverは、複数のIPを持つドメイン名があると、それぞれのIPごとに複数のserverを定義したことになるから
ドキュメント : http://nginx.org/en/docs/http/ngx_http_upstream_module.html#server
A domain name that resolves to several IP addresses defines multiple servers at once.
で、localhost
は 127.0.0.1
(IPv4), ::1
(IPv6) どちらともに名前解決される設定になっていた
127.0.0.1 localhost
::1 localhost
つまり
upstream rails-unicorn {
server localhost:3000;
}
は
upstream rails-unicorn {
server 127.0.0.1:3000;
server ::1:3000;
}
と書いてることになっていた。
そのためこんな動作になっていた模様。
-
upstreamにserverを複数書くとラウンドロビンでロードバランシングされるので、IPv4とIPv6で交互にバックエンド(rails-unicorn)を呼ぼうとする。
-
バックエンドのunicornではIPv4しかlistenしていなかったため、
::1:3000
(IPv6で)は繋がらず、
Nginxはconnect() failed (111: Connection refused) while connecting to upstream
とログを残し、
-
繋がらなかった
::1:3000
をしばらくバランシング対象から外すupstream server temporarily disabled while connecting to upstream
-
ここで、まだ他のバランシング先(
127.0.0.1:3000
)があるので、アクセス元にエラーはまだ帰らず、Ipv4の方に接続を試みにいく -
Ipv4ではちゃんとつながるので無事レスポンスが返される
-
バランシングから外された
::1:3000
しばらくするとNginxは::1:3000
をバランシングに復帰させてみる -
もちろんつながらないので、以下これが繰り返されてエラーログが吐かれ続ける
おわり
バックエンドがIPv6でもlistenしていれば server localhost:3000
と書いててもエラーは出ないが、同じサーバをIPv4とIpv6でラウンドロビンさせることに意味はないので localhost
とは書かず 127.0.0.1
か ::1
かで書くようにしたほうが良さそう。
複数のIPを持つドメイン名があると、それぞれのIPごとに複数のserverを定義したことになるのは localhost
に限らず(むしろ特殊)どんなドメインに対してもなので、気をつけておかないと他でも嵌りそう。