AWS Batch の配列ジョブの挙動が確認できるチュートリアルをやってみた

コンテナイメージを作ってプッシュして AWS Batch のもろもろを作って配列ジョブを実行する、という一連の流れを試せます

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

コンバンハ、千葉(幸)です。

AWS Batch には配列ジョブという考え方があり、ひとつのジョブの中で 2 ~ 10,000 の子ジョブを生成できます。

それぞれの子ジョブには環境変数AWS_BATCH_JOB_ARRAY_INDEXがセットされ自身の配列ジョブの番号が提供されます。その環境変数を利用して処理の振り分けを行う、という挙動を試せるチュートリアルがありましたのでやってみました。

配列ジョブチュートリアルでやること

以下を行います。

  1. コンテナイメージの作成
  2. Amazon ECR へのイメージのプッシュ
  3. AWS Batch ジョブ定義の登録
  4. AWS Bacth ジョブの送信
  5. 配列ジョブの確認

実行元の環境が必要となるのですが、今回は AWS Cloud9 環境を使いました。

AWS CLI と Docker が使用できればどこでも問題ありません。

配列ジョブチュートリアル事前準備

チュートリアルに入る前に事前に済ませておく必要があるタスクがいくつかあります。

実行環境の作成

先述の通り今回は AWS Clou9 を使用します。サクッと作れて AWS CLI も Docker も準備されているのが助かります。

以下の条件で作成しています。

項目
環境タイプ EC2(直接接続)
インスタンスタイプ t3.small
プラットフォーム Amazon Linux2

Cloud9 を使用するユーザー(IAMロール)には AdministratorAccess を割り当てています。

AWS CLI と Docker のバージョンは以下の通りでした。

$ aws --version
aws-cli/1.19.112 Python/2.7.18 Linux/4.14.262-200.489.amzn2.x86_64 botocore/1.20.112

$ docker -v
Docker version 20.10.7, build f0df350

コンピューティング環境の作成

チュートリアルの手順通りに進めたい時は、EC2 のプロビジョニングモデルを選択してください。

コンピューティング環境はジョブが実行される環境を定義するものです。ここで定義した内容に準じて ECS クラスターが作成されます。

わたしは既存の以下の環境を使用しました。プロビジョニングモデルとして Fargate を指定しているので、後続の手順でコマンドを少し変えてあげる必要がありました。

項目
タイプ マネージド型
プロビジョニングモデル Fargate
最大 vCPU 16
サブネット パブリックサブネット

作成時の画面イメージは以下です。



ジョブキューの作成

ジョブキューはジョブがコンピューティング環境で実行されるまでキューイングされる場所です。ジョブキューには1つ以上のコンピューティング環境(最大3つ)を関連づけられます。

先ほどのコンピューティング環境を指定してTest-Job-Queueという名称で作成しました。



1. コンテナイメージを構築する

環境変数AWS_BATCH_JOB_ARRAY_INDEXで与えられた番号に応じて異なる結果を返すコンテナをビルドします。

Cloud9 環境で作業していきます。今回は作業用のディレクトリとしてworkを作成します。

~/environment $ mkdir work
~/environment $ cd work
~/environment/work $

7つの色のリストを持つcolors.txt作成します。

~/environment/work $ cat << _EOF_ >> colors.txt
> red
> orange
> yellow
> green
> blue
> indigo
> violet
> _EOF_
~/environment/work $ cat colors.txt 
red
orange
yellow
green
blue
indigo
violet

コンテナが実行するスクリプトprint-color.shを作成します。ここではパーミッションの考慮は不要です。

~/environment/work $ touch print-color.sh
~/environment/work $ vi print-color.sh 
~/environment/work $ cat print-color.sh 
#!/bin/sh
LINE=$((AWS_BATCH_JOB_ARRAY_INDEX + 1))
COLOR=$(sed -n ${LINE}p /tmp/colors.txt)
echo My favorite color of the rainbow is $COLOR.

AWS_BATCH_JOB_ARRAY_INDEX(0から始まる)に 1 をプラスした行の色を取得し、メッセージを出力するという処理です。

Dockerfileを作成します。colors.txtprint-color.sh/tmpにコピーし、後者にはchmod +xする内容が書かれています。

~/environment/work $ touch Dockerfile
~/environment/work $ vi Dockerfile 
~/environment/work $ cat Dockerfile 
FROM busybox
COPY print-color.sh /tmp/print-color.sh
COPY colors.txt /tmp/colors.txt
RUN chmod +x /tmp/print-color.sh
ENTRYPOINT /tmp/print-color.sh

Docker イメージをビルドします。

~/environment/work $ docker build -t print-color .
Sending build context to Docker daemon  4.096kB
Step 1/5 : FROM busybox
latest: Pulling from library/busybox
009932687766: Pull complete 
Digest: sha256:afcc7f1ac1b49db317a7196c902e61c6c3c4607d63599ee1a82d702d249a0ccb
Status: Downloaded newer image for busybox:latest
 ---> ec3f0931a6e6
Step 2/5 : COPY print-color.sh /tmp/print-color.sh
 ---> 57e45044571f
Step 3/5 : COPY colors.txt /tmp/colors.txt
 ---> bc308abb1d5d
Step 4/5 : RUN chmod +x /tmp/print-color.sh
 ---> Running in a5acf9e57559
Removing intermediate container a5acf9e57559
 ---> f3bbf5c902f6
Step 5/5 : ENTRYPOINT /tmp/print-color.sh
 ---> Running in 77726f28440d
Removing intermediate container 77726f28440d
 ---> 9bc05a8cb26f
Successfully built 9bc05a8cb26f
Successfully tagged print-color:latest

コンテナのテストをします。

~/environment/work $ AWS_BATCH_JOB_ARRAY_INDEX=0
~/environment/work $ while [ $AWS_BATCH_JOB_ARRAY_INDEX -le 6 ]
> do
>     docker run -e AWS_BATCH_JOB_ARRAY_INDEX=$AWS_BATCH_JOB_ARRAY_INDEX print-color
>     AWS_BATCH_JOB_ARRAY_INDEX=$((AWS_BATCH_JOB_ARRAY_INDEX + 1))
> done
My favorite color of the rainbow is red.
My favorite color of the rainbow is orange.
My favorite color of the rainbow is yellow.
My favorite color of the rainbow is green.
My favorite color of the rainbow is blue.
My favorite color of the rainbow is indigo.
My favorite color of the rainbow is violet.

環境変数AWS_BATCH_JOB_ARRAY_INDEXを 0 ~ 6 まで与えてコンテナを起動し、それぞれで行の色が読み取られていることが確認できます。

2. イメージを Amazon ECR にプッシュする

コンテナイメージを作成できたので、それを ECR にプッシュします。ここにプッシュしたものを AWS Batch ジョブがプルすることになります。

まずは ECR のリポジトリを作成します。print-colorという名称を指定します。

~/environment/work $ aws ecr create-repository --repository-name print-color
{
    "repository": {
        "repositoryUri": "012345678910.dkr.ecr.ap-northeast-1.amazonaws.com/print-color", 
        "imageScanningConfiguration": {
            "scanOnPush": false
        }, 
        "encryptionConfiguration": {
            "encryptionType": "AES256"
        }, 
        "registryId": "012345678910", 
        "imageTagMutability": "MUTABLE", 
        "repositoryArn": "arn:aws:ecr:ap-northeast-1:012345678910:repository/print-color", 
        "repositoryName": "print-color", 
        "createdAt": 1646049568.0
    }
}

作成したリポジトリを指定してイメージにタグを付与します。

~/environment/work $ docker tag print-color 012345678910.dkr.ecr.ap-northeast-1.amazonaws.com/print-color

ECR レジストリにログインします。

~/environment/work $ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS \
>     --password-stdin 012345678910.dkr.ecr.ap-northeast-1.amazonaws.com
WARNING! Your password will be stored unencrypted in /home/ec2-user/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

イメージをプッシュします。

~/environment/work $ docker push 012345678910.dkr.ecr.ap-northeast-1.amazonaws.com/print-color
Using default tag: latest
The push refers to repository [012345678910.dkr.ecr.ap-northeast-1.amazonaws.com/print-color]
268a899ccec5: Pushed 
49b2af34aff4: Pushed 
d31505fd5050: Pushed 
latest: digest: sha256:e143604aebee4f640a6451a2d2fae737e683b8f6bf8ec9217416e0ab42004862 size: 1148
~/environment/work $

3. ジョブ定義を作成して登録する

AWS Batch ジョブ定義を作成します。ジョブ定義はジョブ実行時に指定されるものであり、必要に応じて設定をオーバーライドしてジョブが実行されます。ジョブ定義に応じた ECS のタスク定義が生成されます。

インプットの JSON ファイルを作成し、それをオブションで渡す方式でコマンド実行します。

JSON ファイルの作成を横着したかったので、環境変数にセットして実行します。ここではparamasという変数に格納します。

params=$(cat <<EOM
{
  "jobDefinitionName": "print-color",
  "type": "container",
  "containerProperties": {
    "image": "012345678910.dkr.ecr.ap-northeast-1.amazonaws.com/print-color",
    "executionRoleArn": "arn:aws:iam::012345678910:role/ecsTaskExecutionRole",
    "resourceRequirements": [
        {
            "type": "MEMORY",
            "value": "3072"
        },
        {
            "type": "VCPU",
            "value": "1"
        }
    ],
    "networkConfiguration": {
        "assignPublicIp": "ENABLED"
    }
  },
  "platformCapabilities": [
      "FARGATE"
  ]
}
EOM
)

↑ 先述の通り、AWS ドキュメントに記載の内容と変えています。Fargate タイプのコンピューティング環境に対応した内容とするためです。

環境変数paramsをオプションの引数として渡してコマンド実行します。

~/environment/work $ aws batch register-job-definition --cli-input-json "$params"
{
    "jobDefinitionArn": "arn:aws:batch:ap-northeast-1:012345678910:job-definition/print-color:4", 
    "jobDefinitionName": "print-color", 
    "revision": 4
}

↑何回もやり直していたのでリビジョンが 4 になっていますが、初回であれば 1 が返ってきます。

4. AWS Batch 配列ジョブの実行

作成したジョブ定義を指定し、ジョブを実行します。

ここでも JSON ファイルをインプットとして渡します。作成済みのジョブキュー、ジョブ定義を指定し、配列サイズ 7 でジョブ実行する内容が定義されています。

params=$(cat <<EOM
{
  "jobName": "print-color",
  "jobQueue": "Test-Job-Queue",
  "arrayProperties": {
    "size": 7
  },
  "jobDefinition": "print-color"
}
EOM
)

ジョブを実行します。ここではジョブ ID などの出力が返ってくればOKです。

~/environment/work $ aws batch submit-job --cli-input-json "$params"
{
    "jobName": "print-color", 
    "jobArn": "arn:aws:batch:ap-northeast-1:012345678910:job/d4bea16c-8464-4cb9-8fce-336f0191e28b", 
    "jobId": "d4bea16c-8464-4cb9-8fce-336f0191e28b"
}

5, 配列ジョブの確認

ジョブの実行履歴を確認すると、インデックスとして 0 ~ 6 を持つ配列ジョブが実行されたことが確認できます。

AWS_Batch_job_detail

ついでに ECS のサービス画面を覗くと、AWS Batch コンピューティング環境に紐づく ECS クラスター内でタスクが 7 個立ち上がっていたことが分かります。

Amazon_ECS_AWS_Batch

ジョブの実行ログを確認すると、それぞれのジョブが自身のインデックスに基づいたログを出力していることが確認できます。

CloudWatch_Management_Console_batch_task

環境変数AWS_BATCH_JOB_ARRAY_INDEXに応じて異なる処理をする配列ジョブの挙動が確認できました。

配列ジョブチュートリアル失敗集

せっかくなので躓いたところを供養します。ほぼジョブ定義の作成に関するところです。

ジョブ定義が EC2 用になっていることに気づかずジョブ実行

最初のトライの時はドキュメントの記載に則り以下パラメータでジョブ定義を作成していました。

{
  "jobDefinitionName": "print-color",
  "type": "container",
  "containerProperties": {
    "image": "aws_account_id.dkr.ecr.region.amazonaws.com/print-color",
    "resourceRequirements": [
        {
            "type": "MEMORY",
            "value": "250"
        },
        {
            "type": "VCPU",
            "value": "1"
        }
    ]
  }
}

ステップ4でそのジョブ定義を指定してジョブ実行した時に、怒られが発生しました。

$ aws batch submit-job --cli-input-json "$params"

An error occurred (ClientException) when calling the SubmitJob operation: Job Queue is attached to Compute Environment that can not run Jobs with capability EC2

An error occurred (ClientException) when calling the SubmitJob operation: Job Queue is attached to Compute Environment that can not run Jobs with capability EC2

VCPU と MEMORY の組み合わせが正しくない

こちらはジョブ定義を作成する時点で発生したエラーです。

~/environment/work $ params=$(cat <<EOM
> {
>   "jobDefinitionName": "print-color",
>   "type": "container",
>   "containerProperties": {
>     "image": "012345678910.dkr.ecr.ap-northeast-1.amazonaws.com/print-color",
>     "resourceRequirements": [
>         {
>             "type": "MEMORY",
>             "value": "250"
>         },
>         {
>             "type": "VCPU",
>             "value": "1"
>         }
>     ]
>   },
>   "platformCapabilities": [
>       "FARGATE"
>   ]
> }
> EOM
> )
~/environment/work $ aws batch register-job-definition --cli-input-json "$params"

An error occurred (ClientException) when calling the RegisterJobDefinition operation: Error executing request, Exception : Fargate resource requirements (1.00 vCPU, 250 MiB) not valid., RequestId: 33fc96df-e4f0-46db-a366-97d8557befca

An error occurred (ClientException) when calling the RegisterJobDefinition operation: Error executing request, Exception : Fargate resource requirements (1.00 vCPU, 250 MiB) not valid.

AWS CLI リファレンスを見ると vCPU と メモリの組み合わせが載っていたので、それに準じる値に修正しました。

その他

ジョブ定義のパラメータでいろいろやりました。

  • "platformCapabilities": "FARGATE"という誤った形で指定する
    • Invalid type for parameter platformCapabilities, value: FARGATE, type: <type 'unicode'>, valid types: <type 'list'>, <type 'tuple'>
  • "executionRoleArn"の定義漏れ
    • Exception : executionRoleArn must be provided for Fargate jobs.
  • "assignPublicIp": "ENABLED"を指定しない
    • ジョブ定義の作成は成功するが、ジョブ実行が失敗する
      • 今回の環境では NAT Gateway や VPC エンドポイントを用意していないのでパブリック IP を割り当てないとイメージがプルできない

勉強になりました。

終わりに

AWS Batch 配列ジョブのチュートリアルをやってみました。

単純に配列ジョブの挙動を見るだけであればもっと簡単な手段もありそうですが、コンテナイメージの作成から一連の流れで試せたのでお得な気分になれました。

Docker、ECR もついでに触れるので、興味がある方は試してみてはいかがでしょうか。

以上、 チバユキ (@batchicchi) がお送りしました。