[レポート]Everything fails, all the time:分散システムにおける耐障害性のある設計について #AP-19 #AWSSummit

2023.04.21

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

AWS Summit tokyo 2023の「Everything fails, all the time:分散システムにおける耐障害性のある設計について」のセッションレポートです。

セッション概要

【スピーカー】 アマゾン ウェブ サービス ジャパン合同会社 グローバル・オートモーティブ事業部 シニアソリューションアーキテクト

小林 芙美

システムは必ず壊れます。マイクロサービスのような分散システムや、AWS のマネージドサービスを活用したクラウドネイティブなシステムは、上手く設計することで高い耐障害性を実現できます。一方でコンポーネント間の連携が複雑化することで障害の原因特定が困難になったり、挙動を完全に予測することが困難になります。このような分散システムにおいて、高い耐障害性を備えたシステムを設計するための方法をお話しします。

セッション視聴

5/22からAWS Summit Tokyoの登録を行うことでオンデマンドで視聴がいただけます (現地参加された方は改めての登録は不要です)

https://aws.amazon.com/jp/summits/tokyo/

登録済みの場合、以下から直接遷移できます

https://jpsummit.awsevents.com/public/session/view/546

セッションレポート

  • ゴール
    • マイクロサービスによる分散システムにおける課題を理解する
    • 具体的な課題例に対して有効なデザインパターンをしり、実装のイメージをもてるようにする

なぜマイクロサービスで分散させたいか

  • 旅行の予約アプリを例とする
  • モノリスなアーキテクチャで構成
    • 飛行機の予約機能だけスケールアウトしたい
    • アプリケーション全体のスケールアウトが必要
    • 支払い機能だけ追加処理を入れたい
    • 変更しない機能もレグレッションテストする必要ある

分散システムでよく発生する課題とその解決策

  • 忘れては行けないネットワークの存在
    • サービスを分割するとネットワークを挟む分、障害のリスクはあがる
    • モノリスでもクライアントからサーバーへ通信する際にも起こり得る
    • モノリスの場合、同一ノード内のためよりオーバーヘッドの少ない方法を選べる
  • モノリスより障害を前提に考える必要がある
    • ネットワーク障害でサービス間の通信が失敗
    • 特定サービスで流量制限をしている際に、スロットリングエラー
    • データの一貫性をどう担保するのか

一時的なエラーでリクエストが失敗する場合

  • 自動リトライを実装
    • すぐ復旧しない場合、単純にリトライを繰り返すとサーバー負荷があがる
  • エクスポテンシャルバックオフ
    • 1回目、2回目の失敗で復旧までの待ち時間を長くする
    • 複数のリクエストが同じタイミングでバックオフすると、同時にリトライされて負荷があがるタイミングが重なる
  • ジッター
    • リトライの感覚をランダムに変える
    • エクスポテンシャルバックオフと組み合わせる
  • StepFunctionsでリトライを実装
    • マイクロサービス間の調整役につかう
    • ジッターの機能は単体でできないが、エクスポテンシャルバックオフを簡単に実装できる
  • 原因不明な問題により、特定サービスで障害発生
    • 処理が終わるまで同期的に待つ
    • ツアー予約からツアーの推奨システムを呼びだすとする
    • 推奨システムのダウンでツアー予約もタイムアウトになる
  • サーキットブレイカーパターン
    • 障害の伝搬の抑制
    • 呼び出したいサービスに異常が発生した場合に、サービスの呼び出しを停止
  • ツアー予約と推奨システムの間にサーキットブレイカーを置く
  • 実装例
    • 共通ライブラリを使用するパターン
    • サービスメッシュ(App Mesh等)
    • StepFunctionsで実装
      • サーキットの状態はDDBで管理する
      • 正常時、Lambda実行->DynamoDBみてアイテムなし->サービス呼び出し
      • 呼び出しで問題あった場合、
        • 失敗トリガーで状態を更新するLambdaでアイテムを追加
      • 異常時、Lambda実行->DDBみてアイテムあり->サービス呼び出さない
      • Half Open(定期的なリトライ)
      • DynamoDBのTTLでアイテムを自動的に削除する(DynamoDBの機能でアイテムを削除)

データソースが分散されている場合

  • ポリグロット・パーシステンス
    • システム毎にDBを使い分ける
      • ホテル予約はAurora
      • 飛行機予約はDDB
      • 支払い 決済Saas
  • 最後の処理だけ失敗する
    • ホテル予約->飛行機予約->支払いの流れで支払いが失敗
    • システムの整合性を保つのが難しい
  • Sagaパターンによる分散トランザクション
    • 一連の流れの取り消しを行う
    • StepFunctionsを使って実装することもできる

障害パターンを網羅的に予測することは可能か

  • 各サービスの頻繁な更新、呼び出し順所やパターンも多様
    • ある時点でのスナップショットを取ることが難しい
  • AZ障害でシステム全体として定常状態を保てるか
    • ビジネス的な指標を考える
    • ユーザーが支払い処理を完了した数、単位時間あたりの成約数
    • カオスエンジニアリング
    • 実際に本番環境に障害を起こす手法
    • メリット
      • 隠れた問題を見つけることができる
      • 可視化できていなかったメトリクスやログを見つけれる
      • 耐障害性やパフォーマンス向上
    • フェーズを回す(定常状態->仮説->実験->検証->改善)
    • 課題
      • ツール毎に対応するプラットフォームや言語が異なる
      • 複数のツールやスクリプトの組み合わせが必要
      • 本番に影響を与えずに実施することは難しい
    • 安全に実験を止める必要がある
      • AWS AWS Fault Injection Simulatorでカオスエンジニアリングを試せる
      • まずは開発/STG環境が使える

感想

マイクロサービスにおける耐障害性のある設計の重要さから、具体的な課題例・解決策について説明いただきました。

自動リトライ・サーキットブレイカー・Sagaパターン・カオスエンジニアリングについて、具体的なAWSサービスでの実装例も知ることができました。

個人的には、StepFunctionsの汎用性に驚きました。マイクロサービスを実現する際に

文字だけでは伝わらないところもあると思いますので、資料や動画が公開されたらぜひご確認ください。

以上、AWS事業本部の佐藤(@chari7311)でした。