LambdaでAWS Fault Injection Service アクションを試してみた

# はじめに
　先日参加したAWS re:Invent2024で聴講した事例セッション(FSI318：Fidelity Investments: Building for mission-critical resilience)において、AWS Fault Injection Service (以下、FIS)のLambda対応の話がありました。（Lambda対応自体はre:Invent前に発表されていました）

　前回のre:Invent2023でFISのワークショップに参加したので、EC2やRDSでのFIS実験の流れはなんとなく（うっすらと）理解していました。昨年のイベント後はFISに触っていなかったのと、セッションを聴講して、Lambdaでの障害試験を試してみたくなったので、実際にやってみました。

### AWS Fault Injection Service (AWS FIS)とは
　FISは、AWS上で稼働するアプリケーションの回復力を高めるためのマネージドサービスです。カオスエンジニアリングの概念に基づき、システムに意図的に障害を発生させることで、その挙動や耐性を事前に確認することができます。

https://docs.aws.amazon.com/ja_jp/fis/latest/userguide/what-is.html


### 実行環境
今回はaws-cdk-examplesの```api-cors-lambda-crud-dynamodb```をデプロイして使いました。

https://github.com/aws-samples/aws-cdk-examples/tree/main/typescript/api-cors-lambda-crud-dynamodb/

以下のようにフロントにAPI Gatewayがあり、そのバックエンドにLambda、DynamoDBがあるというシンプルなサーバレス構成です。
Lambdaは以下の5つが準備されています。
　getOneItem
　getAllItems
　createItem
　updateItem
　deleteItem

![fis_000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/8cfc7c7c-2ec2-4ecc-2f09-d76f88728613.png)

### FIS実験のアクションによるLambdaへの障害注入方法
　FIS実験がS3バケットに障害試験の内容を書き出し、追加したLambdaレイヤーが定期的に設定ファイルをチェックして内容を読み取り、障害を発生させるというイメージで理解しました。
![fis_001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/5cffaac1-976b-7eb9-2dca-854ff1d51636.png)


## 設定方法

ドキュメントはこちらです。

https://docs.aws.amazon.com/fis/latest/userguide/use-lambda-actions.html

### 作業の流れ
(1) S3バケット作成
(2) Lambda用IAMポリシー作成、IAMロールにアタッチ
(3) Lambdaレイヤー、環境変数追加
(4) FIS用IAMポリシー作成、IAMロール作成
(5) FIS実験の作成、IAMロールアタッチ
![fis_002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/d68673e3-e651-5569-1ce4-ecf268fc6bb5.png)


## S3バケット作成
今回の各処理で使用する専用のバケット `fis-test-lambda` を作成します。

![fis_003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/978465c1-43c2-d344-a070-782f0f26edb0.png)

## Lambda用IAMポリシー作成、IAMロールにアタッチ
Lambda関数から先ほど作成したS3へアクセスするための権限が必要になります。
まずはLambda用のIAMポリシー `fis-test-policy-for-lambda` を作成します。

~~~md:fis-test-policy-for-lambda
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowListingConfigLocation",
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::fis-test-lambda"
            ],
            "Condition": {
                "StringLike": {
                    "s3:prefix": [
                        "FisConfigs/*"
                    ]
                }
            }
        },
        {
            "Sid": "AllowReadingObjectFromConfigLocation",
            "Effect": "Allow",
            "Action": "s3:GetObject",
            "Resource": [
                "arn:aws:s3:::fis-test-lambda/FisConfigs/*"
            ]
        }
    ]
}
~~~

作成したポリシー `fis-test-policy-for-lambda` をLambda関数に紐づいているIAMロールにアタッチします。
![fis_004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/35f11b1b-1882-fad6-6ee3-7cf73d0bfc24.png)


## Lambdaレイヤー、環境変数追加
OS、リージョンによって指定するARNが異なっているのでご注意ください。

https://docs.aws.amazon.com/ja_jp/fis/latest/userguide/actions-lambda-extension-arns.html

今回はap-northeast-1、x86_64の環境になるため、`arn:aws:lambda:ap-northeast-1:339712942424:layer:aws-fis-extension-x86_64:9` を使用しました。

![fis_005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/8c0b9147-f574-d2a0-02c4-156bd72f146d.png)

Lambdaにレイヤーを追加していきます。
![fis_006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/a05264c0-06a1-89b3-fce8-24f48a0cdf46.png)

「ARNを指定」を選択して、先ほど確認したARNを入力し、「検証」をクリックします。
![fis_007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/90601c51-ed85-3623-73c9-7043c41a3c03.png)

説明欄が表示されるので、FISのレイヤーであることを確認して、「追加」をクリックします。
![fis_008.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/4982f297-0cad-cabd-ad51-8c05a9d54fc5.png)

レイヤーが追加されました。
![fis_009.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/25a6f781-6946-4789-8ae1-db636c93135d.png)

続いて環境変数を2つ追加していきます。

| キー | 変数 |
|:-------------------------------|:---------------------------------------------------------|
| AWS_FIS_CONFIGURATION_LOCATION 　| S3設定フォルダのARNを設定<br>`arn:aws:s3:::fis-test-lambda/FisConfigs/`|
| AWS_LAMBDA_EXEC_WRAPPER　　　　　　| /opt/aws-fis/bootstrap　　　　　　　　　　　　　　　　|

![fis_010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/115376bb-3542-ba06-f315-e34a241016bb.png)

## FIS用IAMポリシー作成、IAMロール作成
FIS実験からS3バケットやCloudwatch、Lambdaへアクセスするための権限を付与するため、IAMポリシー `fis-test-policy-for-experiments` を作成します。
~~~md:fis-test-policy-for-experiments
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowFisToWriteAndDeleteFaultConfigurations",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::fis-test-lambda/FisConfigs/*"
        },
        {
            "Sid": "AllowFisToInspectLambdaFunctions",
            "Effect": "Allow",
            "Action": [
                "lambda:GetFunction"
            ],
            "Resource": "*"
        },
        {
            "Sid": "AllowFisToDoTagLookups",
            "Effect": "Allow",
            "Action": [
                "tag:GetResources"
            ],
            "Resource": "*"
        },
        {
            "Action": [
                "cloudwatch:getMetricWidgetImage"
            ],
            "Resource": "*",
            "Effect": "Allow"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogDelivery",
                "logs:PutResourcePolicy",
                "logs:DescribeResourcePolicies",
                "logs:DescribeLogGroups"
            ],
            "Resource": "*"
        }
    ]
}
~~~

IAMロール `fis-test-role` を作成して、先ほどのIAMポリシー `fis-test-policy-for-experiments` をアタッチします。
※既にFIS用にIAMロールを作成している場合は、そちらにアタッチしてもOKです。

![fis_011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/be5664e2-9442-652d-205c-18f64f340a11.png)

## FIS実験の作成、IAMロールアタッチ
最後にFISの実験テンプレートを作成していきます。

Falut Injection Serviceの画面にて「実験テンプレート」を選択し、「実験テンプレートを作成」をクリックします。
![fis_012.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/70176e0e-9f4f-abd4-8c90-6c136a92e53a.png)

「テンプレートの詳細を指定」画面にて説明欄を記入します。
今回は `FIS_Lambda_Add_Delay` と記入しました。実験タイプは「このAWSアカウント」を選択し、「次へ」をクリックします。
![fis_013.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/32953506-3acd-5499-98fd-64bcf0d11996.png)

「アクションとターゲットを指定」画面にて「＋ターゲットを追加」をクリックします。
![fis_014.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/9f31779a-e328-8688-e0a9-3f0b14b56bc8.png)

「ターゲットを追加」画面で、以下を設定していきます。
　名前：lambda
　リソースタイプ：aws:lambda:function を選択
　ターゲットメソッド：リソースID を選択
　「Add new ARN」をクリックします。
 ![fis_015.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/80e3a81b-bf99-f222-b6ea-f43909c1c83b.png)

新たに表示された「Lambda ARN」の項目で対象のLambda関数を選択し、「保存」をクリックします。
![fis_016.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/6b9ea2d3-a1f3-1f79-8ddb-173803de3b5f.png)

ターゲットが作成されましたので、アクションを追加していきます。
![fis_017.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/219bd02c-5d38-762a-dfeb-12034aec66ff.png)

名前を入力して、アクションタイプでは「LAMBDA」を選択後、 `aws:lambda:invocation-add-delay` を選択します。
![fis_018.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/7119fb1c-c025-514a-9e17-ccef3e84296b.png)

すると、ターゲット以下の項目が表示されるようになります。
ターゲットは先ほど作成した `lambda` を選択し、下の各項目に値をセットして「保存」をクリックします。
私がセットしたのは、10分間、100%の起動時に、2500msecのDelayを発生させる形になります。

![fis_019.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/a0879943-2a16-9b3b-a7da-bb11154d7fc2.png)

https://docs.aws.amazon.com/fis/latest/userguide/fis-actions-reference.html#invocation-add-delay

:::note warn
※私が実施した2024/12/30時点で日本語版のドキュメントには、各パラメータの説明がありませんでした。英語版のサイトを日本語訳して確認されることをお勧めします。
:::


| パラメータ                 | 説明                                                                        |
|:-------------------------|:----------------------------------------------------------------------------|
| Duration                 | 障害が継続する時間の長さ                                                   |
| Invocation percentage     | 関数呼び出し時に障害を発生させる割合                                              |
| Startup delay milliseconds | 関数コードの呼び出しと実行の間に待機する時間（ミリ秒単位、0～900,000）。デフォルトは 1000msec |

関数呼び出し時に障害を発生させる割合を指定できるのは便利ですね。中途半端に障害が発生するグレー障害に近い状況が作り出せると思います。

無事、アクションも作成できましたので、「次へ」をクリックします。
![fis_020.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/31e73d1d-a227-1746-8b06-a7643ff23178.png)

サービスアクセスの設定画面で先ほどFIS実験用に作成したIAMロール `fis-test-role` を選択し、「次へ」をクリックします。
![fis_021.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/8f04c186-ad73-bb09-1c99-8a11258edce5.png)

オプション設定をしていきます。
停止条件、レポート設定は特に設定しませんでした。
![fis_022.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/431c74b0-a741-89dc-b773-bb3eb3a9ce71.png)

ログに関しては、チェックを入れて、最初に作成したS3バケットを選択します。Cloudwatch Logsはロググループを新たに作成して指定しました。既存で適切なロググループがある方は、「参照」から選択していただければと思います。
![fis_023.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/c57954ed-4ba2-6b69-81a4-d16767eafd9e.png)

タグは特に設定せず、「次へ」をクリックします。
![fis_024.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/871b35fa-ae3b-415a-552b-c93b164172d4.png)

確認画面になりますので、内容を確認して「実験テンプレートを作成」を選択します。
![fis_025.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/76409e78-a79d-1c74-3ec6-d4f94616450b.png)
![fis_026.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/366f230c-e12e-2751-9273-868dad820116.png)
![fis_027.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/db12fa92-ecc9-80ec-08a5-6aa3f729db40.png)

実験テンプレートができました。
![fis_029.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/37ca33d0-7790-fd5c-80f3-a5774edb0bfb.png)

## 実験を試してみる
実験テンプレートの右上の「実験を開始」をクリックして試してみます。
タグをつける画面が表示されますが、そのまま実験を開始してみます。
![fis_030.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/33a7013c-4566-08bd-fc5f-338e46c62060.png)

無事に開始されました。状態がRunningになっていれば成功です。
![fis_032.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925746/a51b9c68-c445-16ed-df11-55717a38cd15.png)

試しにリクエストを何度か送ってみます。
繰り返しリクエストを送った後なので、Lambda関数がコールドスタートではない状態でも、合計時間が2,500msec超になっています。

```
% curl -X GET https://XXXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/prod/items/d8e26b2b-1346-4734-8d31-35529327510c \
-H 'Content-Type:application/json' -w"\ntime_total: %{time_total}\n"

{"id":"001","name":"FIS test 001","itemId":"d8e26b2b-1346-4734-8d31-35529327510c"}
time_total: 2.874584
```

Cloudwatch LogsのFISのログも見てみます。先ほど設定したアクションがログに出力されていました。
```
{
    "id": "EXP237FqDwrp81F2unz",
    "log_type": "action-start",
    "event_timestamp": "2024-12-30T04:06:53.259Z",
    "version": "2",
    "details": {
        "action_name": "lambda_add_delay",
        "action_id": "aws:lambda:invocation-add-delay",
        "action_start_time": "2024-12-30T04:06:53.246Z",
        "action_targets": {
            "Functions": "lambda"
        },
        "parameters": {
            "duration": "PT10M",
            "invocationPercentage": "100",
            "startupDelayMilliseconds": "2500"
        }
    }
}
```

ちなみにLambda側のログはこちら。
正常時
```
REPORT RequestId: 7d6186fd-309d-4655-b567-5c35f9f64de8	Duration: 75.48 ms	Billed Duration: 76 ms	Memory Size: 128 MB	Max Memory Used: 92 MB	
```

アクション実行中
```
REPORT RequestId: 6869bcb4-f866-48e6-9cc8-f62d56d3ab1a	Duration: 2577.13 ms	Billed Duration: 2578 ms	Memory Size: 128 MB	Max Memory Used: 92 MB	
```

期待通りに処理時間が延びていることを確認できました。

## 気づきとつづき
　今回のお試しでFISによるLambdaの障害試験の流れが掴めました。他の2つの障害パターンについても、試して投稿したいと思います。これまでLambdaの障害試験についてはコードを変更しないと実現できない認識でいましたが、コード変更なしで実現できるようになったのはとても良いことだと思います。
　FIS実験では先行アクションを指定できますので、複数のアクションを作成して、繋げて、徐々に障害の発生率を上げて状況を悪化させるといったこともできそうです。また、1つのLambda関数に複数の障害パターンが同時に発生するといった複雑な障害も実現できそうですので、しっかりと試験パターンを検討できれば、FISによる訓練の質を上げられると考えました。
　色々試して、続きは年明けに別の投稿で記載しようと思います。