FURYU Tech Blog - フリュー株式会社

フリュー株式会社の開発者が技術情報を発信するブログです。

AWS Batchのジョブ定義を任意のDockerイメージタグで更新するスクリプトを作成した話

こんにちは、フリューのピクトリンク開発 SREチームで働いているまさおです🐙

決まった1コマンドを叩けばAWS Batchのジョブ定義を更新できるようにしたい、という気持ちになったためスクリプトを書いてみました。

実際にはこれから紹介するような実装をmiseのタスク*1として用意し、 mise run update-job-definition <イメージタグ名>という風にコンソールで実行すれば指定したイメージ名でジョブ定義を更新できるようにしています。

コード全体

#!/bin/bash  
  
# 引数でイメージタグを受け取る
IMAGE_TAG=$1  
if [ -z "${IMAGE_TAG}" ];then  
  echo "usage $0 <IMAGE_TAG>"  
  exit 1;  
fi  
  
AWS_PROFILE="hoge-developer"
JOB_NAME="hoge-job"
# 1. ジョブ定義のテンプレートを用意
SRC_JOB_DEF_FILE="hoge-job/dev_job_definition.json"
  
# 2. 指定されたイメージタグをもとにジョブ定義を登録
IMAGE_ARN="XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/hoge-batch-job:${IMAGE_TAG}"  
AWS_REGION="ap-northeast-1"  
DEST_JOB_DEF_FILE="/tmp/dev_job_definition.json"  
  
cat ${SRC_JOB_DEF_FILE} | jq --arg image "${IMAGE_ARN}" '.containerProperties.image |= $image' > ${DEST_JOB_DEF_FILE}  
aws batch register-job-definition --job-definition-name hoge-batch-job-definition --type container --cli-input-json file://${DEST_JOB_DEF_FILE} --profile ${AWS_PROFILE} --region ${AWS_REGION} > /dev/null  
if [ $? -ne 0 ]; then  
    echo "Failed to update job definition."  
    exit 1;  
fi  
  
sleep 5  
  
# 3. 古いリビジョンを登録解除
OLD_JOB_DEFINITION=$(aws batch describe-job-definitions --job-definition-name hoge-batch-job-definition --status ACTIVE --profile ${AWS_PROFILE} --region ${AWS_REGION} --output json \  
                  | jq '.jobDefinitions | sort_by(.revision) | reverse | .[1].jobDefinitionArn' \
                  | sed -e "s/\"//"g)  
aws batch deregister-job-definition --job-definition $OLD_JOB_DEFINITION --profile ${AWS_PROFILE} --region ${AWS_REGION} > /dev/null  
if [ $? -ne 0 ]; then  
    echo "Failed to describe old job definition."  
    exit 1;  
fi  
  
# 4. 登録内容をチェック
MAYBE_UPDATED_ARN=$(aws batch describe-job-definitions --job-definition-name hoge-batch-job-definition --status ACTIVE --profile ${AWS_PROFILE} --region ${AWS_REGION} --output json \  
                  | jq '.jobDefinitions | max_by(.revision) | .containerProperties | .image' \
                  | sed -e "s/\"//"g)  
if [ ${MAYBE_UPDATED_ARN} != ${IMAGE_ARN} ]; then  
  echo "Check failed to update job definition. IMAGE_TAG in new job definition is ${MAYBE_UPDATED_ARN}."  
  exit 1;  
fi  
  
echo "Finished to update job definition."  
  
exit 0;

コード詳細

1. ジョブ定義のテンプレートを用意

都度ジョブ定義を一から用意するのは大変なので、最新のジョブ定義を引っ張ってきます。

下記のような感じでdescribe-job-definitionsを叩けばジョブ定義が返ってきます。--status ACTIVE を指定しないと登録解除されたものまで返ってきてしまうので注意しましょう。

また、コマンドを叩かなくともAWSコンソールから取ってくるのでも良いです。

% aws batch describe-job-definitions --job-definition-name hoge-batch-job-definition --status ACTIVE --profile hoge-developer --region ap-northeast-1 --output json | jq '.jobDefinitions[0]'

{
  "jobDefinitionName": "hoge-batch-job-definition",
  "jobDefinitionArn": "arn:aws:batch:ap-northeast-1:XXXXXXXXXXXX:job-definition/hoge-batch-job-definition:6",
  "revision": 6,
  "status": "ACTIVE",
  "type": "container",
  "parameters": {},
  "retryStrategy": {
    "attempts": 1,
    "evaluateOnExit": []
  },
  "containerProperties": {
    "image": "XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/hoge-job:v1.0.0",
    "command": [],
    "jobRoleArn": "arn:aws:iam::XXXXXXXXXXXX:role/service-role/hoge-batch-job-role",
    "executionRoleArn": "arn:aws:iam::XXXXXXXXXXXX:role/service-role/hoge-batch-job-execution-role",
    "volumes": [
      {
        "name": "XXXXX",
...

ここからジョブ登録時には不要なrevision、jobDefinitionArn、statusを削除したものをテンプレートとして保存しておきます。 コードで SRC_JOB_DEF_FILE="hoge-job/dev_job_definition.json" といっているファイルがそうです。

ちなみにスクリプト内で都度最新のジョブ定義を取ってきて不要なフィールドを削除して…とまではしなかったのは、イメージタグ以外はそこまで更新する機会があまりないからです。 もしそこもスクリプト内でやりたいよ、という場合は上記のdescribe-job-definitionsを活用すれば良い感じにできると思います。

2. 指定されたイメージタグをもとにジョブ定義を登録

1.で用意したテンプレートの containerProperties の中にある image を見つけてきて、イメージタグを指定したものに変更しつつ、新しいジョブ定義として登録していきます。

まずはテンプレートの中で指定しているイメージタグを書き換えます。

テンプレートをcatで読み込んで、jqで containerProperties の中にある image を置き換えます。その結果を一時ファイルに書き出します。

cat ${SRC_JOB_DEF_FILE} | jq --arg image "${IMAGE_ARN}" '.containerProperties.image |= $image' > ${DEST_JOB_DEF_FILE}

そうして一時ファイルに保存したジョブ定義の内容をもとにregister-job-definitionを叩きます。

aws batch register-job-definition --job-definition-name hoge-batch-job-definition --type container --cli-input-json file://${DEST_JOB_DEF_FILE} --profile ${AWS_PROFILE} --region ${AWS_REGION} > /dev/null  

3. 古いリビジョンを登録解除

登録解除するには対象のジョブ定義のArnが必要になるので、describe-job-definitionコマンドで取得してきます。

先程登録したジョブ定義を含めアクティブなジョブ定義が全て返ってくるので、jqコマンドで リビジョンの降順(=新しいもの順)にソートしてからひとつ古い方のジョブ定義を取得するようにしています。

そしてそこから jobDefinitionArn の値をダブルクォーテーションがない形で取得してきます。

OLD_JOB_DEFINITION=$(aws batch describe-job-definitions --job-definition-name hoge-batch-job-definition --status ACTIVE --profile ${AWS_PROFILE} --region ${AWS_REGION} --output json \  
                  | jq '.jobDefinitions | sort_by(.revision) | reverse | .[1].jobDefinitionArn' \
                  | sed -e "s/\"//"g)  

そしてそのARNを指定してderegister-job-definition を叩きます。

aws batch deregister-job-definition --job-definition $OLD_JOB_DEFINITION_ARN --profile ${AWS_PROFILE} --region ${AWS_REGION} > /dev/null

これで一つ前のジョブ定義が登録解除されました。

もし古いジョブ定義が複数存在する場合にも対応したければ、ループ処理などをしてあげるとよいでしょう。 登録解除されたものは180日後に自動で削除されます。

4. 登録内容をチェック

ここまでで、最新のアクティブなジョブ定義で指定されているイメージ名はスクリプト実行時に指定したものになっているはずです。

ということで念の為にそれをチェックします。

再びdescribe-job-definitionsを叩きます。

describe-job-definitionsの結果の中から、 max_by(.revision) で最新のリビジョンを選び、 containerProperties の中にある image を取得。 最後にダブルクォーテーションを消しています。

MAYBE_UPDATED_ARN=$(aws batch describe-job-definitions --job-definition-name hoge-batch-job-definition --status ACTIVE --profile ${AWS_PROFILE} --region ${AWS_REGION} --output json \  
                  | jq '.jobDefinitions | max_by(.revision) | .containerProperties | .image' \
                  | sed -e "s/\"//"g)

これを登録したはずのイメージArnと比較して、一致しなければエラーだよ、として終わりです。

if [ ${MAYBE_UPDATED_ARN} != ${IMAGE_ARN} ]; then  
  echo "Check failed to update job definition. IMAGE_TAG in new job definition is ${MAYBE_UPDATED_ARN}."  
  exit 1;  
fi  

以上でジョブ定義の登録、古いものの登録解除、更新チェックが完了です。

最後に

ぱっと見るとなんか色々やってるな…という印象がありますが、基本的にregister-job-definitionとdescribe-job-definitionを叩いているだけなのでやっている事自体はシンプルで、あとは文字列加工をこねこねとしている…という感じです。

AWS CLIは便利ではありますが都度コマンドを組み立てるのが地味に面倒だったりするので、こんな感じでスクリプトにしてやれると少し便利度があがりますね!

それではみなさんよいAWS Batchライフを〜〜〜!🎉

*1:弊社ではmiseをタスクランナーとしても使用しており、このようなデプロイ作業などを任せています。