ECS(EC2)のCloudFormation最小構成

aws

EC2でECSのServiceを動かすCFnテンプレートを書く。以前Fargateで動かしたものを一部再利用する。

ECS FargateでSidecarのFluentdでログをS3に送る構成をCloudFormationで構築する - sambaiz-net

EC2で動かす場合、自分でリソースが不足しないようにインスタンスのスケールを気遣うことになるがprivilegedをtrueにするなどEC2でしかできないことがある。あと同リソースで比較すると安い。

まずはEC2インスタンス以外のリソースを書く。LaunchType以外はFargateのときとほぼ同じ。 LBなしでバッチのようなものを動かすことを想定した最小構成。

ECSCluster:
  Type: AWS::ECS::Cluster
  Properties:
    ClusterName: 'test-cluster'
LogGroup:
  Type: AWS::Logs::LogGroup
  Properties: 
    LogGroupName: 'test-task-log-group'
    RetentionInDays: 1
TaskDefinition:
  Type: AWS::ECS::TaskDefinition
  Properties: 
    RequiresCompatibilities:
      - EC2
    Cpu: '256'
    Memory: '512'
    ContainerDefinitions: 
      - Name: 'app'
        Image: 'busybox'
        EntryPoint: 
          - 'sh'
          - '-c'
        Command: 
          - 'while true; do echo "{\"foo\":1000,\"time\":\"2019-05-09T20:00:00+09:00\"}"; sleep 10; done'
        Essential: 'true'
        LogConfiguration:
          LogDriver: 'awslogs'
          Options:
            awslogs-group: !Ref LogGroup
            awslogs-region: 'ap-northeast-1'
            awslogs-stream-prefix: 'app'
        Environment:
          - Name: 'TZ'
            Value: 'Asia/Tokyo'
    Volumes: 
      - Name: 'varlog'
ECSService:
  Type: AWS::ECS::Service
  Properties:
    Cluster: !Ref ECSCluster
    LaunchType: EC2
    DesiredCount: 1
    TaskDefinition: !Ref TaskDefinition
    ServiceName: 'test-service'

EC2インスタンスとECSクラスタの紐付きはAssociationのようなリソースがあるわけではなく、 ECS-optimized AMIからecho ECS_CLUSTER=<cluster name> >> /etc/ecs/ecs.configで指定したクラスタにインスタンスを登録する。そのために必要なroleを付ける。

各リージョンごとのAMI IDをMappingsで書き並べてもいいが、次のように書くとParameter Storeから最新のAMI IDを取得できる

Parameters:
  ECSAMI:
    Description: AMI ID
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id

なお、amazon-linux2ルートデバイスは/dev/sda1ではなく/dev/xvdaなので 間違えると Client.InstanceInitiatedShutdown: Instance initiated shutdown でインスタンスが落ち続けてServiceがstableにならず失敗する。

ECSAutoScalingGroup: 
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties: 
      AvailabilityZones: 
        !GetAZs ""
      LaunchConfigurationName: !Ref LaunchConfig
      MinSize: "1"
      MaxSize: "1"
EC2Role:
  Type: AWS::IAM::Role
  Properties:
    AssumeRolePolicyDocument:
      Statement:
      - Effect: Allow
        Principal:
          Service: ['ec2.amazonaws.com']
        Action: ['sts:AssumeRole']
    Path: /
    Policies:
    - PolicyName: ecs-service
      PolicyDocument:
        Statement:
        - Effect: Allow
          Action: 
            - 'ecs:CreateCluster'
            - 'ecs:DeregisterContainerInstance'
            - 'ecs:DiscoverPollEndpoint'
            - 'ecs:Poll'
            - 'ecs:RegisterContainerInstance'
            - 'ecs:StartTelemetrySession'
            - 'ecs:Submit*'
            - 'logs:CreateLogStream'
            - 'logs:PutLogEvents'
          Resource: '*'
EC2InstanceProfile:
  Type: AWS::IAM::InstanceProfile
  Properties:
    Path: /
    Roles: 
      - !Ref 'EC2Role'
LaunchConfig: 
  Type: AWS::AutoScaling::LaunchConfiguration
  Properties:
    ImageId: !Ref ECSAMI
    IamInstanceProfile: !Ref EC2InstanceProfile
    UserData:
      Fn::Base64: !Sub |
        #!/bin/bash -xe
        echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config          
    InstanceType: "t2.small"
    BlockDeviceMappings: 
      - DeviceName: "/dev/xvda"
        Ebs: 
          VolumeSize: "30"
          VolumeType: "gp2"

CDKでALBとECS(EC2)クラスタを作成し、ecs-cliでDocker Composeの構成をデプロイする - sambaiz-net