ちょっと話題の記事

AWS Batchを使って5分以上かかる処理を実行してみる

2017.08.17

どうも!西村祐二@大阪です。

Lambdaは現在(2017/8/17)実行時間が最長5分までという制限があります。 そこで、今回はAWS Batchを使って5分以上かかる処理を実行させてみたいと思います。

AWS Batchとは

JobをAWS Batchになげると予め設定しておいたインスタンスを起動し、 ECRまたはDocker Hubからコンテナイメージを取得しタスクを実行してくれます。 また、実行しているタスクがない場合インスタンスを起動してから1時間以内に自動で削除してくれます。

やりたいこと

・CodeCommitに置いてる5分以上かかるスクリプトをコマンド1つで実行したい ・構成はなるべくシンプルにしたい

5分以上かかる処理

今回は例として1分毎にS3に空ファイルを作成する処理を実行してみたいと思います。 下記2つのファイルをCodeCommitの「test-commit」レポジトリにおいています。

2017-08-16 15.15.19

run.shの中身が下記になります。 pythonのプログラムをたたいているだけです。 パスは適宜変更してください。 コンテナ上で実行されることを想定して、絶対パスで記載するのがよいかと思います。

#!/bin/bash

python /usr/local/test-batch/test.py

test.pyの中身が下記になります。 sleepを使って60秒毎にS3の「test-bucket-batch」の中に ファイルを作成する処理を10回繰り返しています。 つまり約10分かかる想定です。

※試す場合はS3のバケット名など変数やパラメータを適宜変更してください。

# -*- coding: utf-8 -*-

import boto3
from time import sleep

# S3
BUCKET_NAME = "test-bucket-batch"
TARGET_DIR = "batch"
FILE_CONTENTS = ""

# Timer
MAX_ITER = 10
SEC = 60

def PutS3(i):
  s3 = boto3.resource('s3')
  bucket = s3.Bucket(BUCKET_NAME)
  filename = str(i+1)

  obj = bucket.put_object(ACL='private', Body=FILE_CONTENTS, Key=TARGET_DIR + "/" + filename, ContentType='text/plain')
  return str(obj)

def count(i):
  print("{}秒経過しました。".format((i+1)*SEC))

if __name__ == '__main__':
  for i in range(MAX_ITER):
    sleep(SEC)
    count(i)
    PutS3(i)

この処理をAWS Batchを使ってコンテナ上で実行してみましょう。

構成図

今回想定する構成は下記画像のようになります。 ユーザはCodeCommitでソースを管理しており、処理を実行したいときに AWS BatchにJobをなげると自動で実行してくれて、 かつ、自動でインスタンスを削除してくれます。

ブログ画像.001

作業概要

・コンテナイメージを作る ・ECRにコンテナイメージを登録する ・AWS Batchを設定する ・AWS Batchを実行してみる ・CLIから実行してみる

事前準備

・上記記載のコードをCodeCommitで管理しておいてください ・Dockerコマンドが使える環境を用意しておいてください ・aws-cliを使えるようにしておいてください

コンテナイメージを作る

今回の処理を実行するためには大まかに下記の環境がコンテナ内に必要となります。

・python ・git ・awscli ・boto3 ・Codecommitからcloneしたソース中にあるrun.shをキックするスクリプト

AWS Batchで使用するコンテナイメージはECRとDocker Hubから取得できます。 もし、Docker Hubで要件にあうコンテナイメージがあればコンテナイメージを作成する必要はありません。

CentOSのコンテナイメージをベースに作成する

予めDockerコマンドを使えるようにしておいてください。 私の場合はDocker for Macを使ってMac上でコンテナイメージを作成しています。

今回はオフィシャルで提供されているCentOSのコンテナをベースにして作成していきます。

//もととなるコンテナイメージをpull
$ docker pull centos

//コンテナイメージを確認する
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest xxxxxxxxxxxx xx days ago 193MB

//コンテナの中に入る
$ docker run -it centos /bin/bash

次からコンテナの中での作業です。

//pipコマンドインストール
[root@xxx]# curl -kl https://bootstrap.pypa.io/get-pip.py | python

//aws-cliインストール
[root@xxx]# pip install awscli

//boto3インストール
[root@xxx]# pip install boto3

//gitインストール
[root@xxx]# yum install -y git

//CodeCommitからcloneして中のrun.shをキックするスクリプト
[root@xxx]# vi /usr/local/init.sh
---------
#!/bin/bash

CODE_REPO=https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/
CODE_DIR=/usr/local/test-batch

git clone --config credential.helper='!aws --region ap-northeast-1 codecommit credential-helper $@' --config credential.UseHttpPath=true $CODE_REPO $CODE_DIR

sh /usr/local/test-batch/run.sh
----------

[root@xxx]# exit

CodeCommitからcloneして中のrun.shをキックするスクリプト」について少し解説しておきます。 このスクリプトでポイントとなるところとしてはgit cloneのconfigのところです。 通常、cloneするときのアクセスキーのやり取りや管理を考える必要があるのですが、 今回はコンテナ起動にIAMロールを付与しSSHの鍵もアクセスキーも不要でcloneできるようにしています。

詳細は下記ブログが大変参考になります!

CodeCommit のGitリポジトリにIAM Roleで接続する

さて、現状ローカルに戻ってきています。先程作業した状態のコンテナイメージを作成しましょう。

//作業時のコンテナIDを確認
$ docker ps -a -n=5
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
XXXXxxxxxxxx centos "/bin/bash" 9 seconds ago Exited (0) 1 second ago elated_hypatia
・
・
・
//「test-container」としてコンテナイメージ作成
$ docker commit XXXXxxxxxxxx test-container

//イメージが作られたことを確認
$ docker images

//コマンドが使えること、ファイルがあることを確認
$ docker run -it test-container /bin/bash

ちなみにDockerfileで表すとおそらくこんな感じです。

FROM centos:latest
RUN curl -kl https://bootstrap.pypa.io/get-pip.py | python
RUN pip install awscli
RUN pip install boto3
RUN yum install -y git
ADD init.sh /usr/local/

CMDはAWS Batchの「Job definitions」のときに設定できるのでイメージを作成するときには不要です。

ECRにコンテナイメージを登録する

マネージメントコンソールから「EC2 Container Service」にアクセスします。

2017-08-16 18.58.40

コンテナイメージを登録するリポジトリ名を「test-repo」として「次のステップ」をクリックします。

2017-08-16_19_00_43

リポジトリが正常に作成されたため「キャンセル」をクリックします。

2017-08-16_19_03_01

リポジトリをクリックし、「test-repo」が作成されていることを確認します。

2017-08-16_19_04_34

表示されたコマンドを参考に作成したコンテナイメージをECRに登録します。

$ aws ecr get-login --no-include-email --region ap-northeast-1
docker login ・・・・・・・・・
ながいログインコマンドが出力されます。

//出力されたコマンドを実行します。
$ docker login -u AWS -p <出力された値> https://xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com
Login Succeeded

//作成したコンテナイメージ「test-container」をもとにタグをつけます
$ docker tag test-container:latest xxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/test-repo:latest

//リポジトリにpushします
$ docker push xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/test-repo:latest

コンテナイメージが登録されたか確認します。

2017-08-16_20_18_46

AWS Batchを設定する

はじめてAWS Batchを行う際に表示される「初期設定ウィザード」はそのまま進めて行き、 デフォルトのままとりあえず作成するのが良いと思います。 そうすることで、ECSに設定するroleやVPCの環境など自動的に作成してくれます。 デフォルトで作成後にあらためて個別で設定していくほうがスムーズに行くかと思います。 デフォルトのまま作成すると、おそらく下記環境が作成されているかと思います。 また、Jobが実行されインスタンスが立ち上がっていたら削除しておいてください。

■Batch ・Job definitions ・Job queues ・Compute environments ■ロール ・AWSBatchServiceRole ・ecsInstanceRole ■VPCの環境

Compute environmentsの設定

デフォルトで作成されたのとは別で 「test-env」として起動するインスタンスの制御ができるCompute environmentsを設定しました。

FireShot_Capture_9_-_AWS_Batch__-_https___ap-northeast-1_console_aws_amazon_com_batch_home

・「Desired vCPUs」は起動するインスタンスの希望するvCPUの数なので画像では0となっていますが、「1」や「2」としておくほうが良いかと思います。

・「Minimum vCPUs」を0に設定しておくと、起動したインスタンスを自動的にterminatedしてくれます。 terminatedされるタイミングとして、実行中のジョブがないかつ、インスタンスが起動して1時間たたないぐらいにterminatedされるようです。 ※Provisioning Modelを「On-demand」であっても、自動的にterminatedされていました。

・今回の処理ではCodeCommitとS3にアクセスするため、Instance roleの「ecsInstanceRole」に権限を付与しておきます。

2017-08-16_23_06_36

また、「Compute environments」を作成すると自動的にECSのクラスターが作成されます。裏ではECSで動いているのでAWS Batchでタスクを実行しているとこちらにも表示されます。

2017-08-17_0_16_07

Job queues

デフォルトで作成されたのとは別で 名前を「test-job-queues」とし、環境を先程作成した「test-env」としてqueuesを作成しました。

2017-08-16 23.59.59

Job definitions

デフォルトで作成されたのとは別で 名前を「test-job-def」とし、コンテナイメージをECRに登録しているコンテナを指定 コンテナがrunされたときにCodeCommitからソースをcloneして処理を実行したいため コンテナ内に設置したinit.shを実行するように指定します。 他はデフォルトまま設定しています。

FireShot_Capture_8_-_AWS_Batch__-_https___ap-northeast-1_console_aws_amazon_com_batch_home

「Command」で指定したコマンドは Docker CMD パラメータに相当します。 つまり、Docker runされるときに指定したコマンドが実行されるため 下記のような流れで処理が進みます。

インスタンスに作成したコンテナイメージがロードされDocker runされる ↓ コンテナ内に設置したinit.shを実行される ↓ CodeCommitからソースをcloneして、中にあるrun.shを実行される ↓ 1分毎にS3にファイルを作成する処理が実行される

AWS Batchを実行してみる

名前を「test-job」とし、先程作成した「Job definitions」「Job queues」を指定して「Submit job」をクリックします。 するとCodeCommit内のプログラムが実行されます。

FireShot Capture 11 - AWS Batch_ - https___ap-northeast-1.console.aws.amazon.com_batch_home

問題なく処理が完了したらS3に下記画像のように空のファイルが10個作成されます。

2017-08-16_16_38_30

また、AWS BatchのJobのStatusがSUCCEEDEDになっているはずです。

2017-08-17_2_52_56

■全体の流れとして下記のように進むと思われます。

AWS Batchにjobがsubmitされる ↓ インスタンスが起動する ↓ インスタンスにコンテナイメージがロードされる ↓ Job definitionsで指定したコマンドによってコンテナ内に設置したinit.shを実行される ↓ CodeCommitからソースをcloneして、中にあるrun.shを実行する ↓ 1分毎にS3にファイルを作成する処理が実行される ↓ 処理が終わると自動的にインスタンスがterminatedされる

CLIから実行

CLIからAWS BatchにjobをSubmitするには下記の形式でコマンドを実行するればSubmitするができます。

aws --profile --region batch submit-job --job-name <名前> --job-queue --job-definition <定義したjob>

下記が今回作成した設定を用いてJobをAWS BatchにSubmitするコマンド例です。 Job名を「test-cli」としています。

$ aws --profile default --region ap-northeast-1 batch submit-job --job-name test-cli --job-queue "test-job-queues" --job-definition test-job-def:1
aaaaaa-bbbbb-ccccc-dddd-eeeeeee test-cli

マネージメントコンソール上からでもJobがSubmitされて実行されていることがわかります。

2017-08-17_3_08_02

さいごに

AWS Batchは動かしたいプログラムが動作する環境と プログラムを実行するスクリプトを含めたコンテナイメージを作成させすれば CodeCommitからcloneし、プログラムを実行、 処理が終わったら自動的にインスタンスを削除するようにできるため 運用やコストの観点で有効なサービスではないでしょうか。

今回触れていませんが、AWS Batchはあの処理が終わってからこの処理を実行したいなど依存関係も設定できるので 使いこなせればより有効に使えそうです。

誰かの参考になれば幸いです。