1
0

Cfnで作るECS on Fargateの最小構成

Posted at

シンプルなECS on Fargateの構成をCloudFrontから作るのに少々苦労したので、テンプレート的にまとめようと思います。

構成図

スクリーンショット 2024-08-17 19.00.45.png

前提

  • aws-cliのインストール済
  • AWSアカウント作成済
  • aws-cliのconfigureをセットアップ済

ディレクトリ構成

.
├── architecture.drawio
├── ecr
│   ├── Dockerfile
│   ├── deploy.ecr.sh
│   ├── parameter.ecr.json
│   ├── push.ecr.sh
│   └── template.ecr.yaml
├── ecs
│   ├── deploy.ecs.sh
│   ├── parameter.ecs.json
│   └── template.ecs.yaml
└── vpc
    ├── deploy.vpc.sh
    ├── parameter.vpc.json
    └── template.vpc.yaml

なお、コードはこちらをご覧ください。
各ディレクトリ内の parameter.<リソース>.json および ecr/push.ecr.sh を書き換えることでご自身の環境でもご利用できます。

各種ポイント

コードは詰まったポイントのみ抜粋します。
詳細はこちらをご覧ください。

ECS -> ECRの通信

ECSがECRからimageをpullする際、以下の2つの方法が存在します。

NAT Gatewayを利用 各種エンドポイントを利用
構成図 スクリーンショット 2024-08-17 19.01.15.png スクリーンショット 2024-08-17 19.00.45.png
メリット
  • シンプルな構成
  • 可用性が高い
  • スケーラブル
  • インターネットを経由しないためセキュア
  • セキュリティグループによる細かなアクセス制御が可能
  • NAT Gatewayより低コスト
デメリット
  • 各種エンドポイントより高コスト
  • インターネットを経由する
  • 多くのエンドポイントを管理する必要あり
コスト
  • 稼働時間: $0.062/h
  • データ量: $0.062/GB
  • 稼働時間:$0.042/h(合算値)
  • データ量: 約$0.01/GB

上記からケースにもよりますが、基本的には各種エンドポイントを使用する方がセキュアかつ安価に構築することが可能です。
そのため、今回は各種エンドポイントを利用しています。
なお、必要となるエンドポイントと用途は以下のとおりです。

エンドポイント 用途
s3(Gateway) ECRからのimage pull
ecr.dkr ECRからのiamge pull
ecr.api ECRからのiamge pull
logs
(必須ではない)
ECSサービスのログ取得

logsに関しては必須ではありませんが、これがなければサービスのログを取得することができなくなるので、特別な理由がなければ作成することを推奨します。

S3エンドポイントのみGateway型であるため、VPCのstackで記述しており、それ以外のエンドポイントはECSのstackで記述しています。

vpc/template.vpc.yaml
  S3Endpoint:
    Type: "AWS::EC2::VPCEndpoint"
    Properties:
      ....
ecs/template.ecs.yaml
  DkrEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ....
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref Endpointsg
      ....

  ApiEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ....
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref Endpointsg
      ....

  LogsEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ....
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref Endpointsg
      ....

ここで注意してほしい点が、ecsで記述しているエンドポイントに PrivateDnsEnabled を有効化していることです。
PrivateDnsEnabled パラメータはインターフェースVPCエンドポイントがプライベートDNSを使用するかどうかを制御します。
つまり、有効化するとVPC内のみで通信が解決し、無効化すると通信がインターネットを経由することになります。
今回は、VPC内のみで通信が解決してほしいので有効化しています。

また、それぞれのインターフェースVPCエンドポイントにはセキュリティグループを付与できます。
全て以下に従うセキュリティグループを付与します。

  • インバウンドルール:
    • ポート範囲: 443 (HTTPS)
    • プロトコル: TCP
    • 送信元: Fargateタスクが存在するサブネットのセキュリティグループ
  • アウトバウンドルール:
    • 基本的にすべてのトラフィックを許可(0.0.0.0/0)(必要に応じて特定の範囲を許可)
ecs/template.ecs.yaml
  Endpointsg:
    Type: AWS::EC2::SecurityGroup
    Properties:
      ....
      SecurityGroupIngress:
        - SourceSecurityGroupId: !GetAtt ECSsg.GroupId
          Description: from ecs
          IpProtocol: tcp
          FromPort: 443
          ToPort: 443
      ....

ECSのExecutionRole

ECSにはTaskを実行するためのExecutionRoleを設定する必要があります。
必要に応じて権限を付与することになりますが、最小の権限は arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy に全て用意されています。
そのため、以下のようにIAM Roleを作成してあげます。

ecs/template.ecs.yaml
  ECSRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "ecs-tasks.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

こちらで作成したIAM RoleをTaskDefinitionで指定すればOKです。

ECSとALBの紐付け

ALBでTargetGroupを設定する際、一般的には以下のようにTargetsパラメータでバランシング対象を設定すると思います。

ecs/template.vpc.yaml
  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      ....
      Targets:
        - Id: !Ref EC2Instance

しかしECSでは異なり、ECSサービスからALBのTargetGroupを参照しにいきます。
そのため、以下のようにTargetGroupでは特に指定せず、ECSServiceのLoadBalancers.TargetGroupArnで紐づけるようにします。

ecs/template.ecs.yaml
  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      VpcId:
        "Fn::ImportValue": { "Fn::Sub": "${ProjectName}-VpcId" }
      Name: !Sub ${ProjectName}-alb-tg
      Protocol: HTTP
      Port: 80
      TargetType: ip
      
  ECSService:
    Type: AWS::ECS::Service
    DependsOn: ALBListener
    Properties:
      ....
      LoadBalancers:
        - ContainerName: !Sub ${ProjectName}-container
          ContainerPort: 80
          TargetGroupArn: !Ref TargetGroup
      ....
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0