AWS Batch + Fargate + EFSをCloudFormationで構築


AWS BatchをFagateで構築したのですが、Fargateの
CloudFormationサンプルが少なかったので参考に投稿します。
ついでにAWS BatchからEFSのマウントも行ってみました。

Fargateを利用する場合、vCPUとMemoryは指定の組み合わせのものだけが利用可能で
vCPUは最大でも4までになります。
リソースに関する定義はEC2とFargateでは結構違うので要注意です。

AWS BatchのEFSマウントは少し苦労したのですが以下の記事を参考にしました。
記事のオペレーションをCFn化しています。
Introducing support for per-job Amazon EFS volumes in AWS Batch

EFSのテンプレート

セキュリティグループのIDですがRef参照の場合
デフォルトVPCに属するかどうかで動きが変わるところがあるので要注意です。
今回は以下のように取得しています。
!GetAtt LambdaSecurityGroup.GroupId

template_efs.yaml
AWSTemplateFormatVersion: '2010-09-09'  

Parameters:
  Env:
    Type: String
  Prifix:
    Type: String

Mappings: 
  EnvMap: 
    dev:
      # EFSを構築するVPCのID
      MyVpcId: "vpc-xxx"
    # EFSを構築するサブネット
      MySubnetID1: "subnet-xxxx"
      MySubnetID2: "subnet-xxxx"
      MySubnetID3: "subnet-xxxx"

Resources:
  LambdaSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !FindInMap [EnvMap, !Ref Env, MyVpcId]
      GroupDescription: Security group for Lambda

  MountTargetSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !FindInMap [EnvMap, !Ref Env, MyVpcId]
      GroupName: !Sub
        - "${prefix}mount-target-security-group"
        - { prefix: !Ref Prifix }
      GroupDescription: Security group for mount target
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: "2049"
          ToPort: "2049"
          CidrIp: 0.0.0.0/0

  # EFS FileSystem
  EfsFileSystem:
    Type: AWS::EFS::FileSystem
    Properties:
      FileSystemTags:
        - Key: "Name"
          Value: "EfsFileSystem For xxxx"
      PerformanceMode: maxIO
      BackupPolicy:
        Status: 'DISABLED'
      Encrypted: False

  EfsMountTarget1:
    Type: AWS::EFS::MountTarget
    Properties:
      FileSystemId: !Ref EfsFileSystem
      SubnetId: !FindInMap [EnvMap, !Ref Env, MySubnetID1]
      SecurityGroups:
      - !GetAtt MountTargetSecurityGroup.GroupId

  EfsMountTarget2:
    Type: AWS::EFS::MountTarget
    Properties:
      FileSystemId: !Ref EfsFileSystem
      SubnetId: !FindInMap [EnvMap, !Ref Env, MySubnetID2]
      SecurityGroups:
      - !GetAtt MountTargetSecurityGroup.GroupId

  EfsMountTarget3:
    Type: AWS::EFS::MountTarget
    Properties:
      FileSystemId: !Ref EfsFileSystem
      SubnetId: !FindInMap [EnvMap, !Ref Env, MySubnetID3]
      SecurityGroups:
      - !GetAtt MountTargetSecurityGroup.GroupId

  # AccessPoint
  EfsAccessPoint:
    Type: 'AWS::EFS::AccessPoint'
    Properties:
      FileSystemId: !Ref EfsFileSystem
      AccessPointTags:
        - Key: "Name"
          Value: "EfsAccessPoint For xxx"
      PosixUser:
        Uid: "1001"
        Gid: "1001"
      RootDirectory:
        CreationInfo:
          OwnerGid: "1001"
          OwnerUid: "1001"
          Permissions: "0777"
        Path: "/mnt/efs"      

Outputs:
  EfsFileSystemId:
    Value: !GetAtt EfsFileSystem.FileSystemId
  EfsAccessPointArn:
    Value: !GetAtt EfsAccessPoint.Arn
  EfsAccessPointId:
    Value: !Ref EfsAccessPoint
  MySubnetID1:
    Value: !FindInMap [EnvMap, !Ref Env, MySubnetID1]
  MySubnetID2:
    Value: !FindInMap [EnvMap, !Ref Env, MySubnetID2]
  MySubnetID3:
    Value: !FindInMap [EnvMap, !Ref Env, MySubnetID3]
  MyVpcId: 
    Value: !FindInMap [EnvMap, !Ref Env, MyVpcId]

  SecurityGroupIdForLambda:
    Value: !GetAtt LambdaSecurityGroup.GroupId

AWS Batchテンプレート

template_batch.yaml
AWSTemplateFormatVersion: '2010-09-09'  

# これらのパラメータはtemplate_efs.yamlで生成したものを親テンプレートから受け取る
Parameters:
  Env:
    Type: String
  Prifix:
    Type: String
  EfsSubnetID1:
    Type: String
  EfsSubnetID2:
    Type: String
  EfsSubnetID3:
    Type: String
  MyVpcId:
    Type: String
  EfsFileSystemId:
    Type: String
  EfsAccessPointId:
    Type: String

Mappings: 
  EnvMap: 
    dev:
      EcrRepoForBatch: "xxx.dkr.ecr.ap-northeast-1.amazonaws.com/repo-name:latest"

Resources:
  SecurityGroupForBatch:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref MyVpcId
      GroupDescription: Security group for Batch Compute Env
      Tags:
      - Key: "Name"
        Value: !Sub
         - "${prefix}security-group-batch"
         - { prefix: !Ref Prifix }

  # コンピューティング環境
  MyComputeEnv:
    Type: "AWS::Batch::ComputeEnvironment"
    Properties:
      Type: MANAGED
      State: ENABLED
      ComputeEnvironmentName: !Sub
        - "${prefix}my-compute-env"
        - { prefix: !Ref Prifix }
      ComputeResources:
        # 最大vcpu数
        MaxvCpus: 256
        # 最小vcpu
        # This parameter isn't applicable to jobs that are running on Fargate resources,
        # and shouldn't be specified.
#        MinvCpus: 0
        SecurityGroupIds:
          - !GetAtt SecurityGroupForBatch.GroupId
        Type: FARGATE
        Subnets:
          - !Ref MySubnetID1
          - !Ref MySubnetID2
          - !Ref MySubnetID3

  # JOBキュー定義
  MyJobQueue:
    Type: AWS::Batch::JobQueue
    Properties:
      ComputeEnvironmentOrder:
        - Order: 1
          ComputeEnvironment: !Ref MyComputeEnv
      State: ENABLED
      Priority: 1
      JobQueueName: !Sub
        - "${prefix}my-job-queue"
        - { prefix: !Ref Prifix }

  MyJobRole:
    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/AmazonElasticFileSystemFullAccess'

  # JOB定義
  MyJobDefinition:
    Type: AWS::Batch::JobDefinition
    Properties:
      Type: container
      PlatformCapabilities: 
        - FARGATE
      JobDefinitionName: !Sub
        - "${prefix}my-job-definition"
        - { prefix: !Ref Prifix }
      Timeout:
        AttemptDurationSeconds: 43200
      RetryStrategy:
        Attempts: 1
      ContainerProperties:
        # 自動IP割当(プライベート環境の場合はDISABLEDにする)
        NetworkConfiguration:
          AssignPublicIp: ENABLED
        FargatePlatformConfiguration:
          PlatformVersion: 1.4.0
        Image: !FindInMap ["EnvMap", !Ref Env, "EcrRepoForBatch"]
        JobRoleArn:  !GetAtt MyJobRole.Arn
        # リソース定義。VPCとMEMORYの組み合わせがあるのでドキュメント参照
        ResourceRequirements:
          - Type: VCPU
            Value: 4
          - Type: MEMORY
            Value: 8192
        Volumes:
          - EfsVolumeConfiguration:
              FileSystemId: !Ref EfsFileSystemId
              AuthorizationConfig:
                AccessPointId: !Ref EfsAccessPointId
                Iam: "ENABLED"
              # ホスト内のルートディレクトリとしてマウントする
              # Amazon EFS ファイルシステム内のディレクトリ
              # AccessPointを指定する場合、ルートは「/」にする必要がある。
              RootDirectory: "/"
              TransitEncryption: "ENABLED"
            Name: "efs-volume"
        MountPoints:
          - ContainerPath: "/mnt/efs"
            SourceVolume: "efs-volume"
        ExecutionRoleArn: !GetAtt MyJobRole.Arn
        Command:
          - 'job.sh'

Dockerfile

AWS Batchのイメージ作成用Dockerfileサンプル。
Amazon Linux2をベースにpython3.8とAWSCLIv2をインストールしています。

FROM public.ecr.aws/amazonlinux/amazonlinux:latest

RUN yum install -y amazon-linux-extras
RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm

# AWS CLIv2インストール
COPY awscliv2.zip /tmp
RUN unzip /tmp/awscliv2.zip -d /tmp/awscli
RUN /tmp/awscli/aws/install --bin-dir /usr/bin --install-dir /usr/bin/aws-cli

#Python3.8インストール
RUN amazon-linux-extras install python3.8 -y
RUN yum install -y python38-devel
RUN pip3.8 install --upgrade pip
RUN pip install setuptools --upgrade
RUN pip install boto3

以上