Dev Study
← 分散システムの土台
レッスン 13 / 13·約 9 分·第12期 分散システムの土台

壊れる前提で組む — 決めておくのは「壊れたとき何が起きるか」

リクエストがアプリケーションに届くまで
  1. ブラウザURL入力
  2. DNS名前→IP
  3. ルータ経路選択
  4. ロードバランサ振り分け今ここ
  5. Nginx受け口
  6. アプリ処理今ここ
  7. DB保存

12本かけて複数台になると何が始まるかを見ました。最終回は機構を足さず、1つの姿勢にまとめます。

まず結論

この期で見てきたものは「壊れないようにする工夫」ではなく、壊れたとき何が起きるかを先に決める仕掛けでした。

だから設計の問いは「落ちないか」ではなく「落ちたとき誰が止まり、誰が続き、何秒か」です。決めなければ、決めていないという結果が出ます。

? 読む前に予測

同じ役割のサーバを3台に増やしました。3台が同時に落ちる確率は必ず下がるでしょうか。

当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。

抽象原理

12本が言っていたのは、同じ1つのこと

この期で足した道具を並べます。再送、冪等キー、過半数、リーダー選出、2相コミット。どれも「壊れないようにする」道具ではなく、壊れたとき何が起きるかを先に決めておく道具でした。

1本目で2台目を置いた瞬間に「片方だけが落ちている」状態が生まれ、4本目でそれを外から見分けられないと分かりました。だから再送するしかなくなり、再送するならどの依頼かを識別する鍵が要ります。9本目の過半数は少数派を止める選択でした。

共通するのは、どれも障害を無くしていない点です。起きたとき何が起きるかを1つに決めているだけです。

具体実物

冗長化したのに、まとめて落ちる

この発想が最も分かりやすく失敗するのが冗長化です。AWS の文書は1台の中の故障をこう書きます。

If these failures do happen, it's safe to assume that everything else will fail too. Technically, we say that they all share fate. Fate sharing cuts down immensely on the different failure modes that an engineer has to handle.

1台のときは全部が運命を共にするので、故障は全部か無かでした。2台目を置くと運命が分かれます——分かれた場合には。同じ電源・ラック・ゾーンなら分かれません。

Google SRE Book は Borg の配置判断をこう説明します。

Borg won't run all of a job's tasks on the same rack, as doing so means that the top of rack switch is a single point of failure for that job

台数ではなくどの単位の下に置いたかで決まります。3台でもラックが1つなら単一障害点は1つです。

✓ ここまでの確認

「アプリを3台に増やしたので単一障害点は無くなりました」。まず何を確かめますか。

抽象原理

決めておくべきは3つ

一般化すると、決めておくことは3つです。

  1. どこまでが一緒に落ちるか(障害の単位)
  2. どれだけ待ったら諦めるか(タイムアウト予算)
  3. 諦めたあと何を返すか(縮退の姿)

2つ目には一次ソースの裏づけがあります。etcd の文書は検知を速くする代償をこう書きます。

If heartbeat interval is too low, etcd will send unnecessary messages that increase the usage of CPU and network resources. On the other side, a too high heartbeat interval leads to high election timeout. Higher election timeout takes longer time to detect a leader failure.

同じ文書は、待つ原因が相手だけではないとも書きます。

The network isn’t the only source of latency. Each request and response may be impacted by slow disks on both the leader and follower.

つまりタイムアウトを調整しても相手の状態は分かりません。決まるのは「自分が何秒待つか」だけです。

3つ目には、失敗が続いたとき呼び出しを止めるサーキットブレーカという機構があります。本教材は一次ソース未確認のため名前の紹介に留めますが、呼び出し側の振る舞いを決める道具であって、呼び出し先の故障を止める道具ではありません。

決めていない場合に、何が既定値として選ばれるか
障害の単位たまたま同じ場所に集まり、台数だけ増える
決めなければ次も既定値
待つ時間実装の既定値か無制限になる
最後まで
諦めた後例外がそのまま利用者へ出る
  • 「決めない」という選択肢は無い。決めなければ既定値になる
  • 既定値は、たいてい設計者が望まないほうに倒れている
具体実物

止め方を変えるだけで、一桁変わる

「壊れたとき何が起きるか」が数字で見える例が、10本目の実測です。

リーダー停止から新リーダーまで(etcd 3.5.17、3ノード、選挙タイムアウト1000ms、同一ホストのDockerでノード間遅延ほぼゼロ、各3回)表
止め方 1回目 2回目 3回目
SIGTERM(正常終了) 89ms 108ms 129ms
SIGKILL(即時停止) 1150ms 1531ms 1571ms

同じ「止める」で一桁違います。正常終了なら座を明け渡すので残りは待たずに選挙でき、黙って消えれば選挙タイムアウトまで待ちます。4本目の「落ちているのか遅いのか区別できない」が、そのまま1秒の待ちです。

遅延のある環境でも選挙タイムアウト次第でも変わるので「etcd の選挙は1.5秒」とは一般化できません。読むべきは絶対値ではなく、止め方という決められる要素が一桁の差を生む点です。

12本目は反対側でした。準備済みトランザクションを残したまま PostgreSQL を再起動しても、ログに recovering prepared transaction と出て、同じ行への書き込みは止まったままでした。再起動しても解けないように作ってあります。

✓ ここまでの確認

呼び出し先が応答しないとき、タイムアウトを短くすると何が変わりますか。

⇄ 2つを見比べる
具体例 A

3つのサブネットが全部同じAZ

3つ作られ名前も分かれているので冗長に見えますが、指定されたAZが全て同じなら、そのゾーンが落ちたとき3つとも落ちます。仮想化編『クラウドは他人のコンピュータ — リージョンとAZの物理』の差分です。

名前の数と、障害の単位の数表
見えるもの 数 落ちる単位
リソース名 3 -
指定されたAZ 1 1
具体例 B

同じラックに載った3タスク

タスクを3つに増やしても、同じラックならラック上部のスイッチが単一障害点になります。Google SRE Book は Borg がこの配置を避けると述べます。落ちる単位は1つです。

台数と、障害の単位の数表
見えるもの 数 落ちる単位
タスク数 3 -
ラック 1 1

クラウドのAZ指定と、データセンターのラック配置。道具は違いますが、共通する構造は何でしょうか。

「いくつあるか」と「どこに乗っているか」のどちらを数えているかに注目。

この章のまとめ

  • 障害は例外ではなく既定の状態。設計とは壊れたとき何が起きるかを決めておくこと。
  • 冗長化が効くのは運命が分かれている場合だけ。同じ障害の単位に置けばまとめて落ちる(相関故障)。数えるのは台数ではなく単位。
  • タイムアウトを調整しても相手の状態は分からない。決まるのは自分が何秒待つかだけで、短くすれば遅い相手を切る回数が増える。
  • サーキットブレーカも呼び出し側の振る舞いを決めるもので、呼び出し先の故障は止めない。「入れれば防げる」は成り立たない。
  • 決めていない箇所には既定値が入る。どの単位に分けたか・何秒待つか・諦めた後に何を返すかは、決めなければ勝手に決まる。

自分の現場で確かめる

読んだだけでは身につきません。手元の環境で実際に見てみてください。

環境:

理解度チェック

読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。

問題 1 / 4正解: 0

「マルチAZ構成にしたので障害に強い」という設計書があります。何を見て検証しますか。

用語集(5語)
障害の単位(fault domain / failure domain)
一緒に落ちる範囲。AWS は fault domain、SRE Book は failure domain と書く。電源・ラック上部のスイッチ・AZ が単位。
運命を共にする(fate sharing)
同じ単位の中のものが全部まとめて落ちること。AWS の文書は1台の中の故障について、全部が運命を共にするので扱う故障の型が激減すると書く。
相関故障
複数の実体が同じ原因で同時に落ちること。冗長化した実体を同じ障害の単位に置くと起きる。本教材の語。
タイムアウト予算
呼び出し先ごとに「ここまで待ったら諦める」と決めた値。短くすれば待ちは減るが、遅い相手を切る回数が増える。
サーキットブレーカ
失敗が続いたとき呼び出しを止め、即座に失敗を返す機構。呼び出し側の振る舞いを決めるもので、呼び出し先の故障は止めない。本教材は一次ソース未確認のため名前の紹介に留める。