AWS CLI で AWS Batch にジョブを送信する (第３回)

AWS CLI を使って AWS Batch を実行するまでを解説します。
このドキュメントを読むためには AWS EC2、AWS S3 および docker に対する基本的な知識が必要です。
なお、ジョブのスケジュール設定については解説しません。

## 目次

全３回です。

1. [はじめに](https://qiita.com/aokad/items/f28ffa5ae984678817f6)
2. [パラメータを使ってジョブを送信する](https://qiita.com/aokad/items/e08a9355e76a31711149)
3. 環境変数を使ってジョブを送信する            ← イマココ

AWS CLI 公式リファレンスはこちらです。[aws batch](https://docs.aws.amazon.com/cli/latest/reference/batch/index.html)

## パラメータ実行による問題

前回までに AWS Batch にパラメータを使用してジョブを送信しました。
ジョブ定義で単純なコマンドを設定していれば問題ありませんが、少し複雑なコマンドを記述しようとすると問題があります。

一言でいうとパラメータの前後にテキストが入ると置き換えられない、という問題です。

実例を挙げて説明します。

**問題ないケース**

パラメータが置き換えられて実行されます。

```Bash
# ジョブ定義
"command": ["ash", "run.sh", "Ref::Input", "Ref::Output"],

# ジョブ送信
aws batch submit-job --parameters Input="s3://input.txt",Output="s3://output.txt" ...

# docker コンテナ起動時(※)
docker run ${IMAGE} ash run.sh s3://input.txt s3://output.txt
```

※ docker コンテナ起動は AWS Batch 側で行いますので、ユーザがコマンド実行するわけではありません。

**問題あるケース**

ジョブ実行に前処理が発生し、コマンドが2つ以上になるような時です。

```Bash
# ジョブ定義
"command": ["ash", "-c", "aws s3 cp s3://xxx /xxx; ash run.sh Ref::Input Ref::Output"],

# ジョブ送信
aws batch submit-job --parameters Input="s3://input.txt",Output="s3://output.txt" ...

# docker コンテナ起動時
# ！！！パラメータが置き換えられていない！！！
docker run ${IMAGE} ash -c "aws s3 cp s3://xxx /xxx; ash run.sh Ref::Input Ref::Output"
[ERROR] Ref::Input: No such file or directory
```

**対策**

パラメータではなく環境変数を使用します

```Bash
# ジョブ定義
"command": ["ash", "-c", "aws s3 cp s3://xxx /xxx; ash run.sh ${INPUT} ${OUTPUT}"],

# ジョブ送信
aws batch submit-job --container-overrides "environment=[{name=INPUT,value=s3://input.txt}, {name=OUTPUT,value=s3://output.txt}]" ...

# docker コンテナ起動時
docker run --env INPUT=s3://input.txt --env OUTPUT=s3://output.txt ${IMAGE} ash -c "aws s3 cp s3://xxx /xxx; ash run.sh $Input $Output"
```

次項よりパラメータの代わりに環境変数を使ってジョブを送信する方法を記載します。

## 1~5. の手順

以下 1.~5. は[前回](https://qiita.com/aokad/items/e08a9355e76a31711149)と同じため記載省略します。
「5. ジョブキューの作成」まで終えてから「6. ジョブ定義」に進んでください。

 1. AWS CLI の準備
 2. docker イメージを作成する（オプション）
 3. AWS 情報を環境変数にセット
 4. コンピュート環境を作成
 5. ジョブキューの作成

## 6. ジョブ定義

パラメータではなく環境変数を作成します。
environment:name INPUT, OUTPUT が今回の環境変数です。ジョブ送信の時に設定しますので、ここでは空です。

```Bash
ECSTASKROLE="arn:aws:iam::${AWS_ACCOUNTID}:role/AmazonECSTaskS3FullAccess"

cat << EOF > job-definition.spec.2.json
{
  "image": "aokad/aws-wordcount:0.0.1",
  "command": ["ash", "-c", "ash run.sh \${INPUT} \${OUTPUT}"],
  "vcpus": 1,
  "memory": 500,
  "environment": [
    {
      "name": "INPUT",
      "value": ""
    },
    {
      "name": "OUTPUT",
      "value": ""
    }
  ],
  "jobRoleArn": "${ECSTASKROLE}"
}
EOF

aws batch register-job-definition \
  --job-definition-name mybatch-job-def2 \
  --type container \
  --container-properties file://job-definition.spec.2.json \
> register-job-definition.log

JOB_DEFINITION_ARN=$(jq -r '.jobDefinitionArn' register-job-definition.log)
```

## 7. ジョブの送信

ジョブを送信する前に、入出力用の S3 バケットを作成し、適当なデータを置いておきます。
前回準備していれば不要です。

```Bash
cat << EOF > Humpty.txt
Humpty Dumpty sat on a wall,
Humpty Dumpty had a great fall.
All the king's horses and all the king's men
Couldn't put Humpty together again.
EOF

aws s3 cp Humpty.txt s3://${S3_BUCKET}/
```

環境変数に値をセットしてジョブを送信します。

```Bash
aws batch submit-job \
    --job-name "mybatch-job-try2" \
    --job-queue "${JOB_QUEUE_ARN}" \
    --job-definition "${JOB_DEFINITION_ARN}" \
    --container-overrides "environment=[{name=INPUT,value=s3://${S3_BUCKET}/Humpty.txt},{name=OUTPUT,value=s3://${S3_BUCKET}/Humpty.count.env.txt}]"
```

「AWSコンソール」→「Batch」→「ジョブ」に新しく作成されています。
新しくジョブを送信したい場合は、この「7. ジョブの送信」だけを繰り返せばよいです。

EC2 インスタンスが終了し、s3://${S3_BUCKET}/Humpty.count.env.txt が出力されていれば成功です。
なお、スクリプトの実行ログは「AWSコンソール」→「ClowdWatch」→「ログ」→ロググループ「/aws/batch/job」から確認できます。

## 8. 片付け

[前回](https://qiita.com/aokad/items/e08a9355e76a31711149#8-%E7%89%87%E4%BB%98%E3%81%91)と同じため省略

## まとめ

※ 2018 年 3 月時点の情報です。最新版では更新されている可能性があります。

その１）
お気づきの方もいるかもしれませんが、コンピュート定義にディスクの指定がありません。
ジョブを実行する EC2 インスタンスに対しディスク容量を増やすことは独自 AMI 等を作成すれば不可能ではありませんが cpu や memory 程気軽には調整できませんでした。
そのため、大きなデータを扱うことが困難ですが、全体で 9.8Gbyte （docker コンテナデフォルトサイズ）に収まるデータなら扱えますので、用途さえマッチすれば有用だと思います。

その２）
AWS Batch はキューがあり、優先度を持てるところが特徴です。
そのため、1つのコンピュート環境で複数種類のジョブを優先度の高い順に処理したい場合は有効でしょう。しかし、パイプラインのように優先順位なく、上流から順番に逐次実行するようなケースでは使いどころが難しいかもしれません。

その３）
本文中では触れませんでしたが、AWS Batch は Amazon ECS のクラスターとタスク定義を利用して作られています。
[Amazon ECS によるタスク実行の記事はこちらです。](https://qiita.com/aokad/items/b3762e3cc10db6104858)

またまだ発展途上の感がありますので、今後機能が大きく改善されるかもしれません。
AWS Batch はひとまず以上です。