Kagi.com サービス不安定問題の解決
- 調査中 - デプロイ後に問題が発生し、チームが解決作業を進めている。 (1月12日 16:45 UTC)
- 監視中 - 問題の原因と見られる設定変更を元に戻し、サービスが正常に戻ることを継続的に監視している。 (1月12日 18:30 UTC)
- 更新 - 安定性を完全に回復するため、一時的にトラフィックを停止し、ユーザーをこのページにリダイレクトする予定。サービスへの負荷を制御された形で復旧させる間、状況の進展に応じて追加の詳細を提供する予定。 (1月12日 20:26 UTC)
- 監視中 - トラフィックは復旧しており、サービスが完全に正常へ戻ることを引き続き監視している。 (1月12日 21:14 UTC)
- 解決済み - すべてのサービスは正常に稼働中。問題解決まで待ってくれたユーザーに感謝を表明した。
事後分析
- Kagiの技術リーダーであるZacが、先週のサービス障害に関する詳細な事後分析を共有した。
- この事件への対応として、シニアエンジニアのSethとDevOpsエンジニアのLuanが協力して作業した。
- サービスを悪用し、インフラのボトルネックを突いた行為者が存在しており、即時の緩和措置を講じるとともに、コードとコミュニケーションの複数の領域で改善作業を進めている。
事案の経過
- 1月12日午後5時30分ごろ、内部監視とユーザーからの問題報告を通じて、インフラ障害が発生していることを把握した。
- 問題の性質としては、さまざまな地域のユーザーに対して読み込みの遅延やページタイムアウトを引き起こしていた。
- 問題の解決には相当な時間を要し、その背景、進捗状況、今後の計画について説明した。
技術的な問題解決の過程
- 当初は偶然にも、VMの追加RAMリソースへのアップグレードと同時に問題が発生した。
- 監視では高いレイテンシと、アプリケーションのデータベース接続プールの問題が報告されていた。
- 接続プールは飽和状態に達しており、これは接続総数が設定された最大接続数の上限を超えていたことを意味する。
- データベースの内部的な健全性とクエリ性能を評価する間、いくつかのインスタンスを置き換えて混雑緩和の効果を試した。
- インスタンスの一部を置き換えることが有効に見えたため、すべての接続プールを一度に完全にリセットするためにユーザートラフィックを一時停止した。
- データベースの状態を調査したところ、ユーザーテーブルの行に対する高い競合が根本原因であることが明らかになった。
- この競合により書き込みレイテンシが急激に増加し、アプリケーションの接続プールにバックプレッシャーがかかり、最終的に利用可能なすべての接続が枯渇した。
- Kagiはこれまで、GCPで利用可能な最も安価なシングルコアのデータベースを使用しており、これはデータベースを容易に麻痺させるリスクを抱えていた。
- 悪質な行為者を特定し、24時間以内に作成されたアカウントや、短時間で60,000回以上の検索を実行した単一ユーザーアカウントを突き止めた。
- 当該アカウントの検索機能を削除し、問題を引き起こしていた特定の書き込みを無効化するホットフィックスを発行した。
- 深夜までに問題は完全に解決され、行為者が戻ってくる兆候がないか引き続き注意深く監視している。
今後の対応
- この事件から多くを学び、システムをさらに強化し、インシデント発生時のコミュニケーションプロセスを改善するための即時の計画をすでに進めている。
- まず、ステータスページの更新が迅速ではなかったことを認めている。
- ユーザーに自動化された内部監視をより簡単に公開できるステータスページプラットフォームへ移行し、プラットフォームの健全性をリアルタイムで把握できるようにする予定。
- 問題を引き起こすクエリを直接緩和し、同様の欠陥がさらに存在するかを確認するために負荷テストを実行している。
- 追加の監視を導入し、インフラ内の正しい箇所をより早く指し示せるようにして、今回のように誤ったシグナルを追いかけて時間を無駄にしないようにする予定。
- この種の悪用を検知するシステムを強化しており、性能面だけでなくコストも直接発生するため、自動化された制限を設定してそれを適用する必要がある。
- 新しい制限はこの投稿時点ですでに施行されており、その影響を監視し、必要に応じて継続的に調整していく予定。
- Kagiへのアクセスが誤ってブロックされたと考えられる場合は、support@kagi.com へ連絡してほしいと呼びかけている。
GN⁺の見解
- Kagiは、ユーザーテーブルの行競合による書き込みレイテンシの問題に直面し、それがアプリケーションの接続プールにバックプレッシャーを与えてサービス停止を引き起こした。
- この問題は、KagiがGCPで最も安価なシングルコアのデータベースを使用していたことで生じたリスクの結果だった。
- Kagiチームは今回の事件を通じて、システムの強化、ユーザーとのコミュニケーション改善、悪用防止のための自動化された制限の設定といった措置を講じることで、サービスの安定性と透明性を高めようとする努力を示した。こうした取り組みは、ユーザーにより信頼できるサービスを提供しようとするKagiの意思を反映している。
1件のコメント
Hacker News の意見
最初は、VM に RAM を追加するインフラのアップグレードと障害がまったく同じ時刻に起きたことは完全な偶然だと判明したが、こうした「偶然」は本当によく起き、問題を追跡している間はその存在自体を疑わせる
その状態で慌てると、結局ほかのものを壊す緊急修正(hotfix)を押し込むことになり、そこからはずっと苦しくなる
マーフィーの法則はシステム管理者と開発者に容赦ない
好きな言葉が一つあって、「なぜ/どう直したのか分からないなら、実は直っていないのかもしれない」というもの
「幸い」自分のクエリとは無関係だったが、こういう偶然が二つ重なると本当に怖い
こういうことを十分に経験した結果、より多くの仮定を疑い、まだ検証されていないものを確認済みデータとして扱わない習慣がついた
バイアスや早合点を完全になくせたわけではないが助けにはなったし、オープンな姿勢を保つのはかなり難しいことだ
エンジニアにとって重要な能力は、障害対応の変更を批判的に推論し、デバッグし、「分離してテスト」できること。見た目よりずっと難しく、たいていシニア寄りのスキルだ
この問題を Discord に報告したユーザーの一人だった。Kagi は好きだが、ステータスページがすべて正常と表示していたのはかなり残念だった
実ユーザーに影響する障害の最中でもステータスページが優先事項ではないように見えて不安だったので、今後は正確に更新されることを期待したい
過去によく依存していたサービス、例えば GitHub はステータスページをすぐ更新してくれたので、問題が自分の端末側ではなくサービス側で認識済みの問題なのだと分かって安心できた
今回は、その日に雪が降る前に近くで開いている食料品店を探す必要があったのに、結局 Google に行くことになり、少しがっかりした
それでも Kagi を使っていた時間の 99.9% は Google より良かったので使い続けるつもりだし、事後分析で述べられていたようにステータスページのコードを別のサービス/プラットフォームへ移してほしい
結局、ステータスページに何かを載せること自体が一つの会話になり、その会話がエンジニアの時間と注意を奪い、その分だけ障害復旧が遅れる
コミュニケーションと実際の復旧のバランスを取らなければならないが、常に正解が明確なわけではない
人員が十分なら Technical Incident Manager がコミュニケーションを担当し、コミュニケーション側にもエンジニアを追加できるが、常に可能とは限らない。一部のシステムは特殊で、ドキュメントが不足しており、計測も足りない
個人的には、問題の兆候が見えたらすぐに「発生の可能性がある問題を調査中」という大きく曖昧な告知を出し、後で詳細を埋めるか撤回するやり方を好む。しかし、私が働いた会社はその考えを好まなかった
そのとき Kagi に一気に引かれ、一部のクエリでは行ったり来たりしながら使ってみたが、LLM、Perplexity、そして Google が検索ページ上で直接答えてくれるケースが増え、Kagi に残るクエリは多くない
Kagi が何らかの形で Perplexity と合体したら、かなり面白そうだ
最後まで表示しないことも多い
この障害があまりにも見覚えのあるもので、驚くほどだ
個人的には認めたくないほど何度もこれとまったく同じ種類の障害を扱ってきたし、Kagi チームのようにデータベース接続プールの状態という迷路に入り込み、新しいインスタンスを投入したりトラフィックを「リセット」すれば解決すると信じたりして、同じ緩和策を試したが無駄だった
こうした障害では、データベースの一般的な飽和指標である CPU 使用率や IOPS などがあまり動かないことも助けにならない。クエリ遅延は高く見えるのに、「CPU と IOPS には余裕があるな……」と思ってしまい、いつものようにロック競合が隠れていることを見落とす
経験上、DB 接続プールの異常の 98% は DB 自体の異常に由来する。Kagi がどのリレーショナルデータベースを使っているのかは分からないが、DB のグローバル I/O 待ち時間(秒/秒)、グローバルなロック取得時間(秒/秒)、そして正規化されたクエリ別の実行時間(秒/秒)をグラフ化することを強く勧める
これに CPU 使用率チャートを加えれば、大規模な性能問題の大半を素早く特定できるダッシュボードになる
それとは別に、検索クエリがリレーショナルデータベースへの書き込みを引き起こすという点は少し驚きだ。リレーショナルデータベースはユーザー設定やログイン管理のような用途だけに使われていると思っていた
Kagi が利用量集計、例えばカウンタのインクリメントをリレーショナルデータベースで行っているなら、規模が大きくなったときに破綻する非常に典型的な失敗モードだ
検索結果をブロックする場合のように、検索によって間接的な書き込みが発生することはあり得るし、閲覧履歴や分析も当然あるだろう
それでも、検索 1 回ごとに書き込みロック競合が起きるような何かが何なのかは明らかではない
すべてのスタートアップがいつかは経験すること。自分も経験したことがあり、本当に苦しかった。
こうした問題を防ぐ能力を作る時間やリソースが足りないこともあるし、特定の問題が実際に起こり得ると考えもしないまま不意打ちを食らうこともある。
透明性も重要で、学ぶことも重要だが、時には補償も重要だ。Kagiは、サービスを利用できなかった時間に対して検索クレジットの提供を検討すべきだ。
とりわけ、リアルタイムの対応が不十分だったことを自ら認めているのだからなおさらだ。
有料サービスの障害は、「ユーザーが商品である」サービスの障害とは同じではない。
内部システムに対する可観測性がどの程度あるのか、多くを物語っている。
もっと早く気づくべきだったと言うのは簡単だが、適切なDatadogダッシュボードとSplunkクエリがあれば、はるかに早く明確になっていたはずだ。
今回の件を学習の機会にして、より良いモニタリングに投資してほしい。
今回の件は100%学習経験でしたが、可観測性についてもう少し文脈を補足できます。
Kagiは小さなチームで、この種のイベントに対応できる人員は実質3人、しかも3つのタイムゾーンに分散しています。私と中核開発者にとって、これはWebキャリアの初期段階での出来事で、すでにあらゆる経験を積んだシリコンバレーのベテランというわけではありません。
学ぶべきことが多いのは当然ですが、Kagiをゼロから作ってきたぶん、ここまで来た道のりとこれから向かう方向には誇りを持っています。
可観測性には、この6か月ほど前からより真剣に取り組み始めました。今ではダッシュボードも多く、会社のチャットチャンネルに直接入り、関係者を呼び出すアラートもあります。
DBの主担当者として、GCPのQuery Insightsは大いに役立っています。障害中にもモニタリングは鳴っていましたし、Query Insightsも「犯人」のクエリを示していましたが、世界中のあらゆるモニタリングがあっても、根本原因や最も効率的な緩和策を解釈する経験が不足していることはあります。
言い換えると、注意しないと私たちのシステムが見せているものに自分たちがガスライティングされないための知恵が、まだ足りません。振り返れば、GCP Query Insightsは100%正確で、アプリケーション領域のバグではなかったと言えます。
成長のおかげで、今ではチームをかなり拡大できるようになりましたし、以前にもSREコンサルティングを受けたことがあり、今後もフルタイムまたはパートタイムの支援をさらに受けて、改善を続けていくつもりです。
1人のユーザーがスクレイパーを走らせて、サービスを7時間もダウンさせたということ? 外から「予想しておくべきだった」と言うのは簡単だと分かっているが、テスト中に誰も「検索がものすごく大量に発生したらどうなる?」と聞かなかったというのは不思議だ。
https://news.ycombinator.com/item?id=39019936
要約すると、私たちは中核人員が非常に少ない若いチームで、全員が複数の役割を同時に担っています。まだ専任のSREチームはありません。
「検索がものすごく大量に発生したらどうなる?」について言えば、https://kagi.com/statsを見ると、すでに「多くの検索」は発生しており、1日40万件に近づいています。日常的には、システムは十分な余裕容量を持って動作しており、一部の自動スケーリング措置もあります。
問題は、一部のユーザーが病的なケースを悪用する細部にありました。私たちの経験不足のため、どのような自然なトラフィックや病的なトラフィックを事前に予測し、シミュレーションできたのか分かっていなかったのです。
同時検索ユーザー2万人の負荷をシミュレーションするのは、初期に試してみる価値のある実験に聞こえますし、似たこともしました。しかし今回の障害を見る限り、それでもこの問題は捕まえられなかったでしょう。
これまでに本番サービスにセキュリティスキャナーを走らせた人は10人ほどいて、その時に発生したトラフィックは今回の障害より多いものでした。
機能も作らなければならない状況で、この種の開発のバランスを取るのは非常に難しく、もっとやるべきだったのは確かです。別の投稿でも述べたように、近いうちにチームを拡大し、こうした取り組みに薄く広く分散しすぎないようにするつもりです。
後から振り返れば言えることはたくさんありますが、私たちがどのようにここまで来たのか、もう少し透明に伝わっていればと思います。
特に、誰かが初めてそのようなやり方で攻撃してきたのならなおさらだ。
比較すると、私が扱っているシステムはFAANG規模ではないが、リクエスト率の面ではKagiより確実に大きい。Kagiもすぐに学ぶだろうし、その間にこうした問題がさらに起きても、むしろある程度は構わないと思う。正しい方向に進んでいるというサインでもある。
Kagiの有料ユーザーとしてダウンタイムを経験して、Googleの信頼性をどれほど当然のものとして受け止めていたかに気づいた。
Googleは過去20年間、1回ほどを除けば私にとってダウンしたことがなかった。検索エンジンへのアクセスを失うのはかなり致命的だ。
Kagiは本当に気に入っていてお金を払っているが、利用2か月目にダウンタイムを経験したのはかなり気がかりだった。事後分析は好きだが、読む機会がないことを願う。
それでも今回の経験によって、Kagiがよりレジリエントで信頼できるサービスになることを期待している。
検索エンジンは、メールプロバイダーやISPのようにロックインのあるサービスではない。
Kagiが高速で、どこでもうまく動くことに確実に依存していた。
顧客先で新しいネットワーキングツールの概念実証を実行していた時のことを思い出す。実行して2分ほど経つと、顧客のネットワーク全体が落ちた。
私たちは隔離されたサンドボックス領域にいたので、私たちの製品がネットワーク全体の障害を引き起こす方法はなかったが、頭の中では「そんなはずないよな、そうだよな……そうだよな?!?!”と思っていた。
「あとでブロックしたアカウントと連絡が取れたところ、そのアカウントは当社の検索結果を自動スクレイピングするためにアカウントを使ったと主張しました。これは利用規約で認められていない行為です。」
可能なすべての入力 RPC/API/HTTP リクエスト、特に公開リクエストにはQPS 制限をかけるべき
オートコンプリート機能付きの検索機能があったのだが、速くタイピングするユーザーをサポートしようとして、そのエンドポイントのレート制限を意図的に外していた
ある日の午前6時ごろ、テネシー州の誰かが出勤して財布をキーボードの上に置き、その財布がキーを1つ押しっぱなしにして、各キー入力ごとに API を叩き始めた
当然、15分ほど経つと DB が非常に不安定になり、DB の遅延が大きくなりすぎて Web サーバーの1台がクラッシュした。連鎖障害が続き、本番クラスタ全体が落ちた
その日にレート制限が再び追加されたのは言うまでもない