AWS Fargate 上の Batch job を Lambda から実行する

# 目的

AWS Fargate 上の AWS Batch job を Lambda から実行します。
便利なので Lambda から Batch を実行したかったんです。

## 概要図
![20221209.drawio (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163276/353acf86-5181-5ba0-9619-b479c2517753.png)

# 準備
## AWS Batch
### コンピューティング環境の作成
AWS Batch のトップページから「コンピューティング環境」->「作成」と選択します。
Fargate を選択して、名前を入力します。
環境名は「takahara-fargate-batch」としました。
![スクリーンショット 2022-12-10 13.49.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163276/ce97fdf1-72cd-582c-8b1b-f2babd6d7e7c.png)

次にスポットインスタンスとvCPU数を設定します。
ここは料金と処理速度性能に関わります。
スポットインスタンスは料金が安くなる代わりに、処理を中断する可能性がごく僅かにあります。
![スクリーンショット 2022-12-10 13.50.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163276/82f86b48-c79d-01d2-26d6-8f3e0b861160.png)

ネットワーク設定はデフォルトVPC・パブリックサブネット・セキュリティグループを選択しました。
ECRやS3などのサービスのエンドポイントのドメイン名解決にはインターネットアクセスもしくはVPCエンドポイントが必要です。
パブリックサブネットを避けたい時はプライベートサブネットに NAT ゲートウェイを建てるか、各種サービスにVPCエンドポイントを設置しましょう。

確認画面から「コンピューティング環境を作成」ボタンを押しましょう。

### ジョブキューの作成
トップページから「ジョブキュー」->「作成」と選択します。
Fargate を選択して、名前を入力します。
ジョブキュー名は「takahara-fargate-job-queue」としました。
「接続されたコンピューティング環境」は先ほど作成したコンピューティング環境を選択してください。
![スクリーンショット 2022-12-10 14.13.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163276/756b2a65-e833-e3da-767e-188cfff64ec9.png)

### ジョブ定義
トップページから「ジョブ定義」->「作成」と選択します。
Fargate を選択して、名前を入力します。
ジョブキュー名は「takahara-fargate-batch-task」としました。
パブリックIPは割り当てましょう。(理由は上述のようにインターネットアクセスが必要なため)
![スクリーンショット 2022-12-10 14.15.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163276/00e9e302-349c-e9dc-b34f-e5ec69550551.png)

Docker Image はデフォルトですが、コマンドに`["echo","Ref::word"]`を設定しています。
Refを使うと、Lambda からジョブを実行した時にパラメータを引数として渡せます。
今回は`word`を引数として渡しています。
![スクリーンショット 2022-12-10 14.24.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163276/2df319fa-e58f-7d3f-11a5-45a855caab01.png)

ECS タスクの実行ロールはデフォルトの「ecsTaskExecutionRole」を選択しましたが、このままだと不便なことも多いです。
たとえば Batch job が S3 にアクセスしたい時です。
この IAM ロール作成方法は後述します。
![スクリーンショット 2022-12-10 14.24.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163276/96fc79ec-b3d4-9c09-d4e7-23ef1aef7493.png)

ユーザーはroot、ログドライバーはawslogsを選択します。
![スクリーンショット 2022-12-10 14.29.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163276/840cebb5-8fba-87da-6358-f5fe84b8df28.png)
これでAWS Batchの準備は完了です。

## Lambda
### コード
LambdaのコードはTypeScriptで記述しました。
AWS SDK は v3 を使用しました。
公式ドキュメント以外はほぼ資料がありませんでしたが、よく読むと v2 とはパラメータの渡し方が異なるだけでした。
```ts
import { BatchClient, SubmitJobCommand } from "@aws-sdk/client-batch";

const batchClient = new BatchClient({ region: "ap-northeast-1" });
const JOB_QUEUE = "takahara-fargate-job-queue";
const JOB_DEFINITION = "takahara-fargate-batch-task";

export const handler = async () => {
  const params = new SubmitJobCommand({
    jobName: String(Math.floor(new Date().getTime() / 1000)),
    jobQueue: JOB_QUEUE,
    jobDefinition: JOB_DEFINITION,
    parameters: { word: "AWS Bacth job!" },
  });
  return batchClient.send(params);
};
```
たったこれだけです。
job 名は unix time で現在時刻を入れています。
parameters は Batch 実行時に引数として渡せる変数を格納します。

コードは下記リポジトリにアップロードしています。
Lambda用ビルド設定はレポジトリの方を参照してください。
https://github.com/KensukeTakahara/aws-batch-by-lambda

### デプロイ
Lambda関数を作成してください。
ランタイムは Node.js 18 を選択しました。
![スクリーンショット 2022-12-10 14.56.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163276/27a7f5c3-2b8f-2489-6eb5-5598606f0ae8.png)

作成が完了したらコードをデプロイしてください。

IAM ロールを追加で設定します。
下記ページから IAM ロール編集画面に移動してください。
![スクリーンショット 2022-12-10 15.31.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163276/18619312-3a9e-5118-d8f7-eab6e046021b.png)

インラインポリシーで下記のポリシーを追加しました。
```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "batch:SubmitJob",
            "Resource": [
                "arn:aws:batch:ap-northeast-1:XXXXXXXXXXXX:job-definition/takahara-fargate-batch-task",
                "arn:aws:batch:ap-northeast-1:XXXXXXXXXXXX:job-queue/takahara-fargate-job-queue"
            ]
        }
    ]
}
```
`XXXXXXXXXXXX`は各自の AWS アカウント ID に差し替えてください。

以上で準備は完了です！

# 実行
Lambdaのテスト画面からテストを実行してみましょう。
![スクリーンショット 2022-12-10 15.35.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163276/bec5d41a-df2c-453a-cfea-803c8799ed68.png)
実行は成功するはずです。

AWS Batch のジョブ画面を見るとジョブが作成されています。
![スクリーンショット 2022-12-10 15.38.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163276/76a77997-f941-9c3a-122a-9d6040c8855a.png)

ここから Batch の出力結果を CloudWatch logs で確認できます。
![スクリーンショット 2022-12-10 15.40.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163276/a252d137-73c8-b1b7-e0d5-49ae46bf116b.png)
無事、Lambda から渡した文字列が出力されていますね。

# まとめ
無事、Lambda から AWS Fargate 上の Batch job が実行できました。
これで柔軟な Batch 実行が可能になりますね。

# おまけ
## ECS タスク実行ロール
ECS タスクがデフォルトの IAMロール しか使えないと非常に不便なので、独自の IAM ロールを使います。
やり方は簡単で IAM ロールの「信頼関係」のタブで ECS タスクを信頼されたサービスに設定しましょう。
```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ecs-tasks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

```