7 ポイント 投稿者 GN⁺ 2024-09-03 | 1件のコメント | WhatsAppで共有
  • 分散システムの中核的な難しさはレイテンシよりも 部分障害 にあり、1台のマシンでは単純な例外処理で済むことも、複数マシンではプロトコル設計の一部になる
  • 堅牢な分散システムには、実際の複数マシン、データセンターネットワーク条件、大規模データセットでしか現れないバグがあるため、単一マシンのシステムよりコストが高い
  • 運用可能なシステムを作るには、調整を減らして 独立性 を高め、バックプレッシャー・部分可用性・メトリクス・パーセンタイル指標を基本ツールとして使うべき
  • 機能フラグ、ID空間設計、データ局所性、サービス抽出は、マイグレーションや拡張の過程で 障害範囲と調整コスト を下げる実務的手法である
  • CAP定理はシステム構築の出発点というより 設計を批判的に検討するための道具 に近く、分散システムでは技術的課題と同じくらい、チームや組織間の社会的調整も難しい

分散システムを初めて学ぶときに見落としやすいこと

  • 分散システムエンジニアの多くの教訓は、プロダクショントラフィックで経験した失敗や 運用上の傷 から生まれるが、初心者エンジニアにはもっと直接的で実行可能な文脈が必要である
  • Fallacies of Distributed ComputingCAP theorem は独学には役立つが、経験の浅いエンジニアがすぐに動くには抽象的すぎる
  • 分散システムはより多くのマシンと資本を必要とするため、より多くのチームと大きな組織が絡みやすい
    • 社会的問題は、たいていソフトウェア開発者の仕事で最も難しい部分であり、分散システム開発では特に目立ちやすい
    • 技術的解決より社会的解決のほうが効率的で満足度が高いこともあるが、背景・教育・経験によってエンジニアは技術的解決に傾きがちである

障害とコストを前提に設計する

  • 分散システムは頻繁に失敗し、さらに悪いことに 部分的に失敗 する
    • 単一プロセスの mutex unlock 失敗ならプロセスをクラッシュさせれば済むかもしれないが、分散 mutex の unlock 失敗はロックプロトコルの中に組み込まれていなければならない
    • 「2台のマシンに両方 write を送ればよい」や「成功するまでリトライすればよい」という発想では、片方の write だけ成功してもう片方が失敗する状況を十分に扱えない
    • スイッチ障害、GC pause によるリーダー消失、成功したように見える socket write のリモート側失敗、遅いディスク1台がクラスタ通信プロトコル全体を遅くする状況が起こりうる
  • 堅牢な分散ソリューションには 単一マシンのソリューションより多くのコスト がかかる
    • 複数マシンでしか起こらない障害があり、VM やクラウドはコストを下げるが、手元の1台のコンピュータで設計・実装・テストするほど安くはならない
    • 共有マシンに収まりきらないデータセット規模やデータセンターネットワーク条件でしか表れない障害には、実際の分散環境が必要になる
    • シミュレーションは有用だが、分散システムのバグをすべて代替できるわけではない
  • 堅牢なオープンソースの分散システムは、堅牢な単一マシンシステムよりはるかにまれである
    • 多数のマシンを長期間動かすコストは、オープンソースコミュニティにとって負担になる
    • 趣味開発者やディレッタントは、すでに持っているマシンと余暇時間でオープンソースを作るため、複数マシンを立ち上げて維持し、費用を払う開発者を見つけにくい
    • 企業のエンジニアが一部の空白を埋めることはあるが、その組織の優先順位が利用者側の組織の優先順位と一致するとは限らない

調整を減らし、障害を吸収する

  • マシン間の 調整 は可能な限り避けるべきである
    • 水平スケーラビリティの本質は、マシン同士が通信し合意する必要を最小限にできるほど独立してデータを配置することにある
    • 2台のマシンが何かについて合意しなければならないたびに、サービス実装はより難しくなる
    • 情報伝達速度には上限があり、ネットワーク通信は思っている以上に不安定で、合意に関する直感は間違っていることがある
    • Two GeneralsByzantine GeneralsPaxos 実装の難しさ を理解すると役立つ
  • 問題がメモリに収まるなら、分散システムエンジニアにとってはたいてい 些細な問題 に近い
    • データが数回のポインタ参照先にあるのではなく、いくつかのスイッチを越えた先にあるとき、それを高速に処理するのはずっと難しい
    • 単一マシン向けのアルゴリズムや実装は豊富だが、分散システム向けの文献や実装ははるかに少ない
  • 「遅い」は最もデバッグしにくい問題である
    • ユーザーリクエストに関わる複数のシステムのうち1つ以上が遅いかもしれないし、複数マシンをまたぐ変換パイプラインの一部が遅いかもしれない
    • 問題の説明自体が不具合箇所に関する手がかりをほとんど与えず、普段見ているグラフには現れない部分障害が潜んでいることもある
    • 性能低下が非常に明確になるまで、時間・予算・ツールといったリソースを十分に得にくい
    • DapperZipkin はこの理由で作られた
  • システム全体に バックプレッシャー を実装しなければならない
    • バックプレッシャーとは、サービスする側のシステムが要求側システムに障害を通知し、要求側がその障害を処理して自分自身と相手システムの過負荷を防ぐ仕組みである
    • 過負荷やシステム障害の間にリソース使用を制限することは、堅牢な分散システムの基本構成要素である
    • 実装としては、リソースが制限されたり障害が起きたりしたときに新しいメッセージを捨てる、あるいはユーザーにエラーを返す形が多く、どちらの場合もメトリクスを増加させるべきである
    • 他システムへの接続やリクエストには、タイムアウトと指数バックオフが必須である
    • バックプレッシャーがないと、連鎖障害や意図しないメッセージ損失が起こりやすい
  • 部分可用性 を提供する方法を見つけるべきである
    • 部分可用性とは、システムの一部が失敗しても一部の結果を返せる能力のこと
    • 検索システムは、結果品質とユーザーを待たせる時間のあいだでトレードオフし、制限時間を過ぎたら集められた結果だけを返すことができる
    • プライベートメッセージングでは、一部のユーザーだけに障害が起きるほうが、全ユーザーの一部メッセージが消えるより良い場合がある
    • プライベートメッセージングの障害が、公開画像アップロードのような無関係な機能に影響しないよう、障害ドメインをどこまで分離するかを決めなければならない

観測・容量・デプロイを運用ツールにする

  • メトリクスは、プロダクションでシステムが実際に何をしているかを知るための 必須手段 である
    • レイテンシのパーセンタイル、特定動作の増分カウンタ、変化率といったメトリクスは、信じているシステム動作と実際の動作のあいだのギャップを縮める
    • 20日目のシステム動作が15日目とどう違うのかを把握できるかどうかが、成功したエンジニアリングと失敗した呪術的運用を分ける
    • メトリクスは問題や挙動を理解するのに必要だが、次に何をすべきかを教えるには十分ではない
  • ログファイルは有用だが 嘘をつきやすい
    • ある種のエラーログがファイル容量の大半を占めていても、実際のリクエスト比率は低いかもしれない
    • 成功ログの大半は重複であり、ディスクを圧迫しうるし、エンジニアはどのエラーログが有用かをしばしば見誤る
    • ログは、そのコードを見たことのない人が読む前提で残したほうがよい
    • ログで見た異常をメトリクスで確認せずに過大評価すると、障害が長引くことがある
  • 平均より パーセンタイル を使うべきである
    • 50th、99th、99.9th、99.99th パーセンタイルは、ほとんどの分散システムで平均より正確で有用である
    • 平均は評価指標がベルカーブ分布に従うことを前提にするが、エンジニアが重視する指標の多くはそうではない
    • 分散システムのレイテンシがベルカーブ分布に従わないなら、平均レイテンシは誤った意思決定と理解につながる
  • 容量見積もりの力を身につけるべきである
    • 作業に必要なマシン台数を把握できるかどうかが、長持ちするシステムと3か月で置き換えが必要になるシステムを分ける
    • 2012年末時点の一般的なマシンは24GBメモリを持ち、OS に4〜5GB、リクエスト処理に少なくとも数GBが必要で、tweet id は8バイト、といった概算が必要になる
    • Jeff Dean の Numbers Everyone Should Know は期待値をつかむのに役立つ
  • 機能フラグ はインフラをロールアウトする方法である
    • 機能フラグはフロントエンドの A/B テストだけでなく、インフラ置き換えにも強力に使える
    • 「big cutover」方式は、発見が遅れたバグのためにロールバックを強いられ、多くの失敗プロジェクトを生んできた
    • 単一データベースから、新しいストレージを隠蔽したサービスへ移るときは、新サービスへの write の並列送信を少しずつ増やし、バックフィル完了後にユーザー応答には使わない状態で read を試し、旧新システムの read 比較を行ったうえで、実際の read を段階的に増やせる
    • 問題が起きたら、フラグ値を下げるか 0 に即座に戻せるため、トラフィック量を調整しながらデバッグと実験ができる
    • 機能フラグは、コード内の局所的複雑さを、全体の単純さと回復力に交換するトレードオフである
    • 複数バージョンのインフラとデータが例外ではなく常態だと受け入れなければならない

データモデルと境界設計

  • ID空間はシステムの形を決める
    • 1つのデータに到達するのに必要な ID が多いほど、データをパーティショニングする選択肢は増える
    • 1つのデータに必要な ID が少ないほど、システム出力を消費しやすい
    • Twitter API v1 は tweet を単一の 64 ビット数値 ID で参照・生成・削除しており、この ID は他のデータと結び付いていない
    • tweet 数が増えると、同じユーザーの tweet を同じマシンに保存して、ユーザー tweet timeline や購読 timeline を効率的に構成できるが、公開 API が tweet id だけで全 tweet をアドレス指定できるようにしていたため、どの user がどの tweet id を所有しているかを知るための参照サービスが必要になる
    • 代案として、tweet の参照に user id を要求する、あるいは tweet id 自体に user id を含めることもできるが、後者には tweet id がもはや k-sortable numeric ではなくなるコストがある
    • ID 構造は、非公開データの匿名性解除、想定外のクローリング、自動増分 ID の問題、Insecure Direct Object References のような攻撃に影響しうる
  • データ局所性 を活用すべきである
    • データ処理とキャッシュを永続ストレージの近くに置くほど、処理効率は高くなり、キャッシュ整合性と速度を保ちやすい
    • ネットワークはポインタ参照や fread(3) よりも障害とレイテンシが多い
    • 局所性は空間だけでなく時間にも適用される
    • 複数ユーザーがほぼ同時に同じ高コストのリクエストを行うなら、1つにまとめられるし、同種データへのリクエストが時間的に近接して発生するなら、より大きな1つのリクエストに束ねられる
    • こうした方法は通信オーバーヘッドを下げ、障害管理を容易にする
  • キャッシュされたデータを永続ストレージへ書き戻すのはよくない
    • とくに分散システム経験が浅い人が最初に設計したシステムでは、この欠陥が現れやすい
    • 「Russian-doll caching」をうたう実装では、目立つバグに遭遇する可能性が高い
    • 典型的な症状は、ユーザー情報、たとえば screenname、email、hashed password が以前の値に戻ってしまう現象である
  • コンピュータは思っている以上に多くの仕事ができる
    • 2012年末の軽量なウェブサーバーでも、6個以上のプロセッサ、24GB メモリ、非常に大きなディスク容量を備えている
    • 現代的な言語ランタイム上の比較的複雑な CRUD アプリケーションでも、単一マシンで数千 requests per second を数百ミリ秒以内に処理できる
    • ほとんどの場合、マシンあたり数百 requests per second は、運用能力の観点では誇れる数値ではない
    • アプリケーションをプロファイリングし、測定に基づく効率化を導入すれば、より高い性能を得るのは難しくない
  • CAP定理はシステム構築法ではなく 設計を批判するための道具 として使うべきである
    • CAP定理はあまりに一般的で、可能な解法空間が広すぎるため、第一原理として機能するシステムを導くには向いていない
    • システム設計について、CAP が下位システムに課す制約を繰り返し検討していけば、より良い設計に到達できる
    • C、A、P のうち CA は選べない
  • サービスを抽出すると、カプセル化とデプロイの利点を得られる
    • ここでいうサービスとは、ストレージシステムより高いレベルのロジックを含み、通常はリクエスト・レスポンス型 API を持つ分散システムを指す
    • 特定のコードがシステム内部ではなく別サービスにあるほうが変更しやすいか、継続的に検討すべきである
    • 抽出されたサービスはライブラリのようにカプセル化を提供しつつ、クライアントシステムのライブラリをアップグレードするよりも、変更のデプロイを速く容易にできる
    • 小さなサービスはコードと運用依存が少なく、厳格な境界によってライブラリで許されがちな近道を減らせる
    • 複数のクライアントシステムがある場合、共有ライブラリでは各クライアントのデプロイ調整が必要になり、データ破損がデプロイ順序に依存するならさらに難しくなる
    • クライアントシステムの保守担当者が異なると、優先順位が合わず、アップグレードへの合意を得るための社会的調整コストも大きくなる
    • 代表的なユースケースは、変更中のストレージ層を、より使いやすく表面積の小さい API を持つサービスの背後に隠すことである

1件のコメント

 
GN⁺ 2024-09-03
Hacker News のコメント
  • この記事では CALM(論理的単調性としての一貫性) をぜひ扱うべきだった。CAPよりはるかに理解しやすく、より根本的な結果であり、経験の浅い人でも非常に堅牢な分散システムを作れるようにしてくれる
    冪等性、CRDT、WAL、RaftはいずれもCALM原則の特殊ケースである
    https://arxiv.org/pdf/1901.01930

    • その論文より、この記事のほうが 6年先 に出ていた
    • bloom のリポジトリを見るとかなり停滞しているようだが、今も作業が続いているのか気になる
  • ここでは抜けているが、一般原則として気に入っているものがある。正確に一度だけの配送は不可能だという点だ
    最大一度、または少なくとも一度なら可能だが、どの失敗モードを受け入れるかを選び、それに合わせて設計しなければならない

    • ただし、それは同じトランザクションドメインを共有していない、または論理的に単調でない 2つの分散システムの間 での話である
      クラスタデータベースで、ある行のデータを別の行へ移すことは可能であり、それをメッセージが配送されたと解釈することもできる
      システム全体が冪等であるか、分散システム全体を一緒にロールバック可能な単一の単位として扱えるなら、正確に一度だけの配送を得られる。つまり、ドメイン外の他システムに対する副作用があってはならない
      どちらも一種の 論理的単調性 に当たる。冪等性は見分けやすく、トランザクション性もWALやRaftのようなアルゴリズムを通じた単調性に基づいている
      この記事では CALM(論理的単調性としての一貫性) を扱うべきだった。CAPよりはるかに理解しやすく、より根本的な結果である
      https://arxiv.org/pdf/1901.01930
    • 分散システムを設計する際に、この概念を知らないエンジニアをキャリアを通じて多く見てきた。本当に強調してもしすぎることはない
    • 任意のレベルのネットワーク障害がある環境では、少なくとも一度の配送 も不可能である
    • この教訓で重要なのは、「そして実はそれは必要ですらない」という点である
    • Apache Flink は、チェックポイント機構に参加するデータソースとデータシンクと一緒に使えば、エンドツーエンドの正確に一度の保証を提供する
      An Overview of End-to-End Exactly-Once Processing in Apache Flink (with Apache Kafka, too!) — https://flink.apache.org/2018/02/28/an-overview-of-end-to-en...
      Flink's Fault Tolerance Guarantees — https://nightlies.apache.org/flink/flink-docs-release-1.20/d...
  • 「問題をメモリに載せられるなら、おそらく取るに足らない問題だ」の系として、メモリに載せられる範囲 は思ったよりずっと大きい
    大容量RAMが何かは分かっているつもりだったが、主要クラウドがSAP HANA向けに 12TB VM を提供し始めたときに考えが変わった
    記事でも「コンピュータは思ったより多くのことができる」とごく短く触れているが、そこでは24GBのマシンの話しかしていない。2012年時点という点を考慮しても、当時でもRAMがその10倍あるマシンはかなりあったはずだ

    • 比較的シニアなエンジニアでも、この間違いをかなり頻繁にする。顧客ごとの分析データが最大 100GB のSaaSなら、結局はシャーディングしたPostgresで十分だ
  • 一緒に働いている人の中で最も有望な人たちに、この文書を共有している
    Lookoutで働いていたとき、Jeff Hodgesがこのエッセイを発表として共有し、最後に「エンジニアリングが政治的でないふりをするな」という系を付け加えた
    コードが自ら語ると思っている人たちは、何かがどう作られるかに影響を与え、実際の成果を得るうえで重要な側面を見落としている
    10年たった今でも、エンジニアリングリーダーシップ と通常のSRE/DevOpsの基礎能力とみなされるものの交点を、ここまで簡潔に理解している人は多くない

    • このテーマについて、ほかに読むべき良い資料があれば知りたい
  • 以前の議論
    https://news.ycombinator.com/item?id=5055371
    346 points|jcdavis|12 years ago|42 comments
    https://news.ycombinator.com/item?id=12245909
    386 points|kiyanwang|8 years ago|133 comments

  • 素晴らしい一覧で、実用的で現実的な説明 がよい。バズワードもなく、「マイクロサービス」もない
    この助言の多くは、単一マシンのシステムにも当てはまると思う。プログラム間IPCや、1つのプロセス内のスレッド調整のように、ある程度分散した下位コンポーネントが多く存在しうる
    単一マシンの統合メモリという概念も、ある程度は虚構だが、それでもハードウェアは「本物の」分散環境より良い保証を提供できる
    記事で「分散」と「単一マシン」を比較している助言の多くは、マルチスレッドとシングルスレッド の比較にもかなりよく当てはまる
    さらに別の軸として、プログラムを作って複数の人に実行してもらうよう配布すれば、それも一種の「分散」状況になる。野に異なるバージョンが存在し、互換性やアップグレードの問題を気にする必要があるため、記事で触れられている機能フラグも関係する
    分散性は、単一CPUから複数CPU、密に結合された複数コンピュータ、世界中に分散した複数コンピュータまで続くスペクトラムに近く、その間に複数の地点と複数の次元がある

    • 「分散システム」という言葉には、デプロイ方式に関する制約は含まれていない。定義上の核心的な性質は、異なる 制御フロー がメッセージ配送を通じてネットワーク越しに通信するということだけである
      同じマシン上の複数プロセスがlocalhostで通信するのも、非常に有名な分散システムの例であり、実際に多くの人がそこで分散システムを初めて学ぶ
    • AMDが各チップレットごとに異なるメモリ空間を提供していたかもしれない、近すぎるだけにいっそう惜しい隣の宇宙のことをよく考える
      そうであれば、誰もがすべてのコードを美しい 分散メモリMPIプログラム として書いていただろう。フォールスシェアリングも消え、通信パターンを明示的に深く考えるようになっていたはずだ
  • ここ数年の間に、この記事の著者と少し一緒に働く機会があった。Jeffは、私が学んできた人たちの中でも最も 洞察力があり前向きな人 の一人だった
    自分が抱える困難について驚くほど率直で、メンタリングや助言にも気持ちよく応じてくれた

  • 「分散システムは頻繁に失敗するから違う」で重要なのは、単純な失敗率ではなく、複数ノードからなるシステムでの失敗率 である
    そして「分散システムの問題」は、ネットワークで接続された複数サーバーだけで起きるわけではない。互いに関係を持つノードの集合であれば、論理的につながったディスク上のファイルや、異なる入出力デバイスのバッファでも似た問題が起きる

    • その通り。実際、メモリに固定された単一スレッドのプロセス1つを超えるほぼすべてのソフトウェアシステムで、この種の問題は起こりうるし、実際に起きる
      一部の古参は、こうした問題を緩和する過程で生じる過度な複雑さをあざ笑い、単に単一サーバーでソフトウェアを動かせばはるかに単純になると不満を言う
      現実には、AS/400やVAXftの時代でさえその主張はほとんど正しくなく、より混沌とした マルチユーザー・マルチプロセスUnix の世界にはなおさら当てはまらなかった
  • 「問題をメモリに載せられるなら、おそらく取るに足らない問題だ」は、一部の分散システムエンジニアの間でよくある誤りである
    まったく取るに足らないわけではなく、単に扱うべき問題が相補的な領域へ移るだけだ
    この誤りは、きちんと最適化すれば 単一マシン でできることを100台のクラスタで処理しなければならない状況に簡単につながる