defrag.works

海外の技術ブログで気になったものを日本語訳してメモしています

高可用性の簡単な歴史

www.cockroachlabs.com

以前、「営業時間」が記載されているウェブサイトを見たことがあるのですが、実店舗の電気が点いているときだけ「営業」しているのです。コンピュータは毎日一日中稼働できるのに、なぜそうでないのかと戸惑い、少し不満に思いました。私はインターネットの驚異的な稼働率保証に慣れきっていました。

しかし、インターネットが普及する以前は24時間365日の高可用性は「あるもの」ではなかったのです。可用性は望ましいものでしたが、根本的な権利として感じるものではありませんでした。コンピュータは必要なときにだけ使うものであり、依頼が来るのをぼんやりと待っているようなものではなかったのです。インターネットが普及するにつれ、それまで珍しかった現地時間午前3時の依頼が世界中に広がるビジネスアワーになり、コンピュータがその依頼に応えられるかどうかが重要になりました。

しかし、多くのシステムではリクエストに対応するコンピュータが1台しかなく、単一障害点となっていました。このような事態を回避するためには、ニーズを満たすことができる複数のコンピュータに負荷を分散させる必要がありました。しかし、分散計算にはそのよく知られた長所もあれば、特に同期やシステム内の部分的な故障を許容する(耐障害性)という鋭い側面もあります。エンジニアの各世代は時代のニーズに合わせてこれらの解決策を反復してきました。

データベースがどのように流通するようになったかは、コンピュータサイエンスの他の分野に比べて発展がずっと遅れている難問であるため、特に関心が高いです。確かにソフトウェアは何らかの分散計算の結果をローカルデータベースで追跡していましたが、データベースの状態自体は1台のマシンに保持されていました。なぜでしょうか?マシン間で状態を複製するのは難しいからです。

この記事では分散データベースが歴史的にどのように耐障害性を扱ってきたか、また、高いレベルでの高可用性とはどのようなものかについて見ていきます。また、さまざまなタイプの高可用性システムを紹介し、ダウンタイムに脆弱なアーキテクチャの運用コスト(および財務コスト)についても言及します。

耐障害性 vs 高可用性

高可用性の多彩な歴史に触れる前に、しばしば同義語として扱われるこの2つの用語の違いを明確にしておきたいと思います。この2つの用語は非常に密接に関連していますが、同じではありません。

耐障害性はサービスの中断をゼロにすることを意味します。どこかで障害が発生した場合、システムは即座にバックアップソリューションに切り替わり、サービスは中断することなく継続されます。一方、高可用性はサービスが高可用性であることを意味しますが、常に利用できるわけではありません。システムは高可用性であっても耐障害でない場合もあります。私は一般的に高可用性は耐障害性の一側面だと考えています。つまり、ある種の「障害」(可用性)に対処するものであり、他の側面については必ずしも言及しません。

この例はやや作為的なものですが、基本的に誰もがストリーミングコンテンツを視聴するため、視聴者が特定のビデオを視聴できるかどうかを決定するデジタル著作権管理サービスを考えてみましょう。このサービスは常にクエリを提供し返すという意味で、高可用性になるように構成することができます。しかし、特定のバックエンドデータを正しく処理できず、エラーを返したりすべてのリクエストを拒否したりする状態になることがあります。この場合、可用性は高い(到達可能で答えを返している)のですが、システム内の何かが原因で誤動作しているため、耐障害ではありません。

この例の注意点は「バグ」と耐障害性は紙一重であるということです。 しかし、耐障害性の考え方はシステムが予期せぬ出来事を優雅に処理し、優れたユーザー体験を提供し続けることができることです。 (この例に興味を持たれた方はエンジニアがインスタンス障害に強いサービスを実装するために、Netflixのカオスモンキーが本番でインスタンスをランダムに終了させる方法をご覧になることをお勧めします。)

では、高可用性データベースの例を見ていきましょう。

高可用性データベースの種類は?

高可用性データベースは一般的に2つのカテゴリーに分けられますが、最近では第3のカテゴリーが一般的になってきています:

  1. アクティブ・パッシブ・データベース: リクエストを処理するアクティブなノードと災害時にすぐに使えるホットスペアを持つデータベース
  2. アクティブ・アクティブ・データベース: データベースがデータをシャードし、データベースへの書き込みを実行するアクティブノードを持つ場合
  3. マルチ・アクティブ・データベース: データベースには少なくとも3つのアクティブノードがあり、各ノードはクラスタ内のあらゆるデータに対して競合を発生させることなく読み取りと書き込みを行うことができます

アクティブ・パッシブ・アベイラビリティとは?

アクティブ・パッシブ・アベイラビリティとはデータベースがリクエストを処理するアクティブなノードを持ち、災害時に備えてホットスペアを持つことを意味します。アクティブ・パッシブ・アベイラビリティ・モデルは1つのノードがすべてのリクエストを受け取り、それをフォロワーにレプリケートするという2つのノードの概念で動作します。

昔はデータベースは1台のマシンで動作していました。ノードが1つしかなく、そのノードがすべての読み込みと書き込みを処理していました。部分的な障害というものは存在せず、データベースは稼働しているか停止しているかのどちらかでした。

1つはコンピュータが24時間アクセスされるため、ダウンタイムがユーザーに直接影響する可能性が高いこと、もう1つはコンピュータが常に要求される状態に置かれるため、故障する可能性が高くなることです。この問題を解決するにはリクエストを処理できるコンピュータを複数台用意することが必要であり、ここから分散データベースの物語が始まります。

シングルノードの世界では1つのノードで読み書きを行い、その状態をセカンダリのパッシブなマシンに同期させるというのが最も自然な解決策でした。こうして生まれたのがアクティブ・パッシブ・レプリケーションです。

アクティブ・パッシブは最新のバックアップによる高可用性を実現するための初期のステップでした。アクティブノードに障害が発生した場合、パッシブノードへのトラフィック誘導を開始することでアクティブノードに昇格させることができました。できる限りダウンしたサーバーを新しいパッシブ・マシンで置き換えます(そして、その間にアクティブ・マシンが故障しないことを祈ります)。

当初、アクティブノードからパッシブノードへのレプリケーションは同期的な手順でした。つまり、パッシブノードがそれを承認するまで変換はコミットされませんでした。しかし、パッシブノードがダウンした場合にどうするかは不明でした。確かにバックアップシステムが利用できない場合にシステム全体がダウンするのは理にかなっていませんが、同期レプリケーションではそれが起きてしまうのです。

さらに可用性を高めるためにデータを非同期で複製することも可能です。アーキテクチャは同じですが、データベースの可用性に影響を与えることなく、アクティブノードとパッシブノードのいずれかがダウンしても対応できるようになっています。

非同期のアクティブ・パッシブは新たな一歩を踏み出しましたが、まだ大きな欠点がありました:

  • アクティブ・ノードが停止するとパッシブ・ノードにまだレプリケートされていないデータが失われる可能性があります(クライアントにはデータが完全にコミットされていると信じられていたにもかかわらず)。
  • トラフィックを処理するために1台のマシンに依存することで、1台のマシンの最大利用可能リソースに縛られることになります。

99.999%の高可用性を求める: 多くのマシンへの拡張

インターネットが普及するにつれてビジネスのニーズは規模と複雑さを増していきました。データベースにとっては1つのノードで処理しきれないほどのトラフィックを処理する能力が必要であり、「常時接続」の高可用性を提供することが必須となりました。

多くのエンジニアが他の分散技術に携わった経験を持つようになった今、データベースがシングルノードのアクティブ・パッシブセットアップを越えて、多くのマシンにデータベースを分散できることは明らかでした。

シャーディング

そこで、アクティブ・パッシブレプリケーションをよりスケーラブルなものにするために、シャーディングを開発しました。

この方式ではクラスタのデータを何らかの値(行数や主キーのユニーク値など)で分割し、それらのセグメントを複数のサイトに分散させ、それぞれのサイトにアクティブ・パッシブのペアを持たせます。そして、クラスタの前に何らかのルーティング技術を追加して、クライアントをリクエストに応じた適切なサイトに誘導します。

シャーディングはワークロードを多くのマシンに分散させスループットを向上させるだけでなく、より多くの部分的な障害に耐え、単一障害点を排除することでより高い耐障害性を実現することができます。

このような利点がある一方で、システムのシャーディングは複雑でチームにとって大きな運用負担となります。シャードを意図的に管理することはアプリケーションのビジネスロジックにルーティングが入り込んでしまうほど大変なことでした。さらに悪いことに、システムのシャード化の方法を変更する必要がある場合(スキーマの変更など)、それを達成するために多大な(あるいは途方もない)エンジニアリングが必要になることがよくありました。

シングルノードのアクティブ・パッシブシステムはトランザクションのサポートも提供していました(強力な一貫性ではないにしても)。しかし、シャード間でトランザクションを調整するのは非常に難しく複雑なため、多くのシャードシステムはトランザクションを完全に見送ることを決定しました。

www.youtube.com

アクティブ・アクティブ・アベイラビリティとは?

アクティブ・アクティブ・アベイラビリティとはデータベースが少なくとも2つのアクティブなノードを持ち、データをシャードしてデータベースへの書き込みを実行することを意味します。アクティブ・アクティブ・アベイラビリティはアクティブ・パッシブから発展したもので、クラスタ内のノードが読み取りと書き込みを行うことで、データベースを単一のマシンを超えて拡張することができます。

シャード化されたデータベースは管理が難しく機能も十分でないため、エンジニアは少なくとも問題の1つを解決するシステムの開発に着手しました。その結果、トランザクションをサポートしないものの、管理が劇的に容易になるシステムが誕生しました。アプリケーションのアップタイムに対する要求が高まる中、チームのSLA達成を支援することは賢明な判断でした。

これらのシステムの背景にあるのは各サイトがクラスタのデータの一部(またはすべて)を格納し、そのデータの読み取りと書き込みを行うことができるというアイデアです。あるノードが書き込みを受けると、そのコピーを必要とする他のすべてのノードに変更が伝搬されます。2つのノードが同じキーに対する書き込みを受け取った状況を処理するために、コミットする前に他のノードの変換が競合解決アルゴリズムに供給されました。各サイトが「アクティブ」であることから、アクティブ・アクティブと呼ばれるようになりました。

各サーバーがすべてのデータの読み取りと書き込みを処理できるため、シャーディングはアルゴリズム的に達成しやすく、デプロイの管理も容易になりました。

可用性という点ではアクティブ・アクティブが優れていました。ノードに障害が発生した場合、クライアントはデータを保存している別のノードにリダイレクトさせればよかったのです。データのレプリカが1つでも生きていれば、読み込みも書き込みも可能です。

この方式は高可用性では優れていますが、一貫性やデータの正確性とは根本的に相反する設計になっています。各サイトはキーに対する書き込みを処理できるため(フェイルオーバーのシナリオではそうなる)、処理中のデータを完全に同期させることは非常に困難です。その代わりに不整合を「平滑化」する方法について粗い粒度の決定を行う競合解決アルゴリズムによって、サイト間の競合を調停するのが一般的なアプローチです。

この解決は、クライアントがプロシージャに関する回答を受け取った後、理論的にはその回答に基づいて他のビジネスロジックを実行した後に行われるため、アクティブ-アクティブ・レプリケーションによってデータに異常が発生しやすくなります。

しかし、アップタイムの重要性を考えると、ダウンタイムのコストは潜在的な異常のコストよりも大きいと判断され、アクティブ・アクティブがレプリケーションの主流となりました。

規模に応じた正しさ: コンセンサスとマルチ・アクティブ・アベイラビリティ

アクティブ・アクティブは高可用性を提供するというインフラが抱える大きな問題に対処しているように見えました。しかし、それはトランザクションを放棄することで実現したに過ぎず、強力な一貫性を必要とするシステムには説得力のある選択肢がないままでした。

例えば、Googleは広告ビジネスに巨大で複雑なシャード化されたMySQLシステムを使用しており、SQLの表現力に大きく依存してデータベースへのクエリを任意に行うことができました。これらのクエリはパフォーマンスを向上させるためにセカンダリインデックスに依存することが多いため、その元となるデータとの整合性を完全に保つ必要がありました。

やがてシステムの規模が大きくなり、シャード化されたMySQLに問題が生じるようになったため(Spencer Kimball氏はこのポッドキャストでシャード化されたMySQLAdWordsに関する実体験を語っています)、同社のエンジニアは大規模な拡張性を持つシステムとビジネスが求める強力な一貫性を両立する問題をどう解決するかを想像し始めました。アクティブ・アクティブはトランザクションをサポートしていないためそのような選択肢はなく、新しいものを設計する必要がありました。その結果、コンセンサスレプリケーションをベースとした一貫性を保証しながらも高可用性も実現できるシステムが完成しました。

コンセンサスレプリケーションではあるノードに書き込みが提案されると他のいくつかのノードにレプリケートされます。過半数のノードが書き込みを承認すると、書き込みをコミットすることができます。

コンセンサスと高可用性

コンセンサスレプリケーションは同期レプリケーションと非同期レプリケーションの間のスイートスポットに位置するというのがここでの重要な考え方です。つまり、システムの可用性に影響を与えることなく少数派のノードがダウンしてもクラスタは許容できるのです。(ダウンしたマシンのトラフィックを処理するための注意事項があります。)

しかし、コンセンサスの代償として書き込みを行うために他のノードと通信する必要があります。ノード間で発生するレイテンシを減らすために、同じアベイラビリティゾーンにノードを配置するなどの方法がありますが、これは高可用性とトレードオフになります。

例えばすべてのノードが同じデータセンターにある場合、ノード間の通信は高速ですがデータセンター全体がオフラインになることには耐えられません。ノードを複数のデータセンターに分散させると書き込みに必要なレイテンシは増加しますが、データセンター全体がオフラインになってもアプリケーションを停止させずに済むため、可用性を向上させることができます。

マルチ・アクティブ・アベイラビリティとは?

マルチ・アクティブ・アベイラビリティはデータベースが少なくとも3つのアクティブなノードを持ち、各ノードがクラスタ内の任意のデータに対して競合を発生させずに読み取りと書き込みを実行できることを要求します。

CockroachDBはGoogle Spannerの論文から学んだことの多くを実装しています(ただし原子時計は必要ありません)。その中にはコンセンサスレプリケーションの他にも可用性をよりシンプルにする機能があります。この仕組みを説明しアクティブ・アクティブと区別するために、マルチ・アクティブ・アベイラビリティという用語を作りました。

アクティブ・アクティブ vs マルチ・アクティブ

アクティブ・アクティブはクラスタ内のどのノードでもキーの読み取りと書き込みを行うことができ、書き込みをコミットした後にのみ受け入れた変更を他のノードに伝搬させることで可用性を実現します。

一方、マルチ・アクティブ・アベイラビリティではどのノードでも読み取りと書き込みを行うことができますが、書き込みの際にはレプリカの大部分が同期するようにし、読み取りは最新バージョンのレプリカからしか行えません。

高可用性という点ではアクティブ・アクティブは1つのレプリカが読み書きともに利用可能であればよく、マルチ・アクティブは過半数のレプリカがオンラインでなければコンセンサスを得られません(システム内の部分故障は依然として許容されます)。

しかし、これらのデータベースの可用性の下流にあるのは一貫性の違いです。アクティブ・アクティブデータベースはほとんどの状況において書き込みを受け入れるように努力しますが、その後、クライアントがそのデータを現在または将来にわたって読むことができるかどうかについては保証されません。一方、マルチ・アクティブデータベースはそのデータが後で一貫性を持って読めることが保証される場合にのみ書き込みを受け付けます。

ここから先は?

過去30年の間にデータベースのレプリケーションと可用性は大きく進歩し、今では世界中に広がる展開をサポートし、決してダウンすることがないように感じられるようになりました。この分野の最初の進出はアクティブ・パッシブレプリケーションによって重要な基礎を築きましたが、最終的にはより優れた可用性とより大きなスケールが必要になりました。

そこで業界はデータベースの2つの主要なパラダイムを開発しました: アクティブ・アクティブは書き込みを素早く受け付けることを第一に考えるアプリケーション向けで、マルチ・アクティブは一貫性を必要とするアプリケーション向けです。

量子もつれを利用して分散状態を管理する次のパラダイムに移行できる日が来ることを私たちは楽しみにしています。

データベースのレプリケーションアベイラビリティの次のフェーズを定義することがあなたのコーヒーブレークの白昼夢であるなら、ここで私たちのオープンポジションをチェックしてみてください。

さらに読む