チャプター9では分散システムにおける故障検知、フェイラーディテクターの実装パターンやその課題について深く掘り下げられていました。
まず、みなさんが分散システムの故障検知みたいな他のマシンが生きているかどうか、動いているかどうかを検知するために実装しなさいと言われたらどんなパターンで実装するでしょうか。
一番ナイーブというか思いつく自然なパターンとしてあるのがハートビート、ピングスタイルですね。
これが一番最初にまず紹介されていました。
このハートビートスタイルはどういう実装方法かというと、直接ネットワーク通信を使って相手のマシンが動いているかどうかを、
ピングコマンドとかUNIXにありますけれども、ピングコマンドのように定期的にポーリングしたり、スケジュールで動かしたりして確認するような仕組みですね。
これちょっとSaaSとかプロダクトで考えてみると、皆さんももし自分が本番環境で管理しているウェブサイトの健全状態を、
ステータスケークとかアップタイムロボットみたいな外部サービスで定期的にチェックしている方もいるかもしれませんが、まさにあのようなプロダクトと似たメカニズムですね。
このシンプルで分かりやすい定期的にネットワーク通信を使ってピングを送るこのハートビートスタイルですけれども、
このハートビートスタイルに大きな課題がありました。
それはフォルスポジティブ、つまり誤検知ですね。
誤検知が結構発生しやすいという点がこのハートビートスタイルの課題になっています。
例えば、マシン自体は全く問題なく健全に動いているのに、
そのマシンにピングを打っているそのネットワークのホップの間で、
一時的なネットワークの寸断とかコンジェッションですね、混雑とかによってピングの返事、応答が一時的に返ってこないとした時には、
本当はマシンは動いているのに、ピングをする側、検知をする側でマシンは動いていない、
マシンは正常に機能していないと誤って結論をつけてしまう可能性がありますね。
フォルスポジティブですね。
で、これ結構起きるんですね。
僕も今、eコマースの会社でSREやってて、
さっき説明したようなステータス計画、アップタイムロボットみたいな外部サービスでシンセティックチェックみたいなのをしたりしますし、
こういうサービス間のピングチェックみたいなのも動いてますけれども、結構起きます。
なので、これを避けるためにどういうことをするかというと、
ハートビートスタイルを使っている限りなんですけど、軽減策としては、
例えば、タイムアウトの時間を伸ばしますね。
例えば、そのピングでコマンドを打って、1秒以内に結果が返ってくるかどうかという設定だったとしましょう。
この1秒以内だと、例えば、リージョンを超えたピングの応答だったりしたときに、なかなか返ってこなくて、
本当はマシンは動いているのにネットワークがたまたま遅かったから、1秒を超えてしまったので、
マシンは動いていないと結論を付けてしまう可能性があるので、
例えば、タイムアウト時間を1秒から3秒に伸ばしてみる、みたいな点もよく使われたりしますね。
これはもちろんトレードオフでして、タイムアウト時間を伸ばすということは、
もし本当にマシンが死んでしまっていた場合は、それを気づく時間が遅れるということですね。
本当にマシンが動いていなかったら、それを早く検知して障害対応しなきゃいけないので、
タイムアウト時間を伸ばすということは、誤検知を減らすというメリットはある一方で、
本当に動いていないときに気づくまでの時間が伸びるという、これはこれでかなり欠点があります。
じゃあ他にどういうことをするかというと、例えば1秒でタイムアウトを設定していたとして、
リトライ回数を増やすですね。
まずは1回目、ピンを投げますと、1秒以内に結果が返ってきませんでした。
この段階でマシンは動いていないというふうに結論するんじゃなくてですね、
例えばリトライを3回までしますと、じゃあ1回目の1秒のピングで結果が返ってこなかったからもう1回投げると。
もう1回投げて、それも返ってこなかったからもう1回投げる。
3回リトライして、1秒かける3回のピングをして、それでも結果が返ってこなかったら、
最終的にマシンは動いていないと判断するという可能性もありますね。
この手法もさっきのタイムアウト時間を伸ばすと同じで、
リトライの回数を増やせば増やすほど本当にマシンが動いていないときに、
マシンは動いていませんでしたというふうに結論付けるまでの時間が伸びることにつながってしまうので、
これはサービス全体の可用性とかを考えたときには、トレードオフなのでリトライ回数を増やせばいいというわけではありません。
ここら辺が結構難しいポイントですね。
タイムアウト時間を伸ばすとかリトライ回数を増やすみたいなところで、
語源値のリスクを軽減することができるんですけども、
Heartbeatスタイルを使っている限り、根本的にこの問題を解決するのが非常に難しいということが本書でも書かれていましたね。
先ほどもSynthetic Testみたいなところで、この問題によく当たると言ったんですけども、
これについてもちょっと詳しく説明しようかなと思います。
例えば、プライライトというブラウザーテスティングツールを使っているんですね。
これはどういうことかというと、JavaScriptとかPythonとかでブラウザーのテストを表現するわけですね。
まずはこのURLに行ってください。
このURLに行ったら、DOMツリーの中からIDとかクラス名とかでこのボタンを探してください。
このボタンを押してください。
このボタンを押した後に3秒待って、帰ってきたHTMLレスポンスの中にチェックアウトサクセスフルみたいな文言があることを確認してください。
それを確認したらテスト成功ですみたいなブラウザーテスト確保とかできるんですね。
これもやっぱり結構フレーキというかフォルスポジティブが多いです。
例えばテスト実行環境自体は北米のマシンで動かしているとして、
でもテスト対象のウェブサイトが日本のウェブサイトだったりするとリージョンを超えたテスト実行になってしまうんですよね。
だからネットワークのホップがなかなか遠いと。
なので例えばタイムアウトデフォルトの1秒だとするとフォルスポジティブが多くて、
本当はウェブサイトはちゃんと動いているのにウェブサイトがダウンしたというふうにご検知してページされてしまうみたいな、
呼ばれてしまうみたいなそういうのが多いのでタイムアウト回数を増やしたりとかリトライ回数を増やしたりとかっていうのをしますけれども、
ハートビートスタイルを使っている限り解決できない問題ではあるかなと思っています。
ということでこの根源的な課題を抱えているハートビートスタイルのほかにどのようなアルゴリズムが発明研究されてきたのかというところなんですけれども、
次の紹介されていた面白い興味深かったプロトコルがありますと、
これはSWIMプロトコルというのが紹介されていました。
このSWIMプロトコルの正式名称はちょっと長いんですけど、
スケーラブル・ウィークリー・コンシスタント・インフェクション・スタイル・グループ・メンバーシップ・プロトコルなんでこれSWIMプロトコルという、
別称アウトソースとハートビートとかっていう形でも紹介されていました。
このSWIMプロトコルはどういうプロトコルかというと、ゴシップベースのプロトコルに近い考え方で、
ハートビートの課題を克服しようとしています。
ゴシップベース、これ新しいキーワードだと思うんですけれども、
ゴシップっていうのはカタカナのゴシップですね。
噂話みたいな感じで考えてもらっていいんですけど、
例えばマシンがグループを形成しているみたいなのも本当に人間のコミュニティアナロジーで考えるんですね。
例えばあそこのご近所さん最近引っ越したらしいよとか、
ちょっとゴシップっていうとネガティブな印象があって誰かの悪口とかそういうのが広がっていく様子になっちゃう、
イメージされちゃうと思うんですけど、
そのメンバー間で誰々が最近引っ越したみたいな情報が噂話のように広がっていくと。
このゴシップベースというのはまさにそのような考え方を実装されているアルゴリズムになっています。
このSWIMプロトコルの説明をしていきますね。
SWIMプロトコルではまずダイレクトピング、ハートビートをします。
なんでこのハートビートも一部では使っているんですね。
まず自分が直接ピングしに行きます。
相手のマシンに行ってますかってことでピングするわけですね。
ここまではハートビートと一緒。
そこでもし返事が返ってこなかった場合、
ここですぐにダウンしたって判断するのがハートビートでしたね。
SWIMプロトコルは我慢強いんですよ。
もうちょっと我慢して、ここですぐにダウンしたって判断するわけではない。
じゃあどうするかというと、他のマシンにそのマシンの状態を確認してもらうんですね。
これをさっきのダイレクトピングと違って、インダイレクトピング、つまり間接的なピングって言いますけども、
自分からマシンにつなぎに行ったらダウンしたというか返事が返ってこなかったんですけど、
他の生きてることが確認を取れているマシンに、
あいつから返事こないんだけど、そっちからどう?と他のノードに聞きに行くわけですね。
そこからも返答がなかったら、そのマシンを疑わしい状態、サスペクティングステータスとして認知しますと。
自分からもダメだった、お隣さんのマシンからもダメだった。
これは落ちてるんじゃないか、疑わしいというステートをつけるわけですね。
このサスペクティング、あいつはサスペクティングだって、ミステリー小説みたいになってきましたけども、
シンプルなハートビートスタイルに加えてゴシップベースを組み合わせたSWIMプロトコルというのがありました。
そこでもう一つのプロトコルを紹介していこうと思います。
本章で紹介されていたもう一つのプロトコルが、ファイ・アクルーラル・フェイリーディテクションということで、
ファイというのは確率度のパイですね。
数学とかでそれこそ演習率のパイと言ったりしますけれども、変数を表すときのファイ、パイです。
アクルーラル・フェイリーディテクションということで、これはなかなか読んでて熱くなったんですけど、
ジャイスト、北陸先端科学技術大学院とかかな、日本の研究機関がコンピューターサイエンスとかをやっているとよくたまに目にする方も多いし、
ここに行かれたり研究されている方もSNSとかTwitterとかネットワークで見たりするんじゃないかなと思うんですけど、
ジャイストで開発されたものだと書いてありましたね。
このファイ・アクルーラル・フェイリーディテクションは、
アパッチ・アッカとかアパッチ・カサンダラといった有名な分散システムでも実際に使われているアルゴリズムです。
僕がリサーチした限り2004年にこの開発者の林原さんかなという方が白紙論文として研究された論文を元に発表されたものだと理解しているんですけども、
そのアクルーラル・フェイリーディテクションがこれがなかなか面白いんですね。
この従来の故障検知が生きているか死んでいるか、
そのマシンが動いているか動いていないかというゼロイチで判断してましたよね。
さっきのSWIMプロトコルも自分で検知したりとか他のマシンに検知させたりという風に検知しに行くんですけど、
その結果っていうのは落ちてる、落ちてないゼロか1のバイナリーでしたと。
このファイアクルーラル・フェイリーディテクションはファイの部分は変数だと説明しましたが、
これがマシンが落ちている確率度なんですね。
だからゼロイチのバイナリーではなくてマシンが落ちている確率っていうのをファイで表現します。
このファイの値を計算していくんですね、統計的に。
これはいくつかの変数を元に確率度っていうのを計算していて、
特に有効な変数として紹介されているのが2つあって、
まず1つが最後にマシンが返事してからどれぐらい時間がたったかっていう情報ですね。
だから1秒前にマシンはヘルシーだった、動いてたっていう情報と、
1時間前にマシンがヘルシーだったという結果が返ってきたときに確率度を考えてみたら、
1秒前にマシンが生きてたんだから今も動いてるよねという判断する方が確率としては今マシンが動いてるという確率が高いですよね。
1時間前に動いていたかもしれないけど、1時間前は結構後ろなので、
その間にマシンが落ちた可能性というのはかなり高くなってきます。
なので、例えばその最後にマシンがいつ動いているという情報を送ってきたかっていう要素を組み合わせて確率の倍を計算したりしますね。
もう1つの面白い要素が、過去の応答履歴から統計的にそのマシンは普段どれくらいの速度で返答するのかという情報が使われます。
これは面白くて、どういうことかというと、
10回ピン語を送ったとしていつも統計的に100ミリセック以内に返答してくると。
そうするとこのマシンは普段100ミリセックの結果で応答するということをファイの確率の計算に適用するわけですね。
これ結構実生活とか実体験に基づいた割と考え方としてはシンプルなのかなと思っています。
これは例えば誰かとミーティングしたりとかご飯の待ち合わせをする時とかに、
あの人は普段から時間を厳守するので今日はまだ出社してないよね。
何かあったのかなみたいな判断する感覚に近いかもしれません。
単なるタイムアウトよりもその人のパーソナリティと言いますか、
そのマシンの過去の実績をもとに確率度を計算するので、より洗練された方法と言えると思います。
これはブッククラブの参加者の方に教えてもらった記事がありまして、
開発者の林原さんの記事がありました。
これまさにそこにどういうインスピレーションを得て開発のヒントになったかというのが書いてありまして、
その記事のちょっと抜粋を見上げますね。
他の人には待ち合わせの時間に少し遅れるとすぐ問い合わせのメールを出したり電話をかけるという友人が、
私にはそのような問い合わせを全くしませんでした。
その理由は当時の私がよく遅れるからだということでした。
実はこれが新しい故障検出方式であるアクリルアル故障検出方式のヒントになったのです。
と書いてあってまさにあの実体験の経験をもとに、
あの人は普段から遅れているなあなのか、あの人はちゃんと時間を守るなあみたいな過去の実績をこの確率とファイの計算に適応するというなかなか面白いイノベーティブな発想なんじゃないかと、
僕は読んでいて結構日本の方が開発したものがアパッチアッカとかアパッチカサンドラで使われているという事実もそうですし、
その発想の何て言うんでしょう、そうきたかみたいなところが結構読んでいて興奮したものですね。
はい、ということで以上がざっくりと内容の振り返りになりまして、
最後にあのブッククラブで盛り上がった観点というのもつまみ食いしようかなと思います。
今回のブッククラブはノースアメリカとAPACのタイムゾーンということで私は当日参加できませんでしたので、
読書メモとかディスコードから盛り上がったところをつまみ食いしつつ個人の感想をお伝えするという形にしようかなと思います。
このブッククラブのエピソードを昔から聞いてくださっているリスナーの方は、
どのようなスタイルでブッククラブをやっていてとかっていうのをご存知だと思うんですけど、
今までは今タイムゾーンをロテーションする形にしているので、
ノースアメリカとAPACの時に僕が参加できないからAPACリーダーみたいなのを置いていました。
今まではテッペイさんがAPACリーダーとしてブッククラブのファシリテーションをしてくれていました。
過去のエピソードを聞いた方はすでにご存知の通り、彼が最近ロンドンにいらっしゃいましたので、
今度は別の方、畑山敦史さんをAPACリーダーに引き継いでくださったので、
あつに当日の司会進行を任せました。改めてありがとうございます。
あつもが過去にエピソードを何度も出てくれている方ですので、よかったら聞いてみてくださいね。
ということで、今回の章を読んでいて個人的に注目点というのは、
ゴシップというスタイルの情報共有のスタイルですね。
ゴシップ、さっきも説明しましたけど、噂話が広まっていく様子のように、
どのマシンが落ちているのか、あるいはどのマシンがヘルシーで動いているのかという情報、
ノードリストであったり、その情報をノード間で共有していくスタイルというのは、
なかなか面白いかなと思っています。
その各ノードがランダムに選んだ数ノードに対して自分の持っている情報を伝播していくんですけど、
これがどんどんどんどんマシンのシステム全体に行き渡っていくというのは、
実世界でも誰かの噂話がどんどんどんどんコミュニティの中に広がっていくみたいな、
リアルというかダイナミックな、ダイナミズムのある考え方で面白いなと思っています。
この故障検知自体の概念は、私たちが普段開発しているアプリケーションの世界でも非常によく出てくるかなと思います。
例えば、モバイルクライアント開発している人がバックエンドに、バックエンドちゃんと生きているかなみたいなリクエストを送ったりとか、
あとKubernetesなんか使っていたらマイクロサービスとか分散システムを動かすので、
他のマシンが動いているかというのは非常に重要な情報ですよね。
Kubernetesの世界でいうと、ライブネスプルーブとかレディネスプルーブみたいなキーワードとかも出てきたりすると思います。
ライブネスというのは、プロセスが生きているかどうか。
レディネスというのは、さらに先でリクエストを受け付けられる準備ができているかどうかという判断ですね。
例えば、ライブネスというのはポストグレスのプロセスが立ち上がっているかどうか。
レディネスというのは、例えばスキーマイグレーションが終わって実際にデータベースのクエリをハンドルできるかどうかみたいな判断ですけどね。
APサーバーを構築する際とかにプロセスが動いているどうかのチェック用のエンドポイントを
スラッシュヘルスとかスラッシュヘルスZみたいな感じで実装した経験がある方も多いのではないかなと思います。
これもまさに故障検知の意思といいます。
似たような考え方を分散システムで考えてみましょうというのがこの本章でしたね。
さて、今回説明してきたハートビート、スイムプロトコル、ファイアクルーラルフェイラーディテクション、
ちょっとロンドンテックトークブッククラブ名物になりかけてきたんですけれども、またアナロジーで頑張って紹介してみようかなと思います。
難しいコンセプトをYouTubeみたいな動画ではなく、ポッドキャストで紹介するというチャレンジをしているので、
ちょっと面白おかしいアナロジーとかを使って今回のプロトコルを振り返ってみようと思います。
今回のアナロジーはですね、朝、ティーネイジャーとかお子さんをね、なかなか起きてこないお子さんを起こすシーンっていうのを想像してほしいんですね。
じゃあどういうことかというと、2階建ての家を想像して、
そろそろ学校が始まる時間なのでですね、階段の下から自分の子供にそろそろ起きる時間だよと大声で呼びかけるんですね。
もしかしたらこれが毎朝の日常ですみたいなリスナーの方もいるかもしれません。
そろそろ起きる時間だよとしたから大声で呼びかけます。
これがシステムで言うところのまさにハートビートやピングですね。
自分の方からちゃんと起きているかどうか確認したい対象、つまりそれは2階の自分の部屋で寝ているお子さんですね。
そろそろ起きる時間だよとピングを送ってきます。
さて、今日は月曜日の朝なんですけどなかなか返事が来ませんと。
じゃあここであなたはすぐに今日はまだ寝込んでいるのは夢の中かな、もしくは風邪をひいてダウンしてしまっているのかもしれないなと判断するかどうかというところですね。
ハートビートのタイムアウト、リトライなしというと、とりあえず1回そろそろ起きる時間だよと言って起きてこなかった。
この時点で、もしかしたら風邪をひいて寝込んでダウンしてしまっているのかもしれないなと判断するのがハートビート、ピングスタイルです。
でも、もしかしたらこれがフォルスポジティブかもしれませんよね。
誤検知かもしれませんよね。
例えば、実はすでに子供は起きていて、ヘッドホンで音楽を聞いているだけかもしれませんし、
たまたまボーッとまだ起きたばっかりで、意識がもろうとしていて、自分の僕の声がまだ届かなかっただけかもしれません。
もしくは、もう起きてスマホをいじっているのに、返事も聞こえていたのに返事をするのが面倒なだけかもしれませんよね。
もしかしたら、風邪をひいて寝込んでいる状態をダウンだとした時に、その全く反対で、とても元気でピンピンしているけど、
ちょっと今は反抗期、入りかけであえて返事をしない意地悪をしているだけかもしれませんよね。
物理的には全く問題なく元気でピンピン起きているのに、
僕の階段下からのピング、ハートビートに応答しないというだけで、風邪で寝込んでいる、ダウンしていると誤って判断してしまうのは時期焦燥かなと思います。
これが分散システムにおける語源値の典型的な例です。
じゃあどうするか。
まずはタイムアウトですかね。
1回の呼びかけで返事が返ってくるんですけども、すぐに返事が返ってこないからといってちょっと我慢強く
30秒ぐらい待ってくるわけですね。
また起きてこないかなみたいな。
タイムアウト1秒から30秒に伸ばすと、我慢強さというのはペアレンティングにも求められるところですけれども、
それでもダメだったら何回か呼んでみるわけですよね。
そろそろ9時間だよーと。
2回目だよーみたいな。
3回目だよーみたいな感じでどんどんどんどんリトライしていくわけですね。
それでもダメだ場合。
どうするかというところで、
じゃあちょっとスイムプロトコルのアナロジーを紹介しましょうね。
自分から声かけてダメなら他のやつからインダイレクトに聞いてみるということで、
もし兄弟とか姉妹が起きてきて、兄弟とか姉妹がすでに起きていたら、
そういえばお姉ちゃんは今起きてそうだったみたいな感じで他の人に聞いて情報を集めるイメージですかね。
もしくは自分の夫婦のパートナーでもいいですし、一緒に住んでいるご家族でもいいかもしれませんけど、
兄弟姉妹たちが起きてゴロゴロしてたよーみたいな風に言うのか、
布団から全然出てこないんだよね。
ちょっと心配みたいな感じで言うのか。
それによって起こそうとしている子供がダウンしているかどうかの判断をするのかもしれませんよね。
スイムプロトコル。
そして自分からのハートビートにも返事が来ないし、
兄弟姉妹とか自分のパートナーが起こしに行っても起きてこない。
そしたら、もしかしたらあの子は今日風邪をひいてダウンしているかもしれないみたいなサスペクティング状態を
家族の中で伝播していくわけですよね。ゴシップ。
そして家族の半分以上がサスペクティング状態だとしたら、
いやちょっとあの子は風邪でダウンしているかもしれないみたいなようにデクレアするという状態かもしれません。
じゃあ一方でもう一つの3つ目のファイアクルーラフェイルディテクション。
確率度πでマシーンが落ちているかどうかを判断するアナロジーだったらどうでしょうかね。
その子の今までの実績を元に判断するイメージですよね。
例えば週末はいつも友達と夜な夜なオンラインゲームをして夜更かしをしているとか、
あとは本を読んで夜更かしをしているとか、
あとは勉強頑張って夜更かしをしているみたいな感じ。
だったら月曜日の朝はいつも遅く起きてくると、
毎週月曜日は起こそうとしても起きてこないというのがもうルーチンになっているっていうのは、
まさにその子の過去の実績から起きてくるかどうかというのが分かる状態ですね。
だから確率度を計算しているわけです。
どうせ今日も風邪をひいたわけじゃなくて、
昨日の夜頑張っただけだろうかなとか、
昨日の夜ゲームで遊びすぎただけだろうかなという、
今までの情報から判断するわけですね。
過去の数理統計上のデータから、
子供が月曜日の朝に風邪でダウンしているかどうかの確率φを瞬時に計算するというのが、
今大人の頭の中でソロ版のように計算されているわけですけど、
まさに現実世界でもよくあるパターンなんじゃないかなと思います。
それを適用したのがファイアクルーラルフェーラーディテクションということで、
ちょっとアナロジーを使って、
ハートビート、スイムプロトコル、ファイアクルーラルフェーラーディテクションを紹介してみましたが、
いかがだったでしょうか。
今回の本章自体、チャプター9自体はですね、
ページ数にしたら結構短いんですけども、
個人的にはなかなか結構テイクアウェイがあって、
すごい面白い章でした。
分散システムにおいてどのノードが現在動いているかどうか、
共通認識を持つための重要な基盤となるテクニックの数々ですので、
ぜひ本章を手に取っていただけたらいいかなと思います。
ということで、そろそろクロージングに行こうと思います。
次のロンドンテックトークブッククラブは、いよいよチャプター10、リーダーエレクションに入っていきます。
これはなかなかごっつい章ですね。面白いと思います。
キーワードとしては、
プリアルゴリズムとか、キャンディでオーディナリーオプティマイゼーションとか、
リングアルゴリズムとか結構出てきますので、
リーダーエレクション頑張って一緒に読んでいきましょう。
ということで、振り返りは以上にしようと思います。
今回はチャプター9、フェイリーディテクションについて振り返りました。
分散システムにおいてノードの視覚監視、検知というのが
いかに複雑で奥深いテーマであるか、
そしてそれを解決するためのさまざまなアプローチがあることを学んできましたね。
ということで、それでは次回のチャプター10の振り返し編集でお会いしましょう。
Have a nice day.