こんにちは! teratailエンジニアの鈴木です。
実は11月の終わり頃(11/25)から数週間に渡り、teratailが非常に重く、時にはサーバーからエラーが返ってしまう状況がありました。
本件に関して、ユーザーの皆様に多大なご迷惑をおかけすることになり、大変申し訳ございませんでした。
今回は、不具合に関してのお詫びと共に、本件への対応として行った、GCPロードバランサを使用する上でのApacheの設定について紹介しようと思います。
起こっていた現象
本件によりサイト上に起こっていた現象としては、以下のようなものが挙げられます。
- 時間に対してある程度一定の割合で、クライアントへ502エラーが返る
- リクエスト数が変化する昼間と夜間でも、エラー率に変化がない
- ブラウザでは、サーバーからレスポンスが返らず、画面がフリーズする
画面がフリーズする現象に関しては、サイトのいくつかの機能のために同期XMLHttpRequest(非推奨)を使用していたことが原因です。
teratailの構成
対応方法の説明へ入る前に、まずは現在のteratailの構成について簡単に紹介します。
現在のteratailは、10/26にGoogle Cloud Platformへ移行してから、kubernetesを利用した以下のような構成で動いています。
GCE上の各Podで、Apacheを含むteratailのコンテナが動作しています。
不具合の原因と対応
今回起こった不具合の原因は、一言で言うと、GCPロードバランサによるコネクション維持に対して、Apache側のタイムアウトが短すぎるという点にありました。
以下の記事で、Nginxにおける同様の事例について詳しく解説されています。
Tuning NGINX behind Google Cloud Platform HTTP(S) Load Balancer
GCPロードバランサを利用することで、クライアントとアプリケーションとのコネクションは、次の2つに分かれます。
- クライアント-GCPロードバランサ間のコネクション
- GCPロードバランサ-GCEインスタンス間のコネクション
上記の記事によれば、後者のコネクションは、通信の有無にかかわらず10分間程度維持されるようです。
このコネクションをGCPロードバランサが再利用しようとした際に、Apacheがコネクションを切断してしまうと、クライアントへ502エラーが返ってしまいます。
すなわち、Apache側でGCPロードバランサとのコネクションを完全に維持するために、KeepAliveを例えば以下のような値に変更しなければなりません。
KeepAliveTimeout 650 MaxKeepAliveRequests 10000
ApacheのKeepAliveに関する設定については、クライアントとのコネクションが増えすぎるのを防ぐために、KeepAliveTimeoutを十分短くすべきだ、と言われることが多いように思いますが、GCPロードバランサにおいては事情が異なります。
このような考え方でいくつかのチューニングを行った結果、最大0.8 [RPS]あったエラーを、0.01 [RPS]未満(コンソール画面で確認不能)まで減らすことができました。
最後に
繰り返しになりますが、今回の不具合でユーザーの皆様にはご迷惑をおかけし、大変申し訳ありませんでした。
また今回の場合は、完全なサービス停止ではなくリクエストがたまに失敗するという形だったこともあり、開発チームの認識が遅れ、不安定な状況が長引いてしまいました。
今後このようなことがないよう、対策として、エラーをより細かく監視できる仕組みやチームの体制を整えました。
継続的な監視と改善を行い、サービスの安定化に努めてまいります。
今後ともteratailをよろしくお願いいたします。