Step Functions から ECS Task を呼びだすときに、実行するシェルスクリプトを指定してみた

# はじめに

システムの中で、1日1回シェルスクリプトを動かしてバッチ処理を行うときがあります。その際に、実行したいシェルスクリプトを複数用意して、時間帯によって実行するシェルスクリプトを指定したいときがあります。シェルスクリプトごとにコンテナイメージを用意する方法もアリですが、1個のコンテナイメージに複数のシェルスクリプトを格納しておいた方が管理が簡単な場合もあります。

ECS Task を呼びだすときに、`containerOverrides` を指定することで、Dockerfile 上の CMD を上書きして実行することが出来ます。これにより、元々定義されていたコンテナイメージの CMD の指定を上書きして ECS Task を実行できます。

今回の記事では、Step Functions から ECS Task を実行するときに、CMD の上書きをしてみて、実行するシェルスクリプトを上書きする方法を確認していきます。



# ECR の作成

まず、コンテナイメージを新たに自作していくために、ECR の Reposutory を作成します。

ECR のページに移動して Create repository を押します。

![image-20220319025515447.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/7ec0694d-2939-65fe-9fef-c0be25143474.png)



- Private な Repository
- 名前を指定

![image-20220319025608481.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/299208bb-6dd6-94de-7d5f-68e5ffc963ca.png)



Create を押します

![image-20220319025630242.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/c757c2d1-1565-8a1a-f191-b9d523545052.png)



ECR Repository が作成されました

![image-20220319025708584.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/ae67b508-6cb6-d923-2054-b58b006502ac.png)



# コンテナイメージ

ECS 上で動かすためのコンテナイメージを自作していきます。CMD で上書きするために、2 つの Bash シェルスクリプトを準備します。シェルスクリプトは、終了コードの違いで 2 種類のものを用意します。

- 終了時のコード `exit 0` 
- 終了時のコード `exit 1` 



成功するシェルスクリプト : `success-shellscript`

```bash
#!/bin/bash
echo "$(date) : Hello, I am success shell script!"
exit 0
```



失敗するシェルスクリプト : `fail-shellscript.sh`

```bash
#!/bin/bash
echo "$(date) : Hello, I am fail shell script!"
exit 1
```



上記の2個のシェルスクリプトを使うための Dockerfile を作成します

- ベースイメージは、Amazon Linux 2
- 実行するシェルスクリプトのファイル名を CMD で渡す。ENTRYPOINT だと ECS Task を実行するときに上書きが出来ないため、CMD で指定すること。

```dockerfile
FROM public.ecr.aws/amazonlinux/amazonlinux:2

WORKDIR /root/workdir
ENV PATH $PATH:/root/workdir

COPY success-shellscript.sh .
COPY fail-shellscript.sh .

CMD [ "success-shellscript.sh" ]
```



Dockerfile を使って、コンテナイメージを Build します。

```
docker image build -t xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/stepfunctions-ecs:0.0.1 .
```



ECR に Login をしたあとに

```
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com
```



ECR に Push します

```
docker push xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/stepfunctions-ecs:0.0.1
```





# ECS Task Definition を作成

作成したコンテナイメージを動かすために、Task Definition を作成してきます。

Create new task definition

![image-20220319030333691.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/439fb439-e406-ffaa-536c-c3da0f23d72b.png)



- 名前を指定
- ECR の URI を指定
- Port mappings では、ポートを公開しないので何も指定しない

![image-20220319030530597.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/56be6967-5c73-54d3-cfc6-265af2deea7c.png)



Next を押します

![image-20220319030841685.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/f906808f-c7d8-ff32-eafb-609f16ee2bfe.png)



- Task に紐づけるリソースを指定
- Task のロールを必要に応じて指定

![image-20220319031023536.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/a7314ece-5de3-d562-1ca3-ce62fb6b1daf.png)



残りはデフォルトのまま Next を押します

![image-20220319031111069.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/8264144c-b0bb-c242-84b7-206a6f3500e7.png)



Create を押します

![image-20220319031136643.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/594dd178-14a1-9d50-10b0-ae046780c74a.png)



Task Definition が作成されました

![image-20220319031210129.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/eb1a01b8-f716-3742-0e00-247b2e60d9a2.png)



Task Definition の ARN をメモっておきます。後の手順で使います。

![image-20220319110740755.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/14a3d313-0fe3-74ce-d76a-2043cce51342.png)



# Step Functions から ECS Task を呼びだす部分を作成

Step Functions の State machine を作成して、ECS Task を呼びだす部分を作成していきます。

Create state machine を押します

![image-20220319031317374.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/7d139001-61ea-443f-94c4-98f520cef29b.png)



Workflow Studio でフローを作成していきます

![image-20220319031408005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/c72bb368-708c-d047-b766-39b7ee5e2ebf.png)



Workflow Studio が立ち上がったので、ECS の RunTask を配置します

![image-20220319032354141.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/5fac1f47-ec13-3f34-ac28-8bcb0a3a725e.png)



RunTask では、実行するシェルスクリプトを指定したいので、CMD を上書きしたいです。API Parameters で上書きが出来るのですが、どういった パラメータを指定できるか、Run Task に関する API のドキュメントを見ていきます。画面右側にある「API」の文字をクリックすることで、このアクションのドキュメントが直接開けます。便利に使えるので覚えておくといいでしょう。

![image-20220319032613176.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/6d0db841-08cf-ca35-78d6-69a42960f917.png)



こんな感じに Request Syntax のページが開きます。この Request Syntax が、そのまま API Parameters に利用できます。

次の画像にある `overrides.containerOverrides.command` の部分で、Dockerfile の CMD を上書きできます。ここで、実行する シェルスクリプト名を指定していきましょう。

![image-20220319110912617.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/6609975c-4f60-b6a7-0338-bfd579aa4437.png)



注意点として、上の Document では小文字で書かれていますが、実際には[このURL](https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/connect-ecs.html) のようにイニシャル大文字で書かないとけないので気を付けましょう。



Document に従い、API Parameters を次のように指定します

- Cluster : ECS Cluster の ARN を指定
- TaskDefinition : TaskDefinition の ARN を指定
- NetworkConfiguration : Security Group, Subnet の指定
- Overrides.ContainerOverrides.Name : Task Definition で定義したコンテナ名を指定
- Overrides.ContainerOverrides.Command: 実行したいシェルスクリプトの名前を指定

```json
{
  "LaunchType": "FARGATE",
  "Cluster": "arn:aws:ecs:ap-northeast-1:xxxxxxxxxxxx:cluster/test-cluster01",
  "TaskDefinition": "arn:aws:ecs:ap-northeast-1:xxxxxxxxxxxx:task-definition/stepfunctions-ecs-taskdefiniton:1",
  "NetworkConfiguration": {
      "AwsvpcConfiguration": { 
         "AssignPublicIp": "DISABLED",
         "SecurityGroups": [ "sg-03b5c3afba3e352ee" ],
         "Subnets": [ 
             "subnet-0cf48d891b1bea31c",
             "subnet-09023225144fa1c62",
             "subnet-0c414bf2d11f17bba"
         ]
      }
  },
  "Overrides": {
      "ContainerOverrides": [
          {
              "Name": "shellscript",
              "Command": [ "fail-shellscript.sh" ]
          }
      ]
  }
}
```



次のように API Parameters を入力しました。

![image-20220319174628356.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/68c02a4c-0dd9-0f19-5fb5-2a3b32d19786.png)



**<u>Wait for task to complete にチェックを入れる</u>**

これを入れないと、ECS Task の実行結果 (ExitCode) が異常でも、Step Functions としては正常として扱うので、チェックを入れるのが良いです。

![image-20220319192732441.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/f016a2ee-001e-8e48-115c-9a5ddfbbec37.png)



右上の Next を押します

![image-20220319193042348.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/aed352ce-548e-6ece-c6d2-8050aee1d8e9.png)



このまま Next を押します

![image-20220319113005572.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/30720c7b-5eae-c5e4-9d4c-f0f3dbb3c3e7.png)



State Machine の名前や IAM Role を指定します

![image-20220319113253558.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/ff47b1f9-f229-6f07-d074-20b621440e9b.png)



Logging を有効にしてみます

![image-20220319113337426.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/bf201203-cf8b-36aa-d107-76d1a3b4cb87.png)



Create state machine を選びます

![image-20220319113410322.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/8c6f4c0a-200f-646f-098a-59361204ea3a.png)



State Machine が作成されました。

![image-20220319115815335.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/ade28313-50b7-f26c-9a84-c38404e5e5c4.png)



# Step Functions の IAM Role を設定

Step Functions の State machine を作成したときに自動生成された IAM Role の権限を設定していきます。`iam:PassRole` の権限を付与していきます。

State machine の詳細画面にある IAM role ARN を選択します。

![image-20220319120016328.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/d2148a64-80b8-db97-8608-0fb527f5eaab.png)



Create inline policy を選択します

![image-20220319120440640.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/c468c946-d9ce-0e99-3df9-7a722120c35e.png)



以下の JSON を指定します

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                  "iam:PassRole",
                  "events:PutTargets",
                  "events:PutRule",
                  "events:DescribeRule"
            ],
            "Resource": "*"
        }
    ]
}
```



![image-20220319192049501.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/d2f896aa-8d29-326d-1d17-917eb9f5fb58.png)





Create Policy を選択します

![image-20220319120658601.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/7d8fbf77-d0c3-41a5-6f8b-cb4dcaad3634.png)



# 動作確認 : ECS のタスク実行

これで Step Functions から ECS を実行するときに、CMD を上書きする環境が出来ました。State machine を実行して動作確認をしていきます。

State machine 上で、Start execution を選択します

![image-20220319122616593.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/06c24360-76a5-a51d-c12b-16a4ce5a2a6b.png)



Start Execution を押します

![image-20220319122708148.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/6e36a4d4-08ee-a422-3691-53c3b34a74a0.png)



実行が出来ました。Task の中で、`exitcode 1` のものを指定しているので、Failed になっています。想定通りです。TaskSubmittedにある「ECS Task」をクリックしてみます。

![image-20220319193948362.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/0d00624f-68fe-d958-b645-e20966bd59b8.png)



ECS Task のログを確認が出来て、正しく CMD で上書きした failscript が実行されていることが確認できました。

![image-20220319122849321.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/ae25fff8-9112-2057-6590-9a1baf0e8482.png)



# わかったこと

- Task を Run するときに、CMD をオーバーライド可能。ENTRYPOINT はオーバーライド出来ない
- Task を Run するときに、「Wait for task to complete」を ON にしないと、ECS Task の ExitCode を拾えない。つまり、ECS Task の実行結果がどのようになっても、Step Functions 的には正常終了として扱うことになるので、「Wait for task to complete 」は ON にしておくのが良いと思う。