👋

【初心者向け】ALB・ECS連携 をCloudFormationで作ってみる

2024/09/21に公開

はじめに

こちらの記事で紹介しているアプリケーション内の
ALBとECSの連携をCloudFormationで作成したので、まとめてみます。
なかなかドキュメントを読んでいても必要なパラメータが理解しづらかったので、
同じようなことを実装している人のサポートになれば嬉しいです。


赤枠の内容になります

対象読者

  • ALB・ECS連携に興味がある方
  • CloudFormationの基礎的な構文が理解できる方
  • ECSについての基礎的な知識がある方
  • ALBについての基礎的な知識がある方

手順

下記の内容のCloudFormationテンプレートの全文はこちらのGitHubリポジトリ
保管しているので、気になる方は是非ご覧ください。

IAMロールの作成

ECSタスクで利用するIAMロールを作成します。
今回のアプリケーションでは、ECSからDynamoDBとBedrockにアクセスするので、
アクションを許可します。

Resources:
  # --------------------------------------------------#
  # IAM Role
  # --------------------------------------------------#
  ECSTaskRole:
    Type: AWS::IAM::Role
    Properties:
      # IAMロールの信頼ポリシーの定義
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - sts:AssumeRole
            Principal:
              Service: ecs-tasks.amazonaws.com # ECSタスクに引き渡すように指定
      Policies:
        - PolicyName: BedrockECRTaskPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - bedrock:*
                  - dynamodb:*
                Resource: '*'
      RoleName: bsc-task-role

ECSクラスターの作成

EcsCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: ecs-cls

ECSタスク定義の作成

ECSタスク定義では、コンテナの構成を定義するものです。

  • ecsTaskExecutionRoleというロールは、ECRからDockerイメージを取得したり、ログを送信したりなどのコンテナの実行に必要な権限を提供する。
  • awsvpcはVPCを利用するNWモードで、Fargetタスクで利用する。専用のENIを付与され、他のVPC内のリソースと通信できる
# タスク定義
  EcsTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      ContainerDefinitions: # コンテナ設定
        - Name: bedrock-container # コンテナ名
          Image: !Ref DockerImageURL # DockerイメージURL
          PortMappings:
            - ContainerPort: 80
              HostPort: 80
              Protocol: tcp
      ExecutionRoleArn: !Sub "arn:aws:iam::${AWS::AccountId}:role/ecsTaskExecutionRole" # タスク実行時に使用されるIAMロールのARN
      TaskRoleArn: !Ref ECSTaskRole # タスクがアクセスするAWSリソースの権限を定義するIAMロールのARN
      Family: bedrock-container
      NetworkMode: awsvpc # VPCを利用したNWモードであり、Fargateの場合必須
      RequiresCompatibilities: ["FARGATE"] # このタスクがFargateを使用して実行される
      Cpu: 1024 # 1vCPU
      Memory: 2048 # 2GB
      RuntimePlatform:
        OperatingSystemFamily: LINUX

ECSサービスの作成

ECSサービスはECSタスクを維持し、ロードバランサなどの機能を設定する。

# サービス
  EcsService:
    Type: AWS::ECS::Service
    DependsOn: # ALBリスナーが作成されてからECSサービスが作成されることを指定
      - ALBListenerHTTPS
    Properties:
      Cluster: !Ref EcsCluster # サービスが属するクラスター
      ServiceName: EcsService
      TaskDefinition: !Ref EcsTaskDefinition # ECSタスク定義
      DesiredCount: 1 # 起動するタスクのデフォルト数
      EnableExecuteCommand: false # ECS Exec機能を無効
      LaunchType: FARGATE # Fargateを使用してタスクがサーバレスに実行される
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: DISABLED
          SecurityGroups:
            - { Fn::ImportValue: ecs-sg-id }
          Subnets:
            - { Fn::ImportValue: private-subnet-a-id }
            - { Fn::ImportValue: private-subnet-c-id }
      # ロードバランサとの連携
      LoadBalancers:
        - ContainerName: bedrock-container # タスク定義で指定したコンテナをALBに関連付ける
          ContainerPort: 80
          TargetGroupArn: !Ref TargetGroupResource # サービスがトラフィックを転送するターゲットグループの指定
      Tags:
        - Key: Name
          Value: bedrock-ecs-service

ターゲットグループの作成

Fargateを使用する場合、TargetTypeipを指定する。

 # --------------------------------------------------#
  # TargetGroup
  # --------------------------------------------------#
  TargetGroupResource:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      VpcId: { Fn::ImportValue: rag-vpc-id }
      Name: bedrock-target-group
      Protocol: HTTP
      Port: 80
      TargetType: ip
      HealthCheckProtocol: HTTP
      HealthCheckPath: "/"
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 2
      HealthCheckTimeoutSeconds: 20
      HealthCheckIntervalSeconds: 30
      Matcher: 
        HttpCode: 200
      Tags:
        - Key: Name
          Value: bedrock-tg

ALBの作成

ALBを作成する場合、ALBとリスナーの2つを作成する必要がある。
リスナールールでターゲットグループへのルーティングやACMで発行した証明書などを設定する。

# --------------------------------------------------#
  # ALB
  # --------------------------------------------------#
  ALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: bedrock-alb
      # パブリックサブネットのみ配置可という意味
      Scheme: internet-facing
      # ALBを指定
      Type: application
      Subnets:
        - { Fn::ImportValue: public-subnet-a-id }
        - { Fn::ImportValue: public-subnet-c-id }
      SecurityGroups:
        - { Fn::ImportValue: alb-sg-id }
      IpAddressType: ipv4
      Tags:
        - Key: Name
          Value: bedrock-alb
  
  # Listener
  ALBListenerHTTPS:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref ALB
      Port: 443
      Protocol: HTTPS
      Certificates:
        - CertificateArn: !Ref ACMCertificateArn
      SslPolicy: !Ref ELBSecurityPolicy
      DefaultActions:
        # Cognitoへのルーティング
        - AuthenticateCognitoConfig: 
              UserPoolArn: { Fn::ImportValue: user-pool-arn }
              UserPoolClientId: { Fn::ImportValue: user-pool-client-id }
              UserPoolDomain: bedrock-1234
              OnUnauthenticatedRequest: authenticate
          Order: 1
          Type: "authenticate-cognito"
        # ターゲットグループへのルーティング
        - TargetGroupArn: !Ref TargetGroupResource
          Order: 2
          Type: forward

まとめ

以上がALBとECSの連携をCloudFormationで実装する場合の記述内容になります。
これまでマネジメントコンソール画面で設定していた内容をCloudFormationで定義すると
結構複雑になるので、作成が大変な印象です。
今後は最近流行っているDify.aiもCloudFormationあるいはTerraformでセキュアな構成で
実装してみたいです。

Discussion