AWS Batchを利用してコンテナを定期実行する~後編~

こんにちは、幅広い視野を持つエンジニアを目指しています田中と申します

この記事はAWS Batchを利用してコンテナを定期実行する~前編~の記事の続きとなります
ECRとECS, dockerを使用します(単語の定義程度の理解で問題ありません)
TL;DR

目次

はじめに

前編をまだ読まれていない方は読んでくださいね
AWS Batchを利用してコンテナを定期実行する~前編~

目標

  1. AWS Batchで応用的な処理を実行する
  2. AWS Batchをジョブスケジューラとして利用する

手順

AWS Batchでカスタムイメージを使用するまで

Jenkinsのシェル実行機能

Jenkinsのシェル実行機能

画像のようにJenkinsのジョブではシェルスクリプトで処理を行うことができますが、AWS Batchでは可能でしょうか。検証してみます

ジョブ設定1

手始めにdockerコンテナのcgroup情報を見てましょう
ジョブ設定1

まず、Jenkinsのジョブと同じようにShebangを書いてシェルスクリプトを記述してみました
すると・・・
ジョブエラー1
エラーになってしまいました。シェルスクリプトとして解釈してくれないようです
公式ドキュメントにはコマンドとしか言及されていません(ジョブ定義のパラメータ – AWS Batch)
そして、どうやら改行も無視されてしまうようです

ところで、EC2のユーザデータやECSのコマンドも1行で書くお約束でした
下記画像はECSのチュートリアルのタスク定義です
ECS Sample App
ワンライナーでコンテンツ書き込むんですね・・・
これをAWS Batchでも試します

ジョブ設定2
こんな感じでしょうか
結果はNGでした。何か妨害工作(exec経由の単一プロセス実行)を感じます…
ジョブエラー2
コマンドの連続実行やパイプはできないようです。もう少し試行錯誤します

脱線
  • 依存ジョブを作る

依存ジョブ
逐次実行はできました
イケてない点

  1. インスタンスを毎回作るので遅い
  2. 次のコマンドに結果を渡せない
  3. 依存ジョブの最大は25個まで
  • ECSクラスタを利用する

AWS BatchはECSクラスタ、つまりはdockerホストなのでAWS CLIからECSのタスクとして発行します

  1. AWS CLIのdokcerイメージを利用する
  2. AWS CLIのコンテナを使ってECSのタスクを発行する

AWS CLIのイメージのCommandに以下のようにしてみます(API KEYやSECRET KEY、regionを環境変数として指定します)

aws ecs register-task-definition --family test --container-definitions "[{\"name\":\"test\",\"image\":\"busybox\",\"cpu\":1,\"command\":["/bin/sh -c \"ls -la && cat /proc/self/cgroup\""],\"memory\":2000,\"essential\":true}]"

成功?
成功・・・あまり良い方法ではないですね。本線に戻ります

dockerイメージを自作

Blackbeltの36ページにはイメージには事前に実行するプログラムを含ませておくように書かれています
予めよく読むべきでした

気を取り直してECRを設定します
AWSコンソールからコンピューティング→Elastic Container Serviceを選びましょう
ECR設定
左メニューからリポジトリを選択し、リポジトリの作成を押下
ECR設定

  • リポジトリ名には任意の名前を入力します

もうリポジトリができました!
ECR使用方法
リポジトリの利用方法が表示されています。ここにあるコマンドはこの後使いますのでメモしておきましょう!(後から見ることもできます)

docker環境の準備

続いてdockerイメージを自作するための環境を作成しましょう
EC2を利用しても良いのですが、AWS BatchがEC2クラスタを構築してくれているのでこちらを使わせてもらいましょう
EC2を利用する場合でも下記コマンドで一発でdocker環境が手に入ります

yum install docker

dockerイメージの構築

dockerイメージを作るためのベースとなるイメージをpullしましょう
Dockerfileを記述しカスタマイズ、docker buildを行っても良いですが、今回は既存の物を使わせてもらいましょう
私はAWSに対して処理を自動化したかったので以下のイメージを使いました

docker pull garland/aws-cli-docker

イメージがpullできたら

docker images

とコマンドを入力します

REPOSITORY                                                                     TAG                 IMAGE ID            CREATED             SIZE
garland/aws-cli-docker                                                         latest              xxxxxxxxxxxx        2 weeks ago        96.4MB

先ほどpullしたイメージがリストされています
このイメージのIMAGE_IDを使ってコンテナを起動しますのでメモしておきましょう!

docker run -it xxxxxxxxxxxx /bin/sh

コンテナのシェルが表示されましたか?
あとはこのコンテナで必要なものをインストールしたり、任意のプログラム/スクリプトをデプロイします
準備が完了したらCtrl+Dを押下してコンテナを抜けましょう
次に、このコンテナからカスタマイズしたdockerイメージを作成します

docker ps -a

と入力すると、先ほどカスタマイズしたコンテナが見えます

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                  PORTS               NAMES
YYYYYYYYYYYY        XXXXXXXXXXXX        "/bin/sh"           2 weeks ago         Exited (0) 2 days ago                       aqours_sunshine

CONTAINER IDを使ってカスタマイズしたイメージを作成します次のコマンドを入力しましょう

docker commit YYYYYYYYYYYY -m="May our dream come true!" skyarch/test/awsbatchbase:latest

-m=”May our dream come true!”のところはコミットメッセージであり、skyarch/test/awsbatchbase:latestはタグ名なので任意のもので構いません
docker imagesを実行すると今作ったイメージが表示されています

REPOSITORY                                                                     TAG                 IMAGE ID            CREATED             SIZE
skyarch/test/awsbatchbase                                                     latest              ABCDEFGHIJKL        2 days ago          116MB

さていよいよこのイメージをECRにアップロードしていきます
まずは、ECRにログインします。ECRをセットアップした際に表示されていたコマンドを入力しましょう

aws ecr get-login --region [ECRを作成したリージョン]
docker login -u AWS -p [ECRにログインするためのシグネチャ] [ECRのリポジトリのURL]

成功すると、

Login Succeeded

と表示されECRに接続することができます
ECRにアップロードするためのタグをイメージにつけましょう

docker tag skyarch/test/awsbatchbase:latest abcdfeghijkl.dkr.ecr.ap-northeast-1.amazonaws.com/skyarch/test/awsbatchbase:latest

そしていよいよpushです!

docker push abcdefgjkl.dkr.ecr.ap-northeast-1.amazonaws.com/skyarch/test/awsbatchbase:latest

成功したらAWS Batchで指定できます。Commandにはデプロイしてあるプログラムやスクリプトを実行するように指定してあげます
自作イメージを指定
さてジョブを発行してみると・・・?
ジョブの実行結果
大成功です!シェルスクリプトが実行できました。そしてシェルスクリプトなので環境変数なりでジョブごとに処理の範囲を決めてあげることで簡単に並列実行できますね!(n件の処理をm個に分割など)

さて、次はAWS Batchをタスクスケジューラとして使いましょう!

CloudWatch Eventとの連携

次はAWS BatchをCloudWatch Eventと連携させてジョブを定期実行させます
まず、下記のようなLambda関数を作りましょう!

batch = boto3.client('batch')

def submit_job(event):
    result = batch.submit_job(
    jobName=event['JobName'],
    jobQueue=event['JobQueue'],
    jobDefinition=event['JobDefinition']
    )
    return result

def lambda_handler(event, context):
    result = submit_job(event)
    print(result)
    return None

続いてCloudWatch Eventをトリガーとして追加します

CloudWatch Event設定1

LambdaのDesigner→トリガーの追加→CloudWatch Eventを選択
CloudWatch Event設定1

CloudWatch Event設定2

ページ下部から作成したCloudWatch Eventを編集します
CloudWatch Event設定2

CloudWatch Event設定3

CloudWatch Event設定3
ページ右上のアクションから編集を選択

CloudWatch Event設定4

スケジュールを設定します(時刻はUTCです)
CloudWatch Event設定4
cron式と言いつつ、”日”のフィールドに”*”を指定するとエラーになるので”?”を指定します

CloudWatch Event設定5

ユーザデータを指定します
先ほどLambda関数を作成しましたが、Lambdaのeventにデータを入れる設定をします
CloudWatch Event設定5

  • ターゲットにLambda関数を選択
  • 機能のセレクトボックスから先ほど作成したLambda関数を選択
  • 入力の設定を展開し、定数(JSONテキスト)を選択
  • 入力欄に下記内容を入力
{ "JobName": "任意のジョブ名", "JobQueue": "作成したジョブキューの名前", "JobDefinition": "作成したジョブのarn" }

UIが頻繁に変わることに加え、ユーザデータの指定の場所がとても分かりづらいです!
これで、cron式で指定した日時になるとジョブが実行されて幸せになれます!!

まとめ

AWS Batchを使うことでスケーラブルでマネージ不要なHPC環境を低コストに実現できる!
AWS BatchとCloudWatch Eventを使うことでジョブを任意の日時に自動実行させることができ、マネージ不要のタスクスケジューラとして使える!
ここまで読んでくださりありがとうございました!
記事は以上になります。
このたびはご覧戴き、ありがとうございました。

・引用元および参考にさせていただいたサイト様
【公式】AWS Batchの開始方法
【公式】Docker
【公式】Docker hub
(初心者向け)Dockerの使い方
AWS BatchでPythonスクリプトを定期的に実行する
(CloudWatch Eventのトリガー使用方法およびLambdaのキックスクリプトをそのまま使用させていただいております)
 

投稿者プロフィール

tanaka
OS非依存のスクリプト言語によるバッチ処理が得意です。C言語やJavaなどのソフトウェア開発の経験もあります。幅広い視野でプログラミングすることを第一に総合的なエンジニアを目指すため、ネットワーク管理や監視を通して勉強をし、Linuxの知識を高めていきたいです。

コメントを残す

メールアドレスが公開されることはありません。

Time limit is exhausted. Please reload CAPTCHA.

ABOUTこの記事をかいた人

OS非依存のスクリプト言語によるバッチ処理が得意です。C言語やJavaなどのソフトウェア開発の経験もあります。幅広い視野でプログラミングすることを第一に総合的なエンジニアを目指すため、ネットワーク管理や監視を通して勉強をし、Linuxの知識を高めていきたいです。