AWS BatchでPythonスクリプトを定期的に実行する

はじめに
---
「DBデータをCSVにしてS3に置いてほしい。毎日ね。」と言われ「今時、EC2でやるのはちょっと。」と思い`AWS Batch`でやってみたので書きます。

前提条件
---
今回は空のCSVファイルをS3バケットに置くだけの簡単なお仕事を行なうPythonスクリプトを例に実施します。

AWS Batchとは
---
バッチコンピューティングジョブを実行するためのマネージドサービスです。
料金はEC2・`AWS Lambda`等の使った分だけ！

こんな時に向いてる
---
 * 定期的に実行したい
 * `AWS Lambda`だとに心細い([参考](http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/limits.html))
 * サーバの面倒を見たくない
 * 安くいきたい(使った分だけ課金)

用語説明
---
 * ジョブ定義
     * どのイメージを使用するか、CPU・メモリはどれくらい使用するか、環境変数などを定義する。
 * コンピューティング環境
     * ジョブを実行するための環境。特定のインスタンスタイプや特定のモデル (c4.2xlarge や m4.10xlarge など) を使うように設定できたりする。
 * ジョブキュー
     * ジョブがコンピューティング環境にスケジュールされるまでの待ち行列を保持するもの。
 * ジョブ
     * ジョブ定義で指定したパラメーターを使用して、コンピューティング環境でコンテナ化されたアプリケーションとして Amazon EC2 インスタンスで実行されるもの。

ソースコード書く
---
書いた。今回使用するものは[こちら](https://github.com/yacchin/put_csv_to_s3)。 


ECR(EC2 Container Registory)にイメージをプッシュ
---
今回はECRを使ってみる。

 * マネジメントコンソールでECSを開く
 * リポジトリ名決める

![スクリーンショット 2017-12-10 22.53.11_censored.jpg](https://qiita-image-store.s3.amazonaws.com/0/27525/ec92079d-4837-3ff9-913d-4031439a636a.jpeg)

 * リポジトリにイメージをPushする

![スクリーンショット 2017-12-10 22.56.19_censored.jpg](https://qiita-image-store.s3.amazonaws.com/0/27525/6471aa43-7f58-3f21-20e5-343536ff6c90.jpeg)


Job Definitionの作成
---
 * `Job definition name`は任意の名前を入れます。
 * `Job Role`は`aws_batch_role`を選択します。
    * aws_batch_roleは下記ポリシーを保持
        * `AmazonEC2ContainerRegistryFullAccess`
        * `AmazonEC2ContainerServiceFullAccess`
 * `Container Image`は先程作成した`リポジトリのURI`を指定します。

![スクリーンショット 2017-12-10 22.59.21_censored.jpg](https://qiita-image-store.s3.amazonaws.com/0/27525/4429ca29-b81a-e6ff-054b-ef025f2340d9.jpeg)


 * Environment
     * `Command`はDockerfileでCMDを指定しているので入力しません。
     * `vCPUs`は`m3.medium`なので`1`を指定します。
     * `Memory(MiB)`は`m3.medium`なので`3072`を指定します。(m3.mediumが3.75GBなので超えないように指定しています。)
     * `Job Attempts`は1度だけRunnableに移行すれば良いので`1`を指定します。
     * `uLimits`は特に指定しません。
     * `Parameter`は特に指定しません。

<img width="1191" alt="スクリーンショット 2017-12-10 19.08.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/27525/85ebcbb5-e323-cc6b-d253-bc3c4453fb9b.png">



 * Environment variables
     * 下記を設定する。
         * `AWS_ACCESS_KEY_ID`
         * `AWS_SECRET_ACCESS_KEY`
         * `AWS_S3_BUCKET_NAME`

 * Security
     * 今回は特に指定しません。
 
 * Volumes
     * 今回は特に指定しません。

 * Mount points
     * 今回は特に指定しません。

Compute Environmentの作成
---
 * マネジメントコンソールでAWS Batchを開きます。
 * Create a compute environment
    * `Compute environment type`で`Managed`を選択します。
    * `Compute environment name`で今回は`execute_python_script_env`を入力します。
    * `Service Role`で`AWSBatchServiceRole`を選択します。
    * `Instance Role`で`ecsInstanceRole`を選択します。
    * `EC2 Key Pair`で任意のKeyを選択します。

![スクリーンショット 2017-12-10 17.10.49_censored.jpg](https://qiita-image-store.s3.amazonaws.com/0/27525/a1383277-a058-71e8-78c5-0e137dda8516.jpeg)



 * Configure your compute resources
    * `Provisioning Model`で今回は`On demand`を選択します。
    * `Allow instance types`で今回は大した処理を行わないかつ安くいきたいので`m3.medium`を選択します。(`optimal`がデフォルトで選択されていますが`c4.large`といったインスタンスが選択されるので状況に応じての選択が重要となります。)
    * `Minimum vCPUs(環境で維持する必要がある EC2 vCPU の最小数。)`は`1`を選択します。
    * `Desired vCPUs(コンピューティング環境で必要なEC2 vCPUSの数)`は`1`を選択します。
    * `Maximum vCPUs(環境で到達できる EC2 vCPU の最大数)`は`1`とします。
    * `Enable user-specified AMI ID`にチェックは入れません。

<img width="1162" alt="スクリーンショット 2017-12-10 17.10.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/27525/456e6aa8-2ee1-4d7a-9628-b022260f1eb7.png">


 * `Networking`
    * `VCP ID`では使用するものを選択します。
    * `Subnets`では使用するものにチェックをつけます。
    * `Security groups`は任意のものを選択します。

![スクリーンショット 2017-12-10 17.11.11_censored.jpg](https://qiita-image-store.s3.amazonaws.com/0/27525/b974fd6e-00e4-5a1b-fa75-f30af5efd07c.jpeg)


 * `EC2 Tags`
    * `EC2 Tag`はお好みで。

Job Queue作成
---
 * `Queue name`は今回`put-csv-to-s3`を入力する。
 * `Priority`は`1`を入力する。
 * `Enable Job queue`はチェックを入れる。
 * `Select a compute environment`は先程作成した`execute_python_script_env`を選択します。

![スクリーンショット 2017-12-10 22.59.21_censored.jpg](https://qiita-image-store.s3.amazonaws.com/0/27525/028d6dc4-a3be-c7bd-ff33-65c086fc839d.jpeg)


Jobの実行
---
ここまで来たらジョブを実行していきます。まず、Jobsで`submit job`を選択します。

 * Job run-time
    * `job name`は`test-put-csv-to-s3`を入力します。
    * `Job definition`は先程作成した`put-csv-to-s3:1`を選択します。
    * `Job queue`は先程作成した`put-csv-to-s3`を選択します。
    * `Job type`は`single`を選択します。
    * `Job depends on`は特に入力しません。

<img width="886" alt="スクリーンショット 2017-12-10 23.08.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/27525/021b64d0-8d67-056d-3f4d-853c40c4d898.png">


 * `Environment`、`Environment variables`は`Job Denitions`で定義したものがセットされているのでそのまま利用します。

submitすると様々な状態にがありますが、詳細は[こちら](http://docs.aws.amazon.com/ja_jp/batch/latest/userguide/job_states.html)を御覧ください。

よっしゃ、成功した。

<img width="1209" alt="スクリーンショット 2017-12-10 21.42.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/27525/70219720-1283-ad15-1375-bb186425d582.png">

S3バケット内に`null.csv`がありました。よさそうです。

![スクリーンショット 2017-12-10 21.41.55_censored.jpg](https://qiita-image-store.s3.amazonaws.com/0/27525/b81ef075-34aa-602f-b2f5-2ecbc6856ebf.jpeg)



lambdaでJobを叩くスクリプト作成
---
下記の関数を作成する。

```python3:lambda_function.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import boto3

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
```

環境変数は下記を指定。

 * `AWS_ACCESS_KEY_ID`
 * `AWS_SECRET_ACCESS_KEY`
 * `AWS_DEFAULT_REGION`(`ap-northeast-1`を指定)


CloudWatch Eventsで実行する時間を指定
---
CloudWatch Eventsのルールより設定を行なう。

 * イベントソース
     * スケジュール
         * Cron式
             * `0 16 * * ? *` (UTCなので日本時間の毎日1時に起動します。)
 * ターゲット
     * `Lambda関数`を指定します。
     * `機能`は先程作成した`submit-job`を選択します。
     * `入力の設定`は下記を入力します。(`xxxxxxxxxxxxx`はアカウントID)
         * `{ "JobName": "put-csv-to-s3", "JobQueue": "put-csv-to-s3", "JobDefinition": "arn:aws:batch:ap-northeast-1:xxxxxxxxxxxxx:job-definition/put-csv-to-s3:1" }`

これで定期的に実行が可能となります。

まとめ
---
`AWS Batch`でPythonスクリプトを定期的に実行することができました。
今後は、CI/CDの部分(イメージ作成・`Job Definition`の更新等)で改善できる余地があるので対応していきたいと思います。

参考文献
---
 * https://aws.amazon.com/jp/batch/
 * http://docs.aws.amazon.com/ja_jp/batch/latest/userguide/what-is-batch.html