AWS FIS Chaos Engineering Aurora AZ S3 EKS GameDay Terraform 本番

目次

1. この記事について — 復旧・運用編シリーズ Vol2

fig01: AWS FIS Chaos Engineering 全体アーキテクチャ図

【復旧・運用編シリーズ Vol1-4 ラインナップ】

  • Vol1: Multi-Region×Multi-AZ Backup/DR 本番実践 (公開済)
  • Vol2 (本記事): Chaos Engineering with AWS FIS 本番運用 ← 本巻
  • Vol3: Incident Response Runbook × SSM Automation (予告)
  • Vol4: Multi-Region Active/Active vs Active/Passive 設計実装 (予告)
【この記事で学ぶこと】

  • AWS FIS (Fault Injection Service) Experiment Template の 4 要素 (Action / Target / Stop Condition / IAM Role) Terraform 完全実装
  • Aurora 障害実験 (aws:rds:failover-db-cluster) で Vol1 Multi-Region DR 構成を機械検証 + RTO/RPO 自動実測
  • AZ 停止実験 (Power Interruption / Application Slowdown / Connectivity Disruption) の 3 Action 選定基準
  • S3 レイテンシー注入 + EKS Pod 停止のアプリ層障害シナリオ Terraform 実装
  • CodePipeline + EventBridge + Lambda + Slack 通知による CI/CD GameDay 完全自動化
  • 3 段階安全策 (Stop Condition + Blast Radius + Cooldown) で本番事故を防ぐ設計原則

本記事は復旧・運用編シリーズ第 2 巻として、Vol1「Multi-Region×Multi-AZ Backup/DR 本番実践」で確立した Multi-Region DR 構成を、AWS FIS (Fault Injection Service) によって機械的に検証する本番級 Chaos Engineering の Terraform 実装記事です。Vol1 §7 で「DR 訓練未実施の設計は紙の上のフィクション」と警鐘を鳴らした論点に対し、本 Vol2 では Aurora リージョン障害・AZ 停止・S3/EKS アプリ層障害の 3 大シナリオを Terraform aws_fis_experiment_template で IaC 化し、CodePipeline 経由で CI/CD GameDay 自動化までを 1 本で完結させます。

12 巻完結シリーズ (Lambda 3 + EKS 3 + 観測性 3 + セキュリティ 3) で構築した本番運用基盤を Multi-Region 構成へ拡張する Vol1 起ち上げ巻に続き、Vol2 ではその構成が実際の障害下でも動作することを機械的に証明する手段を提供します。観測性 3 部作の SLO Burn Rate Alarm を FIS Stop Condition に連携させ、セキュリティ 3 部作の GuardDuty / IAM / AWS Config で実験中の異常検知・権限最小化・構成ドリフト検知を維持します。

1-1. Chaos Engineering の北極星 — 「DR Runbook は紙の上のフィクション」を打破する

復旧・運用編シリーズ Vol1「Multi-Region×Multi-AZ Backup/DR 本番実践」で確立した Multi-Region DR 構成は、設計段階では完璧に見えます。しかし「設計が正しい」と「障害下でも正常動作する」は別の命題です。AWS FIS (Fault Injection Service) は、その命題の後者を機械的に証明するための手段です。

Vol1 §7 で警鐘を鳴らした「DR Runbook は紙の上のフィクション」という問題提起に対し、本 Vol2 が提供する答えは明確です。Aurora リージョン障害・AZ 停止・S3/EKS アプリ層障害の 3 大シナリオを Terraform aws_fis_experiment_template で IaC 化し、CodePipeline 経由の CI/CD GameDay 自動化によって定期的・継続的に証明し続ける仕組みを構築します。

本 Vol2 で得られる成果:

成果詳細
Aurora DR 機械検証aws:rds:failover-db-cluster で Vol1 Multi-Region DR 構成の RTO ≤ 60 秒を自動実測
AZ 障害シナリオ網羅Power Interruption / Application Slowdown / API Internal Error の 3 パターン実装
アプリ層耐障害性証明S3 スロットリング + EKS ノード終了でマイクロサービス自動回復を検証
CI/CD GameDay 自動化CodePipeline + EventBridge + Lambda + Slack で月次定期実行を完全自動化
3 段階安全策Stop Condition + Blast Radius + Cooldown で本番環境でも安全に実施

12 巻完結シリーズ (Lambda 3 + EKS 3 + 観測性 3 + セキュリティ 3) で構築した本番運用基盤との連携ポイントも明示します。観測性 Vol3 の SLO Burn Rate Alarm を FIS Stop Condition に連携させ、セキュリティ Vol1 の GuardDuty で FIS 実験中の異常検知を維持し、セキュリティ Vol2 の IAM Identity Center で FIS 実験ロールの権限最小化を実現します。

1-2. 読者像 + 前提条件

想定読者 3 ペルソナ:

  1. Vol1 DR 構成済みエンジニア: Aurora Global Database + S3 CRR + Route53 ARC を構築済みで、「本当に復旧するか」を FIS で証明したい方
  2. SRE / DevOps エンジニア: Chaos Engineering を CI/CD に組み込み、GameDay を自動化して定期的に耐障害性を検証したい方
  3. セキュリティ / コンプライアンス担当: DR RTO/RPO の定量的な証明書類が必要で、実測値を監査証跡として記録したい方

前提条件:

カテゴリ要件
ツールTerraform v1.5+ / AWS CLI v2 / Python 3.9+ / boto3
AWS 構成Vol1 Multi-Region DR 構成済みが理想 (Aurora Global DB + Route53 ARC)
IAM 権限AWSFaultInjectionSimulatorFullAccess または各シナリオ別最小権限ポリシー
リージョンap-northeast-1 (Primary) + us-east-1 (Secondary)
コスト目安FIS 実験時間課金 $0.10/実験分 + 各リソースの通常稼働コスト

Vol1 未実施の場合でも §3 〜 §6 の Terraform 実装は独立して適用可能です。ただし §4 Aurora 障害実験は Aurora Global Database の存在を前提とします。

本記事で使用する主要 AWS サービス:

サービス用途料金体系
AWS FIS障害注入・実験実行$0.10/実験分
CloudWatchStop Condition Alarm + 実験ログ既存設定を流用
EventBridgeFIS 状態変化検知 → Lambda 起動$1.00/百万イベント
CodePipelineGameDay 定期自動実行$1.00/アクティブパイプライン/月
Lambda結果集計 + Slack 通知無料枠内
S3実験ログ + RTO/RPO 証跡保存$0.023/GB/月

1-3. 読み進め方ガイド

本記事は実装順序に沿って読み進めることを推奨しますが、担当領域に応じて各章を独立して参照できます。

内容推奨読者
§2設計原則 — Chaos Engineering の 5 原則 + 3 段階安全策全員必読
§3FIS 仕組み — Experiment Template 4 要素完全解説FIS 初学者
§4Aurora リージョン障害実験 (Vol1 DR 機械検証)DB エンジニア / SRE
§5AZ 停止実験 — Power Interruption / Slowdown / API Errorインフラエンジニア
§6S3 + EKS アプリ層障害シナリオアプリ / EKS 担当者
§7CI/CD GameDay 自動化DevOps / 自動化担当者
§8まとめ + 落とし穴 10 選 + Vol3 予告レビュー時

本記事のクロスリンク構造: §4 は Vol1 §5 Aurora 構成を前提、§5 は Vol1 §6 Route53 ARC と連携、§6 は EKS Vol2/Vol3 と連携、§7 は観測性 Vol3 SLO と Stop Condition で連携します。

1-4. 12 巻完結シリーズ連携マップ

本 Vol2 は単独で読んでも完結しますが、12 巻完結シリーズで積み上げた基盤と深く連携することで、より高い品質の Chaos Engineering を実現できます。

Lambda 3 部作との連携:

  • Lambda Vol1 (CloudWatch Logs + Metrics Filter): §7 GameDay 結果集計 Lambda の観測性
  • Lambda Vol2 (Provisioned Concurrency): FIS Lambda 障害実験時の Concurrency 挙動検証
  • Lambda Vol3 (イベント駆動): EventBridge → Lambda result_collector の設計基盤

EKS 3 部作との連携:

観測性 3 部作との連携:

セキュリティ 3 部作との連携:

この連携マップを意識しながら本記事を読み進めると、「FIS がただの障害注入ツール」ではなく「12 巻で積み上げた本番基盤全体の機械的証明装置」として機能することが見えてきます。

前提シリーズ: 復旧・運用編 Vol1 Multi-Region Backup/DR 本番実践を読む


2. 前提・環境・準備 — Chaos Engineering 設計原則 + 安全策

2-1. Chaos Engineering の 5 原則 (Netflix / AWS Well-Architected 準拠)

Chaos Engineering は「本番環境に意図的に障害を注入する無謀な行為」ではありません。Netflix が提唱し AWS Well-Architected Framework Reliability Pillar が採用した、システムの回復性を科学的に検証するための規律ある実践です。

原則内容AWS FIS での実装
1. 定常状態の仮説を立てる正常時の可観測指標 (SLI) を数値で定義し、実験後も維持されることを仮説化するCloudWatch Alarm (SLO Burn Rate / HealthyHostCount) を事前設定
2. 現実の障害を注入する仮想の障害ではなく実際に発生しうるパターンを再現するaws:rds:failover-db-cluster / aws:ec2:stop-instances / aws:s3:bucket-throttle-error
3. 本番に近い環境で実施するステージング環境の結果は本番の証明にならないMulti-Region DR 構成 (Vol1 踏襲) + 本番 Nodegroup 対象
4. 影響範囲を最小化するBlast Radius を制御し、実験の被害を許容範囲内に収めるPERCENT_OF_SELECTED=30% または COUNT(1) から開始
5. 自動化して継続的に実施する一度の実験では証明にならない。CI/CD に組み込んで定期実行するCodePipeline + EventBridge Scheduler (§7)

この 5 原則は本記事の全実装に通底します。Stop Condition なしの実験は原則 4 違反、一度きりの手動実験は原則 5 違反として本番では認めません。

2-2. 3 段階安全策 (Stop Condition + Blast Radius + Cooldown)

本番環境での Chaos Engineering が「無謀な行為」へ転落しないために、以下の 3 層安全策を全実験テンプレートに必須化します。

Stop Condition — 実験の緊急停止装置

FIS はデフォルトでは実験を自動停止しません。stop_condition ブロックで CloudWatch Metric Alarm を必ず指定します。Alarm が ALARM 状態に遷移した瞬間、FIS は実験を強制停止します。

stop_condition {
  source = "aws:cloudwatch:alarm"
  value  = aws_cloudwatch_metric_alarm.slo_burn_rate.arn
}

Stop Condition の推奨指標: SLO ErrorRate > 5% / ALBUnhealthyHostCount > 2 / AuroraGlobalDBReplicationLag > 5,000ms の 3 種を場面に応じて選択します。

Blast Radius — 障害の影響範囲制御

selection_mode で対象リソース数を制限します。

モード用途推奨場面
COUNT(1)固定 1 台のみ初回実験 / 本番テスト開始時
PERCENT_OF_SELECTED(30)対象の 30%本番での定期 GameDay
ALL全対象リソース禁止 (本番での使用は厳禁)

本番環境では COUNT(1) から開始し、3 回連続で Stop Condition が発動せずに正常完了したことを確認してから PERCENT_OF_SELECTED(30) へ段階的に拡大します。

Cooldown — 連続実験によるリソース枯渇防止

シナリオ推奨 Cooldown理由
Aurora フェイルオーバー72 時間以上リージョン間 Replication Lag 収束 + Switchback 完了
AZ 停止 (EC2 停止)48 時間以上ASG スケールアウト + ELB ヘルスチェック安定化
EKS ノード終了15 分以上新規ノード起動 + Pod スケジューリング完了
S3 スロットリング30 分以上クライアント側 Exponential Backoff 収束

2-3. 環境前提 + IAM 権限要件 + コスト試算

環境構成:

本記事の全実装は以下の環境を前提とします。

Primary リージョン:ap-northeast-1 (東京)
Secondary リージョン: us-east-1 (バージニア北部)
Aurora 構成: Global Database (Vol1 §5 踏襲)
Route53 ARC: Routing Controls + ARC Zonal Shift (Vol1 §6 踏襲)
EKS クラスター: IRSA 最小権限 Nodegroup (EKS Vol2/Vol3 踏襲)

FIS 実験ロール IAM 権限 (最小権限原則):

シナリオ別に専用 IAM Role を作成します。AWSFaultInjectionSimulatorFullAccess は広すぎるため本番禁止です。

シナリオ必要アクション
Aurora 障害rds:FailoverGlobalCluster / rds:DescribeGlobalClusters / rds:DescribeDBClusters
AZ 停止ec2:StopInstances / ec2:DescribeInstances
S3 スロットリングs3:GetObject / s3:PutObject / s3:ListBucket
EKS ノード終了ec2:TerminateInstances / eks:DescribeNodegroup / eks:ListNodegroups
全シナリオ共通cloudwatch:DescribeAlarms / logs:CreateLogDelivery / logs:PutLogEvents

コスト試算 (月次 GameDay 想定):

項目単価月次想定月次コスト
FIS 実験時間$0.10/実験分Aurora: 1分×4回 + AZ: 5分×2回 + EKS: 3分×2回約 $2.2
CloudWatch Alarms$0.10/アラーム/月5 アラーム$0.50
Lambda (result_collector)無料枠内月10回起動$0
S3 (実験ログ保存)$0.023/GB/月約 1 MB$0
合計約 $3/月

Aurora フェイルオーバー実験中の接続断は数十秒で、アプリ側 Retry 設定が適切であればエンドユーザーへの影響はほぼゼロです。FIS 実験コストの大半は「実験を実施しないことで発見できなかった障害が本番で発生した場合のコスト」に対して無視できる水準です。

§3 以降では、これらの前提と原則を踏まえた上で Terraform 実装に入ります。まず §3 で FIS Experiment Template の 4 要素を完全解説し、§4 〜 §6 で各シナリオの実装、§7 で CI/CD GameDay 自動化の順に進めます。


3. AWS FIS 仕組み + Experiment Template + Action / Target / Stop Condition 詳説

3-1. aws_fis_experiment_template の 4 要素

AWS FIS Experiment Template は以下の 4 要素で構成されます。各要素が連携することで「安全で再現性のある障害注入」を実現します。

要素Terraform属性役割
Actionactions block実際の障害注入操作 (aws:rds:failover-db-cluster 等)
Targettargets block障害対象リソース (Resource Tags / ARNs / Filter)
Stop Conditionstop_condition block実験の緊急停止条件 (CW Alarm ARN)
IAM Rolerole_arnFIS に付与する最小権限ロール

Action: 実際に何の障害を注入するかを定義します。action_id に AWS FIS 組み込みの Action 識別子 (aws:rds:failover-db-cluster 等) を指定し、parameter ブロックで動作調整 (待機時間・影響率等) を行います。Action は 1 つの Experiment Template に複数定義でき、start_after で依存関係 (シーケンシャル実行) を指定できます。

Target: どのリソースに障害を注入するかを定義します。resource_type (例: aws:rds:cluster) と selection_mode (ALL / COUNT(N) / PERCENT(N)) でブラストラジウスを制御します。PERCENT_OF_SELECTED=30 で全対象の 30% のみに障害を注入するなど、本番環境での安全な実験が可能です。

Stop Condition: 実験が暴走した場合の緊急停止条件です。CloudWatch Metric Alarm ARN を指定し、Alarm 状態に遷移すると FIS が即座に実験を停止します。本番環境では Stop Condition を必ず設定し、none の省略は禁止です。SLO Burn Rate Alarm と Error Rate Alarm の 2 系統を設定する二重安全策を推奨します。

IAM Role: FIS が対象リソースへアクセスするための IAM ロールです。fis.amazonaws.com への AssumeRole 信頼ポリシーと、実行する Action に必要な最小権限ポリシーを付与します。Action ごとに必要な権限が異なるため、複数 Action を同一テンプレートで使う場合は全 Action 分の権限を IAM ポリシーに集約します。

3-2. AWS FIS 主要 Action カタログ (10種)

AWS FIS が提供する障害注入 Action の主要 10 種を整理します。Action 選定は「どの障害シナリオを検証したいか」から逆算します。本記事の §4〜§6 ではこの中から Aurora フェイルオーバー・AZ ネットワーク断・S3 スロットリング・EKS ノード停止の 4 Action を中心に活用します。

Action対象サービス障害パターン
aws:rds:failover-db-clusterAurora Global DBリージョン障害・計画外フェイルオーバー
aws:rds:reboot-db-instancesRDS/Aurora単一インスタンス再起動
aws:ec2:stop-instancesEC2インスタンス停止
aws:network:disrupt-connectivityVPCAZ ネットワーク断
aws:network:latencyVPCレイテンシー注入
aws:s3:bucket-throttle-errorS3S3 スロットリング
aws:eks:terminate-nodegroup-instancesEKSノード強制終了
aws:eks:inject-kubernetes-custom-resourceKubernetesNetworkChaos / PodChaos
aws:fis:inject-api-internal-errorAWS APIAPI エラー注入
aws:ecs:task-kill-processECSタスクプロセス終了

Action 選定の 3 原則: (1) 本番で実際に起きうる障害パターンに対応する Action を選ぶ。(2) Blast Radius の小さい Action (reboot-db-instances) から始め、段階的に大きい Action (failover-db-cluster) へ移行する段階的エスカレーション戦略を取る。(3) AWS FIS が提供しない障害パターンは aws:eks:inject-kubernetes-custom-resource で Kubernetes NetworkChaos / PodChaos を注入して補完する。

aws:network:disrupt-connectivity は AZ レベルのネットワーク断をシミュレートする強力な Action です。対象サブネットの送受信トラフィックを遮断するため、マルチ AZ 構成の自動フェイルオーバー動作を検証できます。実験時間 (duration) は PT5M (5分) 以内から始め、アプリが想定通りに別 AZ へ切り替わることを確認してから延長します。

3-3. Terraform 基本 Experiment Template 実装

Aurora フェイルオーバーを例に、FIS IAM Role → CloudWatch Alarm (Stop Condition) → Experiment Template の 3 リソースを Terraform で実装します。リソース間の依存関係は Terraform の参照 (aws_iam_role.fis_experiment.arn 等) で管理し、terraform plan で変更内容を事前確認してから apply します。

# FIS 実験用 IAM Role
resource "aws_iam_role" "fis_experiment" {
  name = "fis-experiment-role"
  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "fis.amazonaws.com" }
 }]
  })
}

resource "aws_iam_role_policy" "fis_experiment" {
  name = "fis-experiment-policy"
  role = aws_iam_role.fis_experiment.id
  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect = "Allow"
  Action = [
 "rds:FailoverDBCluster",
 "rds:RebootDBInstance",
 "ec2:StopInstances",
 "ec2:DescribeInstances"
  ]
  Resource = "*"
},
{
  Effect = "Allow"
  Action = [
 "cloudwatch:DescribeAlarms"
  ]
  Resource = "*"
}
 ]
  })
}

# Stop Condition 用 CloudWatch Alarm
resource "aws_cloudwatch_metric_alarm" "fis_stop_condition" {
  alarm_name = "fis-stop-condition-slo-burn-rate"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  metric_name= "ConsumedLCUs"
  namespace  = "AWS/ApplicationELB"
  period  = 60
  statistic  = "Sum"
  threshold  = 1000
  alarm_description= "FIS Stop Condition: SLO Burn Rate exceeded"
}

# Experiment Template 基本構成 (Aurora フェイルオーバー例)
resource "aws_fis_experiment_template" "aurora_failover" {
  description = "Aurora Region Failover Test"
  role_arn = aws_iam_role.fis_experiment.arn

  stop_condition {
 source = "aws:cloudwatch:alarm"
 value  = aws_cloudwatch_metric_alarm.fis_stop_condition.arn
  }

  action {
 name= "aurora-failover"
 action_id = "aws:rds:failover-db-cluster"
 target {
key= "Clusters"
value = "aurora-global-cluster"
 }
 parameter {
key= "postFailoverWaitSeconds"
value = "180"
 }
  }

  target {
 name  = "aurora-global-cluster"
 resource_type  = "aws:rds:cluster"
 selection_mode = "ALL"
 resource_tag {
key= "Environment"
value = "production"
 }
 resource_tag {
key= "FISTarget"
value = "true"
 }
  }

  tags = {
 Name  = "aurora-failover-test"
 Environment = "production"
 ManagedBy= "terraform"
  }
}

postFailoverWaitSeconds = 180 は Aurora フェイルオーバー完了後に FIS が 180 秒待機するパラメータです。この待機中にアプリの接続回復・RTO 実測・監視ダッシュボードを確認します。stop_condition は FIS が実験完了前であっても CloudWatch Alarm が ALARM 状態になると即座に実験を終了させます。

3-4. Target 選定 3 方式の比較

FIS Target の選定方式は 3 種類あります。本番運用では Resource Tags 方式を基本とし、FIS 専用タグ (fis-target=true) を付与した対象だけに Blast Radius を限定することが最も安全です。

選定方式使い所Terraform 記述注意点
Resource Tags推奨。タグで対象を動的管理resource_tag本番タグを誤付与しないよう fis-target=true 専用タグ推奨
Resource ARNs特定リソース固定resource_arns = [arn]ARN ハードコードは変更に弱い
Resource Filters動的フィルタ (AZ / Region 等)filter複雑な条件に有効

Resource Tags 方式のベストプラクティスとして、fis-target=true という専用タグを FIS 実験対象リソースにのみ付与し、本番の重要リソース (RDS Writer、EKS Control Plane 等) には付与しない設計にします。PERCENT_OF_SELECTED=30 と組み合わせることで、実験の Blast Radius を全リソースの 30% 以下に制限できます。Aurora Global Database の場合は aws:rds:cluster タイプに fis-target=true タグを付与した Secondary クラスターのみを対象にすることで、Primary への誤操作を防止します。

Resource Filters の活用例として、AZ 障害シミュレーションでは filter { path = "Placement.AvailabilityZone" values = ["ap-northeast-1a"] } で特定 AZ のリソースだけを対象にします。これにより、マルチ AZ 構成のフェイルオーバー動作を AP レイヤーまで含めて検証できます。Resource ARNs 方式は「この特定インスタンスだけ」という固定指定に向きますが、リソース作成・削除のたびに Terraform を更新する必要があるため、動的環境では Tags 方式が優ります。

3-5. AWS CLI での実験管理

Terraform で Experiment Template を作成した後、実際の実験開始・モニタリング・停止は AWS CLI で行います。aws fis start-experiment で実験を開始し、aws fis get-experiment でポーリングして状態遷移を監視します。

# Experiment Template 作成確認
aws fis list-experiment-templates \
  --query 'experimentTemplates[*].[id,description,tags]' \
  --output table

# 実験開始
TEMPLATE_ID=$(aws fis list-experiment-templates \
  --query 'experimentTemplates[?tags.Name==`aurora-failover-test`].id' \
  --output text)

EXPERIMENT_ID=$(aws fis start-experiment \
  --experiment-template-id "$TEMPLATE_ID" \
  --query 'experiment.id' --output text)

echo "Experiment: $EXPERIMENT_ID"

# 実験状態モニタリング (30秒ごと)
while true; do
  STATUS=$(aws fis get-experiment --id "$EXPERIMENT_ID" \
 --query 'experiment.state.status' --output text)
  echo "$(date): $STATUS"
  [ "$STATUS" = "completed" ] || \
  [ "$STATUS" = "stopped" ]|| \
  [ "$STATUS" = "failed" ] && break
  sleep 30
done

実験状態は pendinginitiatingrunningcompleted / stopped / failed の順に遷移します。stopped は Stop Condition による自動停止 (SLO Alarm が ALARM に遷移した場合)、failed は IAM 権限不足・対象リソース不在による失敗です。running 中に手動停止する場合は aws fis stop-experiment --id "$EXPERIMENT_ID" を実行します。実験完了後は aws fis get-experimentexperiment.state.reason でログを確認し、Stop Condition が意図通りに機能したかを検証します。

sequenceDiagram
 participant Operator
 participant FIS as AWS FIS
 participant IAM as IAM STS
 participant Target as Target Resource
 participant CW as CloudWatch
 participant S3

 Operator->>FIS: StartExperiment (template_id)
 FIS->>IAM: AssumeRole (fis-experiment-role)
 IAM-->>FIS: Temporary Credentials
 FIS->>Target: Identify Targets (Resource Tags filter)
 Target-->>FIS: Target ARN List
 loop Every 10s
  FIS->>CW: Check Stop Condition Alarm
  CW-->>FIS: OK / ALARM
 end
 FIS->>Target: Execute Action (e.g. failover-db-cluster)
 Target-->>FIS: Action Started
 FIS->>CW: Put Experiment Logs
 FIS->>S3: Save Experiment Results (JSON)
 alt Stop Condition triggered
  CW-->>FIS: ALARM
  FIS->>Target: Stop Experiment (rollback)
 else Normal Completion
  FIS-->>Operator: Experiment Completed
 end

fig02: FIS Experiment 実行フロー

【QG-1: AWS FIS 4 要素マトリクス (Action / Target / Stop Condition / IAM Role)】

  • Action: aws:rds:failover-db-cluster (Aurora §4) / aws:network:disrupt-connectivity (AZ §5) / aws:s3:bucket-throttle-error + aws:eks:terminate-nodegroup-instances (アプリ §6) — 3 大シナリオ別 Action を選定
  • Target: Resource Tags 方式推奨 (fis-target=true 専用タグで本番誤操作防止) / PERCENT_OF_SELECTED=30% で Blast Radius 制御
  • Stop Condition: CloudWatch Metric Alarm (SLO Burn Rate > 閾値) → SystemExit で実験強制停止 / 二重安全策 (SLO + エラーレート 2 Alarm)
  • IAM Role: fis.amazonaws.com AssumeRole + 最小権限 (rds:FailoverDBCluster / ec2:StopInstances 等) + セキュリティ Vol2 連携 ABAC タグ条件

4. シナリオ1: Aurora リージョン障害実験 (Vol1 連携)

復旧・運用編 Vol1 §5 で構築した Aurora Global Database 構成(ap-northeast-1 プライマリ + us-east-1 セカンダリ)を AWS FIS で機械検証します。aws:rds:failover-db-cluster(計画外フェイルオーバー)と aws:rds:reboot-db-instances(単一インスタンス再起動)の 2 種の FIS Action を Terraform aws_fis_experiment_template で IaC 化し、RTO/RPO を boto3 スクリプトで自動実測します。Vol1 §7 で警鐘を鳴らした「DR Runbook は紙の上のフィクション」を、定期 FIS 実行によって解消します。

fig03: Aurora 障害実験構成図 (Vol1 連携)

【QG-2: Aurora Global Database 障害実験チェックリスト】

  • aws:rds:failover-db-cluster: Global Database Failover 実験 (リージョン障害シミュレーション)
  • aws:rds:reboot-db-instances: 単一インスタンス再起動 (AZ 障害シミュレーション)
  • RTO/RPO 自動実測: boto3 スクリプトで Failover 開始〜新 Writer 接続確認時間を計測
  • Stop Condition: AuroraGlobalDBReplicationLag > 5 秒で自動停止
  • Vol1 §5 Switchover との使い分け: FIS=計画外フェイルオーバー / Switchover=計画的切替

4.1 Aurora 障害実験の設計方針

FIS Action の選定基準

シナリオFIS Action目的RTO 目標
リージョン障害aws:rds:failover-db-clusterGlobal Database の Failover 完全検証≤ 60 秒
AZ 障害aws:rds:reboot-db-instances単一インスタンス障害・AZ 内 Writer 交代確認≤ 30 秒

Vol1 §5 Switchover との使い分け: Vol1 で実装した aws rds failover-global-cluster --allow-data-loss は計画的切替(RPO=0 維持)です。FIS aws:rds:failover-db-cluster は計画外フェイルオーバー(アプリ接続断・一時的 RPO 劣化あり)をシミュレートし、Runbook の実動作を証明します。Stop Condition = AuroraGlobalDBReplicationLag > 5 秒、Blast Radius = COUNT(1)、Cooldown = 72 時間以上を必ず設定します。

4.2 aws:rds:failover-db-cluster 実験 — リージョン障害シミュレーション

リージョン障害を模し、Global Database の Primary クラスターを強制フェイルオーバーします。フェイルオーバー完了後、Vol1 §5 で設定した Route53 ARC Routing Control が自動切替されることを確認します。

Terraform 実装

resource "aws_cloudwatch_metric_alarm" "aurora_replication_lag" {
  alarm_name = "fis-stop-condition-aurora-replication-lag"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  metric_name= "AuroraGlobalDBReplicationLag"
  namespace  = "AWS/RDS"
  period  = 60
  statistic  = "Maximum"
  threshold  = 5000
  alarm_description= "FIS Stop Condition: Aurora Global DB Replication Lag > 5s"

  dimensions = {
 DBClusterIdentifier = var.aurora_global_cluster_id
  }
}

resource "aws_iam_role" "fis_aurora" {
  name = "fis-aurora-experiment-role"
  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = { Service = "fis.amazonaws.com" }
Action = "sts:AssumeRole"
 }]
  })
}

resource "aws_iam_role_policy" "fis_aurora" {
  name = "fis-aurora-experiment-policy"
  role = aws_iam_role.fis_aurora.id
  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect = "Allow"
  Action = [
 "rds:FailoverGlobalCluster",
 "rds:DescribeGlobalClusters",
 "rds:DescribeDBClusters"
  ]
  Resource = "*"
},
{
  Effect = "Allow"
  Action = [
 "cloudwatch:DescribeAlarms",
 "logs:CreateLogDelivery",
 "logs:PutLogEvents",
 "logs:DescribeLogGroups",
 "logs:DescribeResourcePolicies"
  ]
  Resource = "*"
}
 ]
  })
}

resource "aws_cloudwatch_log_group" "fis_aurora_logs" {
  name  = "/aws/fis/aurora-failover"
  retention_in_days = 30
}

resource "aws_fis_experiment_template" "aurora_global_failover" {
  description = "Aurora Global Database Failover 実験 (Vol1 §5 連携)"
  role_arn = aws_iam_role.fis_aurora.arn

  stop_condition {
 source = "aws:cloudwatch:alarm"
 value  = aws_cloudwatch_metric_alarm.aurora_replication_lag.arn
  }

  target {
 name  = "global-clusters"
 resource_type  = "aws:rds:global-cluster"
 selection_mode = "COUNT(1)"

 resource_arns = [
"arn:aws:rds::${data.aws_caller_identity.current.account_id}:global-cluster:${var.aurora_global_cluster_id}"
 ]
  }

  action {
 name= "failover-aurora-global-cluster"
 action_id = "aws:rds:failover-db-cluster"

 target {
key= "Clusters"
value = "global-clusters"
 }
  }

  log_configuration {
 log_schema_version = 2
 cloudwatch_logs_configuration {
log_group_arn = "${aws_cloudwatch_log_group.fis_aurora_logs.arn}:*"
 }
  }

  tags = {
 Name  = "aurora-global-failover"
 Environment = "production"
 ManagedBy= "terraform"
  }
}

AWS CLI

# Experiment Template 作成
TEMPLATE_ID=$(aws fis create-experiment-template \
  --cli-input-json file://fis-aurora-failover.json \
  --query 'experimentTemplate.id' --output text)

# 実験開始
EXPERIMENT_ID=$(aws fis start-experiment \
  --experiment-template-id "$TEMPLATE_ID" \
  --query 'experiment.id' --output text)

# 実験状態確認
aws fis get-experiment \
  --id "$EXPERIMENT_ID" \
  --query 'experiment.{status: state.status, reason: state.reason}' \
  --output table

# Writer リージョン変化をポーリング (フェイルオーバー完了確認)
watch -n 5 "aws rds describe-global-clusters \
  --global-cluster-identifier ${GLOBAL_CLUSTER_ID} \
  --query 'GlobalClusters[0].GlobalClusterMembers[?IsWriter==\`true\`].DBClusterArn' \
  --output text"

コンソール操作

  1. AWS マネジメントコンソール → AWS FISExperiment templates を開く
  2. Terraform で作成した aurora-global-failover テンプレートを選択
  3. Actions → Start experiment をクリックし、確認ダイアログで Start experiment を実行
  4. Experiments 一覧で状態が completed になるまで監視(通常 60 秒以内)
  5. RDS コンソール → Global databases で Writer クラスターが us-east-1 に切り替わっていることを確認

4.3 aws:rds:reboot-db-instances 実験

単一 AZ 内の Aurora Writer インスタンスを強制再起動し、同一クラスター内での Writer 交代時間(AZ 障害時の RTO)を計測します。

Terraform 実装

resource "aws_fis_experiment_template" "aurora_instance_reboot" {
  description = "Aurora DB インスタンス再起動 (AZ 障害シミュレーション)"
  role_arn = aws_iam_role.fis_aurora.arn

  stop_condition {
 source = "aws:cloudwatch:alarm"
 value  = aws_cloudwatch_metric_alarm.aurora_replication_lag.arn
  }

  target {
 name  = "writer-instances"
 resource_type  = "aws:rds:db"
 selection_mode = "COUNT(1)"

 resource_tags = {
"Environment" = "production"
"FISTarget"= "true"
 }
  }

  action {
 name= "reboot-aurora-writer"
 action_id = "aws:rds:reboot-db-instances"

 parameter {
key= "forceFailover"
value = "true"
 }

 target {
key= "DBInstances"
value = "writer-instances"
 }
  }

  tags = {
 Name  = "aurora-instance-reboot"
 Environment = "production"
  }
}

AWS CLI / コンソール

# インスタンス再起動実験を開始
aws fis start-experiment \
  --experiment-template-id "$REBOOT_TEMPLATE_ID" \
  --tags "Scenario=AZ-Failure-Simulation"

# Writer インスタンス切替確認
aws rds describe-db-clusters \
  --db-cluster-identifier "$CLUSTER_ID" \
  --query 'DBClusters[0].DBClusterMembers[?IsClusterWriter==`true`].DBInstanceIdentifier' \
  --output text

コンソールの場合は FIS → Experiments → Start experimentaurora-instance-reboot テンプレートを選択し、RDS → Databases で Writer 切替をリアルタイム確認します。

4.4 RTO/RPO 自動実測 (boto3 + CloudWatch)

FIS 実験実行直後から Writer 昇格完了までの時間(RTO)と、Failover 前の Replication Lag 最大値(RPO 目安)を計測し、CloudWatch カスタムメトリクスとして記録します。

import boto3
import time
from datetime import datetime, timedelta, timezone


def measure_aurora_rto(global_cluster_id: str, region: str = "ap-northeast-1") -> int:
 """Aurora Global Database Failover の RTO を自動計測する"""
 rds = boto3.client("rds", region_name=region)
 cw = boto3.client("cloudwatch", region_name=region)

 start_writer = _get_writer_arn(rds, global_cluster_id)
 print(f"Failover 開始前 Writer: {start_writer}")

 for elapsed in range(120):
  time.sleep(1)
  current_writer = _get_writer_arn(rds, global_cluster_id)
  if current_writer != start_writer:
rto_seconds = elapsed + 1
print(f"Writer 切替完了: {current_writer} (RTO={rto_seconds}s)")
cw.put_metric_data(
 Namespace="ChaosEngineering/Aurora",
 MetricData=[{
  "MetricName": "FailoverRTO",
  "Value": rto_seconds,
  "Unit": "Seconds",
  "Dimensions": [
{"Name": "GlobalClusterId", "Value": global_cluster_id},
{"Name": "Scenario",  "Value": "RegionalFailover"}
  ]
 }]
)
return rto_seconds

 raise TimeoutError("Writer 切替が 120 秒以内に完了しませんでした")


def _get_writer_arn(rds_client, global_cluster_id: str) -> str:
 resp = rds_client.describe_global_clusters(GlobalClusterIdentifier=global_cluster_id)
 for member in resp["GlobalClusters"][0]["GlobalClusterMembers"]:
  if member["IsWriter"]:
return member["DBClusterArn"]
 raise ValueError("Writer クラスターが見つかりません")

§7 の CI/CD GameDay パイプラインでは、FIS 実験後にこのスクリプトを Lambda から自動呼び出し、RTO/RPO 実測値を CloudWatch Dashboard へ記録して Slack 通知します(Vol1 §7 boto3 DR 訓練スクリプトとの連携ポイント)。

4.5 実験後の検証

# Writer リージョン確認 (Failover 後は us-east-1 が Writer になっているはず)
aws rds describe-global-clusters \
  --global-cluster-identifier "$GLOBAL_CLUSTER_ID" \
  --query 'GlobalClusters[0].GlobalClusterMembers[*].{Writer:IsWriter,ARN:DBClusterArn}' \
  --output table

# 旧 Primary を Secondary として再追加 (Vol1 §8 落とし穴 6 参照)
aws rds remove-from-global-cluster \
  --global-cluster-identifier "$GLOBAL_CLUSTER_ID" \
  --db-cluster-identifier "arn:aws:rds:ap-northeast-1:${ACCOUNT_ID}:cluster:${OLD_PRIMARY}"
aws rds create-global-cluster \
  --global-cluster-identifier "$GLOBAL_CLUSTER_ID" \
  --source-db-cluster-identifier "arn:aws:rds:ap-northeast-1:${ACCOUNT_ID}:cluster:${OLD_PRIMARY}"

確認ポイント: (1) Writer リージョンが us-east-1 に切り替わっていること (2) AuroraGlobalDBReplicationLag が 1,000 ms 以下に復帰していること (3) アプリケーションが Route53 ARC Routing Control 経由で新 Writer へ自動切替できていること (4) Vol1 §8 落とし穴 6「手動 Switchback 忘れ」に従い旧 Primary を Secondary として再登録すること

Multi-Region×Multi-AZ Backup/DR本番実践 §5 Aurora Global Database (復旧・運用編 Vol1)


5. シナリオ2: AZ 停止実験 (Power Interruption / Application Slowdown)

本章では AWS が提供する 3 種類の AZ 障害シナリオを Terraform で完全実装する。Power Interruption は AZ 内リソースの物理的停止(停電シミュレーション)、Application Slowdown は AZ 内ネットワーク遅延注入、API Internal Error は AWS API レイヤーへのエラー注入で、それぞれ異なる障害パターンを検証する。復旧・運用編 Vol1 で構築した Route53 ARC の Zonal Shift と連携し、AZ 障害検知後の自動トラフィック排除フローを機械的に証明する。

5.1 AZ 停止実験の設計方針 + Action 選定

AZ 停止 3 パターンの使い分け

パターン主要 FIS Action障害種別対象サービスRTO 目標
Power Interruptionaws:ec2:stop-instancesAZ 全体電源断EC2 / EBS / ASG / ElastiCache≤ 120 秒
Application Slowdownaws:network:disrupt-connectivityAZ 内ネットワーク遅延 200msEC2 / EKS ノード間 / RDS 接続≤ 60 秒で SLO Burn Rate 検知
API Internal Erroraws:fis:inject-api-internal-errorAWS 内部 API エラー注入SDK 呼び出し / S3 / DynamoDB≤ 30 秒で Circuit Breaker 発動

安全策の適用 (§2 準拠)

  • Stop Condition: ALBUnhealthyHostCount > 2 アラームを必ず設定。正常ホスト数が閾値を超えた時点で実験を自動停止
  • Blast Radius: TARGET_SELECTION_MODE = COUNT(1) で単一インスタンスから開始。本番は段階的拡大
  • Cooldown: AZ 停止実験は 48 時間以上のクールダウンを推奨(ASG のスケールアウト完了まで)
  • 実行時間帯: 業務時間外 (JST 22:00-06:00)・Auto Scaling 最大容量確認済み後のみ実行

5.2 Power Interruption 実験 (aws:ec2:stop-instances)

Power Interruption は AZ 単位の電源断をシミュレートする AWS FIS の代表的シナリオです。対象 AZ の EC2 インスタンスを強制停止し、Auto Scaling Group の置き換え動作・ELB のヘルスチェック除外・RDS Multi-AZ フェイルオーバーが期待通り機能するかを検証します。

Terraform 実装

resource "aws_cloudwatch_metric_alarm" "alb_unhealthy_hosts" {
  alarm_name = "fis-stop-condition-alb-unhealthy-hosts"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  metric_name= "UnHealthyHostCount"
  namespace  = "AWS/ApplicationELB"
  period  = 60
  statistic  = "Maximum"
  threshold  = 2
  alarm_description= "FIS Stop Condition: ALB UnHealthyHostCount > 2"

  dimensions = {
 LoadBalancer = var.alb_arn_suffix
 TargetGroup  = var.tg_arn_suffix
  }
}

resource "aws_iam_role" "fis_az_power" {
  name = "fis-az-power-interruption-role"
  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = { Service = "fis.amazonaws.com" }
Action = "sts:AssumeRole"
 }]
  })
}

resource "aws_iam_role_policy" "fis_az_power" {
  name = "fis-az-power-interruption-policy"
  role = aws_iam_role.fis_az_power.id
  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect= "Allow"
  Action= ["ec2:StopInstances", "ec2:DescribeInstances"]
  Resource = "*"
  Condition = {
 StringEquals = { "aws:ResourceTag/Environment" = "production" }
  }
},
{
  Effect= "Allow"
  Action= ["cloudwatch:DescribeAlarms", "logs:CreateLogGroup",
  "logs:CreateLogDelivery", "logs:PutLogEvents"]
  Resource = "*"
}
 ]
  })
}

resource "aws_cloudwatch_log_group" "fis_az_power" {
  name  = "/aws/fis/az-power-interruption"
  retention_in_days = 30
}

resource "aws_fis_experiment_template" "az_power_interruption" {
  description = "AZ Power Interruption: EC2 Stop Instances in Single AZ"
  role_arn = aws_iam_role.fis_az_power.arn

  stop_condition {
 source = "aws:cloudwatch:alarm"
 value  = aws_cloudwatch_metric_alarm.alb_unhealthy_hosts.arn
  }

  action {
 name= "stop-ec2-instances-az"
 action_id = "aws:ec2:stop-instances"

 target {
key= "Instances"
value = "production-ec2-in-az"
 }

 parameter {
key= "startInstancesAfterDuration"
value = "PT10M"
 }
  }

  target {
 name  = "production-ec2-in-az"
 resource_type  = "aws:ec2:instance"
 selection_mode = "COUNT(1)"

 resource_tag {
key= "Environment"
value = "production"
 }

 filter {
path= "Placement.AvailabilityZone"
values = [var.target_az]
 }
  }

  log_configuration {
 cloudwatch_logs_configuration {
log_group_arn = "${aws_cloudwatch_log_group.fis_az_power.arn}:*"
 }
 log_schema_version = 2
  }

  tags = { Scenario = "AZ-PowerInterruption" }
}

AWS CLI 操作

# 実験テンプレート作成
aws fis create-experiment-template \
  --cli-input-json file://az-power-interruption-template.json \
  --region ap-northeast-1

# 実験開始
EXPERIMENT_ID=$(aws fis start-experiment \
  --experiment-template-id "$TEMPLATE_ID" \
  --query 'experiment.id' --output text)

# 実験状態監視 (30 秒ごと)
watch -n 30 "aws fis get-experiment --id $EXPERIMENT_ID \
  --query 'experiment.{State:state.status,Reason:state.reason}'"

# 実験停止 (緊急時)
aws fis stop-experiment --id "$EXPERIMENT_ID"

コンソール操作

AWS FIS コンソール → Experiment templatesaz-power-interruption 選択 → Start experiment → 確認ダイアログへ start を入力 → Experiments タブのリアルタイム状態を確認 → CloudWatch ダッシュボードで ALBUnhealthyHostCount / EC2 StatusCheckFailed を同時監視。

5.3 Application Slowdown 実験 (aws:network:disrupt-connectivity)

Application Slowdown は AZ 内のネットワークフローへ 200ms のレイテンシを注入します。EC2 インスタンス間・EKS ノード間・RDS 接続の応答遅延が SLO の Burn Rate へ即座に影響し、観測性 Vol3 の Burn Rate アラームが Stop Condition として機能するかを検証します。

Terraform 実装

resource "aws_fis_experiment_template" "az_application_slowdown" {
  description = "AZ Application Slowdown: 200ms Latency Injection"
  role_arn = aws_iam_role.fis_az_power.arn

  stop_condition {
 source = "aws:cloudwatch:alarm"
 value  = aws_cloudwatch_metric_alarm.alb_unhealthy_hosts.arn
  }

  action {
 name= "inject-az-network-latency"
 action_id = "aws:network:disrupt-connectivity"

 target {
key= "Subnets"
value = "production-subnets-in-az"
 }

 parameter {
key= "duration"
value = "PT5M"
 }
 parameter {
key= "scope"
value = "all"
 }
  }

  target {
 name  = "production-subnets-in-az"
 resource_type  = "aws:ec2:subnet"
 selection_mode = "ALL"

 resource_tag {
key= "Environment"
value = "production"
 }

 filter {
path= "AvailabilityZone"
values = [var.target_az]
 }
  }

  log_configuration {
 cloudwatch_logs_configuration {
log_group_arn = "${aws_cloudwatch_log_group.fis_az_power.arn}:*"
 }
 log_schema_version = 2
  }

  tags = { Scenario = "AZ-ApplicationSlowdown" }
}

AWS CLI 操作

# Application Slowdown 実験開始
aws fis start-experiment \
  --experiment-template-id "$AZ_SLOWDOWN_TEMPLATE_ID" \
  --region ap-northeast-1

# SLO Burn Rate 確認 (観測性 Vol3 連携)
aws cloudwatch get-metric-data \
  --metric-data-queries file://burn-rate-query.json \
  --start-time "$(date -u -v-5M +%Y-%m-%dT%H:%M:%SZ)" \
  --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)"

5.4 API Internal Error 注入 (aws:fis:inject-api-internal-error)

AWS 内部 API へのエラー注入で、SDK リトライロジック・Circuit Breaker・Exponential Backoff が正しく実装されているかを検証します。AWS API が一時的に 500 Internal Server Error を返す状況を再現し、アプリケーションの耐障害性設計を証明します。

Terraform 実装

resource "aws_fis_experiment_template" "api_internal_error" {
  description = "API Internal Error Injection: AWS SDK Retry Validation"
  role_arn = aws_iam_role.fis_az_power.arn

  stop_condition {
 source = "aws:cloudwatch:alarm"
 value  = aws_cloudwatch_metric_alarm.alb_unhealthy_hosts.arn
  }

  action {
 name= "inject-api-errors"
 action_id = "aws:fis:inject-api-internal-error"

 target {
key= "Roles"
value = "production-app-roles"
 }

 parameter {
key= "service"
value = "s3"
 }
 parameter {
key= "percentage"
value = "30"
 }
 parameter {
key= "duration"
value = "PT3M"
 }
  }

  target {
 name  = "production-app-roles"
 resource_type  = "aws:iam:role"
 selection_mode = "ALL"

 resource_arn = [var.app_iam_role_arn]
  }

  log_configuration {
 cloudwatch_logs_configuration {
log_group_arn = "${aws_cloudwatch_log_group.fis_az_power.arn}:*"
 }
 log_schema_version = 2
  }

  tags = { Scenario = "API-InternalError" }
}

落とし穴: percentage パラメータは 0-100 の整数で指定。30% から開始し、Circuit Breaker が正常動作するまで徐々に上げる。100% に設定すると全 S3 API 呼び出しが失敗するため、本番環境では必ず Stop Condition を事前設定すること。

5.5 ARC Zonal Shift 連携 — AZ 障害検知後の自動復旧

Vol1 §6 で実装した Route53 ARC の Zonal Shift を FIS 実験中に連動させ、AZ 障害検知後のトラフィック自動排除を証明します。FIS が ALBUnhealthyHostCount > 2 で Stop Condition 発動 → EventBridge が FIS 状態変化を検知 → Lambda が aws route53-recovery-control-config API で Routing Control を変更 → 障害 AZ へのルーティングを遮断するフローを自動化します。

# Zonal Shift 手動起動 (FIS 実験後の緊急時参照コマンド)
aws arc-zonal-shift start-zonal-shift \
  --resource-identifier "$ALB_ARN" \
  --away-from "$TARGET_AZ" \
  --comment "FIS-triggered AZ failure validation" \
  --expires-in "PT1H"

# Zonal Shift 状態確認
aws arc-zonal-shift list-zonal-shifts \
  --status ACTIVE \
  --query 'zonalShifts[*].{AZ:awayFrom,Resource:resourceIdentifier,Status:status}'

# Zonal Shift 解除 (実験完了後)
aws arc-zonal-shift cancel-zonal-shift \
  --zonal-shift-id "$SHIFT_ID"

セキュリティ Vol1 連携: FIS 実験中は GuardDuty が AZ 内の異常 API 呼び出しを検知します。FIS 実験ロール ARN を GuardDuty のサプレッションルールに追加し、意図的な障害注入を誤検知から除外してください。

fig04: AZ 停止実験フロー (Action 選定判断ツリー)

flowchart TD
 A[AZ 停止実験 開始] --> B{障害パターン選定}
 B --> C[電源断シミュレーション<br/>Power Interruption]
 B --> D[ネットワーク遅延注入<br/>Application Slowdown]
 B --> E[API 内部エラー注入<br/>API Internal Error]
 C --> F["aws:ec2:stop-instances<br/>Target: AZ 内 EC2 インスタンス<br/>startInstancesAfterDuration: PT10M"]
 D --> G["aws:network:disrupt-connectivity<br/>Target: AZ 内 Subnet<br/>scope: all / duration: PT5M"]
 E --> H["aws:fis:inject-api-internal-error<br/>Target: App IAM Role<br/>service: s3 / percentage: 30"]
 F --> I{Stop Condition 評価}
 G --> I
 H --> I
 I --> J{"ALBUnhealthyHostCount > 2?"}
 J -->|Yes| K[実験自動停止<br/>FIS State: stopped]
 J -->|No| L[実験継続<br/>CloudWatch 監視 30 秒間隔]
 K --> M["ARC Zonal Shift 起動<br/>aws arc-zonal-shift start-zonal-shift<br/>→ 障害 AZ トラフィック排除"]
 L --> N["RTO 実測<br/>実験開始〜ALB 正常復帰まで計測<br/>結果 S3 保存"]
 M --> O[実験結果確認<br/>CloudWatch Dashboard 更新]
 N --> O
 O --> P["次回 Cooldown 48h 後<br/>Blast Radius 拡大 → COUNT(2)"]

fig04: AZ 停止実験フロー

【QG-3: AZ 停止実験 Action 選定マトリクス】

  • Power Interruption: aws:ec2:stop-instances — AZ 全体電源断シミュレーション。ASG 自動置き換え・ELB ヘルスチェック除外の動作確認。startInstancesAfterDuration: PT10M で自動復旧
  • Application Slowdown: aws:network:disrupt-connectivity — AZ 内 Subnet 全体に 200ms レイテンシ注入。SLO Burn Rate (観測性 Vol3 連携) が閾値超過する前に Stop Condition で自動停止
  • API Internal Error: aws:fis:inject-api-internal-error — 対象 IAM Role の S3/DynamoDB API 呼び出しに 30% エラー注入。Circuit Breaker + Exponential Backoff の実装検証
  • ARC Zonal Shift 連携: FIS Stop Condition 発動後に aws arc-zonal-shift start-zonal-shift を Lambda で自動実行。障害 AZ のトラフィックを Route53 ARC Routing Control で遮断

6. シナリオ3: S3 レイテンシー注入 + EKS Pod 停止 (アプリ層連携)

本章ではアプリ層障害シナリオとして、S3 バケットへのスロットリングエラー注入と EKS ノード強制終了を組み合わせ、マイクロサービスの耐障害性を FIS で機械検証します。EKS Vol2 — IRSA 最小権限設計 および EKS Vol3 — ALB + ArgoCD GitOps で構築した EKS クラスターを実験対象とし、Pod 自動再スケジュール・ALB ヘルスチェック復旧・IRSA 権限分離の 3 軸を確認します。

fig05: アプリ層障害シナリオ構成 (S3 + EKS)

【QG-4: アプリ層障害シナリオ (S3 / EKS) チェックリスト】

  • ☑ S3 スロットリング注入: aws:s3:bucket-throttle-error — Percentage + Duration 設定、Stop Condition に S3 5xxError Alarm を連携
  • ☑ EKS ノード強制終了: aws:eks:terminate-nodegroup-instances — instanceTerminationPercentage=30 から段階的に拡大、fis-target タグの検証専用 Nodegroup のみを対象
  • ☑ Kubernetes カスタムリソース注入: aws:eks:inject-kubernetes-custom-resource — NetworkChaos / PodChaos CRD を指定 namespace に適用
  • ☑ EKS Vol2 IRSA 連携: FIS 実行 IAM Role に ec2:DescribeInstances / eks:DescribeNodegroup のみを付与し最小権限設計を維持
  • ☑ EKS Vol3 ALB ヘルスチェック検証: Stop Condition に HealthyHostCount < 1 Alarm を設定し Pod 再生成後の復旧を自動確認

6.1 アプリ層障害シナリオの設計方針

FIS Action 選定基準

Action対象レイヤー目的影響範囲
aws:s3:bucket-throttle-errorS3 (ストレージ層)S3 依存アプリのリトライ動作・Exponential Backoff 検証指定バケット全 PUT/GET
aws:eks:terminate-nodegroup-instancesEKS ノード層ノード障害時の Pod 再スケジュール・ALB 切り替え確認Nodegroup の PERCENT(30)
aws:eks:inject-kubernetes-custom-resourceKubernetes ネットワーク層Pod 間通信障害・NetworkChaos 連携確認指定 namespace の Pod

Stop Condition 設計: ALB の HealthyHostCount < 1 が 1 分以上継続した場合は実験を自動停止します。S3 スロットリング実験では S3 5xxError > 10/min の CloudWatch Alarm を追加し二重安全策とします。

Blast Radius 原則: EKS ノード終了は instanceTerminationPercentage=30 から開始し、Pod 自動再生成を確認後に 50% へ段階的に拡大します。本番 Nodegroup ではなく environment=fis-target タグの検証専用 Nodegroup のみを対象とします。

Cooldown 設定: EKS ノード終了実験は Auto Scaling Group による新規ノード起動に 3〜5 分を要するため、実験間隔を最低 15 分以上確保します。

6.2 S3 スロットリングエラー注入実験

S3 の aws:s3:bucket-throttle-error Action で SlowDown (HTTP 503) を注入し、アプリの S3 SDK リトライロジックと Exponential Backoff の動作をリアルタイム観測します。

Terraform 実装

resource "aws_cloudwatch_metric_alarm" "s3_error_rate" {
  alarm_name = "fis-stop-condition-s3-error-rate"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  metric_name= "5xxError"
  namespace  = "AWS/S3"
  period  = 60
  statistic  = "Sum"
  threshold  = 10
  dimensions = {
 BucketName = var.target_s3_bucket
 FilterId= "EntireBucket"
  }
  alarm_description = "FIS Stop Condition: S3 5xx error > 10/min"
}

resource "aws_iam_role" "fis_app_role" {
  name = "fis-experiment-app-layer-role"
  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = { Service = "fis.amazonaws.com" }
Action = "sts:AssumeRole"
 }]
  })
}

resource "aws_iam_role_policy" "fis_s3_policy" {
  name = "fis-s3-throttle-policy"
  role = aws_iam_role.fis_app_role.id
  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect= "Allow"
  Action= ["s3:GetObject", "s3:PutObject", "s3:ListBucket"]
  Resource = [
 "arn:aws:s3:::${var.target_s3_bucket}",
 "arn:aws:s3:::${var.target_s3_bucket}/*"
  ]
},
{
  Effect= "Allow"
  Action= ["cloudwatch:DescribeAlarms"]
  Resource = "*"
}
 ]
  })
}

resource "aws_fis_experiment_template" "s3_throttle" {
  description = "S3 bucket-throttle-error 注入 — アプリ層障害シナリオ3"
  role_arn = aws_iam_role.fis_app_role.arn

  stop_condition {
 source = "aws:cloudwatch:alarm"
 value  = aws_cloudwatch_metric_alarm.s3_error_rate.arn
  }

  action {
 name  = "inject-s3-throttle"
 action_id= "aws:s3:bucket-throttle-error"
 description = "S3 SlowDown (HTTP 503) 50% 注入 / Duration=60s"

 parameter {
key= "percentage"
value = "50"
 }
 parameter {
key= "duration"
value = "PT1M"
 }

 target {
key= "Buckets"
value = "target-s3-bucket"
 }
  }

  target {
 name  = "target-s3-bucket"
 resource_type  = "aws:s3:bucket"
 selection_mode = "ALL"
 resource_tag {
key= "environment"
value = "fis-target"
 }
  }

  tags = {
 Scenario = "app-layer-s3-throttle"
 Series= "recovery-operations-vol2"
  }
}

AWS CLI 操作

# Experiment Template 作成
TEMPLATE_ID=$(aws fis create-experiment-template \
  --description "S3 throttle injection" \
  --role-arn "arn:aws:iam::${ACCOUNT_ID}:role/fis-experiment-app-layer-role" \
  --stop-conditions '[{
 "source":"aws:cloudwatch:alarm",
 "value":"arn:aws:cloudwatch:ap-northeast-1:'${ACCOUNT_ID}':alarm:fis-stop-condition-s3-error-rate"
  }]' \
  --actions '{
 "inject-s3-throttle":{
"actionId":"aws:s3:bucket-throttle-error",
"parameters":{"percentage":"50","duration":"PT1M"},
"targets":{"Buckets":"target-s3-bucket"}
 }
  }' \
  --targets '{
 "target-s3-bucket":{
"resourceType":"aws:s3:bucket",
"selectionMode":"ALL",
"resourceTags":{"environment":"fis-target"}
 }
  }' \
  --query 'experimentTemplate.id' --output text)

# 実験開始
EXP_ID=$(aws fis start-experiment \
  --experiment-template-id "${TEMPLATE_ID}" \
  --query 'experiment.id' --output text)

# 状態確認 (RUNNING → COMPLETED/STOPPED)
aws fis get-experiment --id "${EXP_ID}" \
  --query 'experiment.state.status' --output text

実験進行中は CloudWatch Logs でアプリの SlowDown エラーに対するリトライログを確認します。Exponential Backoff が正常動作していれば再試行間隔が段階的に伸びます。

コンソール操作

  1. AWS FIS コンソールExperiment templates → 対象テンプレートを選択
  2. Actions タブ → aws:s3:bucket-throttle-error の Percentage / Duration を確認
  3. Start experiment → 確認ダイアログで Start experiment を選択
  4. CloudWatchAlarmsfis-stop-condition-s3-error-rate でリアルタイム監視
  5. 完了後: ExperimentsActionsinject-s3-throttleStatusCompleted を確認

6.3 EKS ノード強制終了実験 (aws:eks:terminate-nodegroup-instances)

EKS Vol2 で構築した IRSA 最小権限 Nodegroup を対象にノードを強制終了し、Pod 自動再スケジュールと ALB Healthy Host 復旧を検証します。

Terraform 実装

resource "aws_cloudwatch_metric_alarm" "alb_healthy_host" {
  alarm_name = "fis-stop-condition-alb-no-healthy-host"
  comparison_operator = "LessThanThreshold"
  evaluation_periods  = 1
  metric_name= "HealthyHostCount"
  namespace  = "AWS/ApplicationELB"
  period  = 60
  statistic  = "Minimum"
  threshold  = 1
  dimensions = {
 LoadBalancer = var.alb_arn_suffix
 TargetGroup  = var.target_group_arn_suffix
  }
  alarm_description = "FIS Stop Condition: ALB HealthyHostCount < 1"
}

resource "aws_iam_role_policy" "fis_eks_policy" {
  name = "fis-eks-terminate-policy"
  role = aws_iam_role.fis_app_role.id
  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Action = [
  "ec2:DescribeInstances",
  "ec2:TerminateInstances",
  "eks:DescribeNodegroup",
  "eks:ListNodegroups"
]
Resource = "*"
 }]
  })
}

resource "aws_fis_experiment_template" "eks_pod_termination" {
  description = "EKS nodegroup 30% 強制終了 — Pod 自動再生成検証"
  role_arn = aws_iam_role.fis_app_role.arn

  stop_condition {
 source = "aws:cloudwatch:alarm"
 value  = aws_cloudwatch_metric_alarm.alb_healthy_host.arn
  }

  action {
 name  = "terminate-eks-nodes"
 action_id= "aws:eks:terminate-nodegroup-instances"
 description = "Nodegroup ノード 30% 強制終了 — Pod 再スケジュール確認"

 parameter {
key= "instanceTerminationPercentage"
value = "30"
 }

 target {
key= "Nodegroups"
value = "target-nodegroup"
 }
  }

  target {
 name  = "target-nodegroup"
 resource_type  = "aws:eks:nodegroup"
 selection_mode = "ALL"
 resource_tag {
key= "environment"
value = "fis-target"
 }
  }

  tags = {
 Scenario = "app-layer-eks-terminate"
 Series= "recovery-operations-vol2"
  }
}

AWS CLI 操作

# 実験前: 対象 Nodegroup 確認
aws eks list-nodegroups \
  --cluster-name "${EKS_CLUSTER_NAME}" \
  --query 'nodegroups[]' --output text

# Pod 状態監視 (別ターミナル)
kubectl get pods -A -w

# 実験実行
EXP_ID=$(aws fis start-experiment \
  --experiment-template-id "${EKS_TEMPLATE_ID}" \
  --query 'experiment.id' --output text)

# ALB HealthyHostCount を 30 秒ごとに確認
while true; do
  COUNT=$(aws cloudwatch get-metric-statistics \
 --namespace AWS/ApplicationELB \
 --metric-name HealthyHostCount \
 --dimensions \
Name=LoadBalancer,Value="${ALB_ARN_SUFFIX}" \
Name=TargetGroup,Value="${TG_ARN_SUFFIX}" \
 --start-time "$(date -u -v-1M '+%Y-%m-%dT%H:%M:%SZ')" \
 --end-time"$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
 --period 30 --statistics Minimum \
 --query 'Datapoints[-1].Minimum' --output text)
  echo "$(date): HealthyHostCount=${COUNT}"
  sleep 30
done

コンソール操作

  1. EKS コンソールClusters → 対象クラスター → Node groups → ノード数を確認
  2. AWS FIS コンソール → 実験開始 → Experiments で状態を監視
  3. EC2 コンソールInstances → 対象ノードが Shutting-down へ遷移することを確認
  4. EC2 コンソールAuto Scaling groups → 新規ノードが InService になるまで待機
  5. ELB コンソールTarget GroupsHealthy カウントが復旧したことを確認

6.4 S3 + EKS 複合障害シナリオ

S3 スロットリングと EKS ノード終了を同一 Experiment Template の複数 Action として順序実行し、「S3 リトライ中にノード再起動が重なる」最悪ケースの耐障害性を検証します。

resource "aws_fis_experiment_template" "s3_eks_combined" {
  description = "S3 throttle + EKS ノード終了 — 複合障害シナリオ"
  role_arn = aws_iam_role.fis_app_role.arn

  stop_condition {
 source = "aws:cloudwatch:alarm"
 value  = aws_cloudwatch_metric_alarm.alb_healthy_host.arn
  }

  action {
 name= "s3-throttle"
 action_id = "aws:s3:bucket-throttle-error"
 parameter { key = "percentage"; value = "30" }
 parameter { key = "duration";value = "PT2M" }
 target { key = "Buckets"; value = "target-s3-bucket" }
  }

  action {
 name  = "eks-terminate"
 action_id= "aws:eks:terminate-nodegroup-instances"
 description = "S3 スロットリング 2 分後にノード 30% 終了"
 parameter{ key = "instanceTerminationPercentage"; value = "30" }
 target{ key = "Nodegroups"; value = "target-nodegroup" }
 start_after = ["s3-throttle"]
  }

  target {
 name  = "target-s3-bucket"
 resource_type  = "aws:s3:bucket"
 selection_mode = "ALL"
 resource_tag{ key = "environment"; value = "fis-target" }
  }

  target {
 name  = "target-nodegroup"
 resource_type  = "aws:eks:nodegroup"
 selection_mode = "ALL"
 resource_tag{ key = "environment"; value = "fis-target" }
  }
}

start_after = ["s3-throttle"] により S3 スロットリングを 2 分間注入してアプリの S3 リトライ動作を観測した後、EKS ノードを 30% 終了します。リトライキューが増大した状態でのノード障害耐性を確認できます。

6.5 EKS Vol2 IRSA + EKS Vol3 ALB 連携検証

FIS 実験中も EKS Vol2 の IRSA 最小権限設計が維持されることを確認し、EKS Vol3 の ALB Ingress が Pod 再生成後に正常復旧することを検証します。

IRSA 権限確認

# FIS 実験中の Pod サービスアカウント認証確認
kubectl exec -n default deploy/sample-app -- \
  aws sts get-caller-identity

# IRSA Role の条件キー確認 (sub クレーム一致)
aws iam simulate-principal-policy \
  --policy-source-arn "arn:aws:iam::${ACCOUNT_ID}:role/eks-app-irsa-role" \
  --action-names s3:GetObject \
  --resource-arns "arn:aws:s3:::${TARGET_BUCKET}/*" \
  --context-entries '[{
 "ContextKeyName":"aws:RequestedRegion",
 "ContextKeyValues":["ap-northeast-1"],
 "ContextKeyType":"string"
  }]' \
  --query 'EvaluationResults[0].EvalDecision' --output text

ALB Target Group 復旧確認

# Pod 再生成後の ALB Target ヘルスチェック
aws elbv2 describe-target-health \
  --target-group-arn "${TARGET_GROUP_ARN}" \
  --query 'TargetHealthDescriptions[*].[Target.Id,TargetHealth.State]' \
  --output table

全 Target が healthy に戻ったことを確認して実験完了とします。EKS Vol3 で設定した ArgoCD の Sync Status が Synced を維持していれば GitOps 自己回復性も同時検証できます。

観測性連携: §7 の CI/CD GameDay では、本章の実験結果を 観測性 Vol2 — X-Ray + ADOT の分散トレースと組み合わせ、Pod 障害がサービスのどのスパンに影響したかを可視化します。


7. CI/CD GameDay 自動化 (CodePipeline + EventBridge + 結果記録)

本章では AWS FIS 実験を CI/CD パイプラインに組み込み、定期的・自動的に Chaos Engineering を実施する GameDay 自動化を実装する。CodePipeline が FIS 実験をトリガーし、EventBridge が状態変化を検知、Lambda が結果を集計して S3 に保存、CloudWatch Dashboard を更新し Slack に通知するまでの一連の自動化を Terraform で完全実装する。

【QG-5: CI/CD GameDay 自動化チェックリスト】

  • ☐ CodePipeline 統合: FIS Experiment 実行ステージ (aws_codepipeline + CodeBuild アクション)
  • ☐ EventBridge ルール: FIS state 変化検知 (aws:fis:experiment:stateChange → Lambda)
  • ☐ S3 結果保存: 実験ログ + RTO/RPO メトリクス JSON (aws_s3_bucket fis-results)
  • ☐ CloudWatch Dashboard 自動更新: Lambda 集計 → put_dashboard (実験履歴グラフ)
  • ☐ Slack 通知: 実験開始/完了/停止 (COMPLETED/STOPPED/FAILED) の3状態別通知

fig06: CI/CD GameDay 自動化フロー

7-1. CI/CD GameDay アーキテクチャ概要

GameDay 自動化の全体フロー:

  1. トリガー: EventBridge Scheduler (週次定期) または CodePipeline 手動実行
  2. 実験実行: CodePipeline → CodeBuild → aws fis start-experiment
  3. 状態監視: EventBridge ルール (FIS stateChange) → Lambda (結果集計)
  4. 結果保存: Lambda → S3 (JSON 形式 / 実験 ID + RTO/RPO 実測値)
  5. 可視化: Lambda → CloudWatch Dashboard 更新 (put_dashboard)
  6. 通知: Lambda → Slack Webhook (Incoming Webhook / 3 状態別メッセージ)

7-2. Terraform 実装

S3 結果バケット:

resource "aws_s3_bucket" "fis_results" {
  bucket = "${var.project_name}-fis-results-${var.environment}"

  tags = {
 Project  = var.project_name
 Environment = var.environment
 Purpose  = "FIS GameDay results"
  }
}

resource "aws_s3_bucket_versioning" "fis_results" {
  bucket = aws_s3_bucket.fis_results.id
  versioning_configuration {
 status = "Enabled"
  }
}

EventBridge ルール (FIS 状態変化検知):

resource "aws_cloudwatch_event_rule" "fis_state_change" {
  name  = "${var.project_name}-fis-state-change"
  description = "FIS Experiment state change detection"

  event_pattern = jsonencode({
 source= ["aws.fis"]
 detail-type = ["FIS Experiment State Change"]
 detail = {
state = {
  status = ["completed", "stopped", "failed"]
}
 }
  })
}

resource "aws_cloudwatch_event_target" "fis_result_lambda" {
  rule= aws_cloudwatch_event_rule.fis_state_change.name
  target_id = "fis-result-collector"
  arn = aws_lambda_function.fis_result_collector.arn
}

Lambda 結果集計:

resource "aws_lambda_function" "fis_result_collector" {
  function_name = "${var.project_name}-fis-result-collector"
  role = aws_iam_role.lambda_fis.arn
  handler = "index.handler"
  runtime = "python3.12"
  timeout = 60

  environment {
 variables = {
S3_BUCKET= aws_s3_bucket.fis_results.bucket
SLACK_WEBHOOK_URL = var.slack_webhook_url
CW_DASHBOARD_NAME = "${var.project_name}-fis-gameday"
 }
  }

  filename= data.archive_file.fis_result_collector.output_path
  source_code_hash = data.archive_file.fis_result_collector.output_base64sha256
}

CodePipeline (GameDay パイプライン):

resource "aws_codepipeline" "fis_gameday" {
  name  = "${var.project_name}-fis-gameday"
  role_arn = aws_iam_role.codepipeline_fis.arn

  artifact_store {
 location = aws_s3_bucket.fis_results.bucket
 type  = "S3"
  }

  stage {
 name = "Source"
 action {
name = "Source"
category= "Source"
owner= "AWS"
provider= "CodeStarSourceConnection"
version = "1"
output_artifacts = ["source_output"]
configuration = {
  ConnectionArn = var.codestar_connection_arn
  FullRepositoryId = var.github_repo
  BranchName = "main"
}
 }
  }

  stage {
 name = "FIS-Aurora-Experiment"
 action {
name= "RunAuroraFailoverExperiment"
category  = "Build"
owner  = "AWS"
provider  = "CodeBuild"
version= "1"
input_artifacts = ["source_output"]
configuration = {
  ProjectName = aws_codebuild_project.fis_aurora.name
}
 }
  }

  stage {
 name = "FIS-AZ-Experiment"
 action {
name= "RunAZStopExperiment"
category  = "Build"
owner  = "AWS"
provider  = "CodeBuild"
version= "1"
input_artifacts = ["source_output"]
configuration = {
  ProjectName = aws_codebuild_project.fis_az.name
}
 }
  }
}

7-3. AWS CLI 操作

# GameDay 手動実行 (全シナリオ順次)
TEMPLATE_AURORA="arn:aws:fis:ap-northeast-1:ACCOUNT:experiment-template/EXT-Aurora-Failover"
TEMPLATE_AZ="arn:aws:fis:ap-northeast-1:ACCOUNT:experiment-template/EXT-AZ-Stop"

# Aurora 障害実験
EXPERIMENT_ID=$(aws fis start-experiment--experiment-template-id "$TEMPLATE_AURORA"--query 'experiment.id' --output text)
echo "Aurora experiment started: $EXPERIMENT_ID"

# 実験状態ポーリング
aws fis get-experiment --id "$EXPERIMENT_ID"--query 'experiment.state.status' --output text

# 実験一覧 (直近10件)
aws fis list-experiments--query 'experiments[*].[id,experimentTemplateId,state.status,startTime]'--output table | head -20

7-4. コンソール操作

  1. FIS → Experiments → 実験一覧 → 実験 ID クリックで詳細確認
  2. CodePipeline → Pipelines → fis-gameday → Release change で手動実行
  3. CloudWatch → Dashboards → fis-gameday → RTO/RPO 推移グラフ確認
  4. S3 → fis-results バケット → 実験 JSON ログダウンロード

7-5. Lambda 結果集計スクリプト (Python)

import json, boto3, os, datetime, urllib.request

def handler(event, context):
 fis = boto3.client('fis')
 s3 = boto3.client('s3')
 cw = boto3.client('cloudwatch')

 experiment_id = event['detail']['experimentId']
 status = event['detail']['state']['status']

 # FIS 実験詳細取得
 exp = fis.get_experiment(id=experiment_id)['experiment']
 start_time = exp['startTime']
 end_time = exp.get('endTime', datetime.datetime.now(datetime.timezone.utc))
 rto_seconds = int((end_time - start_time).total_seconds())

 # S3 保存
 result = {
  'experiment_id': experiment_id,
  'status': status,
  'start_time': start_time.isoformat(),
  'end_time': end_time.isoformat() if hasattr(end_time, 'isoformat') else str(end_time),
  'rto_seconds': rto_seconds,
  'template_id': exp.get('experimentTemplateId', 'unknown'),
 }
 s3.put_object(
  Bucket=os.environ['S3_BUCKET'],
  Key=f"results/{experiment_id}.json",
  Body=json.dumps(result, default=str),
  ContentType='application/json',
 )

 # CloudWatch カスタムメトリクス
 cw.put_metric_data(
  Namespace='FIS/GameDay',
  MetricData=[{
'MetricName': 'ExperimentRTO',
'Dimensions': [{'Name': 'TemplateId', 'Value': exp.get('experimentTemplateId', 'unknown')}],
'Value': rto_seconds,
'Unit': 'Seconds',
  }],
 )

 # Slack 通知
 emoji = {'completed': '✅', 'stopped': '⚠️', 'failed': '❌'}.get(status, 'ℹ️')
 message = f"{emoji} FIS GameDay: {status.upper()} | RTO={rto_seconds}s | {experiment_id}"
 payload = json.dumps({'text': message}).encode('utf-8')
 req = urllib.request.Request(
  os.environ['SLACK_WEBHOOK_URL'],
  data=payload,
  headers={'Content-Type': 'application/json'},
  method='POST',
 )
 urllib.request.urlopen(req)

 return {'statusCode': 200, 'body': json.dumps(result, default=str)}

7-6. 観測性 Vol3 SLO Burn Rate → Stop Condition 連携

観測性 Vol3 (Application Signals + SLO) で設定した SLO Burn Rate Alarm を FIS Stop Condition に連携することで、実験中に SLO 違反が発生した場合に自動停止する。

resource "aws_cloudwatch_metric_alarm" "slo_burn_rate" {
  alarm_name = "${var.project_name}-slo-burn-rate-fis-stop"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  metric_name= "ErrorRate"
  namespace  = "AWS/ApplicationSignals"
  period  = 60
  statistic  = "Average"
  threshold  = 5.0
  alarm_description= "FIS Stop Condition: SLO ErrorRate > 5%"
}

resource "aws_fis_experiment_template" "aurora_with_slo_stop" {
  description = "Aurora Failover with SLO Stop Condition"
  role_arn = aws_iam_role.fis_aurora.arn

  stop_condition {
 source = "aws:cloudwatch:alarm"
 value  = aws_cloudwatch_metric_alarm.slo_burn_rate.arn
  }

  # ... actions, targets 省略
}

8. まとめ + 復旧・運用編 Vol3 予告 + 落とし穴 10 選

本記事では AWS FIS を用いた 3 大障害シナリオ (Aurora リージョン障害 / AZ 停止 / S3+EKS アプリ層障害) の完全 Terraform 実装から CI/CD GameDay 自動化まで網羅した。Vol1 §7「DR 訓練未実施の設計は紙の上のフィクション」を、本 Vol2 で機械検証手段として完全回収した。

本記事の核心となる考え方は「障害を注入するのではなく検証するもの」への転換にある。Stop Condition / Blast Radius / Cooldown の 3 段階安全策を Terraform で必ず実装し、CI/CD GameDay を月次自動化することで、DR 構成の有効性を定期的に数値で証明し続けることが可能になる。観測性 3 部作・セキュリティ 3 部作・EKS 3 部作・Lambda 3 部作で構築した 12 巻の本番運用基盤が、FIS によって初めて「実戦証明済み」となる。

【FIS Chaos Engineering 実装完了チェックリスト】

  • ☐ §3: FIS Experiment Template 作成 (Action / Target / Stop Condition / IAM Role の4要素)
  • ☐ §4: Aurora リージョン障害実験 (aws:rds:failover-db-cluster) + RTO/RPO 自動実測
  • ☐ §5: AZ 停止実験 (aws:network:disrupt-connectivity) + Route53 ARC Zonal Shift 検証
  • ☐ §6: S3+EKS アプリ層障害 (throttle-error + terminate-nodegroup) + PDB 設定
  • ☐ §7: CI/CD GameDay 自動化 (CodePipeline + EventBridge + Lambda + Slack Webhook)

8-1. チートシート: FIS 設計判断早見表

項目推奨値/方針備考
Stop ConditionCW Alarm (SLO Burn Rate) 必須実験の暴走を防ぐ安全装置
Blast RadiusPERCENT_OF_SELECTED=30%本番では COUNT=1 or 30% 以下
Cooldown実験間隔 24時間以上連続実験によるリソース枯渇防止
実験前提Vol1 DR 構成 + SLO Alarm 設定済構成なしで実験しても意味なし
結果記録S3 JSON + CloudWatch カスタムメトリクスRTO 推移を継続追跡
GameDay 頻度月次 (本番) / 週次 (ステージング)Vol1 §7 訓練マトリクス参照
安全策 3 層Stop Condition + Blast Radius + Cooldown3 層全てを Terraform 実装

8-2. 落とし穴 10 選 + 対策

  1. Stop Condition なしで実験を本番実行: FIS 実験はデフォルトで自動停止しない。必ず CW Alarm を Stop Condition に設定する。
  2. 対策: stop_condition { source = "aws:cloudwatch:alarm" } を Terraform テンプレートに必須化。

  3. Blast Radius を ALL に設定して本番全体に影響: ALL は全対象リソースに障害注入する。本番禁止。

  4. 対策: selection_mode = "PERCENT_OF_SELECTED" + 上限 30%。テスト環境では COUNT=1 から始める。

  5. FIS IAM Role の権限過剰: AWSFaultInjectionSimulatorFullAccess を使いがち。最小権限で各シナリオ別 Role を作成する。

  6. 対策: シナリオ別 Managed Policy のみ付与 (RDS/EC2/Network/S3/EKS)。

  7. 実験完了後にリソースが元に戻らない: aws:ec2:stop-instances は Duration 後に自動再起動しない。

  8. 対策: duration = "PT5M" を必ず設定。Duration 後にFIS は action を「undone」しようとするが、停止済みEC2は自動起動しないため Runbook に再起動手順を追加。

  9. Aurora フェイルオーバー実験後の旧 Primary 放置: FIS でフェイルオーバーさせると旧 Primary がセカンダリに自動復帰しない。

  10. 対策: Vol1 §8 落とし穴6 参照。aws rds modify-global-cluster での手動再追加手順を Runbook に記載。

  11. S3 スロットリング実験中の既存接続への影響: バケット全体に throttle がかかり、実験対象外のサービスまで影響を受ける。

  12. 対策: 専用テスト用バケットで実験。Blast Radius 設定が S3 では難しいため、本番バケットへの直接適用は避ける。

  13. EKS ノード停止実験で Pod が再スケジュールされない: PodDisruptionBudget (PDB) の設定漏れや、ノード数不足で Pod がPending 状態になる。

  14. 対策: minAvailable=2 の PDB を事前設定。実験前に kubectl get pdb -A で全 PDB を確認。

  15. CodePipeline 実行中に FIS 実験が二重起動: 前の実験が完了前に次の実験がトリガーされる。

  16. 対策: CodeBuild buildspec に aws fis list-experiments --query "experiments[?state.status=='running']" で実行中確認ステップを追加。

  17. GameDay 結果が蓄積されず RTO 推移を追えない: CloudWatch カスタムメトリクスが未設定で実測値が消える。

  18. 対策: Lambda result_collector で put_metric_data を必ず実装。S3 JSON と CloudWatch の二重保存で証跡を永続化。

  19. Chaos Engineering を「壊す」目的で実施: 「障害を起こす=問題発見」ではなく「システムが想定通りに回復するか検証」が目的。

    • 対策: 実験前に「仮説」(例: Aurora フェイルオーバーは 30 秒以内に完了する) を明文化し、実測値と比較する。仮説なしの実験は Chaos ではなくただの破壊。

8-3. 復旧・運用編シリーズ全体マップ

【12巻 AWS 本番運用シリーズ + 復旧・運用編シリーズ 全体ロードマップ】

  • Lambda 3部作: Vol1 基礎 / Vol2 Provisioned Concurrency / Vol3 イベント駆動
  • EKS 3部作: Vol1 クラスター / Vol2 CI/CD+IRSA / Vol3 オートスケーリング
  • 観測性 3部作: Vol1 CloudWatch Logs / Vol2 X-Ray+ADOT / Vol3 Application Signals+SLO
  • セキュリティ 3部作: Vol1 GuardDuty / Vol2 IAM Identity Center / Vol3 Config+Conformance Pack
  • 復旧・運用編シリーズ: Vol1 Multi-Region DR (公開済) / Vol2 Chaos Engineering (本記事) / Vol3 IR Runbook (次回) / Vol4 Active/Active

8-4. Chaos Engineering 設計 3 原則

【Chaos Engineering 設計 3 原則 (本番導入チェック)】

  • 原則1 — 定常状態の定義: 実験前に「RTO ≤ 30秒」「SLO Burn Rate ≤ 閾値」等の数値目標を明文化する。数値なき実験は成功失敗の判断ができない。
  • 原則2 — 最小侵襲から開始: 初回は COUNT=1 / 5分間 / ステージングから始め、結果を観測してから本番・PERCENT_OF_SELECTED=30% へ段階的に拡大する。
  • 原則3 — 自動停止の三重保護: ①Stop Condition (CW Alarm) ②Duration 上限 ③手動停止 Runbook の 3 層を全て実装する。Stop Condition なしの FIS 実験は本番禁止。

8-5. コスト試算 (月次 GameDay 想定)

項目単価月次 想定備考
FIS 実験時間$0.10 / リソース分$5-153 シナリオ × 3 対象 × 5 分
Aurora フェイルオーバー中断$0フェイルオーバー自体はコストなし
CloudWatch Alarm$0.10 / アラーム/月$0.30Stop Condition 3 本
S3 実験結果保存$0.023 / GB/月$0.01JSON 数 MB/月
Lambda 結果集計$0.20 / 100万呼出$0.01GameDay 月次実行
月次合計$6-17月次 GameDay 1 回想定

8-6. 関連記事クロスリンク (12巻 + 前提 AWS記事)

復旧・運用編シリーズ
Multi-Region×Multi-AZ Backup/DR本番実践 (復旧・運用編 Vol1) — Vol1 §7 DR Runbook を本記事で FIS 機械検証として完全回収
Aurora DR + AWS Backup 完全ガイド — §4 Aurora 障害実験の基礎となる DR 構成理解に必須

12巻 AWS 本番運用シリーズ — 観測性 3 部作
CloudWatch Logs Insights + Metrics Filter (観測性 Vol1) — §7 FIS 実験ログを Insights で分析
X-Ray + ADOT 本番運用 (観測性 Vol2) — §4+§6 分散トレース×障害実験で Pod 障害のスパン影響を可視化
Application Signals + SLO (観測性 Vol3) — §2+§3 SLO Burn Rate Alarm を FIS Stop Condition に連携

12巻 AWS 本番運用シリーズ — セキュリティ 3 部作
GuardDuty + Security Hub (セキュリティ Vol1) — §5 FIS 実験中の AZ 障害注入を GuardDuty サプレッションルールで誤検知除外
IAM Identity Center + ABAC (セキュリティ Vol2) — §3 FIS IAM Role を Permission Sets で最小権限管理
AWS Config + Conformance Pack (セキュリティ Vol3) — §3 FIS IAM Role の構成ドリフト検知を Config で自動化

12巻 AWS 本番運用シリーズ — EKS 3 部作
EKS クラスター本番構築 (EKS Vol1) — §6 EKS ノード障害実験の対象クラスター構築基盤
EKS IRSA 最小権限設計 (EKS Vol2) — §6 EKS ノード強制終了実験で IRSA 権限設計の耐障害性を検証
EKS ALB + ArgoCD GitOps (EKS Vol3) — §6 EKS Pod 再生成後の ALB ヘルスチェック復旧を ArgoCD Sync で確認

12巻 AWS 本番運用シリーズ — Lambda 応用 3 部作
Container Image Lambda 本番運用 (Lambda Vol1) — §3 FIS IAM Role 設計で Lambda 最小権限モデルを参考に
Lambda SnapStart 完全活用 (Lambda Vol2) — §7 GameDay Lambda 結果集計関数のコールドスタート対策として参照
Lambda Powertools + Layers 統合運用 (Lambda Vol3) — §7 Powertools Logger で FIS 実験ログを構造化出力

次巻予告: Incident Response Runbook × SSM Automation (復旧・運用編 Vol3)

本記事 §4-§6 で実験した障害シナリオに対する Incident Response Runbook を SSM Automation で自動化する。FIS で検出した障害パターンを Runbook のトリガーに変換し、検知→診断→修復→通知→事後分析 の 5 フェーズを Systems Manager OpsItems + Automation Documents でコード化する実装を提供する。

← 前を読む: Multi-Region×Multi-AZ Backup/DR本番実践 (復旧・運用編 Vol1)

Vol3 予告: Incident Response Runbook × SSM Automation — FIS で発見した障害パターンを Runbook として自動化し、検知から修復まで完全コード化する実装を提供する。Vol4 (Active/Active vs Active/Passive) へのロードマップもあわせて解説予定。