OpenShift Infra:マスターノードのマルチアベイラビリティゾーン設計は2つのAZか3つのAZか?

クラウドインフラ全般を担当しているソリューションアーキテクトの伊藤です。この記事はOpenShift Advent Calendar 2019 - Qiita 10日目のエントリになります。
アドベントカレンダーと言えば、毎年息子にレゴのアドベントカレンダーを買ってあげており、息子が「クリスマスまであと何日!?」と毎朝楽しみに開いていたのですが、 今年は遂に要らないと言われてしまいました。10歳を過ぎサンタクロースの正体に気が付いてしまったのかもしれません。しくしく(;O;)。

■はじめに

通常OpenShiftをデプロイする際、「マスターノードを3ノードで構成してください。」とお願いをしています。
「高可用性のためです」

クラウドにはAZ(アベイラビリティゾーン)と呼ばれる障害範囲を局所化するための概念が存在します。
ネットワークスイッチやストレージ、電源など1ノードより大きく集約されているコンポーネントで、インフラ障害が発生してしまった際に同時に影響してしまう範囲を示しています。
クラウドでは利用者がどのノードがどこに収容されているか、ある1つのコンポーネントが障害を起こしてしまった時に影響を受けるノードは誰なのか、ということを意図的に分からないようにしているので、ある程度の大きな障害を見越してAZという形で障害範囲を示しています。
つまり3つのノードを1つのAZに集約してしまうと、障害が発生した際には同時に3つのノードが失われてしまう可能性があります。
3つのノードが独立した可用性を獲得するには、それぞれ別のAZを割り当てる必要があります。

■クラウドは3ノード、3AZで高可用性を担保させるべし

f:id:takitou:20191211011110p:plain:w400

OpenShiftのマスターノードはクラウド上にデプロイされるクラスタシステムになります。
クラウド上にデプロイされるクラスタシステムにとって、理想的な高可用性を実現させるには3つのノードと3つのAZが必要です。
f:id:takitou:20191211011126p:plain
「高可用性のためであればネットワーク機器とか2ノードでHA構成をしているし、Active/Backupということで2ノードではダメなんでしょうか?」
「Active用のAZ、Backup用のAZという役割なので、2つのAZを使って構成してはダメなんですか?」

では、以下の観点で考えてみましょう。

■観点と前提

・ある機能(今回はOpenShift マスターノード)において高可用性(HA)を実現したい
・2ノードでHA もしくは 3台以上でHAを実現するという2つのプランの中で検討する
・HAを実現した上でやり取りされる実際のデータについては今回の観点から外す、あくまでもHAを構成、維持する仕組みという観点に限定する

■2台でHA

2台のノードがActive-Standby(Active-Backup/Master-Slave)環境を実現してHAを実現します。
時代遅れということではなく、現在でも環境次第では多く利用されています。

環境次第とは?
HAを実現するにあたっては、必ずハートビートと言われるHA内の別のノードにステータスを送る通信を行っています
この通信はActiveノード、Standbyノードぞれぞれの役割を選出する時にあたっては、
ある1つのノードは「私は内藤(というノード名と仮定)である、優先順位は100である」、また別のノードは「私は伊藤(というノード名と仮定)である、優先順位は90である」
→優先順位の高い内藤がActiveに選定される、といった形で通常は優先順位などで主従関係を決めます。
また、HAが構成された後はActive側が「内藤は元気だけど、伊藤は大丈夫?」→Standby側が「伊藤は元気です!」などと言う非常に簡単な挨拶(ハートビート)を行い正常性を確認します。

★この通信に使うネットワークはそれぞれのノードが正常に動作している時に、通信が途絶えるような状況は起きてはいけません!!★

ハートビート用のネットワークの通信が途絶えてしまうと、スプリットブレインが発生します。
スプリットブレインとは、それぞれのノードが対向ノードと通信できず対向ノードが正常に動作できていないと判断し、両方がActiveになることを指します。
・内藤「おれがデータを処理するんだ」、伊藤「おれがデータを処理するんだ」
このような状況が発生してしまうと、そのHAシステムは正常に動作しているとは言えず、データ整合性欠如、データ破壊、通信断といった障害が発生します。
2ノード構成では、1ノードで異常が発生すると、残りは1ノードになるので1ノードで処理を継続することが普通に発生しうるということです。

#マルチアクティブ(Active-Active)構成によるHAであっても、データの処理の仕方が違うため上記のような不正の状態が起きないだけで、HAを構成するという観点では同様の考え方になります。

この★スプリットブレインという状況は絶対に防がないといけません★
ハートビート用のネットワーク通信が途絶えないように、通常は
・中間経路から壊れるものを排除するため、ノード間は直接通信ケーブルで接続する
・中間経路にスイッチ等壊れるものが混入する場合は、ハートビート用のネットワークを2つ以上用意する
という堅牢な構成を実現します。
★「通信が途絶えるときは対向がダウンもしくは異常動作した時だ!!」 ★
f:id:takitou:20191211011219p:plain:w600
というわけで、環境次第の環境とは、「絶対に通信が途絶えないネットワークが提供されている」ということを指すということがお分かりいただけたかと思います。


さて、絶対通信が途絶えてはならないハートビート用のネットワークですが、
クラウドの利用者側はこの「途絶えてはいけない!!」ということを獲得できるのでしょうか?

・・・できませんね?、実際にプライベートクラウドの設計構築を行うと分かりますが実現できません。利用者に通信が途絶えないネットワークの提供を保証することはできません。
なぜならクラウドにおいて利用者視点では、中間経路がどのようになっているか関知するすべがないからです。

ここで3ノード以上でHAを実現するということに光が当たります。

■3ノード以上でHA

では、どこかで通信が途絶えるようなことが発生しうる環境において、HAを実現するにはどのようにしたらよいでしょうか?
それは分散合意という仕組みです。
OpenShiftのマスターノードでは、etcdという分散KVSでRaftと呼ばれる分散合意のアルゴリズムが使われています。
マスターノードが使うデータをetcdで扱う事で、ノードがダウンしてしまってもクラスタ自体は動作を続けることができます。

それぞれのノードが通信を行い、通信できているノード数をそれぞれのノードが管理しています。普段は3ノード以上で集合体を構成しており、誰か1ノードがリーダーとして高い権限を保有しています。
3ノード以上で構成された集合体でノードダウン/ネットワーク通信障害などが発生した場合、依然通信できているノード数が過半数を上回って(マジョリティ)いれば処理を継続することができます。集合体のリーダーがマジョリティに残っていれば、リーダーは変わらず処理は継続されます。
マジョリティ側にリーダーが存在しない場合、マジョリティ側でリーダーの再選出が行われます。
そして少数側(マイノリティ)は動作を停止します。(2ノードHA時のようにスプリットブレインからのデータ破壊、不整合が起きないようにするため保全モードに入る)

これにより、通信が途絶える可能性のあるネットワーク環境でHAを実現します。
f:id:takitou:20191211011234p:plain

では、3ノードでHAを構成した環境で、1対1対1と全台のネットワークがそれぞれスプリットしてしまったケースですが、
これは二重障害と呼びます。通常一般的なシステム設計において二重障害は起きてしまったらしょうがない、コストバランスが取れなくなるので考慮しないことが多いです。
2ノードHAにおいても、HAのハートビート用の通信は通常2つ用意されるので、これもまた二重障害は考慮しないということになるので同じ条件です。

HAを構成するにあたってはノードがダウンするか否かということより、ノード間の(原因は問わず)通信が途絶えるか否かというのが大事です。
HAの心髄はネットワークにあります。
そしてあるひとつのAZがダウンしてしまう事は十分考えられるので、1つのAZに1つのノードを配置するという考えに至ります。

以上が、3ノード、3AZが必要な理由になります。
3ノードを2AZに収容する設計では、2ノード収容されている側のAZで障害が発生した場合に、安全のためクラスタは動作を停止してしまうということもご理解頂けます。

■クラウド時代のインフラ設計は障害の局所化がポイント

ここからは余談になりますですが、オンプレのデータセンタで3つAZを作るにはどうしたらよいでしょうか?
計算するためのノードを生かすには、ネットワーク、ストレージとサーバへの電力の3つが必要です。
ネットワークとストレージはAZの数だけ別に準備をします。ネットワークは自律的に経路交換し多重障害に耐えられる層のネットワークコンポーネントまではAZの数だけ準備する必要があります。

一方データセンタに供給される電力は一般的なデータセンタでは変電所から2つの経路で引き込みが行われますので、そこで分けると2つのAZしか作れません(;O;)しくしく。。。
サーバに供給されるまでの電力経路は以下になります。
発電所 => 変電所 = (責任分界点) > 受電設備 => 非常用発電設備 => UPS => ラック内電源バー
この順番で電気が巡ってきますので、以下のような構成にしてAZを4つ作ります。

f:id:takitou:20191211010520p:plain

ここで作られた4つのAZのうち普段は3つのAZを使ってクラスタを構築します。
そして、もしこの構成において一番重要な変電所の1つで障害が発生し停電してしまった場合はどのようにクラスタを維持しましょうか?
しばらくは非常用発電機(72時間)やUPS(5分)が、それぞれのAZに電力を供給し続けますが、いずれはブラックアウトが発生します。
もし変電所Bに障害が発生したとき、AZ1,AZ2,AZ3のAZを用いてクラスタを構築していたら、ブラックアウトしてしまってもマジョリティが生存することになりますので無視して良いです。
しかし変電所Aに障害が発生したら、このクラスタはマイノリティしか生存しないことになり、クラスタを維持することができません。
この場合、ここで使っていないAZ4のAZを用いてクラスタを補強するとともに、AZ1かAZ2のノードをクラスタから待避させて、ブラックアウトしたときにマジョリティが生存する構成にします。これで重大障害が起きてもシステムを維持させることができます。

このように何かが起きたとき、その何かによって影響する範囲はどこなのか?何は助かるのか?ということが瞬時にわかるシンプルなインフラを構築することで、大規模なリソースを早く安価に簡単に供給することができるようになります。
そしてこのようなインフラのSLAを考慮した上で、アプリケーション側(元々の話だとetcd)で高い可用性を担保するというクラウドの正しい使い方ができるようになります。

#極力アルゴリズムや技術、ソフトウェア固有の情報を取り除いて、専門的な用語を最小限に抑えた情報を目指しました。

#2ノードHAにおいてQuorumなどの仕組みでマジョリティを確保するやり方もありますが、これはLinux-HA以外のファイヤウォールやロードバランサなどにおいては
一般的ではない概念なので2ノードHAは純粋に2ノードでHAを目指している構成で説明しました。

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。