【セッションメモ】AWS Summit 2022 - Design for Resilience - 如何にしてクラウドアプリケーションの耐久力を高めるか
まえおき
先週開催されたAWS Summitのセッションの中で「Design for Resilience - 如何にしてクラウドアプリケーションの耐久力を高めるか」というセッションがとてもよかった&勉強になった。AWS要素は少なくどのクラウドサービスを利用しても適用できるSREの基礎的な内容で、新人やクラウドをこれから始める人はぜひ1回は聞いてほしい内容だった。
6月30日までセッションの動画アーカイブが公開されるらしく、復習しながらメモを書いてみた。
アーカイブを見たい場合はAWS Summit Japnanページからログインして、Design for Resilience - 如何にしてクラウドアプリケーションの耐久力を高めるか(SP-04)
というセッションを検索すると出ると思う。
資料も早く公開になったらいいな。
レジリエンスとは
言葉の定義
- 「抵抗力」、「回復力」、「弾力性」
- 最近では「困難な状況にも関わらず、しなやかに適応する能力」と言われる
情報システムにおけるレジリエンス
- 定常的に発生しうる部分的な障害に対する適応力 → HA(High Availability)
- MTBF(平均故障間隔)・MTTR(平均復旧時間)
- めったに発生しない広範囲の障害に対する回復力 → DR(Disaster Recovery)
- RTO(目標復旧時点)・RPO(目標復旧時間)
- 両方の目的は「アップタイムを長く、ダウンタイムを短く、データ損失は最小限にする」
レジリエンスの重要性
- 我々の日常生活・ビジネスはシステムに大きく依存している
- メール、生産、物流、ネットの買い物、銀行のATM...
- 今後システム依存度はどんどん強くなり、代替手段を戻すことも簡単ではない
障害とは
- システムが正常な稼働状態を維持できなくなること
- 障害の原因 → コントロールが効きにくい(すべての原因・変化を事前に想定して取り除くことは難しい)
- コードや設定、データや状態、コアインフラ、災害シナリオ
- 障害の影響 → コントロールが効きやすい(影響を最小限にすることはやりやすくなっている)
- エラー応答、レイテンシー増大、接続性低下、サービス中断
- 障害の原因を取り除くことに過当に投資するよりも、障害の影響を小さくすることに投資する
クラウドにおけるレジリエンス
- ユーザとAWSの責任共有モデルより成立する
- お客様
- Resilience in the Cloud(クラウド上のレジリエンス)
- AWS上に構築するもののレジリエンス → クラウドの設定など
- アーキテクチャ、開発プロセス、組織と文化
- AWS(クラウド)
- Resilience of the Cloud(クラウドそのもののレジリエンス)
- AWSが提供するクラウドサービス → データセンターやハードウェアのレジリエンスを含む
アーキテクチャ
基本戦略
- ウェブサーバ、DB、ネットワークに障害が起きたときにシステムの全体機能を維持させる
冗長化
- ウェブサーバとDBか各1台、各可用性が0.99であるときのアプリケーションの可用性
- 0.99 × 0.99 = 98%
- ウェブサーバとDBか各2台、各可用性が0.99であるときのアプリケーションの可用性
- 1-(1-0.99)^2 × 1-(1-0.99)^2 = 0.9999 × 0.9999 = 99.98%
- ウェブサーバとDBか各3台、各可用性が0.99であるときのアプリケーションの可用性
- 1-(1-0.99)^3 × 1-(1-0.99)^3 = 0.999999 × 0.999999 = 99.9998%
隔離
- シングルデータセンターデプロイメント
- 1つのデータセンターにアプリケーションをデプロイすること
- データセンター単位の障害が発生した場合アプリケーションは機能しない
- マルチアベイラビリィゾーンデプロイメント(Multi-AZ)
- 複数のデータセンターにシステムを分散させてデプロイすること
- データセンター単位の障害からアプリケーション全体を保護
- 各AZは個別の電源、冷却システムを持っている。自然災害の影響範囲で意味がある距離を持っている → 同時の障害の影響の受けにくい
- マルチリージョンデプロイメント
- 複数のリージョンにシステムを分散させてデプロイすること
- AWSリージョン単位の障害からアプリケーション全体を保護
- Route53でルーティング、AuroaDBでデータ複製、DynamoDBのグローバルテーブル、S3のクロースリージョン
構造
- サービス指向アーキテクチャ
- アプリケーションの全機能でリソースを共有すると、一部の機能で発生した障害がアプリケーション全体に波及しやすい
- アプリケーションを複数の独立したドメイン(サービス)に分割し、サービスにリソースの境界を設定することで、障害の影響は印意を特定のサービスに限定することができる
- 在庫サービスで障害があっても、カートサービスは影響は受けない
- セルアーキテクチャ
- サービスが所有するリソースをすべてのトラフィックが共有すると、一部で発生した障害がサービス全体に波及しやすい
- サービスをさらに複数の独立した区画(セル)に分割し、トラフィックをルーティングすると、セルにリソースの隔壁を設定することで障害の影響範囲を特定のセルに限定できる
- 在庫サービス「セル1」で障害があっても、在庫サービス「セル2」は影響を受けにくい
振る舞い
- アプリケーション構成の依存関係も重要
- 障害は依存関係をたどりながら波及する可能性が高い → サービスAが障害があったら、サービスAを呼ぶサービスBにも影響がある
- リトライ
- 呼び出すサービスが一時的に利用できなくなったら、再度サービスを呼ぶ
- ただし、短い時間で複数リトライをすると過負荷がかかり、余計にサービスの回復が遅れる可能性がある
- 指数バックオフ(Exponential Backoff)などにより、呼び出しの間隔を増やしたり、ランダムな待機時間をおいて、リクエストの衝突を避ける
- タイムアウト
- 呼び出しサービスが応答しない場合、タイムアウトよりリソースを開放する
- タイムアウトがないとサービスのリソースを消費し続けてしまい、過負荷がかかる → 障害の拡大
- 呼び出しサービスが応答しない場合、タイムアウトよりリソースを開放する
- フォールバック
- サービスの呼び出しに失敗した場合、あらかじめ準備した応答を返却することでエラーの連鎖を遮断する
- 以前応答のキャッシュやStaticな応答を利用
- 代替手段を利用して応答する場合、慎重に検討が必要
- 代替手段の可用性も考慮しないといけなくなる
- 代替手段が副作用を持っている場合がある
- サービスの呼び出しに失敗した場合、あらかじめ準備した応答を返却することでエラーの連鎖を遮断する
- サーキットブレーカー
- 呼び出しサービスの応答失敗率が一定を超えた場合、以降の呼び出しを即座に失敗させて過負荷から保護する
- サーキットブレーカーの状態
- オープン → サービスを呼ばずにリクエストを失敗させる
- ハープオープン → 一部のリクエストを呼び出しサービスに転送。成功率が閾値に到達したら、クローズ状態になる
- クローズ → すべてのリクエストを呼び出しサービスに転送
- レート制限
- 実行数が一定値に到達した場合、超過したリクエストを即座に失敗させて過負荷から保護する
- 失敗の代わりにキューイング(Queueing)するとのオプションもあり
開発プロセス
モニタリング
- モニタリングを洗練させるのは重要
UCM(User-Centric Monitoring)
- ユーザに近い場所から監視する
- サービスの外側から内側へ
- 影響から原因へ
- 正常性メトリックス
- 障害がユーザに影響しているか
- エラーレート、レイテンシー、リクエスト数、カナリアテスト失敗など
- 必ずアラームを設定し、アラームが鳴ったらオンコールエンジニアに連絡をする
- 診断メトリックス
- 障害の原因は何か
- リソース可用性、リソース使用率、エラーログ・トレース、依存関係の正常性メトリックスなど
- すべてにアラームを設定する必要はなく、ユーザへの影響に直結するもののみ設定する
ダッシュボーディング
- 最重要なメトリックスは最上部に大きく表示
- 人は上にあるものを重要なものだと認識する
- 正常性メトリックスを配置する
- 画面解像度の低いデバイス用にレイアウト
- 横方向のスクロールは気づきにくい
- 単一のタイムゾーン(UTC)で表示
- 同一の時間軸と分解能でデータを表示
- アラームの閾値でグラフに注釈をつける
- グラフの状態が正常か異常を一発でわかるようにする
- グラフの説明文をテキストで表示
- APIの機能、正常時のグラフの模様など
- 障害時の原因探しに参考にURLなどを記載してもよい
- アプリケーションの修正時はダッシュボードの修正が必要かを考える
- 本番前の環境でもダッシュボードが利用できるようにIaCで管理することがおすすめ
デリバリ
2つの前提
- デリバリを自動化すること
- 手動の変更には再現性がない
- 操作ミスの危険性にさらされる
- 変更を細かい単位でデプロイすること
- 変更が大きいと障害時の原因探しを難しくする可能性がある
フラクショナルデプロイメント
- デプロイ順序
- ソース
- ビルド
- テスト(サービス単体テスト)
- テスト(複数サービスの統合テスト)
- テスト(本番同等構成での運用テスト) - One-Box
- サービスの一つの区画、セルアーキテクチャの1つのセル
- テスト(本番同等構成での運用テスト) - Fleet
- 本番
5.テスト(本番同等構成での運用テスト)-One-Box
でも、段階的にデプロイする- ブルーグリーンデプロイ
- ローリングアップデート
2フェーズデプロイメント
- ロールフォワード同等にロールバックの成功率も重要 → 後向互換性
- ロールバックが失敗する一般的な理由は「プロトコール」の変更
- S3オブジェクトをXMLで読み書きしていたが、形式をJSONに変更(v2)
- その後デプロイ後問題が発生し、ロールバック(v1)
- 旧バージョンではJSONオブジェクトを読むことができない
- デプロイを分割する
- JSONとXMLを両方読めるようにデプロイ。書き込みはXMLのみ(v2)
- 安定したら、書き込みもJSONに変更(v3)
- ロールバックしてもJSONを読むことができる(v2)
- テスト時はロールバックが成功することも確認する
テスト
カオスエンジニアリング
- 単純に本番環境でランダムに仮想マシンを落とすことではない → 厳密にコントロールされた環境で行われる仮説検証のプロセス
- アプリケーション全体が障害から期待通りに回復することを検証する
- カオスエンジニアリングの流れ
- 定常状態の定義を決める
- アプリケーション全体が正常に稼働していることを、正常性メトリックスより定義する
- 仮説を構築
- このような障害が起きても、アプリケーションはこの振る舞いをして回復する"
- 構築時に仮説通りにいかなかいことを見つかったら、アーキテクチャを見直す
- 実験
- 仮説に基づいて人為的な障害をアプリケーションで起こして、メトリックスをウォッチする
- 想定通りに
- 障害が検出されたか
- 障害が通知されたか
- 障害が抑止さrたか
- 仮説より障害影響が大きくなったら、実験をストップ
- 仮説が成立したら、再度定常状態の定義を実施
- 仮説が成立しない場合、未知の問題を本当の障害の前に発見したことになる → 実験成功
- アーキテクチャ、メトリックス、デリバリなどの見直して再度実験
- カオスエンジニアリングの目的
- アプリケーションの回復力を高める
- チームに障害対応を自身を構築する
- 発生させる障害の例
- インスタンス停止
- ネットワークのレイテンシー増大・バケットロス
- プロセス停止
- CPU・I/O・メモリーストレス
- APIのエラー
- APIのスローダウン
- 障害発生の準備
- 一部はAWS Fault Injection Simulatorで起こすことができる
- アプリケーションレベルの障害はライブラリやプロキシのレイヤで個別対応が必要
- サービスのリクエストに対して一定の比率で特定のレスポンスを返すなど
- カオスエンジニアリングはいきなり本番で始めない
- まずは本番同等の環境を準備してGameDay型式で実施する
- 十分経験を積んだあとに、デリバリパイプラインにカオスエンジニアリングを組み込んで自動化する
- 自動化した状態で経験を積んだ後に、初めて本番環境で実施する
組織と文化
動機付け
明確で完全なオーナーシップの定義
- 一つのチームは一つのサービスを所有
- 一つのサービスは一つのチームが所有
- チームはサービスの全構成要素を所有
- チームはサービスの全フェーズを所有
不明確で不完全なオーナーシップの影響
- アーキテクチャや開発プロセスの習熟度が低下
- 理解すべき範囲が広い
- 他チームへの依存が生じる → 自分事ではない
- 結果、サービスの理解が落ちるので障害の対処が遅れる
- リソースの境界設定や振る舞いの最適化が抑制
- 逆コンウェイの法則が発生
- 複数サービスを所有していると、サービス間でリソースの共有が起きる
- サービスを共有しているチームでは、他サービスからの障害連鎖を防止する意識が薄くなる
- 一部の構成要素・フェーズに改善意識が縮小
- チームが所有している構成要素、フェーズ以外のものは改善の責任がない
- 障害の対処のが極小、表面的
明確で完全なオーナーシップの影響
- アーキテクチャや開発プロセスの習熟度が向上
- 理解すべき範囲が狭い → サービスをよく理解しているので障害への対処が早くなる
- リソースの境界設定や振る舞いの最適化が促進
- 逆コンウェイの法則がいい方向で発生
- 他チームの障害に巻き込まれないようにする
- 一部の構成要素・フェーズに改善意識が拡大
- サービスの問題にはチーム全体が責任を持つ
組織形成 → COE(Correction of Error)
- 障害の復旧後にアーキテクチャや開発のプロセスを深く分析して改善のためのアクションを特定する
- 分析の過程で障害の影響、経緯、原因対策を記述したポストモーテムを作成
- 作成したポストモーテムは他のチームに公開することで、他のチームの改善に貢献する
- ポストモーテムの構造
- 障害の概要
- 障害の影響、経緯、対策のハイレベルな情報
- メトリックス
- 障害の影響や問題の特定に利用したメトリックスのグラフ
- 必要なメトリックスがない場合は、モニタリングを見直す必要がある
- 影響
- 障害の影響を受けたユーザ数、時間や程度、間接的なビジネスへの影響
- タイムライン
- 障害中に発生したすべてのイベントを時系列に並べたもの
- ラーニング
- 障害の対処・分析を通じて学習できたことを記載
- アクションアイテム
- アーキテクチャや開発プロセスの改善策を優先度・責任者・期限と合わせて記載
- 障害の概要
- AWS SystemsmanagerでCOEテンプレートが利用できる
- 個人や人を分析しない
スケール → 週次の運用ミーティング
- アジェンダ
- "Ops Win"発表
- オペレーションの改善で大きな効果を出した事例を紹介
- COEポストモーテムレビュー
- 障害があったチームで作成したポストモーテムを紹介&フィードバック
- ダッシュボードレビュー
- チームのダッシュボードを紹介&フィードバック
- ダッシュボードを紹介するチームは直前のルーレッドで決定
- ルーレッドはオープンソースで公開中
- "Ops Win"発表
まとめ
- クラウドのレジリエンスは責任共有モデルから成立
- アーキテクチャ
- 振る舞い
- 異常に応じた振る舞いを選択し、依存関係による障害の連鎖を遮断
- 構造
- アプリケーション内部のリソース境界により障害の波及を防止
- 基本戦略
- すべてのリソースを冗長化し、隔離したロケーションに展開
- 振る舞い
- 開発プロセス
- テスト
- 仮説検証プロセスを通じて、アプリケーションの回復力を強化
- デリバリ
- 段階的なデプロイと変更の分割により障害の影響を緩和
- モニタリング
- ユーザーを中心に監視することで障害の検出と診断を短縮
- テスト
- 組織と文化
- スケール
- すべてのチームがオペレーションの改善に貢献できる機会を設定
- 組織形成
- 体系的に事後分析プロセスを通じてプラクティスを学習
- 動機付け
- チームとサービスの境界を一致させることで課題意識を醸成
- スケール
- The Amazon Builders'Library
- Amazonで利用している開発や運用のプラクティスを紹介しているページ
- AWS Well-Architected Framework - 信頼性の柱
- 1つ1つの取り組みが快適な睡眠を保証する