AWS Batchとは

AWS re:Invent 2016 で発表されたサービスで、2017/06より東京リージョンがリリースされました。

どんなサービスなのか簡潔に言うと、
ECSを利用して、処理が実行している間だけ自動でEC2インスタンスを起動し、終わったら自動でterminateしてくれるサービスです。

今回、試しに簡単な処理を実行するものを作ってみたので、その手順を書きたいと思います。

AWS Lambdaとの違い

処理を実行する時だけ起動するという点では、よく似ているサービスにAWS Lambdaがあります。

関連: Serverless FrameworkでAWS Lamda関数を作成する

しかし、似ているようで、全く別のサービスとなっています。
以下が、Lambdaとの相違点です。

  • Lambdaは最長5分しか実行できないが、AWS Batchは実行時間に上限はない
  • LambdaはFaaSなので開発言語が限定されているが、Batchは環境構築を自分で行うので自由
  • Batch単体で定期実行はできないので、別途定期的にキックするトリガーを用意する必要がある

まず、サーバーの環境構築は自分で行う必要があります。
具体的には、あらかじめDockerイメージを作成しておいて、
処理起動時に、AWS Batchが指定されたDockerレポジトリからイメージを取得してコンテナを立ち上げるという流れになります。

自分で環境構築するため、Lambdaのように開発言語を限定されないので、自由度が高いです。

また、最長実行時間に上限がないため、時間のかかる処理に向いています。

AWS Batchのオートスケーリング

オートスケーリングは、AWSがよしなにやってくれます。
自分で制御することも可能です。
その他、以下のような特徴があります。

  • クラスタ数の上限を自分で設定することも可能
    • インスタンス数ではなくvCPU数で指定する
  • 使用されるEC2インスタンスタイプを指定することもAWSにおまかせすることもできる
    • t2系などは選択できない
    • m4 familyのような柔軟な設定も可能(その中からAWSが最適なものを選ぶ)
    • optimal(おまかせ)を選択すると最新の C、M、および R インスタンスファミリーから最適なものが選択される
  • 立ち上がったインスタンスは、実際にECSやEC2の画面で確認できる

構成図

今回構築した環境の構成図です。

qiita-aws-batch.png

今回は上図の構成で構築してみました。
処理のフローは以下になります。

  1. jobをsubmitし、Batchのqueueに登録(FIFO)
    • スケジューラがqueueのjobの優先順位を把握する
  2. ECSがEC2インスタンスが起動させる
    • 既に起動している場合は、起動しているインスタンスが使用される
    • 既にEC2が起動している場合は、ほとんどタイムラグなくjobが実行されるが、起動していない場合は、job実行開始が、インスタンスが起動する時間分タイムラグとなる
    • 立ち上がったインスタンスは、ECSやEC2の画面で確認できる
  3. AWS ECRに登録されているコンテナイメージを使って、EC2内にコンテナを立ち上げる
  4. コンテナ内で処理を実行する
  5. 処理が終わったら、EC2インスタンスを削除する
  6. 後続のjobがある場合は、削除されずにそのまま使用される

トリガーについて

まず、AWS Batchは単体で定期実行することができないので、別途トリガーを用意する必要があります。
方法は色々ありますが、AWSのサービスを利用するのであれば、トリガーを投げるLambdaをCloudwatchで定期実行させるのが簡単かと思います。

job

AWS Batchは、キックされるとjobにキューを溜めます。
基本的にFIFO(First In, First Out)ですが、他のjobとの依存関係や優先度を指定することも可能です。

ECSで処理実行

jobが実行されると、ECSが、EC2クラスタインスタンスを立ち上げます。

次に、ECSがDockerレポジトリ(今回はECR)からDockerイメージを取得して、
EC2クラスタインスタンス上にDockerコンテナを立ち上げます。

このコンテナ上で、処理が実行されます。
処理の重さに応じてオートスケーリングが行われます。

図の例では、データソースから取得したデータ加工して、HDFSに書き出すという処理が行われています。

処理が終了したら、EC2クラスタインスタンスが削除されます。

環境構築手順

AWS ECRにDockerイメージの登録

まずは、以下の記事を参考に、Dockerイメージを作成して、AWS ECRに登録してください。
AWS ECRにdockerイメージを登録する

AWS Batchの環境構築

では、AWS Batch環境構築をします。

Cloud Formationを利用して環境構築をするので、
まず、awscliなどをインストールした、Cloud Formationを実行できる環境を用意してください。
Cloud Formationの環境構築手順については割愛します。

Cloud Formation実行

yamlを作成

以下のyamlを作成します。
適宜、ご自分の環境にあった設定に読み替えてください。

aws-batch.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Build AWS Batch environment

Parameters:
  SubnetIds:
    Description: Subnets For ComputeEnvironment
    Type: List<AWS::EC2::Subnet::Id>
    Default: subnet-xxxxxxxx,subnet-yyyyyyyy
  SecurityGroupIds:
    Description: SecurityGroups For ComputeEnvironment
    Type: List<AWS::EC2::SecurityGroup::Id>
    Default: sg-zzzzzzzz
  ContainerImage:
    Description: Uri of container image to use in ECS
    Type: String
    Default: xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/sample-aws-batch-repo
  # あらかじめ、key pairを作成しておいてください
  KeyPair:
    Description: key pair
    Type: String
    Default: sample_key

Resources:
  ####### IAM #######
  # ECSインスタンスを実行するrole
  ecsInstanceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - ec2.amazonaws.com
          Action:
          - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role
        - arn:aws:iam::aws:policy/AmazonEC2FullAccess
        - arn:aws:iam::aws:policy/AmazonECS_FullAccess
        - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
        - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
      Path: "/"
  # Set InstanceProfile
  ecsInstanceProfile:
    Type: "AWS::IAM::InstanceProfile"
    Properties:
      Roles:
        - !Ref ecsInstanceRole

  # AWS Batchを実行するrole
  AWSBatchServiceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - batch.amazonaws.com
            - ecs-tasks.amazonaws.com
          Action:
          - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole
      Path: "/service-role/"

  ####### ComputeEnvironment #######
  SampleComputeEnv:
    Type: "AWS::Batch::ComputeEnvironment"
    Properties:
      Type: MANAGED
      # 作成したIAMから取得
      ServiceRole: !GetAtt AWSBatchServiceRole.Arn
      ComputeEnvironmentName: sample-batch-compute-env
      ComputeResources:
        Ec2KeyPair: !Ref KeyPair
        # 最大vcpu数、最大インスタンス数となります
        MaxvCpus: 256
        # 最小vcpu 0にすると自動でインスタンスをterminateしてくれる
        MinvCpus: 0
        # 起動するインスタンスの希望するvCPUの数
        DesiredvCpus: 1
        SecurityGroupIds: !Ref SecurityGroupIds
        Type: EC2
        Subnets: !Ref SubnetIds
        # 作成したVPCから取得
        InstanceRole: !GetAtt ecsInstanceProfile.Arn
        InstanceTypes:
          # 使用するインスタンスタイプ、optimalならおまかせ
          - optimal
        Tags: {"Name": "Batch Instance - sample"}
      State: ENABLED

  SampleJobQueue:
    Type: AWS::Batch::JobQueue
    Properties:
      ComputeEnvironmentOrder:
        - Order: 1
          ComputeEnvironment: !Ref SampleComputeEnv
      State: ENABLED
      # jobの優先順位
      Priority: 1
      JobQueueName: sample-batch-queue

  SampleJobDefinition:
    Type: AWS::Batch::JobDefinition
    Properties:
      Type: container
      JobDefinitionName: sample-batch-definition
      ContainerProperties:
        Command:
          - sh
          - /usr/local/init.sh
        # memoryの上限
        Memory: 4048
        Vcpus: 2
        Image: Image: !Ref ContainerImage
      RetryStrategy:
        Attempts: 1

syntax check

yamlのsyntax checkをします。

$ aws cloudformation validate-template --template-body file://aws-batch.yml

実行

実行して、環境を作成します。

$ aws cloudformation create-stack \
      --stack-name sample-batch \
      --template-body file://aws-batch.yml \
      --capabilities CAPABILITY_IAM \

確認

作成されたことを確認します。

$ aws cloudformation describe-stacks --stack-name sample-batch

"StackStatus": "CREATE_COMPLETE", になっていることを確認してください。

削除

削除する場合は以下のコマンドを実行してください。

$ aws cloudformation delete-stack --stack-name sample-batch

jobを実行する

今回は、awscliを手動で実行してトリガーとします。

まず、以下のコマンドで、jobの設定ファイルとなるjsonのテンプレートを生成します。

$ aws batch submit-job --generate-cli-skeleton > submit-job.json

生成されたjsonファイルを、以下のように編集します。
パラメータや、環境変数を設定することができます。

submit.json
{
    "jobName": "sample-batch-job",
    "jobQueue": "sample-batch-queue",
    "jobDefinition": "sample-batch-definition",
    "parameters": {
        "KeyName": "string"
    },
    "containerOverrides": {
        "environment": [
            {
                "name": "access_token",
                "value": "abcdef1234"
            }
        ]
    }
}
項目 設定値
jobName job名
jobQueue 作成したJobQueueを指定
jobDefinition 作成したJobDefinitionを指定
parameters パラメータ
environment 環境変数

以下のコマンドでjobを実行します。

$ aws batch submit-job --cli-input-json file://submit-job.json

response

{
    "jobId": "a94985d8-52d6-4ea0-95ac-2ccb0dcabc25",
    "jobName": "sample-batch-job"
}

job実行

jobが実行されると、以下の順でjobが移動していきます。
submitted -> runnable -> starting -> running -> succeeded

Screen Shot 2017-12-03 at 17.50.53.png

また、runnableのタブに入ったまま、runningへ移動しない場合は、ECSの起動周りで問題が起きている可能性が高いので、EC2インスタンスにsshでログインして、原因を調査してください。

EC2インスタンス確認

EC2インスタンス一覧の画面で、

Batch Instance - sampleという名前でインスタンスが起動していることが確認できます。

Screen Shot 2017-12-03 at 16.28.47.png

実行結果Statusを確認

AWS BatchのJob画面で、実行結果のStatusを確認できます。
以下のように、succeededのタブにjobが移動していれば成功です。
3つ結果がありますが、今回の結果は一番下の選択されているものです。

Screen Shot 2017-12-03 at 18.20.05.png

もし、failedのタブに入れば、失敗しているので、failedタブのJob IDをクリックして、エラーの原因を調査してください。

Cloudwatchで出力結果の確認

出力結果がある場合は、Cloudwatch の /aws/batch/job ロググループで確認することができます。

Screen Shot 2017-12-03 at 18.16.17.png

Batch完了後

自動でインスタンスがterminateされます。

Screen Shot 2017-12-03 at 16.44.07.png

まとめ

一通り触ってみて、バッチ処理を実行することはできました。
jobが登録されてからEC2インスタンスが立ち上がるので、リアルタイムで実行する処理には向いていませんが、非同期で時間のかかる処理を実行するのには向いてそうです。

別途、オートスケーリングの検証などもしてみたいと思います。

以上

参考