10
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

[JAWS-UG CLI] AWS Batch #3 より実践的な使い方

前提条件

EC2への権限

EC2、ECS、AWS Batch などに対してフル権限があること。

0. 準備

0.1. ジョブの作成と実行の完了

AWS Batch #2 ジョブの作成と実行 が終わっていること

0.2. 変数の確認

#1, #2 から引き続き利用する変数を確認します

コマンド
cat << ETX

    CFN_STACK_NAME:       ${CFN_STACK_NAME}
    COMPUTE_ENV_NAME:     ${COMPUTE_ENV_NAME}
    JOB_QUEUE_NAME:       ${JOB_QUEUE_NAME}
    JOB_DEFINITION_NAME:  ${JOB_DEFINITION_NAME}
    CONRAINER_PROPS_FILE: ${CONRAINER_PROPS_FILE}

ETX
結果(例)

    CFN_STACK_NAME:       aws-batch-xxxxxxxxxx
    COMPUTE_ENV_NAME:     aws-batch-managed-xxxxxxxxxx
    JOB_QUEUE_NAME:       aws-batch-job-queue-xxxxxxxxxx
    JOB_DEFINITION_NAME:  aws-batch-job-def-xxxxxxxxxx
    CONRAINER_PROPS_FILE: aws_batch_container_props.json

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

1. サンプルアプリケーション

  • AWS Batch で 定例バッチ1 を実装してみます
  • COBOL での帳票出力を AWS Batch で動かします
  • 作成された帳票は S3 へアップロード
  • ジョブには S3 へのアップロード権限があるロールを付与
  • CloudWatch Events + Lambda から毎分ごとに Job を Submit

1.1 Docker リポジトリの作成

ジョブ実行ロジックを push するためのリポジトリを作ります

コマンド
DOCKER_REPO=$( aws ecr create-repository \
  --repository-name ${CFN_STACK_NAME}/sample \
  | jq -r '.repository.repositoryUri' \
) && echo ${DOCKER_REPO}
結果(例)
xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/aws-batch-xxxxxxxxxx

1.2. #1 で生成したスタックから変数の取得

CloudFormation スタックの生成結果を再取得

コマンド
CFN_STACK_RESULT=$( aws cloudformation describe-stacks \
  --stack-name ${CFN_STACK_NAME})

スタックの生成結果(Output)から、必要な変数を抜き出します。

コマンド
S3_BUCKET_NAME=$( echo ${CFN_STACK_RESULT} \
  | jq '.Stacks[].Outputs[]' \
  | jq -r 'select(.OutputKey=="S3Bucket").OutputValue' )

BATCH_JOB_ROLE=$( echo ${CFN_STACK_RESULT} \
  | jq '.Stacks[].Outputs[]' \
  | jq -r 'select(.OutputKey=="BatchJobRole").OutputValue' )

cat << ETX

    S3_BUCKET_NAME: ${S3_BUCKET_NAME}
    BATCH_JOB_ROLE: ${BATCH_JOB_ROLE}

ETX
結果

    S3_BUCKET_NAME: aws-batch-xxxxxxxxxx-s3bucket-xxxxxxxxxxx
    BATCH_JOB_ROLE: arn:aws:iam::xxxxxxxxxxxx:role/aws-batch-xxxxxxxxxx-BatchJobRole-xxxxxxxxxxxxx

1.3. ジョブ定義の作成

コンテナ定義ファイルを再生成します。

コマンド
cat << EOF > ${CONRAINER_PROPS_FILE}
{
  "image": "${DOCKER_REPO}",
  "command": ["Ref::Arg1", "Ref::Arg2"],
  "jobRoleArn": "${BATCH_JOB_ROLE}",
  "environment": [
    { "name": "AWS_DEFAULT_REGION", "value": "${AWS_DEFAULT_REGION}"},
    { "name": "AWS_S3_BUCKET", "value": "${S3_BUCKET_NAME}"},
    { "name": "APP_VERSION", "value": "production!"}
  ],
  "vcpus": 1,
  "memory": 100
}
EOF
cat ${CONRAINER_PROPS_FILE}

コンテナ定義ファイルの検証

コマンド
jsonlint -q ${CONRAINER_PROPS_FILE}

これを使い、ジョブ定義を更新します

コマンド
aws batch register-job-definition \
  --job-definition-name ${JOB_DEFINITION_NAME} \
  --container-properties file://${CONRAINER_PROPS_FILE} \
  --type container
結果(例)
{
    "jobDefinitionArn": "arn:aws:batch:us-east-1:xxxxxxxxxxxx:job-definition/aws-batch-job-def-xxxxxxxxxx:1", 
    "jobDefinitionName": "aws-batch-job-def-xxxxxxxxxx", 
    "revision": 2
}

1.4. ジョブ投入に使う変数の確認

流したジョブを識別できるよう名前を決めます

コマンド
JOB_NAME="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:2

2. ジョブ投入のための追加リソース生成

2.1. CloudFormation スタックの生成

テンプレートの取得

コマンド
curl --location --output ${CFN_STACK_NAME}-job.yaml \
  https://raw.githubusercontent.com/supinf/aws-batch-refarch/master/cloudformation/job-executor.yaml

CloudFormation スタックの生成

コマンド
aws cloudformation create-stack \
  --stack-name ${CFN_STACK_NAME}-job \
  --template-body file://${CFN_STACK_NAME}-job.yaml \
  --parameters ParameterKey=JobName,ParameterValue=${JOB_NAME} \
               ParameterKey=JobQueueName,ParameterValue=${JOB_QUEUE_NAME} \
               ParameterKey=JobDefinitionArn,ParameterValue=${JOB_DEFINITION_ARN} \
  --capabilities CAPABILITY_IAM
結果(例)
{
    "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/aws-batch-xxxxxxxxxx-job/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

このスタックにより以下のリソースが作成されます

  • Job 投入 Lambda
  • 定期的にその Lambda を蹴る CloudWatch Events

2.2. 生成されたリソースから変数を取得

スタックの生成を待機

コマンド
aws cloudformation wait stack-create-complete \
  --stack-name ${CFN_STACK_NAME}-job
結果
返り値なし

生成結果を取得

コマンド
CFN_STACK_RESULT=$( aws cloudformation describe-stacks \
  --stack-name ${CFN_STACK_NAME}-job \
) && echo ${CFN_STACK_RESULT} | jq .

スタックの生成結果(Output)から、必要な変数を抜き出します。

コマンド
TRIGGER_ARN=$( echo ${CFN_STACK_RESULT} \
  | jq '.Stacks[].Outputs[]' \
  | jq -r 'select(.OutputKey=="BatchJobEvent").OutputValue' )
echo ${TRIGGER_ARN}
結果(例)
arn:aws:events:us-east-1:xxxxxxxxxxxx:rule/BatchJobTriggerRule

3. 実行するジョブの準備

引数 2 つ、環境変数 2 つを受け取り、ファイルを生成する
以下の COBOL プログラムをジョブとして動かしてみます。

(サンプルには COBOL 言語を使っていますが、もちろんシェルスクリプトでも、Java でも Python でも、Swift でも Elixir でも Docker にさえすれば AWS Batch 上で動きます)

3.1. エントリーポイントの用意

AWS Batch からの引数をうまいこと受け取り、かつ出力されたファイルを S3 にアップロードするためのラッパースクリプトを用意します。

ファイル名を決め

コマンド
DOCKER_ENTRYPOINT_FILE="entrypoint.sh"

生成します

entrypoint.sh
cat << EOF > ${DOCKER_ENTRYPOINT_FILE}
#!/bin/bash

if [ -z "\$AWS_DEFAULT_REGION" ]; then
  echo "Missing environment variable 'AWS_DEFAULT_REGION'." 1>&2
  exit 1
fi
if [ -z "\$AWS_S3_BUCKET" ]; then
  echo "Missing environment variable 'AWS_S3_BUCKET'." 1>&2
  exit 1
fi
if [ -z "\$AWS_S3_KEY" ]; then
  AWS_S3_KEY="result-\$(date +%Y%m%d%H%M)"
fi

/usr/local/bin/batch \$@

rc=\$?
if [ \$rc -ne 0 ] ;then
  echo "[Error] Executing batch went wrong..." 1>&2
  echo "ARGUMENTS: "\$@ 1>&2
  echo "APP_VERSION: "\${APP_VERSION} 1>&2
  echo "APP_TARGET: "\${APP_TARGET} 1>&2
  exit \$rc
fi

aws --region \$AWS_DEFAULT_REGION s3api put-object \\
    --bucket \$AWS_S3_BUCKET --key \$AWS_S3_KEY \\
    --body result.txt

rc=\$?
if [ \$rc -ne 0 ] ;then
  echo "[Error] Sending job results went wrong..." 1>&2
  echo "ARGUMENTS: "\$@ 1>&2
  echo "APP_VERSION: "\${APP_VERSION} 1>&2
  echo "APP_TARGET: "\${APP_TARGET} 1>&2
  echo "AWS_S3_BUCKET: "\${AWS_S3_BUCKET} 1>&2
  echo "AWS_S3_KEY: "\${AWS_S3_KEY} 1>&2
  exit \$rc
fi
EOF

3.2. Dockerfile の作成

お決まりのファイル名で

コマンド
DOCKER_FILE="Dockerfile"

生成します

Dockerfile
cat << EOF > ${DOCKER_FILE}
FROM debian:wheezy-slim

ADD https://raw.githubusercontent.com/pottava/docker-cobol/master/examples/batch.cbl /usr/local/src/
ADD entrypoint.sh /

RUN BUILD_PACKAGES="build-essential curl" \\
    && apt-get update && apt-get autoremove -y \\
    && apt-get install -y \${BUILD_PACKAGES} open-cobol python \\
    && chmod +x /entrypoint.sh \\

    # Build application
    && cd /usr/local/bin \\
    && cobc -x /usr/local/src/batch.cbl \\

    # Install AWS-CLI
    && curl --location --silent --show-error \\
        https://bootstrap.pypa.io/get-pip.py | python \\
    && pip install "awscli==1.11.56" \\

    # Clean up
    && apt-get purge -y --auto-remove \${BUILD_PACKAGES} \\
    && find / -depth -type d -name test -exec rm -rf {} \\; \\
    && find / -depth -type d -name \__pycache__ -exec rm -rf {} \\; \\
    && rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/* /root/.cache

WORKDIR /app

ENV AWS_DEFAULT_REGION=ap-northeast-1 \\
    AWS_S3_BUCKET="" \\
    AWS_S3_KEY="" \\
    APP_VERSION=0.1.0 \\
    APP_TARGET=executor

ENTRYPOINT ["/entrypoint.sh"]
EOF

3.3. アプリケーションの Docker イメージ化

ECR のリポジトリ名を指定しつつ、ビルドします。
回線が細いと 15 分ほどかかるかもしれません。

コマンド
docker build -t ${DOCKER_REPO} .
結果(例)
Sending build context to Docker daemon 24.06 kB
Step 1/7 : FROM debian:wheezy-slim
 ---> b7230ec23103
Step 2/7 : ADD https://raw.githubusercontent.com/pottava/docker-cobol/master/examples/batch.cbl /usr/local/src/
Downloading 2.687 kB
 ---> Using cache
 ---> 723925bd3df1

(中略)

Step 7/7 : ENTRYPOINT /entrypoint.sh
 ---> Running in 94ca4974b074
 ---> ca617324a141
Removing intermediate container 94ca4974b074
Successfully built ca617324a141

ECR にログインし、Docker イメージを push しましょう

コマンド
aws ecr get-login | sh
docker push ${DOCKER_REPO}
結果(例)
The push refers to a repository [xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/aws-batch-xxxxxxxxxx/sample]
de07e4081e81: Pushed
f0c9c1e82d65: Pushed
4e4c70200302: Pushed
d4a5f6671fae: Pushed
6db2a2c16cbd: Pushed
latest: digest: sha256:c7e5ef31e1bde0a6ec4327118e5d2976da098e7a5e9fa5a7ee236ceb942e094a size: 1361

4. ジョブの投入

Docker イメージも準備できました。
では事前に CloudFormation で用意しておいた Lambda を CloudWatch Events から定期的に起動し、AWS Batch へ Job を Submit してみましょう。

4.1. CloudWatch Events の有効化

1 分おきに Lambda を蹴るルールは定義済みですが
DISABLED で停止している状態になっています。
ENABLE に更新して処理を開始してみましょう。

コマンド
aws events enable-rule \
  --name $( aws events list-rules | jq '.Rules' \
    | jq "map(select(.Arn==\"${TRIGGER_ARN}\"))" \
    | jq -r '.[].Name' )
結果
返り値なし

4.2. Job ステータスの確認

流れてくる Job の様子を確認します。
1 分おきに結果が増えていくはずです。

コマンド
aws batch list-jobs \
  --job-queue ${JOB_QUEUE_NAME} \
  --job-status SUCCEEDED \
  | jq '.jobSummaryList' \
  | jq "map(select(.jobName==\"${JOB_NAME}\"))"
結果(例)
[
  {
    "jobName": "job-1488375104",
    "jobId": "71895de8-b28c-41eb-b94f-43fe2fb7878a"
  },
  {
    "jobName": "job-1488375104",
    "jobId": "cbc64814-a27a-48cc-9376-d67c95984505"
  }
]

4.3. 出力された帳票の確認

まずは帳票の一覧を表示してみます

コマンド
aws s3api list-objects \
  --bucket ${S3_BUCKET_NAME} \
  | jq '.Contents' \
  | jq 'map({Key: .Key, LastModified: .LastModified})'
結果(例)
[
  {
    "Key": "result-201703011749",
    "LastModified": "2017-03-01T17:49:13.000Z"
  },
  {
    "Key": "result-201703011750",
    "LastModified": "2017-03-01T17:50:18.000Z"
  }
]

どれかを選び、ダウンロードしてみましょう

コマンド
S3_KEY="result-201703011749"
コマンド
aws s3api get-object \
  --bucket ${S3_BUCKET_NAME} \
  --key ${S3_KEY} ${S3_KEY}
結果(例)
{
    "AcceptRanges": "bytes",
    "ContentType": "binary/octet-stream",
    "LastModified": "Wed, 01 Mar 2017 17:49:13 GMT",
    "ContentLength": 121,
    "ETag": "\"190d1fecd45c7996ab8f124cd60a42ee\"",
    "Metadata": {}
}

ファイルを開いてみます

コマンド
cat ${S3_KEY}
結果(例)
     2 17:48:59        from-AWS-Lambda
production!     0dfc1g7b47
   1 49 12
   2 49 12
   3 49 12
   4 49 12
   5 49 12

完了

より実践的な使い方は以上です。
AWS Batch #4 環境の破棄

 


  1. AWS Batch にはまる ユースケース ではないものの、できないわけではありません。ネタです。 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
10
Help us understand the problem. What are the problem?