#起こったこと
・クライアント(Pythonスクリプト)に、AzureVMに構築したAPIの応答が帰ってこない。
・サーバ側(VM)は正常に応答したログが記録されている。
・クライアントをcurlに変更すると何故か正常応答する。
#構成
・Pythonクライアント→インターネット→Azure NSG→VM NIC→Nginx→Glassfish→SpringBoot
・SpringBootはファイル生成処理が挟まる関係上、リクエスト受領後約5分後に応答を返す。
#結論
PublicIPのアイドルタイムアウト設定が初期値4分のままになっていたため。
これにより、4分間パケットのやり取りがないTCPコネクションがAzure側で掃除されていた。
アイドルタイムアウトを10分などに伸ばすと事象解消し正常にAPI応答が受領できるように。
下記がよくまとまっていたが、PublicIPのTCPアイドルタイムアウトが4分で固定と記載されている。
これは誤りで実際は4~30分で設定可能と思われる。
https://jpaztech.github.io/blog/network/snat-options-for-azure-vm/
VMのPublicIPに限らずApplicationGatewayのPublicIPや
LoadBalancer、NATGWなどSNATするもの全てでアイドルタイムアウト設定があるので、
応答に時間がかかるシステム構築では考慮が必要。
日本語の記事はあまり見当たらなかったが、英語だといくつかhitした。
https://docs.microsoft.com/en-us/answers/questions/152494/tcp-connections-dropped-after-approx-5-mins-of-ina.html
#調べたこと①
・Pythonクライアント→localhost(自分のノートPC)→Tomcat→SpringBootでリクエスト→応答あり
・Pythonクライアント→インターネット→Azure NSG→VM NIC→Glassfish→SpringBoot→応答なし
・Pythonクライアント→localhost→VM NIC(lo)→Glassfish→SpringBoot→応答あり
・Pythonクライアント→localhost→VM NIC(lo)→→Nginx→Glassfish→SpringBoot→応答あり
→なんとなくAzureが怪しい、、
#調べたこと②
クライアント側PCをwireshark、VM側をtcpdumpでパケットキャプチャを実施。
クライアント側は、httpsコネクションを受領後音沙汰なし。
netstatするとずっとコネクションはESTSABLISHEDのままとなっていた。
VM側は、コネクション確立後、約5分後正常にパケットを送信していたが、
ACKが帰ってこないため何度かリトライを実施(Azureではコネクションが消されているため)。
その後、FIN_WAIT1しRSTして切断していた。
→そりゃないコネクションにパケットを送っても届かないよね、、、
#調べたこと③
curlすると何故か応答が帰ってくる、、、??
→wiresharkしたところ、何やらTCP Keep-Aliveしている。
→そのためAzureでアイドルタイムアウトで消される対象にされず延命されていた。
→でもlinuxのデフォルトTCP Keep-Aliveは7200秒(=2時間)で始まると聞いたぞ、、??
sysctl -a | grep keepalive
でVMの設定を確かめるも、
net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9
やはりデフォルトの2時間。
結果、curlの仕様を調べたらすぐに出てきました!
デフォルトで60秒おきにTCP Keep-Aliveしてくれるようです。
Use --keepalive-time to specify how often in full seconds you would like the probe to get sent to the peer. The default value is 60 seconds.
ちなみに、linuxのtcp_keepalive_timeの設定値を180秒など短くすることでも本事象は解消できそう。
この辺はシステム特性に応じて検討が必要ですね。
#その他対処方法
Pythonのコード内でTCP Keep-Aliveを実装することもできそう。
通常のrequestsモジュールでは無理なのでsocketとかを使って書いていくみたい。
あまり深入りしなかったので、詳しくは分かりません。
https://www.programcreek.com/python/example/4925/socket.SO_KEEPALIVE
#参考
TCP Keep-Alive/http Keep-Aliveの仕組みと違い
https通信をtcpdumpでキャプチャしてWiresharkする方法
Azure VM の外部接続 (SNAT) オプション まとめ