AWS Batchの環境をCloudFormationを使って自動構築し簡単なアプリケーションを実行するまで

## 本記事でやること

- AWS Batchのコンピューティング環境やジョブ定義などをCloudFormationを使って構築する
- AWS Batchにジョブを送り、S3のバケット名を取得するアプリケーションを実行する
 
上記を達成するために予め行うこと

- AWS ECRを使ってアプリケーションのDocker imageをpushする

以下に関しては、記載しないので他の記事を参照してください。

- AWS ECSを使用するにあたって必要なVPCやSubnet、セキュリティーグループの作成
- AWS BatchやECSにアクセスするためのIAMロールの作成

## 対象読者

- Dockerについて一通り基礎的な書籍を読んで、何か動くものを作りたい方
- AWS BatchやCloudFormationを使って何かしたい方


## 使用言語

python 3.6.3


## AWS ECRを使ってアプリケーションのDocker imageをpushする

AWS ECSがコンテナを作成する際にpullするDocker imageを予めAWS ECRにpushする。
また、アプリケーションのファイルやディレクトリ構成は以下の通り。

```tree:ディレクトリ構成
.
├── infra
│   ├── Dockerfile
│   ├── .vault_password
│   ├── .aws
│   │    └── config
│   │    └── credentials
│   └── batch_config
│       └── cloudformation.yml
└── run.py
```

```Dockerfile:Dockerfile
FROM hogehoge.ecr.ap-northeast-1.amazonaws.com/python:latest

# userの作成
RUN groupadd -g 10000 test && \
    useradd  -u 10000 -g test -s /bin/bash test && \
    echo 'test:test' | chpasswd

ENV HOME /home/test
WORKDIR $HOME
RUN chown test:test -hR ./

USER test

ADD --chown=test:test infra/ $HOME

# 暗号化されているaws認証情報を復元する
RUN ansible-vault decrypt .aws/credentials --vault-password-file .vault_password

ADD --chown=test:test . test
USER root
WORKDIR /home/test/test

CMD ["python", "run.py"]

```

上記、Dockerfileのベースとなっているimageに関しては、[こちらの記事](https://qiita.com/kurakura0916/items/d3b1ab82f308be023ded)で紹介しているので適宜参照してください。（ベースとなっているimageは、pythonイメージにansibleをインストールしたimageです）

Dockerfile内の`CMD`箇所で実行しているアプリケーションのファイルの中身は以下のようになっています。（S3のバケット名を取得するスクリプトになっています）

```python:run.py
from boto3.session import Session

def get_bucket_list():
    resource = Session(profile_name="local").resource("s3")

    for bucket in resource.buckets.all():
        print(bucket.name)

if __name__ == '__main__':
    get_bucket_list()
```


上記Dockerfileを`infra/`以下に置き、下記のコマンドでAWS ECRにプッシュする。


```bash:bash
# ECRにログインする
$(aws ecr get-login --no-include-email --region ap-northeast-1 --profile hoge)

# イメージのビルド（イメージの名前は適当に"python"とする）
docker build -t test-app .

# 上でビルドしたイメージにタグをつける(hogehogeの部分には)
docker tag test-app:latest hogehoge.dkr.ecr.ap-northeast-1.amazonaws.com/test-app:latest

# イメージをAWS ECRにプッシュする
docker push hogehoge.dkr.ecr.ap-northeast-1.amazonaws.com/test-app:latest
```

## AWS Batchの環境をAWS CloudFormationを使って構築する

以下の`cloudformation.yml`ファイルを使ってAWS Batchの環境を構築する。`set parameters`の箇所でのsubnet-idやEC2インスタンスのkeyペア、ECS / BatchのIAMロールのARNは各自用意をしコピペする。

```cloudformation.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Build AWS Batch environment

# =======set parameters======== #
Parameters:
  SubnetIds:
    Description: Subnets For ComputeEnvironment
    Type: List<AWS::EC2::Subnet::Id>
    Default: subnet-id
  SecurityGroupIds:
    Description: SecurityGroups For ComputeEnvironment
    Type: List<AWS::EC2::SecurityGroup::Id>
    Default: sg-id
  KeyPair:
    Description: key pair
    Type: String
    Default: key
  image:
    Type: String
    Description: image in container registory
    Default: hogehoge.ecr.ap-northeast-1.amazonaws.com/awsbatch/test-app:latest
  ecsInstanceRole:
    Type: String
    Description: Role For AWS EcsInstanceRole
    Default: arn:aws:iam::hogehoge:instance-profile/ecsInstanceRole
  AWSBatchServiceRole:
    Type: String
    Description: Role For AWS BatchRole
    Default: arn:aws:iam::hogehoge:role/service-role/AWSBatchServiceRole

# =======Batch======== #
# create ComputeEnvironment
Resources:
  MyComputeEnv:
    Type: "AWS::Batch::ComputeEnvironment"
    Properties:
      Type: MANAGED
      ServiceRole: !Ref AWSBatchServiceRole
      ComputeEnvironmentName: "test-batch-compute-environment"
      ComputeResources:
        MaxvCpus: 256
        MinvCpus: 0
        DesiredvCpus: 0
        SecurityGroupIds: !Ref SecurityGroupIds
        Type: EC2
        Subnets: !Ref SubnetIds
        Ec2KeyPair: !Ref KeyPair
        InstanceRole: !Ref ecsInstanceRole
        InstanceTypes:
          - optimal
        Tags:
          Name: "test-batch-computer"
      State: ENABLED

  # Create JobQueue
  MyJobQueue:
    Type: AWS::Batch::JobQueue
    Properties:
      ComputeEnvironmentOrder:
        - Order: 1
          ComputeEnvironment: !Ref MyComputeEnv
      State: ENABLED
      Priority: 1
      JobQueueName: "s3_get_bucket_list"

  # Create JobDefinition
  MyJobDefinition:
    Type: AWS::Batch::JobDefinition
    Properties:
      Type: container
      JobDefinitionName: "test-batch-job"
      ContainerProperties:
        Memory: 16000
        Vcpus: 1
        Image: !Ref image
      RetryStrategy:
        Attempts: 1
```

aws cliを使って以下のコマンドでAWS Batchの環境を自動構築する。

```bash:bash
aws cloudformation create-stack \
--stack-name aws-batch \
--template-body file://$PWD/infra/batch_config/cloudformation.yml \
--profile hoge
```

## AWS Batchにジョブを送り、S3のバケット名を取得するアプリケーションを実行する

上記のコマンドでAWS Batchの環境が構築されたことを確認できたら、以下のコマンドでジョブを送信する。

```bash:bash

JOB_DEFINITION_ARN=$( aws batch describe-job-definitions \
  --job-definition-name "s3_get_bucket_list" \
  --status ACTIVE \
  --profile hoge \
  | jq -r '.jobDefinitions | max_by(.revision).jobDefinitionArn' \
) && echo ${JOB_DEFINITION_ARN} 

aws batch submit-job \
--job-name "test-job" \
--job-queue "s3_get_bucket_list" \
--job-definition ${JOB_DEFINITION_ARN}  \
--profile hoge
```

ジョブのキューがSUCCEEDEDになったら、AWS CloudWatch LogsにS3のバケット名が出力されていることが確認できたら無事完了です。

また、無事完了できたら今回CloudFormationで作成した諸々の環境は、削除しておきましょう。
削除するためには以下のコマンド実行します。

```bash:bash
aws cloudformation delete-stack --stack-name aws-batch --profile hoge
```

## 終わりに

[前回の記事](https://qiita.com/kurakura0916/items/d3b1ab82f308be023ded)では、ローカル環境でDockerコンテナを作成・起動をしAWS S3のバケット名を取得しましたが、今回はAWS Batchを使ってECSでコンテナを作成・起動を行いました。
また今回はターミナルでコマンドを叩いてAWS Batchにジョブを送信しましたが、AWS lambdaを使ってジョブを送信することも可能です。またCloudwatch eventsでlambdaを定期的に起動することが出来ればBatchも定期実行することができます。次回は、AWS lambdaでBatchにジョブを送るlambda functionに関する記事を作成出来ればと思います。