前置き
3月からSREとして働いている井之口です。
これまではバックエンドエンジニアとして働いていたのですが、ポジションも変わってインフラに関わる事も多くなりました。
今回はELBの設定を変更する機会があったので、その際に学んだ事をまとめようと思います。
構成
- NginxがRailsのプロキシサーバーとして立っていて、ELBから負荷分散されている一般的な構成です
- ALBアイドルタイムアウトは60秒に設定されています
- Nginxの
keepalive-timeout
は60秒に設定されています
簡単に用語について説明しておきます
ALBアイドルタイムアウト
- ALBが接続先であるNginxへ向けたTCPコネクションを保持する時間
keepalive_timeout
- NginxがALBへ向けたTCPコネクションを保持する時間の事
対応内容
ではなんらかの理由でALBアイドルタイムアウトを伸ばさなければいけなくなったとしましょう。
今回は120秒に伸ばしたいのでまずアイドルタイムアウトのみ伸ばしてみます。
- ALBのアイドルタイムアウトを伸ばす (120秒)
- Nginxのタイムアウトは60秒のまま
このようなイメージです
実はこの設定は不十分でAWS公式でもELBの接続先にNginxやApacheを置く場合は、接続先のタイムアウトを伸ばす事が推奨されています。
ELBのバックエンドサーバーとしてApacheまたはNGINXを使用するための最適な設定を教えてください。
なぜ問題があるのか?
- 60秒を超える処理が実行された場合、ALBより先にNginxがタイムアウトをする事で、Nginx側からTCPコネクションが切断される
- ALB側からは、TCPコネクションが切断されたのを検知するには、keepaliveで一定周期で相手が生存しているか確認応答をするまで待つ必要がある
参考 - 結果、切断の確認をする前に別リクエストがALB側から貼られているTCPコネクションを使ってしまうと、Nginx側は既にコネクションを切断しているので、504エラーになる。
上記の様な事が発生する可能性があるため、Nginxの設定も変更する必要がありそうです。
追加で設定を入れてみます
追加対応
- ALBのアイドルタイムアウトを伸ばす (120秒)
- Nginxの
keepalive_timeout
をALBのアイドルタイムアウト以上にする (240秒) - Nginxの
proxy_read_timeout
を伸ばす - Nginxの
client_header_timeout
client_body_timeout
を設定する
- 120秒を超える処理が実行される時に、ALBがNginxより先にタイムアウトをする事で、ALB側からコネクションが切断される
- ALB側からコネクションを切った後にNginxがコネクションを切るので、新しくALBから入ってくるリクエストが、切断されているコネクションに流れてくる可能性がない。
これで安全にコネクションを切断できる様になりました。
他に設定したプロパティの意味
proxy_read_tmeout
- Nginxがプロキシ先(Rails)に投げたリクエストをタイムアウトする時間
- つまりクエリ等が原因で時間がかかっている場合は、この時間を伸ばさないと結局タイムアウトすることになる
- この記事はあくまで暫定対応前提のため、非同期処理やクエリチューニングなど考えた方がより良いです
client_header_timeout
- クライアント(今回はALB)がNginxに投げたheaderをNginxが読み込むまでの時間のタイムアウト時間
client_body_timeout
- 上記のリクエストボディ版
疑問点
- TCPコネクションが切断される時の挙動
- TCPコネクションを切断する場合って、FIN -> ACKでやりとりをしてから切断すると思っていたので、その時点で切断された事検知できるんじゃないのかなと思った。この辺り詳しい方教えていただければ嬉しいです。
終わりに
SREになり、バックエンドの業務ではあまり意識しないレイヤーの知識もサービスの信頼性を高める必要だと実感しています。
まだまだ未熟者ですが、これからもサービスの信頼性を向上させるにはどうすれば良いか考えながら業務をしていこうと思っています