LINE NotifyやGoogle APIで希に接続が失敗する
結論から書くと記事タイトル通りで、スタブリゾルバとIPv6が原因で外部APIの接続に失敗する。やっかいだったのは以下の2点
・ほぼAPIに繋がるが希に失敗するので原因が特定しにくい
・UbuntuのServer版なら勝手にキャッシュしないだろうという認知バイアス
仕事で外部サービスへの接続は過去に何十回も構築しており、一度も同様の不具合が無かったので頭を抱えることとなった。
言語はPHP。LINE Notifの接続はfile_get_contents。Google APIの接続はGoogle_Client。失敗時のエラーは以下
LINE Notify
PHP Warning: file_get_contents(https://notify-api.line.me/api/notify): failed to open stream: HTTP request failed! HTTP/1.1 400
Google_Client
PHP Fatal error: Uncaught GuzzleHttp\Exception\ConnectException: Connection refused for URI https://oauth2.googleapis.com/token in /srv/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php
AWSのロードバランサーはIPが頻繁に変更されるので外部API側も似たような仕様だと推察
最初は相手側の不具合かと思ったが、それにしても頻度がそこそこ多いので自身のシステムを疑うことに。ただバッチ処理を2度叩くと成功したりと訳が判らず。エラーが400と出ているので経験と勘だけで探る。とりあえず名前解決が怪しいのでresolve.confを見ると
resolve.conf
nameserver 127.0.0.53
127.0.0.53ってなんや・・・。調べるとスタブリゾルバという名のDNSキャッシュらしい。昨今のサービスは外部接続が当たり前で、且つAWSのロードバランサーは頻繁にIPが変わるので追随出来ないのに何故こんな機能をサーバー版でオンにしているのだろうか。LINE側もGoogle側もロードバランサーが同様かどうかは不明だが可能性的には高いのでスタブリゾルバをオフにする
/etc/systemd/resolved.conf
[Resolve]
DNSStubListener=no
DNS=8.8.8.8 8.8.4.4 2001:4860:4860::888
/etc/resolve.confのシンボリックリンクを書き換え
$ cd /etc/
$ ln -sf ../run/systemd/resolve/resolv.conf resolv.conf
再起動
$ systemctl restart systemd-resolved
systemd-resolvedの再起動で/etc/resolve.confも書き換わる。LINE Notifyはこれで解決して接続エラーは出来なくなった。
希に繋がらないGoogle API
Google APIもスタブリゾルバのオフで繋がる頻度は高くなったが、繋がらない事が極めて希の頻度で発生していた。とりあえず不具合時にpingを打つと返ってこない
$ ping oauth2.googleapis.com
PING oauth2.googleapis.com(nrt20s09-in-x0a.1e100.net (2404:6800:4004:80b::200a)) 56 データ長(byte)
--- oauth2.googleapis.com ping 統計 ---
送信パケット数 19, 受信パケット数 0, パケット損失 100%, 時間 18439ミリ秒
Google側でIPv6が開いているのでIPOE環境だとv6で接続に行ってしまう。とりあえずv4で叩くと正常にIPが引ける
ping -4 oauth2.googleapis.com
PING oauth2.googleapis.com (172.217.31.170) 56(84) バイトのデータ
64 バイト応答 送信元 nrt12s22-in-f10.1e100.net (172.217.31.170): icmp_seq=1 ttl=115 時間=9.19ミリ秒
64 バイト応答 送信元 nrt12s22-in-f10.1e100.net (172.217.31.170): icmp_seq=2 ttl=115 時間=10.1ミリ秒
原因は不明だがIPv4で解決出来るならIPv4を優先接続にして回避を試みる。
/etc/gai.conf
precedence ::ffff:0:0/96 100
(コメントアウトを消して有効にする)
一応接続不具合はこれで解消した。IPv6のGoogle DNSがGoogle APIを引けないのは謎のまま。