Help us understand the problem. What is going on with this article?

Fargateでバッチサーバを作る

Fargateについて

FargateはAWSのサービスで、Dockerコンテナを実行する基盤の一つです。
以下、Fargateの利点とECSとの違いを説明しています。

https://aws.amazon.com/jp/fargate/
クラスター管理が不要

まず、ECSとの一番の違いはクラスターを事前に用意しなくていいところです。
ECSではEC2インスタンスを用意して、その上でTaskやServiceを設定していました。
FargateではTaskやServiceを作るだけでコンテナを動かすことができます。

https://aws.amazon.com/jp/about-aws/whats-new/2018/08/aws-fargate-now-supports-time-and-event-based-task-scheduling/
AWS Fargate が時間とイベントベースのタスクスケジューリングをサポート

https://aws.amazon.com/jp/fargate/pricing/
AWS Fargate の料金は、コンテナイメージのダウンロード (docker pull) を開始した時点から Amazon ECS タスク* が終了するまでに使用された vCPU およびメモリリソースに基づいて計算され、最も近い秒数に切り上げられます。1 分の最低料金が適用されます。

さらに最近、時間やイベントをトリガーに動かすことができるようになりました。
これを使えば、バッチ実行時のみ課金されるので利用料を節約できます。

例えばECSだと、バッチ利用時にEC2インスタンスを起動し、利用後に停止するという手順が必要でした。
しかし、FargateではEC2インスタンスを管理する必要はなく、Taskが動いている時間だけ課金されます。

気をつける点として、Fargateでは使用するvCPUによって使えるメモリ量が制限されます。
例えば、メモリを大量に使用する処理の場合、最大で30GBのメモリを使うには4vCPUを選択する必要があります。
費用対効果を考えてEC2やECSを使い分ける必要はあると思います。

実装

それでは実装に入って行きます。
今回はCloudFormationのみでFargateを作っていきます。

ポイント

  • バッチ名やバッチ実行に必要なコマンド、cronの設定はParametersに記述しています。
  • 今回実行するバッチ(Dockerコンテナ)はCFnで環境を構築後にECRにPushしてください。
    • Dockerhubから直接取ってくる場合は、ECRの部分を削除後ECSTaskDefinitionimageを書き換えてください。
  • FargateにはvCPUは1024、memoryは2048を設定しています。
    •  コンテナにはMemoryReservationで1024を設定しています。
  • バッチを実行するだけなのでAssignPublicIpはDISABLEDにしています。
  • セキュリティグループは事前に作成し、SecurityGroupsに設定します。
    • CFnで作っている場合は、importValueなどを使って設定してください。
  • Privateサブネットも事前に作成し、Subnetsに設定します。
    • AssignPublicIpはDISABLEDなので、サブネットもPrivateなものを使います。
  • DesiredCountは0に設定しています。
    • 実はここが味噌で、サービスは作るけど普段は動かさないので、0に設定します。
  • TaskCountは1に設定しています。
    • サービスは動かしませんが、定期実行用のTaskは動かしたいので、1に設定します。
fargate.yml
---
AWSTemplateFormatVersion: '2010-09-09'
Description: 'fargate scheduled task for batch execution'
Parameters:
  BatchName:
    Type: String
    Description: A name of batch
    Default: 'fargate-batch'
  EntryPoint:
    Type: String
    Description: A command execute at start
    Default: '/bin/sh'
  ScheduleExpression:
    Type: String
    Description: Cron settings
    Default: 'cron(0 1 * * ? *)'
Resources:
  ECR:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: !Sub "${BatchName}"

  CloudwatchLogsGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "${BatchName}/app"

  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Sub "${BatchName}"

  TaskRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ecs-tasks.amazonaws.com
            Action:
                - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
        - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
      Path: /
      RoleName: { "Fn::Join" : ["-", [ !Sub "${BatchName}-ecs-task", { "Ref" : "AWS::Region" }]] }

  ECSTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties: 
      Cpu: 1024
      Memory: 2048
      Family: !Sub "${BatchName}"
      RequiresCompatibilities:
        - FARGATE
      ExecutionRoleArn: !Sub "arn:aws:iam::${AWS::AccountId}:role/ecsTaskExecutionRole"
      TaskRoleArn: !Ref TaskRole
      NetworkMode: awsvpc
      ContainerDefinitions:
        - Name: !Sub "${BatchName}"
          Image: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${BatchName}"
          MemoryReservation: 1024
          EntryPoint:
            - !Sub "${EntryPoint}"
          LogConfiguration:
            LogDriver: 'awslogs'
            Options:
              awslogs-group: !Sub "${BatchName}/app"
              awslogs-region: !Sub "${AWS::Region}"
              awslogs-stream-prefix: !Sub "${BatchName}"

  ECSService:
    Type: AWS::ECS::Service
    Properties:
      Cluster: !Ref BatchName
      LaunchType: "FARGATE"
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: DISABLED
          SecurityGroups:
            - <<SecurityGroups>>
          Subnets:
            - <<Subnets>>
      DesiredCount: 0
      TaskDefinition: !Ref ECSTaskDefinition

  TaskScheduleEvents:
    Type: AWS::Events::Rule
    Properties:
      Name: cron-event
      ScheduleExpression: !Ref ScheduleExpression
      State: ENABLED
      Targets:
        - Id: 1
          RoleArn: !GetAtt TaskRole.Arn
          EcsParameters:
            TaskDefinitionArn: !Ref ECSTaskDefinition
            TaskCount: 1
          Arn: !GetAtt ECSCluster.Arn

所感

Scheduled taskを使ってFargateを動かすとdocker compose runと同じように、
1回きりの実行(one-off)になり、バッチには最適な実行環境だなと感じました。
EC2インスタンスを使用しないので、手間が省けていいです。

http://docs.docker.jp/compose/reference/run.html

ただし、Fargateに限らずECSもそうなんですが、エラーの確認が大変です。
EC2インスタンスがあれば、sshしてログやリソース使用量を確認できるのですが、Fargateでは難しそうです。

簡単にお安くバッチ環境がほしいという方には最適かなと思います。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away