AWS Batch 配列ジョブのワークフロー例 を絵に書き起こして理解してみた

かいた絵を載せたかっただけです

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

AWS Batch について色々調べています。

AWS Batch ジョブには依存関係という考え方があり、以下の種類があります。

  • 標準の依存関係
  • SEQUENTIALの依存関係(配列ジョブのみ)
  • N_TO_Nの依存関係(配列ジョブのみ)

それぞれの挙動について取り上げられた例が AWS ドキュメントにあるのですが、初見で理解が追いつきませんでした。

そういった時にはやることは一つしかありませんので、そう、絵を描きました。

配列ジョブのワークフロー例はこんなことを言ってる

描いた絵が以下です。

AWS Batch array job2

このエントリの 90 % がいま終わりました。

AWS Batch の配列ジョブ

AWS Batch ジョブを送信する際に明示的に指定することで、配列ジョブにできます。

マネジメントコンソールから操作する際のイメージは以下です。

AWS_Batch__Job_submit

配列ジョブとして送信する際にはあわせて 2~10,000 のサイズを指定します。そのサイズの数だけ子ジョブが生成されます。

子ジョブは、ジョブキューやジョブ定義、ジョブ送信時にオーバーライドされた vCPU やメモリなどのパラメータを共通して持ちます。同じ値が設定される、という意味です。各子ジョブにはインデックス番号が採番され、環境変数 AWS_BATCH_JOB_ARRAY_INDEX でコンテナに渡されます。

AWS Batch ジョブの依存関係

ジョブには依存関係を設定できます。他のジョブ ID を指定することで依存関係を持つことになり、最大 20 のジョブ ID を指定できます。

例えばジョブ A の送信時にジョブ B を依存先として指定した場合、以下の考え方となります。

  • ジョブ A は、ジョブ B の実行が成功するまでペンディング状態となる
    • ジョブ B が成功したのち、ジョブ A が実行される
    • ジョブ B が失敗した場合、ジョブ A は実行されることなく失敗ステータスとなる

配列ジョブでのみ指定できる依存関係のタイプもあります。SEQUENTIALN_TO_Nです。

SEQUENTIALの場合、他のジョブ ID を指定できません。配列ジョブの中の子ジョブの間で依存関係が構成されます。

インデックス番号 0 から始まり、0 が成功したら 1 に、1 が成功したら 2 に……といった形で依存関係が持たれます。(逆に言うと、この依存関係を構成しない限り配列ジョブの子ジョブは連続性を持ちません。)

Array Jobs - AWS Batch より)

N_TO_Nの場合、複数の配列ジョブの子ジョブ同士で依存関係が構成されます。同じインデックス番号を持つ子ジョブ同士で依存関係が持たれます。

(同上)

マネジメントコンソールからジョブを送信する際のイメージは以下です。SEQUENTIALの場合はジョブ ID が指定できないことが分かります。

そして配列ジョブでない場合のイメージが以下です。

ジョブタイプが選択できないことが分かります。

配列ジョブのワークフロー例

事前知識を仕入れたところで改めて例を眺めてみます。

ここでは以下のジョブが共通のジョブキューに送信されるケースが取り上げられています。

  • ジョブA
  • ジョブB(配列ジョブ)
  • ジョブC(配列ジョブ)
  • ジョブD(配列ジョブ)
  • ジョブE

ジョブキュー(ひいてはコンピューティング環境)は共通ですが、ジョブ定義はジョブごとに固有のものを持ちます。

ジョブごとのSubmitJob API を実行する際のパラメータが例示されます。

ジョブ A

ここでは配列ではない標準的なジョブです。

S3 バケット A 内のオブジェクトのリスト化および検証を行う処理が想定されています。

{
    "jobName": "JobA",
    "jobQueue": "ProdQueue",
    "jobDefinition": "JobA-list-and-validate:1"
}

ジョブ B

ジョブ A に依存する配列ジョブです。配列ジョブのサイズは 10,000 で、CPU を多く使用する処理が想定されています。

処理の内容はバケット A 内の個々のオブジェクトに処理を行い、結果をバケット B にアップロードするというものです。

{
    "jobName": "JobB",
    "jobQueue": "ProdQueue",
    "jobDefinition": "JobB-CPU-Intensive-Processing:1",
    "containerOverrides": {
        "resourceRequirements": [
            {
                "type": "MEMORY",
                "value": "4096"
            },
            {
                "type": "VCPU",
                "value": "32"
            }
        ]
   }
    "arrayProperties": {
        "size": 10000
    },
    "dependsOn": [
        {
            "jobId": "JobA_job_ID"
  }
    ]
}

ジョブ C

ジョブ B にN_TO_Nタイプの依存関係を持つ配列ジョブです。配列サイズは同じく 10,000 です。ジョブ B が CPU を必要とするジョブであったのに対しジョブ C はメモリを多く必要とするものです。

ジョブ B によってバケット B にアップロードされたオブジェクトのメタデータを DynamoDB に書き込み、結果をバケット C にアップロードします。

{
    "jobName": "JobC",
    "jobQueue": "ProdQueue",
    "jobDefinition": "JobC-Memory-Intensive-Processing:1",
    "containerOverrides": {
        "resourceRequirements": [
            {
                "type": "MEMORY",
                "value": "32768"
            },
            {
                "type": "VCPU",
                "value": "1"
            }
        ]
   }
    "arrayProperties": {
        "size": 10000
    },
    "dependsOn": [
        {
            "jobId": "JobB_job_ID",
            "type": "N_TO_N"
        }
    ]
}

ジョブ D

ジョブ C に依存する配列ジョブです。配列サイズは 10 で、SEQUENTIALタイプの依存関係も持ちます。

DynamoDB やこれまでの S3 バケットに対してクエリを行い検証を行い、結果を S3 バケットに出力する処理が想定されています。検証のステップが 0 ~ 9 まで分かれておりそれを順に実行する、と考えると分かりやすいでしょう。

{
    "jobName": "JobD",
    "jobQueue": "ProdQueue",
    "jobDefinition": "JobD-Sequential-Validation:1",
    "containerOverrides": {
        "resourceRequirements": [
            {
                "type": "MEMORY",
                "value": "32768"
            },
            {
                "type": "VCPU",
                "value": "1"
            }
        ]
   }
    "arrayProperties": {
        "size": 10
    },
    "dependsOn": [
        {
            "jobId": "JobC_job_ID"
        },
        {
            "type": "SEQUENTIAL"
        },
 
    ]
}

ジョブ C に対する依存関係とSEQUENTIALの依存関係、2種類が存在することがわかります。

ジョブ E

最後のジョブで、非配列ジョブです。クリーンアップおよびパイプラインの完了通知を行う処理が想定されています。

{
    "jobName": "JobE",
    "jobQueue": "ProdQueue",
    "jobDefinition": "JobE-Cleanup-and-Notification:1",
    "parameters": {
        "SourceBucket": "s3://JobD-Output-Bucket",
        "Recipient": "pipeline-notifications@mycompany.com"
    },
    "dependsOn": [
        {
            "jobId": "JobD_job_ID"
        }
    ]
}

各ジョブの挙動がイメージできました。

終わりに

AWS ドキュメントの配列ジョブのワークフロー例を絵に描いてみました。

理解してから読み直すとそこまで複雑なものでもないのですが、初見ではなんのこっちゃでした。

皆さんのなんのこっちゃを軽減できれば何よりです。

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