LoginSignup
2
1

More than 3 years have passed since last update.

AWS Batchで最新の(optimized)AMIをコンピューティング環境に適用するジョブ

Last updated at Posted at 2018-06-18

2018年7月、東京リージョンにFargateがやってきます。

Fargate導入に向け、既存のECS向けに作った資産は極力Fargateで動かす方針です。
テンポラリなジョブはすべてAWS Batchで実行する予定です。
AWS Batchの土台となるEC2には定期的に最新のAMIを自動で適用して、その流れでproxy設定もAMIに仕込む。という作業もAWS Batchでさせるつもりなので、そのためのBatchを紹介します。

Dockerfile

元となるジョブ定義に使用するコンテナイメージを作成するためのDockerfileです。

Dockerfile
FROM alpine:3.6

RUN apk update \
 && apk upgrade \
 && apk add --no-cache \
        python \
        bash \
        curl \
 && apk add --no-cache --virtual .build-deps \
        wget \
        curl \
        py-pip \
        unzip \
 && wget --no-check-certificate -O /tmp/packer.zip https://releases.hashicorp.com/packer/1.2.4/packer_1.2.4_linux_amd64.zip \
 && unzip /tmp/packer.zip -d /usr/local/bin \
 && curl -o /bin/jq -L https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64 \
 && chmod +x /bin/jq \
 && pip install awscli \
 && apk add tzdata \
 && cp -p /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
 && apk del tzdata \
 && apk del .build-deps \
 && rm -rf /var/cache/apk/* \
 && rm -rf /tmp/* /var/tmp/*

COPY packer.sh /bin/
RUN chmod +x /bin/packer.sh

COPY packerfile.org /

CMD packer.sh

packerテンプレート

現時点(18/7/12)において、HTTPプロキシ設定では/etc/sysconfig/dockerに対するNO_PROXY 設定は 169.254.169.254だけとなっておりますが、169.254.170.2,/var/run/docker.sockが描き漏れています。(/var/run/docker.sockは不要かもしれません)
私の地道な布教の甲斐あって、現時点19/8/12では修正されてなされておられますご様子です。

packerfile.org
{
    "builders":
    [
        {
            "type": "amazon-ebs",
            "ami_name": "{{%ENV%}}-{{%SOURCE_AMI%}}-{{%AMI_NAME}}",
            "region": "ap-northeast-1",
            "source_ami": "{{%SOURCE_AMI_ID%}}",
            "instance_type": "m4.large",
            "security_group_id": "{{%SECURITY_GROUP_ID%}}",
            "subnet_id": "{{%SUBNET_ID%}}",
            "ssh_timeout": "1m",
            "ssh_username": "ec2-user",
            "tags": {
                "base-ami": "{{%SOURCE_AMI_ID%}}"
            }

        }
    ],
    "provisioners": [
        {
            "type": "shell",
            "execute_command": "{{ .Vars }} sudo -E sh '{{ .Path }}'",
            "inline": [
                "sudo echo 'http_proxy={{%HTTP_PROXY%}}' >> /etc/ecs/ecs.config ",
                "sudo echo 'https_proxy={{%HTTPS_PROXY%}}' >> /etc/ecs/ecs.config ",
                "sudo echo 'no_proxy=169.254.169.254,169.254.170.2,/var/run/docker.sock' >> /etc/ecs/ecs.config ",
                "sudo echo 'env http_proxy={{%HTTP_PROXY%}}' >> /etc/init/ecs.override ",
                "sudo echo 'env https_proxy={{%HTTPS_PROXY%}}' >> /etc/init/ecs.override ",
                "sudo echo 'env no_proxy=169.254.169.254,169.254.170.2,/var/run/docker.sock' >> /etc/init/ecs.override ",
                "sudo echo 'export http_proxy={{%HTTP_PROXY%}}' >> /etc/sysconfig/docker ",
                "sudo echo 'export https_proxy={{%HTTPS_PROXY%}}' >> /etc/sysconfig/docker ",
                "sudo echo 'export no_proxy=169.254.169.254,169.254.170.2' >> /etc/sysconfig/docker "
                      ]
        }
    ]
}

AMI入れ替えスクリプト

コンテナ内で実行する、AMI入れ替え作業をする元となるシェルスクリプトです。
一般的には、AWS Batchで実行するジョブの本体はs3等に置いておいて、Batch実行時にs3に取得しに行く流れみたいですが、コードの管理はgitLabでしっかりして、デプロイまでの流れは自動化するので大丈夫です。

packer.sh
#!/bin/bash

#####################
# ジョブ定義側で以下環境変数を設定する。
AMI_NAME=
BASE_COMPUTE_ENVIRONMENT=
HTTP_PROXY=
HTTPS_PROXY=
SECURITY_GROUP_IDS=
SUBNET_ID=
ENV=

YYYYMMDD=`date '+%Y%m%d%H%M'`

delete_batch_compute_environment()
{
        COMPUTE_ENVIRONMENT_ARN=$1
        COUNT=0
        LOOP_COUNT=10

        while [ ${COUNT} -lt ${LOOP_COUNT} -a "${STATE}" != "DISABLED" ]; do
                # 既存のコンピューティング環境をDISABLE
                aws batch update-compute-environment --compute-environment ${COMPUTE_ENVIRONMENT_ARN} --state DISABLED

                STATE=`aws batch describe-compute-environments \
                | jq '.computeEnvironments[]| select(.computeEnvironmentArn == "'${COMPUTE_ENVIRONMENT_ARN}'")|.state ' -r`

                let COUNT++
        done

        if [ ${COUNT} -lt ${LOOP_COUNT} ];then
                # DISABLEDになってもすぐに削除できないので3秒wait
                sleep 3
                aws batch  delete-compute-environment --compute-environment ${COMPUTE_ENVIRONMENT_ARN}
        else
                echo "computing environment is not DISABLED"
                exit 1
        fi

}

# regionの設定
aws configure set default.region ap-northeast-1

# 最新のオプティマイズAMI名を取得
aws ssm get-parameters  --names /aws/service/ecs/optimized-ami/amazon-linux/recommended > ssm_get_parameters.org

# 最新のオプティマイズAMI名を取得
SOURCE_AMI=`cat /ssm_get_parameters.org | jq .Parameters[0].Value -r | jq .image_name -r`
SOURCE_AMI_ID=`cat /ssm_get_parameters.org  | jq .Parameters[0].Value -r | jq .image_id -r`

echo "#### base ami: ${SOURCE_AMI} ####"

# packer設定ファイル作成
cat packerfile.org | sed -e "s/{{%AMI_NAME%}}/${AMI_NAME}/g" \
                         -e "s/{{%SOURCE_AMI%}}/${SOURCE_AMI}/g" \
                         -e "s/{{%SOURCE_AMI_ID%}}/${SOURCE_AMI_ID}/g" \
                         -e "s/{{%HTTP_PROXY%}}/${HTTP_PROXY}/g" \
                         -e "s/{{%HTTPS_PROXY%}}/${HTTPS_PROXY}/g" \
                         -e "s/{{%SECURITY_GROUP_ID%}}/${SECURITY_GROUP_ID}/g" \
                         -e "s/{{%SUBNET_ID%}}/${SUBNET_ID}/g" \
                         -e "s/{{%ENV%}}/${ENV}/g" \
                         > /packerfile.json

echo "#### packerfile.json ####"
cat /packerfile.json

# optimized AMIを作成
LATEST_SOURCE_AMI_ID=`packer build /packerfile.json | tee output.log | tail -2 | head -2  \
                      | awk 'match($0, /ami-.*/) { print substr($0, RSTART, RLENGTH) }'`

if [ "${LATEST_SOURCE_AMI_ID}" == "" ]; then
    echo "#### output.log ####"
    cat /output.log
    exit 0
fi

echo "#### optimized ami: ${LATEST_SOURCE_AMI_ID} ####"

# 最新のオプティマイズAMIから作成したAMIが登録されていないか確認する
# 仕様として、tagにbase-ami $SOURCE_AMI を含むことを記す
LIST_RECENT_SOURCE_AMI_ID=`aws ec2  describe-images --filters  Name=tag-key,Values=base-ami,Name=tag-value,Values=${SOURCE_AMI_ID} \
            | jq .Images[].ImageId -r`

for RECENT_SOURCE_AMI_ID in  ${LIST_RECENT_SOURCE_AMI_ID}; do
    LIST_COMPUTE_ENVIRONMENT_ARN+="`aws batch  describe-compute-environments \
                          | jq '.computeEnvironments[] | select(.computeResources.imageId == "'${RECENT_SOURCE_AMI_ID}'") | .computeEnvironmentArn' -r` "
done

for OLD_COMPUTE_ENVIRONMENT_ARN in ${LIST_COMPUTE_ENVIRONMENT_ARN} ; do

        # 新規コンピューティング環境の名称
        NEW_COMPUTE_ENVIRONMENT=${BASE_COMPUTE_ENVIRONMENT}-${YYYYMMDD}

        # 最近(RECENT) のAMIを使用したコンピューティング環境の設定ファイルを作成する
        # 元の環境のstate が ENABLEDでないとあとで失敗する
        aws batch describe-compute-environments \
        | jq '.computeEnvironments[] | select(.computeEnvironmentArn == "'${OLD_COMPUTE_ENVIRONMENT_ARN}'")| {computeEnvironmentName,type,state,computeResources,serviceRole}' \
        > /compute_environment.org

        echo "#### compute_environment.org ####"
        cat /compute_environment.org

        # 最新(LATEST) のAMIを使用したコンピューティング環境の設定ファイルを作成する
        cat compute_environment.org | sed -e "s/\"imageId\": \".*\"/\"imageId\": \"${LATEST_SOURCE_AMI_ID}\"/g" \
        -e "s/\"computeEnvironmentName\": \".*\"/\"computeEnvironmentName\": \"${NEW_COMPUTE_ENVIRONMENT}\"/g" \
         > /compute_environment.json

        echo "#### compute_environment.json ####"
        cat /compute_environment.json

        echo "#### create job environment:${NEW_COMPUTE_ENVIRONMENT} ####"
        NEW_COMPUTE_ENVIRONMENT_ARN=`aws batch create-compute-environment \
                                    --cli-input-json file://compute_environment.json | jq '.computeEnvironmentArn' -r`

        LIST_JOB_QUE=`aws batch describe-job-queues \
        | jq '.jobQueues | map(select(.computeEnvironmentOrder[].computeEnvironment=="'${OLD_COMPUTE_ENVIRONMENT_ARN}'")) | .[].jobQueueArn' -r`

        for JOB_QUE in ${LIST_JOB_QUE} ; do
                # job queueのstatusをupdateするために、ジョブに登録されている環境とorder を取得する
                LIST_JOB_QUE_COMPUTE_ENV=(`aws batch describe-job-queues --job-queues ${JOB_QUE}\
                                  | jq '.jobQueues[].computeEnvironmentOrder[].computeEnvironment' -r`)
                LIST_JOB_QUE_ORDER=(`aws batch describe-job-queues --job-queues ${JOB_QUE}\
                                  | jq '.jobQueues[].computeEnvironmentOrder[].order' -r`)

                UPDATE_LIST=""
                # ジョブが登録されている環境のうち、古いコンピューティング環境を新しいものと置き換える
                for ((i=0; i < ${#LIST_JOB_QUE_COMPUTE_ENV[@]}; i++)) ;do
                        if [ ${LIST_JOB_QUE_COMPUTE_ENV[i]} = ${OLD_COMPUTE_ENVIRONMENT_ARN} ]; then
                                UPDATE_LIST+="order=${LIST_JOB_QUE_ORDER[i]},computeEnvironment=${NEW_COMPUTE_ENVIRONMENT_ARN} "
                        else
                                UPDATE_LIST+="order=${LIST_JOB_QUE_ORDER[i]},computeEnvironment=${LIST_JOB_QUE_COMPUTE_ENV[i]} "
                        fi
                done

                echo "#### register job queue: ${JOB_QUE} ####"
                # ジョブ再登録
                aws batch update-job-queue --job-queue ${JOB_QUE} --compute-environment-order ${UPDATE_LIST}
        done

        echo "#### delete old compute environment: ${OLD_COMPUTE_ENVIRONMENT_ARN} ####"
        # コンピューティング環境の削除
        delete_batch_compute_environment ${OLD_COMPUTE_ENVIRONMENT_ARN}
done

exit 0
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1