re:Invent 2024: Stripeが実現する99.9995%の可用性 - 技術と文化
はじめに
海外の様々な講演を日本語記事に書き起こすことで、隠れた良質な情報をもっと身近なものに。そんなコンセプトで進める本企画で今回取り上げるプレゼンテーションはこちら!
📖 AWS re:Invent 2024 - How Stripe achieves five and a half 9s of availability (FSI321)
この動画では、Stripeが実現している99.9995%という驚異的な可用性について、その技術的な実装方法と組織文化の両面から解説しています。Cell-based architectureによる障害の分離、Gray failureへの対応、カオステストによる障害シミュレーション、そして大規模な負荷テストなど、具体的な技術的アプローチを詳しく説明しています。また、「最悪の日を毎日練習する」「機械にやらせるべき仕事を人にさせない」「extreme ownershipを実践する」という3つの重要な組織文化の要素についても言及し、特に100万以上のCPUを監視に専念させるなど、具体的な取り組みを紹介しています。
※ 画像をクリックすると、動画中の該当シーンに遷移します。
re:Invent 2024関連の書き起こし記事については、こちらのSpreadsheet に情報をまとめています。合わせてご確認ください!
本編
Stripeの99.9995%可用性:その重要性と課題
みなさん、ようこそ。本日は、Stripeが可用性99.9995%(Five and a half 9s)を実現する方法についてのセッションにご参加いただき、ありがとうございます。Stripeは、世界中の高度に規制された金融業界を支え、毎日、毎週、毎年、世界中で数兆ドルを移動させる経済の基盤として機能しています。そのため、システムが中断することなく機能し続けることは、単に重要というだけでなく、絶対に不可欠なのです。私はBalmohan Singh(Ballu)と申します。AWSのPrincipal Solutions Architectとして、過去4年以上にわたってStripeと密接に協力し、AWSで実行される耐障害性のあるアーキテクチャを構築し、99.9995%の可用性を達成するシステムの構築を支援してきました。本日は、Stripeの2人のリーダーからお話を伺います。Core InfrastructureのヘッドであるAbhisek Chatterjeeが、Stripeが継続的に高い信頼性を追求することの重要性について、そしてDavid Reaverが、高い信頼性を実現するための技術的な側面についてお話しします。
では、なぜ信頼性が重要なのでしょうか?なぜそれが問題になるのでしょうか?例を挙げてみましょう。信頼性の高いシステムは、障害が発生するまで気付かれません。たとえば、消防署に電話をかけるとき、つながることを期待し、消防車が来ることを期待します。しかし、それが常に実現するとは限りません。約100年前、ロンドンの街で家屋火災が発生しました。 近所の人が火災を目撃し、消防署に電話しようとしましたが、その時間帯は電話システムがダウンしていました。そのため、何度試みても消防署に連絡することができませんでした。消防署が対応できた時には、すでに手遅れでした。この事故は、その日に電話システムが機能していれば防げたはずでした。
この事故から私たちは何を学んだのでしょうか?信頼性とは、最高の状態にあるシステムや平均的な状態にあるシステムの特性ではありません。信頼性とは、最も必要とされる時、負荷がかかっている時、ストレスがかかっている時、つまり圧力がかかっている時の、最悪の状態にあるシステムの特性なのです。システムは最も弱い部分の性能以上にはなりえません。 この事故をきっかけに、ロンドンで世界初の緊急通報システムが誕生し、それが通信業界全体の信頼性向上キャンペーンの始まりとなりました。今日でも、世界中の緊急対応システムは99.9995%、つまりFive and a half 9sの可用性を目標としています。
Five and a half 9sとは、1ヶ月間で許容されるシステム停止時間がわずか13秒であることを意味します。これは1ヶ月の260万秒のうちのたった13秒であり、エラーの余地はほとんどありません。一般的な消費者向けアプリケーションでは3.5 9s(99.95%)を目標とし、これは1ヶ月あたり約22分の停止時間に相当します。しかし、Stripeはビジネス取引の生命線を支えているため、より高い基準が必要なのです。彼らは、耐障害性は一度の修正や単純なアップグレードではなく、継続的な取り組みであることを理解し、積極的に投資を行ってきました。
Stripeにおける信頼性の文化構築:Abhisek Chatterjeeの視点
それでは、Stripeが継続的に高い信頼性を追求することの重要性について、Abhisek Chatterjeeからお話しいただきます。ありがとう、Ballu。みなさん、こんにちは。私はAbhisekと申します。StripeのCore Infrastructure組織のヘッドを務めています。 Core Infrastructureは、Stripeのアプリケーションをグローバルな規模で確実かつ最適に実行するための、すべてのミッションクリティカルなTier Zeroインフラストラクチャシステムの拠点です。これには、複数のAWSリージョンで実行されている、コンピューティングインスタンス、ネットワーキング、データベース、そして災害復旧システムが含まれます。
また、私たちはカード決済処理のインフラストラクチャや、エッジインフラ、そしてバックエンドスタックも管理しています。これは非常に広範な領域をカバーしています。よく「どうやって高可用性システムを構築しているのか」と質問されますが、本当の問題は「なぜそれを行うのか」です。調査によると、決済が拒否された場合、ユーザーの40%がそのビジネスを離れてしまうことが分かっています。これがStripeのビジネスにどれほど大きな影響を与えるか、想像していただけると思います。
私たちのミッションは、インターネットのGDPを成長させ、グローバルな商取引を支えるインフラストラクチャを提供することです。つまり、単なるAPIコール以上に、リアルタイムでの価値交換を実現しているのです。ユーザーは私たちに非常に高い基準を期待しており、だからこそ一つ一つの取引が重要なのです。一秒一秒が大切で、皆様からの信頼が何より大切です。私たちは、ユーザーが毎日Stripeを使用する際に、素晴らしい体験を得られることを大切にしています。
可用性の欠如がもたらす影響は、金銭的な損失だけにとどまりません。大規模な障害は、組織の長期的な評判を危険にさらす可能性があります。Stripeでは、すべての取引を迅速に処理し、不正を防ぎ、確実に資金が適切な場所に届くようにする必要があります。そして、それを実現する際には完璧に見えるようにしなければなりません。なぜなら、結局のところ、Stripeはお客様のアプリケーションに統合されているからです。すべてが順調な時は誰も気付きませんが、問題が発生すると、ユーザーに長く残る影響を与えてしまいます。これこそが可用性の意味するところです。先ほどBalmohanが言及した緊急ホットラインシステムと同じレベルの真剣さで、私たちは一つ一つの取引に対応しています。文字通り、私たちにとっては生死に関わる問題なのです。
では、この背後にある秘訣は何でしょうか?確かにテクノロジーは最前線にありますが、信頼性は単なる指標ではなく、マインドセットなのです。今日のトークは2つのパートに分かれています。私の同僚であるDavidが信頼性と可用性の技術的な側面について説明し、その後、私がStripeにおける信頼性の文化をどのように構築し、その文化が私たちの成功をどのように支えているかについてお話しします。
高可用性システムの技術的戦略:David Reaverの解説
Stripeのprincipal software engineerであるDavid Reaverです。Stripeのエンジニアは、障害は避けられないものだということを深く理解しています。しかし、それはユーザーが障害を知る必要があるということではありません。これから、Stripeが5.5ナインの可用性を維持するために使用している技術的な戦略についてお話しします。このトークのこのパートの目的は、皆さんが高い信頼性を維持するために、自身のシステムや組織に持ち帰ることができるものを少なくとも1つ得ていただくことです。
何かが失敗した時、その特定の失敗がなぜ起きたのかを問うだけでは十分ではありません。将来的にこの種の失敗の影響範囲をどのように縮小できるかについても考える必要があります。言い換えれば、このシステムが別の理由で再び失敗した場合、影響を受けるユーザーをできるだけ少なくするにはどうすればよいか、という視点です。根本原因を共有しない類似の障害についても、その影響範囲を縮小する方法を考えることが重要です。
例えば、データベースのシャードで断続的な書き込み失敗が発生しているとします。多くの書き込みが行われており、その一部が失敗している状況です。もちろん、なぜこれが起きているのかを調査します。プライマリーノードのディスクの不具合かもしれませんし、CPUの競合かもしれません。あるいは、バグのあるクエリが原因かもしれません。原因が何であれ、その根本的な問題を修正することは重要ですが、私たちはさらに一歩踏み込んで考えます。マーフィーの法則は必ず何らかの形で私たちを襲うものだと想定します。データベースのシャードは常に何らかの理由でダウンする可能性があるのです。そこで、そのような障害の影響範囲をどのように縮小できるかを考えます。
この具体的なケースでは、よく考えた結果、最大のシャードのサイズを縮小する必要があるという結論に至るかもしれません。なぜなら、シャードがダウンした際の影響を受けるユーザー数を最小限に抑えられるからです。しかし実は、アーキテクチャ自体に影響範囲の縮小を組み込む方法があります。基本的なレベルでは、cell-based architectureがシステムにこれを組み込む一般的な方法です。Stripeでは多くのシステムでcell-based architectureを採用しています。
Cellとは、トラフィックを独立して処理するシステムの分離されたコピーです。この例では、cell routerがリクエストを受け取り、そのリクエストを個々のcellにマッピングします。リクエストがcellに入ると、そのcellは他のcellと通信することなく、リクエストをエンドツーエンドで処理します。これによりシステムの障害を分離することができます。例えば、cell oneがダウンしても、トラフィックの3分の1にしか影響が及びません。3分の1というのは決して良い数字ではありませんが、通常は何百ものcellがあります。ただし、1000のcellをスライドに収めるのは無理なので、この例で示しています。
Cell-based architectureは素晴らしいものですが、コストもあります。最大の課題は技術的な複雑さです。このトークを見ているスタートアップの方々が「Stripeのやり方を真似しよう」と考えているなら、まずはモノリスから始めることをお勧めします。モノリスの方がはるかに理解しやすいので、できる限りモノリスで進めてください。そして、システムとその特性を理解してから、将来的にcell-based architectureに拡張すればいいのです。また、cell-based architectureは通常より多くのハードウェアを必要とします。余裕を持たせることができないのです。例えば、すべてのサーバーが同じユーザーと通信するモノリスでは、あるユーザーが時々スパイクを起こしても、他の場所のハードウェアを使って対応できます。一方、cellの場合は、各cellが独自にトラフィックスパイクに対応できる必要があるため、平均的により多くのハードウェアを使用することになります。
Stripeにおいて、Cellは単にランダムな障害を防ぐだけでなく、変更管理にも関わっています。社内のソフトウェアやアーキテクチャの変更は、一度に1つのCellずつ展開します。もし何か問題があってバグが発生した場合でも、自動的にロールバックされ、他のCellには影響を与えることなく、1つのCellと一部のユーザーにのみ影響が限定されます。Cell-based architectureに関する重要なヒントですが、これは段階的に導入することができます。よくある誤解として、Cell-based architectureではシステム全体で同じCellを使用する必要があるというものがありますが、それは違います。StripeにはCell routerが1つあり、各CellがStripeのコピーであるというような誤解がありますが、実際はそうではありません。私たちはストレージサブシステムやAPIサービスメジャーなど、それぞれの部分で独立してCell-based architectureを使用しています。つまり、アーキテクチャ全体を一度に変更する必要はないのです。
システムが故障する場合、完全な障害は実は最も良い結果の1つとなることがよくあります。例えば、ノードの電源が抜かれて電力が供給されない場合、クライアントはヘルスチェックの失敗を確認することで比較的簡単に検知できます。数秒後には、そのノードが正常でないと判断して他のノードに切り替えることができます。より検知と対応が難しいのが、完全な障害よりもはるかに微妙な、部分的または断続的な問題として定義されるgray failuresです。gray failuresの例としては、故障しかけているストレージボリュームによる高いディスクレイテンシー、下流のルーターがパケットをドロップすることによる高いネットワークレイテンシー、あるいはKubernetesのような最新のアーキテクチャでよく見られる、同じノード上の別のpodがCPUを使い切ってしまうnoisy neighbor問題などがあります。
gray failureのシナリオでは、障害が散発的で明確でないため、クライアントがノードの異常を検知するのが難しくなります。ヘルスチェックが正常と異常の間で揺れ動き、クライアントがステータスを判断できない状況が発生することがあります。また、ヘルスチェックは合格しているものの、gray failureが非常に低いパフォーマンスとして現れ、単一のクライアントでは検知が困難な場合もあります。Stripeでは、異常検知を使用して、他のノードと比較して異常な動作をしているノードを自動的に発見します。この際、リクエストレイテンシーは有効な指標となります。例えば、ステートレスなAPIノードのフリートがある場合、すべてのリクエストレイテンシーの指標を中央システムに送信します。あるノードが他のノードと比較して著しく高いレイテンシーを示している場合、そのノードをサービスメッシュから除外します。調査のためにノードは保持しますが、トラフィックの処理は行いません。
異常検知を始める際の2つの重要なヒント:まず、開始時は一度に1つのノードだけがgray failureを経験すると仮定します。新しい異常検知システムにフリートの大部分を制御させると、問題が発生する可能性があります。例えば、あるAvailability Zoneでレイテンシーが上昇している場合、新しいシステムがフリートの3分の1を停止させてしまうことは避けたいものです。また、ノードに異常検知を自己報告させないようにします。すべてのノードに異常を検知するエージェントをインストールすれば良いと考えないでください。外部システムによる検知が必要です。潜在的に障害が発生しているノードは非常に信頼性が低く、その報告を信用することはできません。メトリクスを監視して、ノードが正常かどうかを判断する外部システムを持つ方がはるかに堅牢です。
今年、Stripeはデータベースストレージシステムの大部分をAmazon EBSに移行しました。これは、アーキテクチャを簡素化し、障害が発生したノードの再構築にかかる時間を短縮することで信頼性を向上させるためでした。移行中、レガシーシステムの一部に非常に非効率なクエリがあり、実行時にデータベースのキャッシュを破壊してしまうことでレイテンシーの問題が発生しました。ただし、ほとんどの場合、これはEBSの問題ではないことが分かっていました。私たちはIOPS制限やスループット制限の範囲内で運用しており、使用している各ボリュームから期待通りのパフォーマンスを得ていました。
レイテンシーの問題が発生した際、私たちは標準的なデバッグツールを試してみました。もちろん、まずはクエリを確認します。なぜそんなに多くのデータを取得しているのか? Linuxファイルシステムを見て、キャッシュはどうなっているのか?ブロックレイヤーを確認して、レイテンシーはどうなっているのか?しかし、ほとんどの場合、さらに一歩踏み込んで、EBSが実際に何を見ているのかを確認したいと考えていました。この点で、EBSは私たちのビジネスにとって素晴らしいパートナーでした。この課題に直面したとき、彼らは私たちと緊密に協力して問題を完全に理解し、その結果、これらのギャップに対応するために特別に設計された新機能を開発し、展開してくれました。
例えば、最近彼らはIOPS制限やスループット制限に達しているかどうかを示すCloudWatchメトリクスをリリースしました。また、ボリュームが潜在的に故障しているかどうかを知らせるCloudWatchメトリクスも提供しています。先月からは、EBSがLinuxカーネルのEBSブロックデバイスを通じて詳細なパフォーマンスメトリクスを公開するようになりました。これにより、EBSが見ている非常に細かい詳細を確認できるようになりました。これらの新しいメトリクスとシステムにより、EBSボリュームがスロットリングされているのか、不健全な状態なのか、あるいはスタックの他の場所に問題があるのかを、自信を持って素早く診断できるようになりました。そのため、クエリの問題が発生したときに、多くの時間とエネルギーを節約できるようになりました。
Stripeのload testingとcontinuous delivery:信頼性向上への取り組み
Gray failureは厄介で、異常検知は本当にクールですが、Gray failureから身を守る他の方法はあるのでしょうか? システムは似たような方法で故障します。あるサービスで学んだ教訓を、別のサービスで再び学び直したくはありません。これらのGray failureを予防、検出、修復する方法を事前に見つけ出したいものです。この答えの1つとして、Stripeではカオステストとフォールトインジェクションを使用しています。カオステストは大きなテーマですが、このコンテキストでは、システムに意図的に障害を注入し、自動修復が機能することを確認することで、ノード障害やGray failureをシミュレートすることについて話しています。
フォールトインジェクションの例をいくつか挙げると:IPテーブルを使ってパケットをドロップすることで意図的にネットワークレイテンシーを増加させたり、ノードのネットワークを完全に遮断したりできます。同じノード上の問題のあるポッドからのCPU競合をシミュレートするために、CPUで無限ループを実行することもできます。また、人為的なディスクレイテンシーを導入したり、一部の書き込みをドロップしたりして、誤動作しているディスクをシミュレートすることもできます。Stripeでは、システム全体でこれらの障害を注入し、人間の介入なしにこれらのシステムが自動的に修復されることを確認するためのカオステストフレームワークを運用しています。
私たちは各チームに、自分たちのサービスでこれらのテストを有効にすることを推奨しています。もちろん、新しいサービスの初日からすべての障害を有効にして、それに対処することを期待するわけではありません。そうすれば単にページングが発生し、誰もが不幸になるだけです。サービスが成熟するにつれて、より多くの障害を有効にしていき、QA環境では常にシステムを稼働させています。そこで最も多くの問題が見つかります。また、本番環境でも非常に管理された状況で有効にすることができ、本番環境でもこれらの障害に対処できるという確信を持つことができます。
多くのシステムは非常にスパイキーな負荷を持っています。つまり、ほとんどの時間はピークトラフィックからはるかに低い状態で動作し、時々トラフィックが急激に増加するということです。ユーザーが大量のトラフィックを発生させた時に、真夜中に呼び出されることがないよう、私たちは皆、十分な余裕を持ったシステムを設計したいと考えています。しかし、そこにはコストがかかります。まず、この余裕を持たせるためのハードウェアは高価です。10倍の余裕が必要なら、10倍のハードウェアが必要かもしれません。また、システムを特定のポイントを超えてスケールさせることが技術的に困難な場合もあり、その解決策を見つけるために高給なエンジニアを雇う必要があります。
トラフィックスパイクは、特にStripeにとって単なる厄介事ではありません。トラフィックスパイクは、ユーザーにとって最も重要な時期に発生します。大規模なセール、大きなイベント、あるいは時々必要となる他のシステムからの移行などが原因かもしれません。例えば、先週はBlack FridayとCyber Mondayがありました。これは多くのStripeの顧客にとって年間で最大のイベントです。実際、Stripeを利用する35,000の加盟店が先週、過去最高の売上を記録しました。また、Stripeにとって、BFCMは来年の新しい基準となります。これは単なるスパイクではなく、これからの新しい標準になるのです。
これらの重要なイベント中の障害は、特に痛みが大きく、目立ちます。そのため、私たちは絶対に失敗できませんし、過去最高の負荷を失敗の言い訳にすることもできません。なぜなら、そうすることは、ユーザーが良い日を迎えた時に私たちが困る、それを好ましく思っていないと伝えることになるからです。これは当然、そうあってはいけません。ですから、過去最高のトラフィックをユーザーのトラフィックで経験することがないようにしましょう。 ピークユーザートラフィックの何倍もの負荷をかけて、事前にload testを行う必要があります。
Stripeでは、ピークユーザートラフィックの何倍もの大規模な合成トラフィックを使用して、システムに負荷をかけています。これらのload testによって、まず最も弱いリンクを発見することができます。load testを行うと、最終的に何かが破綻するので、その特定のサブシステムを見つけ、最適化し、次回に向けてより良くする方法を見つける必要があります。また、load testは障害が発生する前にリソースの枯渇を特定するのにも役立ちます。例えば、load test中は完全に問題ないシステムでも、CPU使用率が50%で遅延が増加し始め、80%でエラーが発生し始めるかもしれません。システムがload testを生き残ったとしても、そのシステムの運用者は、CPU使用率50%は注意すべきポイント、80%は特に警戒すべきポイントだと認識できます。
最後に、各コンポーネントが負荷に対してどの程度スケールするかを確認し、次のホットスポットを特定できます。これらに驚かされないようにしましょう。私たちは、個々のシステムとAPIのエンドツーエンドフローの両方で、常にload testを実施しています。
私たちは負荷テストを行い、システムの限界を見つけ、それを最適化し、このプロセスを継続的に繰り返しています。これにより、常に実際のユーザートラフィックを大きく上回る余裕を持つことができます。一つの重要なポイントは、負荷テストを使用して、低負荷時でもシステムの効率を改善できる機会を見つけられることです。例えば、パフォーマンスの悪いデータベースクエリを持つシステムがあるとします。低負荷時には、レイテンシーは高めかもしれませんが、SLO内に収まっているため気づきにくいものです。しかし負荷テスト中にそのクエリがサービスをダウンさせる可能性があり、それを発見して最適化することができます。そして負荷テストが終わった後、レイテンシーが30%も改善されていることに気づくかもしれません。これは負荷テストを行う上での素晴らしい副次的効果といえます。
APIリトライと異常検知:Stripeのユーザー中心アプローチ
Stripeは、数千のサービスにわたって1日に数万回の変更をデプロイしています。これらのデプロイの大部分は、gitコミットのマージをトリガーとして自動的に行われ、人間が介在することはありません。Continuous deliveryは従来、市場投入までの時間短縮や開発者の生産性向上の手段として注目されてきました。つまり、手動での面倒なデプロイ作業に開発者の時間を浪費しないようにするということです。これは素晴らしい理由ですが、私はContinuous deliveryが信頼性においてより重要な役割を果たしていると考えています。まず、自動化されたデプロイは一貫して再現可能で、人的ミスの可能性を排除します。例えば、デプロイUIで誤ったボタンを押したり、runbookのbashコマンドでスペースを抜かしたり余分な引用符を入れたりしてファイルシステムを破壊してしまうような自己inflictedなインシデントが減ります。コンピューターがデプロイを実行する場合、そのようなことは起こりません。
第二に、デプロイの頻度が高いほど、1回のデプロイあたりの変更が少なくなります。理想的なケースでは、1回のデプロイに1つのgitコミットだけがある場合、デプロイ中に問題が発生したら、そのgitコミットが原因であることがほぼ確実です。差分がすぐに確認できるため、問題の根本原因が銀の皿に載せられて出てくるようなものです。月に1回しかデプロイしない場合は、何が起こったのかを突き止めるために多くの変更を調査する必要があります。最後に、Continuous deploymentのもう一つの利点は、変更が常態化することで、フリート全体の変更にかかるコストを最小限に抑えられることです。例えば、世界的な問題が発生してフリート全体でセキュリティアップデートが必要になったり、monorepoのコアライブラリで大規模な移行が必要になったりする場合があります。Continuous deliveryが実装されていれば、変更を加えてマージし、デプロイされることを確信して安心して眠ることができます。問題が発生した場合も、移行のために数千のサービスを手動で監視する必要はなく、デプロイのヘルスチェックで検出できます。
APIリクエストが失敗した場合、リトライ可能なAPIに対しては単純にリトライするという、明白でありながらしばしば忘れられがちな解決策があります。リクエストが失敗してリトライし、成功した場合、少し余分なレイテンシーが発生しますが、これは最善ではないものの、失敗よりはましです。特にStripeの決済APIなどの一部のAPIでは、クライアントレベルでリトライを実装できるため、ユーザーは失敗を検知した場合に独自のリトライコードを書くことができます。また、SDKに組み込むこともできます。AWSのSDKは良い例で、数行追加するだけで優れた指数バックオフリトライが実現できます。
Stripeのユーザーは自由にこれを実装できますが、私たちはStripe APIとの統合の容易さを非常に重視しているため、信頼性の恩恵を受けるためにユーザーに負担をかけたくありません。私たちはユーザーに代わってリトライを行うことを好みます。通常、ユーザーからのリクエストをできるだけユーザーの近くで受け付け、適切な地理的リージョンに処理を送信し、そのリージョン内でリトライを行います。これにはいくつかの利点があります。まず、ユーザーの観点からは、APIの不安定さやエラーが少なくなります。問題が発生した場合に時々余分なレイテンシーを感じるかもしれませんが、エラーは減少します。また、リトライは私たちのネットワーク内で行われるため、リトライによる全体的なレイテンシーが少なくなります。例えば、ニューヨークのユーザーからリクエストを受け取った場合、そのリクエストをオレゴンで処理するかもしれません。オレゴンで問題が発生した場合、リクエストをニューヨークに返す前にオレゴンでリトライします。これに対して、ニューヨークからオレゴンに行き、失敗し、ニューヨークに戻ってリトライし、うまくいくことを期待する方法では、レイテンシーが大きくなってしまいます。最後に、ユーザーに代わってリトライを行う場合、私たちのアーキテクチャに関する内部知識を活用してより効率的にリトライを実行できます。例えば、cell-based architectureにおいて、同じセルで何度もリトライするのではなく、データベースの異なるセルでリトライしたり、オレゴンで問題が発生している場合はオハイオでリトライしたりすることができます。
retryに関するプロのヒントをいくつかご紹介します。まず、APIがretryを適切に処理できるようにすることが重要です。単に「失敗したからもう一度試す」というループを作るだけではダメです。APIがidempotentでない場合、問題が発生する可能性があるため、retryを考慮してAPIをidempotencyを意識して設計する必要があります。次に、スタックの多くの層でretryを実装することは避けるべきです。常にユーザーに最も近い外側の層でretryを行うことを推奨します。例えば、データベース層、バックエンドAPI層、フロントエンド層のすべてでretryを実装すると、1つのデータベースの問題が、ここで10回、ここで100回、ここで1000回というようにrequest amplificationを引き起こしてしまいます。これは避けるべき状況です。
特にサービスを始めたばかりの段階では、最も外側の層でのみretryを行う方がより堅牢です。retryは、非常に短時間のダウンタイムを乗り切るための有用なツールです。例えば、データベースシステムでは、プライマリのフェイルオーバーやリーダー選出が発生することがあります。これは数秒しか続きませんが、その間は書き込みができません。そこで、内部のデータベースSDKにretryを設定しているので、ユーザーは気付きません。レイテンシーが少し増えるだけで、フェイルオーバーのたびに大量のエラーが発生することはありません。
時として、私たちの99.9995%という全体的な信頼性は、ユーザー間で均等に分配されているわけではありません。例えば、サブシステムの1つのcellがダウンしていて、ごく少数のユーザーが影響を受けている場合や、feature flagの設定ミスなど、私たちの側のバグでそのfeature flagを有効にしているユーザーが影響を受けることがあります。Stripeが99.9995%の可用性を目指すというのはマーケティング的には素晴らしく、全体としてもその目標を達成していますが、実際にユーザーが気にするのは、自分たちから見たトラフィックの状況です。他のStripeのシステムが正常でも、自分たちのトラフィックがダウンしていれば、全体の99.9995%なんて関係ないのです。
Stripeには、ユーザーが特に問題を抱えている場合に事前に警告を出すシステムがたくさんあります。面白い取り組みの1つは、リクエストが成功したかどうかを判断する際に、標準的なHTTPレスポンスコードだけに頼らないことです。従来的には、HTTPレスポンス200は良好、500はエラーで私たちの責任、400はエラーでユーザーの責任とされていました。しかし、これは単純すぎます。400レスポンスの中にも、日常的で想定内の正当なものがたくさんあります。400レスポンスの急増を監視しているのは、ユーザーのコードや私たちのコードにバグがある可能性、あるいはfeature flagの設定ミスがある可能性があるからです。この異常検知は、特定のAPI、特定の決済方法、さらにはレイテンシーにまで拡張しています。
私たちはよくユーザーと協力して、彼らのインテグレーションを改善します。例えば、「1回で済むAPIコールを10回も呼び出しているような非効率な部分が見られます」といった指摘をすることがあります。これは双方にとって大きなメリットがあります。ユーザーのシステムがより効率的になり、レイテンシーが減少し、コストも下がり、私たちにとっても信頼性が向上します。なぜなら、そのユーザーからの負荷が減り、Stripeの他のユーザーのためにハードウェアリソースを確保できるからです。
Stripeの信頼性文化:3つの重要プラクティス
それでは、技術的な内容を超えて、Stripeがどのように信頼性の文化を構築しているかについて、Abhishekにバトンを渡したいと思います。 信頼性とは単なる指標ではなく、マインドセットです。本日は、Stripeで信頼性を重視する文化を築くことを可能にした3つのプラクティスについてお話しします。最初のプラクティスは、「最悪の日を毎日練習する」というものです。私たちは、すべてのシステムに対して徹底的な好奇心を持っています。様々なシナリオを使ってシステムを限界まで追い込むのは、システムがどのように故障するのかを知りたいからです。システムの限界と故障モードを理解すれば、それらを設計思想に組み込み、システムの信頼性を段階的に向上させることができます。
障害やユーザーへの影響を防ぐための重要なポイントは、障害は避けられないという事実を受け入れることです。Stripeでは、そのような障害に耐えられるシステムを最初から設計しています。システムがいつどのように故障するかは予測できないため、レジリエンシーをシステムの重要な要素として重視しています。先ほど説明した技術を、世界クラスの可観測性と組み合わせることで、ユーザーに影響が出る前に劣化や障害を早期に検出できます。私たちは自動化とセルフヒーリングにも投資しており、システムは様々なシナリオで劣化を検出し、人手を介さずに自動的に回復することができます。
サーキットブレーカーについて考えてみてください。 私たちはシステムを設計する際に、このサーキットブレーカーの考え方を取り入れています。障害が発生した時、システムは自身を素早く適応させることができます。
この適応には、障害の分離、完全な障害を部分的な障害に変換、そしてユーザーへの影響を防ぐための自動復旧が含まれます。 David Reaverが述べたように、私たちは負荷テスト、合成トラフィック、カオスエンジニアリング、障害テストなど、様々な手法を使って一日中システムにストレスを与えています。これを行う理由は、障害を防ぐために自分たちの運命をコントロールしたいからです。私たちは健全な偏執狂的な考えを持って悲観的に考えています。障害は避けられないことを完全に受け入れていますが、私たちは自分たちのタイムラインで障害を起こし、それらの障害がユーザーに影響を与えないようにしたいのです。
Stripeで最悪の日の練習を実際にどのように行っているのでしょうか?これは様々なシミュレーションやゲームデーを通じて行っています。例えば、システムがその障害にどのように対処し、どのように回復するかを確認するため、定期的にアベイラビリティーゾーンへのネットワークを遮断します。もう一つの例として、最悪の日が来るのを待つのではなく、災害復旧機能を実践するために、常にリージョン間でトラフィックを移動させています。
これは2番目のプラクティスである「機械にやらせるべき仕事を人にさせてはいけない」についてです。先ほどBalmohan Singhが述べたように、Stripeは一貫して99.9995%(five and a half 9s)の可用性を達成しています。これは月にわずか13秒というとても小さなエラー予算しかないことを意味します。ちょっと考えてみましょう。人間は早くても分単位での反応しかできません。そのため、このエラー予算内でインシデントを検知して対応するには人間には頼れないのです。一方、機械はミリ秒単位で反応できます。エラー予算内で障害を検知して対応できるのは、機械だけなのです。
では、機械がミリ秒単位で反応するために何が必要なのでしょうか?私たちは100万以上のCPUをシステムの監視と観察に専念させています。これらのシステムは毎日何百万ものメトリクスとヘルスステータスを発信し、毎日何十億もの自動テストを実行しています。私たちはこれらの情報をリアルタイムで組み合わせ、フリート全体での異常や性能低下を検知しようとしています。障害や性能低下を検知した場合、自動化によって非常に素早くその状況から回復できるようにしています。
もう一つ例を挙げましょう。Stripeは独自の社内マネージドデータベーススタックを持っています。このデータベースは、ペタバイト規模の金融データ、1万種類以上の異なるクエリタイプ、5,000以上のコレクション、2,000以上のシャードを持つ、おそらく世界最大規模のデータベースです。Stripeのようなビジネスでは、データベースは常に稼働し続けなければなりません。障害は許されません。five and a half 9sのアップタイムを維持するために、データを処理し、不正を防止し、データ損失がないことを保証する必要があります。
私たちはゼロダウンタイムのデータ移動ソリューションを一から設計・構築しました。このソリューションにより、Stripeはトラフィックが急増した際にリアルタイムでシャードを分割し、トラフィックが少ない時にはそれらのシャードを再統合することができます。その際も、基盤となるインスタンスの耐久性と健全性を維持しています。私たちはデータベースに独自の可観測性を実装しました。例えば、リーダー選出の頻度、データベースウォッチドッグの記録、データベース全体の状態変化、プライマリノードとセカンダリノードの健全性などを監視しています。これらの高精度なシグナルをリアルタイムで使用して判断を下しています。例えば、プライマリノードやセカンダリノードが期待通りに動作していない、あるいはパフォーマンスに影響を与えるほど遅くなっていることを検知した場合、自動的にそのノードをシャットダウンし、バックグラウンドで新しいノードを再構築し、クォーラムに戻します。誰も気付きませんし、最も重要なのは、クリティカルパスに人間が関与していないことです。私たちのデータベースは決してダウンしません。
人間は多層防御を提供する機械を構築し、それらの改善に執着します。Stripeでは、ユーザーがどのような統合方法を選んでいても、ユーザーが体感する品質を監視しています。単なるAPIのレスポンス率だけでなく、私たちは成功の定義を「体験品質メトリクス」と呼ぶものに拡大しています。ユーザーが私たちのサービスをどのように体感しているかを360度の視点で捉え、すべてのプロダクト領域で継続的な改善サイクルを推進しています。まとめると、測定、検知、対応の責任はロボットに任せ、私たち人間はユーザー体験の改善に注力しているのです。
今日は、システムの障害とそれに対する数秒単位での対応について話しましたが、ここからは最も重要なことについて説明します:どのようにして信頼性重視のマインドセットを構築するかということです。素晴らしいアイデアはあくまでもアイデアに過ぎず、それを実現するには強い意志が必要です。 先ほどBalmohanが紹介した、1937年に構築された英国の911緊急通報システムの話を覚えていますか。このシステムが米国の911システムとして導入されるまでに20年かかり、さらに米国全土に普及するまでに30年もの歳月を要しました。
なぜそれほどの時間がかかったのか、疑問に思われるかもしれません。私は、それがextreme ownershipの欠如によるものだと考えています。これは、Stripeで実践している3番目の取り組みです。 信頼性は委員会が所有するものではありません。それはエンジニアとリーダーがそのようなシステムを構築する際に注ぎ込む情熱とプライドから生まれるものであり、品質にはリーダーシップが不可欠なのです。 Stripeでは、アカウンタビリティは非難や処罰の同義語ではなく、オーナーシップを与えることを意味します。ユーザーが必要とする時には、Stripeの全レベルの従業員がページングを受け、インシデントの解決は中央のSREチームやサイロ化された組織ではなく、そのサービスを所有し構築しているチームによって行われます。
意志の力、規律、そして忍耐といった言葉は素晴らしく聞こえますが、どのようにしてこれらを組織に定着させることができるのでしょうか?私たちは、オーナーシップを継続的に測定し監査することでこれを実現しています。Stripeでは毎週、シニアエグゼクティブレビューを実施しており、ルーレットを回してエグゼクティブを選びます。賞品を与えるのではなく、彼らが所有・管理するサービスについての運用上の学びを大きなフォーラムで共有してもらいます。Stripeの各エグゼクティブには、自分が所有・管理するサービスについて深く精通していることが求められます。サービス、インシデント、根本原因分析、SLO違反、運用上のミスについて流暢に議論し、そのサービスを改善してユーザーに世界クラスの体験を提供するために何をするのかを説明できることが期待されています。
リーダーは、自分の担当部分だけでなく、カスタマーエクスペリエンスの全体像に対して責任を負います。運用上のミスが継続的に発生する傾向が見られる場合、私たちはその責任をさらに強化します。しかしStripeは厳しさだけではありません。私たちは良い面も重視しており、毎週、運用上の成功を認識し、報奨を与え、祝福しています。この責任の仕組みは、Stripeが信頼性を文化の柱として拡大し、すべてのリーダーがお客様に対してエンドツーエンドの価値を提供できるようにする上で役立ってきました。
まとめと今後の展望:AWSとのパートナーシップ
まとめとして、 3つの重要なポイントを持ち帰っていただきたいと思います:最悪の日を毎日練習すること。機械にやらせるべき仕事を人にさせないこと - 「人間が設計し、機械が守る」ということを忘れないでください。そして3つ目は、extreme ownershipを実践すること。これは言葉以上のものです - 信頼性と可用性を本当に優先する必要があります。
組織とシステム設計の一環として、これらは非常に重要な考慮事項です。信頼性を重視し、顧客のために一歩踏み込んで支援を行う意思のあるビルダーやリーダーを認識し、評価することが必要です。まず自分自身から始めることを忘れないでください - 自分に責任を持ち、高い基準を設定し、自分が管理または携わるシステムについて深い知識を維持してこそ、周囲の人々にも責任を持たせることができます。これは必ずしも快適なプロセスではありませんが、この不快感こそが適切な文化を育むのです。
最後に、StripeとAWSの素晴らしいパートナーシップについて触れることは非常に重要です。これは私たちの信頼性と可用性の目標を推進する能力の基盤となる部分です。私たちはAWSと多くの共通点を持っています - 私たちは心からのビルダーであり、顧客に執着し、スタックのあらゆる層での運用の卓越性を深く重視しています。また、AWS内で私たちの代弁者となってくれる強力なAWSチームがいることは幸運です。私たちは定期的にEBS、EC2、ネットワーキングなどの重要なチームと技術的なアイデアやフィードバックを共有しています。私個人としても、AWS Customer Advisory Boardのメンバーとして、AWSのリーダーシップチームと直接フィードバックやアイデアを共有できることを非常に光栄に思っています。私たちはAWSとのパートナーシップに大変期待しており、共に勝利し、共に構築していきたいと考えています。
以上で私の発表を終わります。信頼性システムを構築する理由とその方法について、貴重な洞察と専門的なアドバイスを共有してくださったAbhishekとDavidに感謝いたします。セッションを終える前に、皆様のフィードバックは今後のre:Inventセッションの形成に非常に重要ですので、モバイルアプリでのアンケートにご協力いただければ幸いです。ご質問がございましたら、廊下で回答させていただきます。これをもちまして、ご参加いただいた皆様に、登壇者一同より御礼申し上げます。ありがとうございました。
※ こちらの記事は Amazon Bedrock を利用することで全て自動で作成しています。
※ 生成AI記事によるインターネット汚染の懸念を踏まえ、本記事ではセッション動画を情報量をほぼ変化させずに文字と画像に変換することで、できるだけオリジナルコンテンツそのものの価値を維持しつつ、多言語でのAccessibilityやGooglabilityを高められればと考えています。
Discussion