前提条件
EC2への権限
EC2、ECS、AWS Batch などに対してフル権限があること。
0. 準備
0.1. 環境構築
#1 環境構築 Managed 編 または Unmanaged 編 が終わっていること
0.2. 変数の確認
#1
から引き続き利用する変数を確認します
cat << ETX
COMPUTE_ENV_NAME: ${COMPUTE_ENV_NAME}
JOB_QUEUE_NAME: ${JOB_QUEUE_NAME}
ETX
COMPUTE_ENV_NAME: aws-batch-managed-xxxxxxxxxx
JOB_QUEUE_NAME: aws-batch-job-queue-xxxxxxxxxx
0.3. AWS CLIのバージョン
以下のバージョンで動作確認済
- AWS CLI 1.11.36
aws --version
aws-cli/1.11.36 Python/2.7.5 Darwin/13.4.0 botocore/1.4.93
バージョンが古い場合は最新版に更新しましょう。
sudo -H pip install -U awscli
0.4. AWS アカウントの属性
EC2-Classic が見えない AWS アカウントであること。
AWS_SUPPORT_PLATFORMS=$( \
aws ec2 describe-account-attributes \
--query 'AccountAttributes[?AttributeName == `supported-platforms`].AttributeValues[].AttributeValue' \
--output text \
) && echo ${AWS_SUPPORT_PLATFORMS}
VPC
注釈: VPC
の他に EC2
が表示される場合、別のアカウントを作成してください。
1. ジョブの定義
1.1. ジョブ定義名の指定
定義名の指定
JOB_DEFINITION_NAME="aws-batch-job-def-`date +%s`"
同名のジョブ定義がないことを確認
aws batch describe-job-definitions \
--job-definition-name ${JOB_DEFINITION_NAME}
{
"jobDefinitions": []
}
1.2. ジョブ定義の作成
現時点では AWS Batch のジョブ種別には container
しか選べませんが、きっと将来的にはシェルや Linux バイナリが選べるようになるのでしょう。
それはさておき、ジョブの定義
として
実行したいコンテナのプロパティ定義ファイル名を決めます。
CONRAINER_PROPS_FILE="aws_batch_container_props.json"
コンテナ定義ファイルを生成します。
cat << EOF > ${CONRAINER_PROPS_FILE}
{
"image": "busybox",
"command": ["echo", "Ref::Target"],
"vcpus": 1,
"memory": 5000
}
EOF
cat ${CONRAINER_PROPS_FILE}
コンテナ定義ファイルの検証
jsonlint -q ${CONRAINER_PROPS_FILE}
これをジョブ定義として AWS Batch に登録します
aws batch register-job-definition \
--job-definition-name ${JOB_DEFINITION_NAME} \
--type container \
--container-properties file://${CONRAINER_PROPS_FILE} \
--parameters Target="Hello\, AWS Batch :)"
{
"jobDefinitionArn": "arn:aws:batch:us-east-1:xxxxxxxxxxxx:job-definition/aws-batch-job-def-xxxxxxxxxx:1",
"jobDefinitionName": "aws-batch-job-def-xxxxxxxxxx",
"revision": 1
}
2. ジョブの投入
2.1. ジョブ名・ジョブ定義の取得
流したジョブを識別できるよう名前を決めます
JOB_21="job-`date +%s`"
ジョブ定義の中でリビジョンが最新のものの ARN を取得します
JOB_DEFINITION_ARN=$( aws batch describe-job-definitions \
--job-definition-name ${JOB_DEFINITION_NAME} \
--status ACTIVE \
| jq -r '.jobDefinitions | max_by(.revision).jobDefinitionArn' \
) && echo ${JOB_DEFINITION_ARN}
arn:aws:batch:us-east-1:xxxxxxxxxxxx:job-definition/aws-batch-job-def-xxxxxxxxxx:1
2.2. ジョブの投入
早速定義したジョブをひとつ流してみましょう。
aws batch submit-job \
--job-name ${JOB_21} \
--job-queue ${JOB_QUEUE_NAME} \
--job-definition ${JOB_DEFINITION_ARN}
{
"jobName": "job-1484056465",
"jobId": "123abcde-4567-89fg-hijk-0123lmnopqrs"
}
2.3. ジョブの状況を確認
投入されたばかりのジョブは SUBMITTED
というステータスになります。確認してみましょう。
aws batch list-jobs \
--job-queue ${JOB_QUEUE_NAME} \
--job-status SUBMITTED
(タイミングによっては空かもしれません)
{
"jobSummaryList": [
{
"jobName": "job-1484056465",
"jobId": "123abcde-4567-89fg-hijk-0123lmnopqrs"
}
]
}
ちなみにコマンドで --job-status
を指定しないとすべてのステータスかと思いきや、何も指定しない時のデフォルト値は RUNNING
(起動中)です。
しばらくするとステータスが変わるはずなので、RUNNABLE
になるまで以下のコマンドを打ち待ってみます。(Mac であれば watch -n 1
などで待ってもいいですね!)
aws batch list-jobs \
--job-queue ${JOB_QUEUE_NAME} \
--job-status RUNNABLE
{
"jobSummaryList": [
{
"jobName": "job-1484056465",
"jobId": "123abcde-4567-89fg-hijk-0123lmnopqrs"
}
]
}
このジョブ、実はこの先いつになっても RUNNABLE
から先に進むことはありません。
Job の実行に クラスタで使用可能なメモリ を上回る 5,000MB を要求しているためです。
2.4. ジョブの実行をキャンセル
ステータスが SUBMITTED
、PENDING
または RUNNABLE
な
Job はその実行をキャンセルできます。キャンセルします
JOB_21_ID=$( aws batch list-jobs \
--job-queue ${JOB_QUEUE_NAME} \
--job-status RUNNABLE \
| jq '.jobSummaryList' \
| jq "map(select(.jobName==\"${JOB_21}\"))[]" \
| jq -r '.jobId' )
aws batch cancel-job \
--job-id ${JOB_21_ID} \
--reason 'Due to lack of memory.'
返り値なし
しばらくしたらステータスを確認してみます。
aws batch describe-jobs --jobs ${JOB_21_ID}
{
"jobs": [
{
"status": "FAILED",
"container": {
"mountPoints": [],
"image": "busybox",
"environment": [],
"vcpus": 1,
"command": [
"sleep",
"10",
"&&",
"echo",
"Hello, AWS Batch :)"
],
"volumes": [],
"memory": 1000,
"ulimits": []
},
"parameters": {
"WaitTime": "10",
"Target": "Hello, AWS Batch :)"
},
"jobDefinition": "arn:aws:batch:us-east-1:xxxxxxxxxxxx:job-definition/aws-batch-job-def-xxxxxxxxxx:1",
"statusReason": "Due to lack of memory.",
"jobId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"jobQueue": "arn:aws:batch:us-east-1:xxxxxxxxxxxx:job-queue/aws-batch-job-queue-xxxxxxxxxx",
"dependsOn": [],
"jobName": "job-xxxxxxxxxx",
"createdAt": 1484074048650,
"stoppedAt": 1484074857766
}
]
}
status が FAILED
になりました。
細かいことですが、コンテナの実行時パラメタも引き渡したものに置換されていることも確認できますね。
3. ジョブのさまざまな投げ方
3.1. コンテナ定義を上書き Job を投入
次の Job に名前をつけ
JOB_31="job-`date +%s`"
メモリのハードリミットを 100MB に下げて再投入します。
aws batch submit-job \
--job-name ${JOB_31} \
--job-queue ${JOB_QUEUE_NAME} \
--job-definition ${JOB_DEFINITION_ARN} \
--container-overrides '{"memory": 100}'
{
"jobName": "job-1484076698",
"jobId": "123abcde-4567-89fg-hijk-0123lmnopqrs"
}
しばらくすると以下のコマンドで
SUCCEEDED
となった JOB_31 が返ってきます。
aws batch list-jobs \
--job-queue ${JOB_QUEUE_NAME} \
--job-status SUCCEEDED \
| jq '.jobSummaryList' \
| jq -r "map(select(.jobName==\"${JOB_31}\"))[]"
{
"jobName": "job-1484076698",
"jobId": "123abcde-4567-89fg-hijk-0123lmnopqrs"
}
3.2. パラメタを変更して Job を投入
バッチジョブらしく、Job ごとにパラメタを変えてみます。
(コンテナ定義 Command
の Ref:XXX
が都度変わります)
Job に名前をつけ
JOB_32="job-`date +%s`"
デフォルトでは "Hello, AWS Batch :)" と echo していましたが
"Hello, Tokyo!" と echo させる Job を投入します
aws batch submit-job \
--job-name ${JOB_32} \
--job-queue ${JOB_QUEUE_NAME} \
--job-definition ${JOB_DEFINITION_ARN} \
--container-overrides '{"memory": 100}' \
--parameters Target='Hello\, Tokyo!'
{
"jobName": "job-1484078516",
"jobId": "123abcde-4567-89fg-hijk-0123lmnopqrs"
}
しばらくすると以下のコマンドで Job 実行時のコマンドが
デフォルトパラメタを上書きできていたことが確認できます。
aws batch describe-jobs \
--jobs $( candidate=$( aws batch list-jobs \
--job-queue ${JOB_QUEUE_NAME} \
--job-status SUCCEEDED \
| jq '.jobSummaryList' \
| jq -r "map(select(.jobName==\"${JOB_32}\"))[].jobId" ); \
echo ${candidate:-none} \
) | jq '.jobs[].container.command'
[
"echo",
"Hello, Tokyo!"
]
3.3. 依存関係のある Job の投入
とある Job に、別の Job が依存するバッチを想定します。
2 つ Job を投げるので、名前も 2 つ用意します。
JOB_331="job-`date +%s`"
sleep 2
JOB_332="job-`date +%s`"
JOB_331 は 1 時間かかる Job、
JOB_332 は JOB_331 の終了をもって起動する Job として投入。
JOB_331_RESULT=$( aws batch submit-job \
--job-name ${JOB_331} \
--job-queue ${JOB_QUEUE_NAME} \
--job-definition ${JOB_DEFINITION_ARN} \
--container-overrides \
'{"command": ["sleep", "Ref::WaitTime"], "memory": 100}' \
--parameters WaitTime=3600 \
) && echo ${JOB_331_RESULT}
aws batch submit-job \
--job-name ${JOB_332} \
--job-queue ${JOB_QUEUE_NAME} \
--job-definition ${JOB_DEFINITION_ARN} \
--container-overrides '{"memory": 100}' \
--depends-on jobId=$( echo ${JOB_331_RESULT} \
| jq -r '.jobId')
すると JOB_332 は JOB_331 の終了を待つため、
PENDING
ステータスに入ります。
aws batch list-jobs \
--job-queue ${JOB_QUEUE_NAME} \
--job-status PENDING \
| jq '.jobSummaryList' \
| jq -r "map(select(.jobName==\"${JOB_332}\"))"
[
{
"jobName": "job-1484079158",
"jobId": "123abcde-4567-89fg-hijk-0123lmnopqrs"
}
]
3.4. Job の停止
依存関係のある Job の場合、JOB_331 が正常終了したら
JOB_332 も実行されることは容易に想像がつきますが
JOB_331 が失敗ステータスになったらどうなるでしょうか?
terminate-job
を使えば
STARTING
や RUNNING
の Job も FAILED
にできます
aws batch terminate-job \
--job-id $( aws batch list-jobs \
--job-queue ${JOB_QUEUE_NAME} \
--job-status RUNNING \
| jq '.jobSummaryList' \
| jq -r "map(select(.jobName==\"${JOB_331}\"))[].jobId" \
) \
--reason 'Forced to fail.'
返り値なし
しばらくすると、依存していた JOB_332 も JOB_331 同様
FAILED
になっていることが確認できます。
aws batch describe-jobs \
--jobs $( candidate=$( aws batch list-jobs \
--job-queue ${JOB_QUEUE_NAME} \
--job-status FAILED \
| jq '.jobSummaryList' \
| jq -r "map(select(.jobName==\"${JOB_332}\"))[].jobId" ); \
echo ${candidate:-none} \
) | jq .
{
"jobs": [
{
"status": "FAILED",
"container": {
"mountPoints": [],
"image": "busybox",
"environment": [],
"vcpus": 1,
"command": [
"echo",
"Hello, AWS Batch :)"
],
"volumes": [],
"memory": 100,
"ulimits": []
},
"parameters": {
"Target": "Hello, AWS Batch :)"
},
"jobDefinition": "arn:aws:batch:us-east-1:xxxxxxxxxxxx:job-definition/aws-batch-job-def-xxxxxxxxxx:1",
"statusReason": "Dependent Job failed",
"jobId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"jobQueue": "arn:aws:batch:us-east-1:xxxxxxxxxxxx:job-queue/aws-batch-job-queue-xxxxxxxxxx",
"dependsOn": [
{
"jobId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
],
"jobName": "job-xxxxxxxxxx",
"createdAt": 1484080086870,
"stoppedAt": 1484080595978
}
]
}
落ちた理由は Dependent Job failed
になっていますね
4. ログを確認する
4.1. CloudWatch Logs
ジョブでの標準出力は、すべて CloudWatch Logs に保存されています。
4.2 特定 JOB のログストリーム特定
JOB_32 のログを見てみようと思います。
JOB_32_LOG=$( aws logs describe-log-streams \
--log-group-name /aws/batch/job \
| jq '.logStreams' \
| jq "map(select(.logStreamName | index(\"${JOB_32}\")))" \
) && echo ${JOB_32_LOG}
[
{
"firstEventTimestamp": 1484080086870,
"lastEventTimestamp": 1484080086870,
"creationTime": 1484080086870,
"uploadSequenceToken": "xxxxxxxxxx",
"logStreamName": "job-1484078516/123abcde-4567-89fg-hijk-0123lmnopqrs/12345678-abcd-efgh-9012-3456ijklmnop",
"lastIngestionTime": 1484080595978,
"arn": "arn:aws:logs:us-east-1:xxxxxxxxxxxx:log-group:/aws/batch/job:log-stream:job-1484078516/123abcde-4567-89fg-hijk-0123lmnopqrs/12345678-abcd-efgh-9012-3456ijklmnop",
"storedBytes": 0
}
]
4.3 特定 JOB のログ参照
JOB_32 では以下のような内容が出力されたことがわかります。
aws logs get-log-events \
--log-group-name /aws/batch/job \
--log-stream-name $( echo ${JOB_32_LOG} \
| jq -r '.[].logStreamName' ) \
| jq '.events | map({time: (.timestamp / 1000 | strftime("%Y/%m/%d %H:%M:%S")), message: .message})'
[
{
"time": "2017/01/17 14:10:27",
"message": "Hello, Tokyo!"
}
]
完了
ジョブの作成と実行は以上です。
AWS Batch #3 より実践的な使い方へ