AWS Fault Injection ServiceとSystems Manager Documentsで実践Chaos Engineering

## はじめに

システム運用をしていて避けては通れないのが「システム障害」です。
可用性を高めても、運用の仕方によって効果を実感できないことすらあります。例えば、以下のようなあるあるはみなさんも経験されているのではないでしょうか。
- 可用性担保のためにHAクラスタ構成を組んだが運用の中で一度もフェイルオーバーをしたことがなく、怖くて切り替えられない。
- 前回障害試験をしてから時間が経過しており、いざ障害が発生したら想定通りに切り替えが行われなかった。

システム運用の現場では本番環境で発生する予測不可能なイベントに日々付き合わなければなりません。そのようなイベントに対して自信をもって適切に対処するにはどのようなアプローチをとるべきでしょうか。

## Chaos Engineeringとは

Chaos EngineeringはNetflix社が考案した、本番システムに対する破壊的障害を意図的に発生させてその挙動を観察し、対策を講じることで可用性を向上させる運用手法です。

また、以下のブログではChaos Engineeringを「**システムを安定して運用していくことができるという自信**」を構築する取り組みであると述べています。

https://aws.amazon.com/jp/builders-flash/202110/awsgeek-fault-injection-simulator/?awsf.filter-name=*all

代表的なChaos Engineeringのツールとして以下が挙げられます。

- [Chaos Monkey](https://netflix.github.io/chaosmonkey/)
- [Gremlin](https://www.gremlin.com/)
- [pumba](https://github.com/alexei-led/pumba)

## AWS Fault Injection Service (FIS)とは

AWS Fault Injection Service (FIS)はAWSワークロードにフォールトインジェクション実験を行うことができるマネージドサービスです。フォールトインジェクションはカオス工学の原則に基づいています。
以前はAWS Fault Injection Simulatorという名前でしたが、AWS re:Invent 2023からAWS Fault Injection Serviceへ変更になったようです。

https://aws.amazon.com/jp/fis/

AWS FISを使用するには、**ターゲット**、**アクション**、**停止条件**が含まれる**実験テンプレート**を作成します。作成した実験テンプレートを実行することでフォールトインジェクション実験を行います。

![AWS FIS Image](https://docs.aws.amazon.com/ja_jp/fis/latest/userguide/images/experiment-components.png)
>https://docs.aws.amazon.com/ja_jp/fis/latest/userguide/what-is.html より引用

**アクション**
AWS FISが実験で行う操作です。例えば、EC2インスタンスを停止する、CPU負荷をかけるといったアクションが用意されており、実験の目的にあわせて選択できます。アクションは単一または複数を組み合わせて直列または並列で実行できます。

**ターゲット**
アクションを実行するAWSリソースです。特定のリソースを選択したり、タグを指定してまとめて選択することができます。

**停止条件**
Amazon CloudWatch Alarmとして定義したしきい値に到達した場合に実験を停止することができます。

**サポート対象のAWSサービス**
2023年12月時点で以下のAWSサービスをサポートしています。
- Amazon CloudWatch
- Amazon EBS
- Amazon EC2
- Amazon ECS
- Amazon EKS
- Amazon RDS
- AWS Systems Manager


## 実験の計画・ガイドライン

フォールトインジェクション実験を始める前に、以下の原則とガイドラインに従うことが推奨されています。

https://docs.aws.amazon.com/ja_jp/fis/latest/userguide/getting-started-planning.html

実験ターゲットの特定やアプリケーションアーキテクチャの確認はもちろんのことですが、「**定常状態の動作の定義**」はしっかり行いましょう。自分が保守運用する業務システムのベースラインをよく理解していないと、どのような状態が最適であるか定まらず、不適切な対策(過剰にリソースをスケールさせる等)を行ってしまいがちです。「定常状態」を明確に定めた上で、仮説を形成し実験に臨みましょう。


## やってみた

### 事前準備

AWS FISの実験テンプレートで指定するIAMロールを新規作成します。
ここでは簡単のためすべての管理ポリシーを設定しますが、実運用で利用される場合は最小権限の原則に従ってください。


```json:信頼関係
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "fis.amazonaws.com"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": "xxxxxxxxxxxx"
                },
                "ArnLike": {
                    "aws:SourceArn": "arn:aws:fis:ap-northeast-1:xxxxxxxxxxxx:experiment/*"
                }
            }
        }
    ]
}
```

IAMポリシー

![01_IAM_Policy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/ef2a8418-72ea-a1bb-182b-b415288f9681.png)

また、実験ログの出力先として、S3バケットやCloudWatch Logsロググループへの書き込み権限も必要となります。

後述しますが、AWS Systems Manager (SSM) Documentsを使用した実験を行う場合は以下の準備が必要です。

- ターゲットインスタンスへSSM Agentをインストール
- ターゲットインスタンスでアクションを実行するための権限設定(IAMインスタンスプロファイル)

### 実験テンプレートの作成と実行

まず単発の実験テンプレートを作成して実行し、AWS FISの基本的な使い方を理解しましょう。

AWS Resilience Hubから[回復力のテスト]を展開し、[実験テンプレート]-[実験テンプレートを作成]を押下します。

![02_FIS_Template.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/b2157c90-9de3-0b01-ca4f-5364a237a277.png)

「実験タイプ」画面では[このAWSアカウント:{account_id}]を選択して[確認]を押下します。

![02_FIS_Template_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/0cdf690e-e751-3b14-4b08-306db7740b72.png)

「実験テンプレートを作成」画面で、実験テンプレートの[説明]を入力します。

![02_FIS_Template_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/0a28b14e-3311-3765-68a1-287107edcc15.png)

ターゲットを追加します。ターゲットはリソースIDやタグで指定することができます。
ここでは以下のように設定しました。設定できたら[保存]を押下します。

![02_FIS_Template_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/63c0d130-df51-3e7c-a5da-bf2e5ca863f1.png)

アクションを追加します。ターゲットで実行するアクションタイプやターゲットを選択できます。
ここでは以下のように設定しました。設定できたら[保存]を押下します。

![02_FIS_Template_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/227c7b0a-7afd-fc95-51b4-de4fafba108b.png)

設定が完了した「ターゲットとアクション」画面がこちらです。追加のアクションやターゲットを設定したい場合は同様に設定します。

![02_FIS_Template_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/480dbb97-274e-88ed-f4a8-4b0a700cd140.png)

「サービスアクセス」画面において、実験テンプレート用のIAMロールを選択します。
事前にIAMロールを作成していますので[既存のIAMロールを使用する]にチェックをして[IAMロール]を選択します。

![02_FIS_Template_07_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/f2d85dca-b942-0cfb-dd09-0c42b185fd79.png)

「ログ」画面では実験ログデータの出力先を指定します。
事前にS3バケットを作成しましたので、[Amazon S3バケットへの送信]にチェックして[S3バケット名]を入力しました。

![02_FIS_Template_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/e462df27-9697-ec56-b37f-cdb37f4880de.png)

必要に応じて「タグ」を設定し、[実験テンプレートを作成]を押下します。

![02_FIS_Template_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/8f60b152-88b5-51f9-85f3-9b838ad2c614.png)

[作成]を入力して[実験テンプレートを作成]を押下します。

![02_FIS_Template_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/be5e4c77-3056-6fe2-25a4-c64e09bb8800.png)

実験テンプレートが作成できました。[実験を開始]を押下します。

![02_FIS_Template_12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/8cb47882-8c5c-1aee-2ddf-31f5fe7cacee.png)

必要に応じてタグを追加し、[実験を開始]を押下します。

![02_FIS_Template_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/5c47d5da-be64-0354-83da-aae95c658613.png)

[開始]を入力して[実験を開始]を押下します。

![02_FIS_Template_14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/2f4373e7-d5fc-4a86-9b3c-05e96f5261d9.png)

実験が開始され、対象EC2インスタンスが停止しました。

![02_FIS_Template_15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/e368b228-331d-e6cf-da72-c7ea8b129215.png)

実験が完了すると、「状態」が[Completed]になりました。

![02_FIS_Template_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/77a88da1-a2af-2d95-0024-9262e5e35b21.png)


### OS・ミドルウェアの実験を行う

AWS FISではAWS Systems Manager Documentsを使用した実験を行うことができます。
事前定義済みのドキュメントがいくつかありますので使用コマンドとあわせて紹介いたします。

| ドキュメント | 説明 | 使用コマンド(該当箇所抜粋) |
|:-:|:-:|:-:|
| AWSFIS-Run-CPU-Stress  | インスタンスに対して CPU ストレスを実行します。  | stress-ng --cpu {{ CPU }} --cpu-method matrixprod -t {{ DurationSeconds }}s --cpu-load {{ LoadPercent }}  |
| AWSFIS-Run-Disk-Fill  | インスタンスのルートボリュームにディスク容量を割り当てて、ディスクフル障害をシミュレートします。  | fallocate -l "\\$fillup_file_size" "$DISK_FILLUP_PATH"  |
| AWSFIS-Run-IO-Stress  | インスタンスに対して IO ストレスを実行します。  | stress-ng --iomix {{ Workers }} --iomix-bytes {{ Percent }}% -t {{ DurationSeconds }}s  |
| AWSFIS-Run-Kill-Process  | インスタンス内の指定されたプロセスを停止します。  | killall -{{ Signal }} '{{ ProcessName }}'    |
| AWSFIS-Run-Memory-Stress  | インスタンスに対してメモリストレスを実行します。  | stress-ng --vm {{ Workers }} --vm-bytes {{ Percent }}% -t {{ DurationSeconds }}s  |
| AWSFIS-Run-Network-Blackhole-Port  | プロトコルおよびポートのインバウンドトラフィックまたはアウトバウンドトラフィックをドロップします。  | iptables -A fault-out -p \\${PROTOCOL} --dport ${PORT} -j DROP  |
| AWSFIS-Run-Network-Latency  | ネットワークインターフェイスにレイテンシーを追加します。  | tc qdisc add dev \\$INTERFACE parent 1:3 handle 10: netem delay "\\${DELAYMILLISECONDS}"ms   |
| AWSFIS-Run-Network-Latency-Sources  | ネットワークインターフェイスにレイテンシーとジッタを追加します。  | tc qdisc add dev ifb0 root netem delay "\\${DELAYMILLISECONDS}"ms "\\${JITTERMILLISECONDS}"ms   |
| AWSFIS-Run-Network-Packet-Loss  | ネットワークインターフェースにパケットロスが加わります。  |  tc qdisc add dev $INTERFACE parent 1:3 handle 10: netem loss "\\${LOSS}"% |
| AWSFIS-Run-Network-Packet-Loss-Sources  | ネットワークインターフェースにパケットロスが加わります。  | tc qdisc add dev ifb0 root netem loss "\\${LOSS}"%  |

個々のドキュメントのパラメータ詳細については以下のマニュアルをご参照ください。

https://docs.aws.amazon.com/ja_jp/fis/latest/userguide/actions-ssm-agent.html#fis-ssm-docs

:::note warn
ドキュメントで使用されるコマンドは実行時にターゲットへインストールされます。インストールしたくない場合は"InstallDependencies"オプションを"false"にしてください。
:::

事前定義済みドキュメントを使用するには、アクションを追加する際に、アクションタイプで[EC2/SSM]-[aws:ssm:send-command/～]を選択します。

![02_FIS_Template_18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/4af32375-7a1f-8357-5fd1-fda5bf5377b4.png)

アクションパラメータ

![02_FIS_Template_19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/d37115cd-2449-9433-dc63-d10d0922ee89.png)

### SSMカスタムドキュメントを用いた実験

AWSで事前定義されたアクション以外に、OSやミドルウェアに対してカスタマイズした実験を行いたいケースがあるかと思います。
追加するアクションのアクションタイプに`aws:ssm:send-command`を使用することで、任意のSSMドキュメントを使用することができます。

今回は「SysRq」トリガを用いたOSのKernelパニックを発生させるドキュメントを作成して実行してみます。簡単ですが、ドキュメントのコンテンツは以下の通りです。

```yaml:CustomFIS-Run-OS-Panic
description: |-
  ### Document name - CustomFIS-Run-OS-Panic

  ## What does this document do?
  This activates the kill switch "SysRq" and causes a kernel panic on the instance.

  ## Dependencies installed by this SSM Document
  None.

  ## Input Parameters
  None.

  ## Output Parameters
  None.

schemaVersion: '2.2'
mainSteps:
- action: aws:runShellScript
  name: ExecuteOsPanic
  inputs:
    runCommand:
    - |-
      #!/bin/bash

      ########################
      # Fault-specific logic #
      ########################

      echo 1 > /proc/sys/kernel/sysrq
      echo c > /proc/sysrq-trigger

      exit 1;
```

「アクションを追加」で[アクションタイプ]に`SSM`、`aws:ssm:send-commmand`を選択します。

![04_FIS_CustonSSM_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/ab92febd-5d2d-7c13-24e6-6c41febb2e0b.png)

`documentArn`には以下のようにSSM Documentsのarnを入力します。

`arn:aws:ssm:ap-northeast-1:{account_id}:document/CustomFIS-Run-OS-Panic`

実行するとカーネルパニックが発生してOSがフリーズしました。(わかりやすいイメージが無いためお伝えしづらいのですが。)
カスタムドキュメントを使用することで実験の幅が広がりますね。


### シナリオベースの実験

2023/11/7のアップデートでAWS FISがシナリオおよびスケジュールされた実験の一般提供を開始しました。

https://aws.amazon.com/jp/about-aws/whats-new/2023/11/aws-fault-injection-simulator-scenarios-scheduled-experiments/

また、2023/11/30のアップデートで「AZ アベイラビリティ: 停電」および「クロスリージョン: 接続性」のシナリオがリリースされたことを発表されました。

https://aws.amazon.com/jp/about-aws/whats-new/2023/11/aws-fault-injection-service-two-requested-scenarios/

AWS Resilience Hubの[回復力のテスト]-[シナリオライブラリ]から事前定義されたシナリオを利用することができます。

![03_FIS_Scenario_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/5b8e5dd0-9399-0d0c-628e-1a8b9a6ac2ea.png)

事前定義されたシナリオを紹介します。

| カテゴリ | シナリオ | 説明 |
|:-:|:-:|:-:|
| EC2  | インスタンス障害  |  インスタンスを停止し、アクション期間の終了時 (デフォルトでは 5 分) に再起動します。 |
| EC2  | ディスク  |  ターゲットの EC2 インスタンスに注入されるディスク使用量の増加をカスタマイズできます (デフォルトでは、ディスク ストレス アクションごとに 5 分) |
| EC2  | CPU  | 対象の EC2 インスタンスに注入される CPU ストレスの量を増加させるようにカスタマイズできます。デフォルトでは、CPU ストレス アクションごとに 5 分です。  |
| EC2  | メモリ  |  対象の EC2 インスタンスに注入されるメモリ ストレスの量を増加させるようにカスタマイズできます。デフォルトでは、メモリ ストレス アクションごとに 5 分です。 |
| EC2  | ネットワーク遅延  |  ターゲットの EC2 インスタンスに注入されるネットワーク レイテンシーの増加量をカスタマイズできます (デフォルトでは、レイテンシー アクションごとに 5 分)。 |
| EKS | ポッドの削除  | 一致したすべてのポッドを終了します。ポッドの再作成は、kubernetes 構成によって制御されます。  |
| EKS | CPU  |  ターゲットの EKS ポッドに注入される CPU ストレスの量を増加させるようにカスタマイズできます (デフォルトでは、CPU ストレス アクションごとに 5 分)。 |
| EKS | ディスク  | ターゲットの EKS ポッドに注入されるディスク ストレスの量を増加させるようにカスタマイズできます (デフォルトでは、CPU ストレス アクションごとに 5 分)。  |
| EKS | メモリ  | ターゲットの EKS ポッドに注入されるメモリ ストレスの量を増加させるようにカスタマイズできます (デフォルトでは、メモリ ストレス アクションごとに 5 分)。 |
| EKS | ネットワーク遅延  | ターゲットの EKS ポッドに注入されるネットワーク レイテンシーの増加量をカスタマイズできます (デフォルトでは、レイテンシー アクションごとに 5 分)。  |
| AZ の可用性 | 停電  | アベイラビリティーゾーン (AZ) での完全な停電の予期される症状を導入します。AZ の可用性について詳しくは、「[停電](https://docs.aws.amazon.com/ja_jp/fis/latest/userguide/az-availability-scenario.html)」をご覧ください。  |
| クロスリージョン | 接続  | 実験リージョンから宛先リージョンへのアプリケーション ネットワーク トラフィックをブロックし、クロスリージョン データ レプリケーションを一時停止します。クロスリージョンの使用方法について詳しくは、「[接続](https://docs.aws.amazon.com/ja_jp/fis/latest/userguide/cross-region-scenario.html)」をご覧ください。  |

https://docs.aws.amazon.com/ja_jp/fis/latest/userguide/scenario-library-scenarios.html

一例として、EC2のメモリシナリオの[ターゲットとアクション]を示します。シナリオに従って複数のアクションがあらかじめ定義されています。ターゲットインスタンスに特定のタグを設定することで実験を行います。

![03_FIS_Scenario_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/fff677b8-bf93-30da-bb01-23786195c547.png)


### スケジュールを設定する (Experiment Scheduler)

実験スケジューラ(Experiment Scheduler)を使用することで、実験テンプレートをあらかじめ決められた時刻に実行したり、定期的に繰り返し実行することができます。

https://docs.aws.amazon.com/ja_jp/fis/latest/userguide/experiment-scheduler.html

実験スケジューラはEventBridge Schedulerを使用しますので実験スケジューラを実行するIAMロールの信頼ポリシーに以下を追加します。

```json:信頼関係
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "scheduler.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

加えて、EventBridge Schedulerが実験を呼び出せるよう、`fis:StartExperiment`アクションを許可するIAMポリシーを追加します。

```json:FIS_StartExperiment_Policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Allow-FIS-StartExperiment",
            "Effect": "Allow",
            "Action": "fis:StartExperiment",
            "Resource": [
                "arn:aws:fis:*:*:experiment-template/*",
                "arn:aws:fis:*:*:experiment/*"
            ]
        }
    ]
}
```

スケジュールを設定したい実験テンプレートをチェックして、[アクション]-[実験をスケジュール]を選択します。

![05_FIS_Schedule_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/6d042368-cb29-f61b-6e33-dbaccea68f2b.png)

「実験をスケジュール」画面へ遷移します。[スケジュール名]は自動的に決められます。

![05_FIS_Schedule_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/89046475-f032-3a3f-3f1e-052a2c01103d.png)

「スケジュールのパターン」を設定します。
ここでは[繰り返しのスケジュール]-[cronベースのスケジュール]を選択してみました。cron式で柔軟なスケジュールを設定できます。

![05_FIS_Schedule_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/13dc89b8-7e9b-5abc-86e0-cf57ab53096e.png)

「時間枠」で[開始日時]や[終了日時]を選択します。過去日は設定できませんので注意してください。

![05_FIS_Schedule_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/8afcf147-e58b-ff82-48f3-4c7e86d25c12.png)

「スケジュールの状態」で[スケジュールを有効にする]のチェックをオンにして、「アクセス許可」でロールを選択肢、[実験をスケジュール]を押下します。

![05_FIS_Schedule_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/4e0a877b-789f-9784-9bae-d5bbeb6ff7ac.png)

[スケジュール]タブから実験スケジュールが追加されていることが確認できます。

![05_FIS_Schedule_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/ab1ee5df-51df-012a-19b9-7fcb325d4d13.png)

スケジュールを無効にしたい場合は対象スケジュールを選択して[スケジュールを無効にする]を選択します。

![05_FIS_Schedule_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/7bad3c29-cd92-fba2-6702-c1abb237115a.png)

フィールドに[無効にします]と入力して[無効にします のスケジュール]を押下します。

![05_FIS_Schedule_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/8c58b7fc-e839-83da-88ec-dd8d22dceec6.png)

[状態]が「Disabled」になったことが確認できました。

![05_FIS_Schedule_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/6c660573-f306-50a4-e68c-516640e4cf60.png)



## AWS FISの料金

実験のアクションにかけた時間(アクションの開始時刻から停止する時刻)だけ課金されます。単価は安くはないため、長時間かかる実験やマルチアカウントで並行して実験する場合は注意が必要です。

| リージョン | 単位 | 料金 |
|:-:|:-:|:-:|
|  AWS GovCloud (米国東部および米国西部) を除くすべてのリージョン  |  1アカウント・アクション1分 | 0.10ドル  |
|  AWS GovCloud (米国東部および米国西部) | 1アカウント・アクション1分  | 0.12ドル  |

https://aws.amazon.com/jp/fis/pricing/

## おわりに

AWS Fault Injection Serviceの機能や操作方法をざっとご紹介しつつ、Systems Manager Documentsによるカスタムドキュメントを加えたFault Injectionを通じて、Chaos Engineeringを体験してみました。今回使用したサービスをアーキテクチャ図として記載します。

![AWS_FIS.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144187/466ad264-604c-da99-197e-3186b943e582.png)

保守運用の現場ではシステム障害は切っても切れない関係で、適切に付き合わなければなりません。システム障害は時として辛い経験になることがある一方で、通常時では得がたい貴重な経験ができ対応者の成長を促すきっかけにもなります。まずはテスト環境からChaos Engineeringを積極的に取り入れて障害対応力を培い、自信につなげていきましょう。