Step FunctionでLambdaでスポットリクエストを実行し、ElasticIPを付け替える(EC2スポットインスタンス自動化5)

# 背景(再掲)
個人向けに最低費用で運用するため、Ec2をスポットインスタンスで使用しているが、時々スポットリクエストが中断される。その都度、再度スポットリクエストで立ち上げなおしているが、この処理を自動化したい。

# 大まかな流れ(再掲)
スポットインスタンスの中断はイベント通知されるので、それをトリガとして、立ち上げ処理を行う。

全体の流れは第1回参照。本記事はスポットリクエストの中断イベントから対象の情報を取得する部分が対象。

# 記事の全体
1. [AWS BackupでEC2のAMIが作成されるので、その最新のAMI IDをイベントから取得してParameterStoreに登録する。](https://qiita.com/silv224/items/0c1d157d34cd8ffb651b)
1. [AWS Lambda(python)でLINEへ通知する。](https://qiita.com/silv224/items/37453c8e7401f3a77d5f)
1. [AWS Lambda(python)でEC2スポットリクエストを実行する。](https://qiita.com/silv224/items/e089df858dc518a99713)
1. [AWS Lambda(Python,boto3)でElasticIPを付け替える。](https://qiita.com/silv224/items/5e5d4b07a92aa10d1bee)
1. [Step FunctionでLambdaでスポットリクエストを実行し、ElasticIPを付け替える。](https://qiita.com/silv224/items/1d8f425eca0f31db7236)**←今回の内容**

# 調べたこと
Step Functionは初めて使用するため、まず利用料金を調べる・・・。
状態遷移の回数にて課金されるが、今回のユースケースでは無料枠に十分収まる。

https://aws.amazon.com/jp/step-functions/pricing/

Eventの取り扱いには細心の注意が必要。

https://dev.classmethod.jp/articles/aws-stepfunctions-state-machine-looped-endlessly-causing-148-dollars-billing/

パラメータ受け渡しについて。

https://dev.classmethod.jp/articles/stepfunctions-parameters-inter-states/

同期 or 非同期で呼び出しが可能。今回のケースでは同期で実装する。

https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/connect-lambda.html

Step Functionsの使い方(Input/Output)
https://zenn.dev/ken_11/articles/4c61683629f45f

# Step Functionsの方針
* ループ構成にしない
    * 想定外に実行されることを避ける。
* 極力SSMからパラメータを取ってくる

※色々やっているうちに、Step Functionを使わなくてもよさそうでしたが、勉強のためにあえて使うことにしました。

### ①Step Functionsの実行
EventBridgeからStepFunctionに連携します。

### ②StepFunctionsの流れ
1. EventBridgeからStepFunctionsに連携し、StepFunctiosのステートマシンが起動される。
1. スポットリクエストのLambdaへEventBridgeからのイベントを受け渡して実行する。
1. スポットリクエストのLambdaの実行結果(Output)から、新規インスタンスIDの有無を見て起動されたかどうか判断する。起動したインスタンスがない場合は、通知用Lambdaの入力(Input)に渡して通知する。
1. インスタンスが起動している場合は、EC2の起動時間を少々待った後にElastic IPアドレスの付け替え用Lambdaに実行結果を連携する。
1. 4.の結果を通知用Lambdaの入力に渡して通知する。

# Step Fuctionsの設定

ステートマシンを作成する。ブランクで作成。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/87827830-94e4-fe0d-49c3-ae0e486c68e5.png)

作成画面が表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/8b5d6107-ed97-e946-c242-74e19019a9d7.png)

ドラッグ&ドロップで必要な要素を追加する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/edb080b2-4f8d-7dc7-8511-588e64bfecc9.png)

Lambda関数を指定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/49d1247a-2dc0-2089-1a90-3ea0ef64f7a9.png)

ひとまず、状態名とLamda関数のみ指定し、全体のフローを作成。
(最初にLambda関数を指定しておくと、ステートマシン実行時のIAMロールに対象の関数に対する実行権限が追加される模様。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/94a21a1c-e231-2d9e-3efa-1a402520b319.png)

結果判定部分では、出力中のインスタンスIDの有無を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/89e0ff7f-c162-2993-1b0a-1e2155c033d5.png)

スポットリクエスト実行後、EC2が起動してElastic IPが関連付けできるまで、少々時間がかかるのでWaitをいれて30秒ほど待つことにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/db50420e-a0e8-4149-cfca-7a2d52fa4f59.png)

Line通知用は入力をメッセージ部分のみを指定しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/6bb5f01e-b835-69ae-3439-60f6757be34f.png)

デバッグ時はログを取得しておいた方がよいでしょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/71899482-648a-9315-dff6-bfe20a03444f.png)

右上の作成を押すと確認画面が出る。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/2af5eed7-e43c-d534-9af1-c1680d96a8c5.png)

ステートマシンが作成される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/0b6d9838-8519-959e-90c1-1e0d6bcdad3c.png)

# StepFunctionの動作確認
最初のLambdaのペイロードに、イベントのサンプルを入れて動作を確認します。
(インスタンスIDのみ実際の起動中EC2インスタンスIDに合わせておきます。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/9e799980-ee67-f04a-46a0-8a87d28da693.png)
```(サンプルイベント).json
{
    "version": "0",
    "id": "12345678-1234-1234-1234-123456789012",
    "detail-type": "EC2 Spot Instance Interruption Warning",
    "source": "aws.ec2",
    "account": "123456789012",
    "time": "yyyy-mm-ddThh:mm:ssZ",
    "region": "us-east-2",
    "resources": ["arn:aws:ec2:us-east-2:123456789012:instance/i-1234567890abcdef0"],
    "detail": {
        "instance-id": "インスタンスID",
        "instance-action": "action"
    }
}
```

実行を押して、以下の確認画面はそのままで実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/0b6996b5-dc13-9b14-1e15-e0a3fd92a4fd.png)

実行結果を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/b0e9d333-a8fb-7751-3659-453feb5dd06e.png)


# EventBridgeの設定

StepFunctionの入力のペイロードはテスト用のイベントを消しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/df0fdfb9-52ae-7dfa-0244-5bc762b21b71.png)

EventBridgeでは、スポットインスタンスの中断イベントを契機に、StepFunctionで作成したステートマシンへ連携します。

対象のイベントパターンは以下の通り。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/ea6c8f3a-773e-4827-e4a2-d645ca2df5ca.png)
ターゲットの指定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309202/36c967a9-28ac-c8fd-1f03-8e0860f43052.png)