Amazon ECS Service ConnectをAWS CloudFormationでデプロイしてみた
こんにちは、シマです。
当ブログサイト(DevelopersIO)では、様々な実装方式で「Amazon ECS Service Connect」のやってみた記事が掲載されています。
しかし、AWS CloudFormationでの実装が見当たらなかったので、今回はAWS CloudFormationで実装し、簡単な動作確認をしてみることにしました。構成については、上記のCDKの記事を参考にしています。
構成
名前空間内にウェブ用とクライアント用の2つのFargateサービスを作成します。タスク定義は同じものを流用していて、動作確認ではAWS CloudShellからECS Execによりクライアント用コンテナへログインします。その後、クライアント用コンテナからcurlコマンドでウェブ用コンテナへアクセスし、サイトの確認を行います。
テンプレートのポイント
Amazon ECS Service Connectをテンプレートで実装する上で、実施した点や注意が必要な点を以下に記載します。
名前空間
Cluster作成時に「ServiceConnectDefaults」を指定すると作成されます。
cluster: Type: AWS::ECS::Cluster Properties: ClusterName: cluster ServiceConnectDefaults: Namespace: local
Amazon ECS Service Connectの設定
コンテナ定義にポートマッピングで名前を指定します。
taskdefinition: Type: AWS::ECS::TaskDefinition Properties: ContainerDefinitions: - PortMappings: - Name: website
サービスに「ServiceConnectConfiguration」を追加します。
「Namespace」はCluster作成時のものを指定し、「PortName」はコンテナ定義で指定したものを指定します。
serviceclient: Type: AWS::ECS::Service Properties: ServiceConnectConfiguration: Enabled: true Namespace: local Services: - DiscoveryName: client ClientAliases: - DnsName: "client.local" Port: 80 PortName: website
考慮事項
AWS CloudFormationによる実装に限った話ではないですが、AWS公式ドキュメントにAmazon ECS Service Connectに関する考慮事項が記載されています。
考慮事項には主に利用するための前提条件が記載されており、個人的に気になった箇所は以下です。
Amazon ECS 最適化 Amazon Linux 2023 AMI は、Service Connect ではサポートされていません。これは、Service Connect エージェントが Amazon Linux 2023 で使用できないためです。
コンテナインスタンスが Service Connect を使用するには、Amazon ECS に最適化された Amazon Linux 2 AMI バージョン 2.0.20221115 以降を実行する必要があります。
現時点(2023/5/31)では、日本語版で上記のように記載されていますが、英語版を確認すると以下のように記載されていました。
Container instances must run the Amazon ECS-optimized Amazon Linux 2023 AMI version 20230428 or later, or Amazon ECS-optimized Amazon Linux 2 AMI version 2.0.20221115 to use Service Connect. For more information about the Service Connect agent, see Amazon ECS Service Connect Agent on GitHub.
そのため、今回はAmazon Linux 2023 AMI version 20230517を使用しました。
※試しにサポートされていないもの(20230322)でデプロイしてみましたが、エラー等は発生せずにデプロイできてしまいました。何かトラブル発生時にはサポートされていないという点にご注意ください。
動作確認
AWS CloudFormationでデプロイしたことによる特殊な点はないので、サクッと動作確認をします。
AWS CloudShellからECS Execによりクライアント用コンテナへログインします。
aws ecs execute-command \ > --cluster cluster \ > --task xxxxxxxxxxxxxxx \ > --container container \ > --interactive \ > --command "/bin/bash" The Session Manager plugin was installed successfully. Use the AWS CLI to start a session. Starting session with SessionId: ecs-execute-command-xxxxxxxxx bash-5.2#
クライアント用コンテナからcurlコマンドでウェブ用コンテナへアクセスし、サイトの確認を行います。
bash-5.2# curl http://web.local/ <html> <head> <title>Sample</title> </head> <body> TEST </body> </html>
問題なくアクセスできました。
使用したテンプレート全体
今回使用したAWS CloudFormationテンプレートファイルを記載します。
template.yml
AWSTemplateFormatVersion: '2010-09-09' Description: Amazon ECS Service Connect Sample # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: AZa: Type: AWS::EC2::AvailabilityZone::Name Default: ap-northeast-1a AZc: Type: AWS::EC2::AvailabilityZone::Name Default: ap-northeast-1c Resources: # VPC # ------------------------------------------------------------# vpctest: Type: AWS::EC2::VPC Properties: CidrBlock: "172.16.0.0/16" EnableDnsSupport: true EnableDnsHostnames: true InstanceTenancy: default Tags: - Key: Name Value: "vpc-test" # RouteTable # ------------------------------------------------------------# rtpublic: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref vpctest Tags: - Key: Name Value: "rt-public" # Subnet # ------------------------------------------------------------# snpublica: Type: AWS::EC2::Subnet Properties: VpcId: !Ref vpctest CidrBlock: "172.16.1.0/24" AvailabilityZone: !Ref AZa Tags: - Key: Name Value: "sn-public-a" rtasnpublica: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref snpublica RouteTableId: !Ref rtpublic snpublicc: Type: AWS::EC2::Subnet Properties: VpcId: !Ref vpctest CidrBlock: "172.16.2.0/24" AvailabilityZone: !Ref AZc Tags: - Key: Name Value: "sn-public-c" rtasnpublicc: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref snpublicc RouteTableId: !Ref rtpublic # Internet Gateway # ------------------------------------------------------------# igw: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: "igw" atigw: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref vpctest InternetGatewayId: !Ref igw rtpublicigw: Type: AWS::EC2::Route Properties: RouteTableId: !Ref rtpublic DestinationCidrBlock: "0.0.0.0/0" GatewayId: !Ref igw # Security Group # ------------------------------------------------------------# sgclient: Type: AWS::EC2::SecurityGroup Properties: GroupName: sgclient GroupDescription: sgclient VpcId: !Ref vpctest SecurityGroupEgress: - IpProtocol: "-1" CidrIp: 0.0.0.0/0 Tags: - Key: Name Value: sgclient sgweb: Type: AWS::EC2::SecurityGroup Properties: GroupName: sgweb GroupDescription: sgweb VpcId: !Ref vpctest SecurityGroupEgress: - IpProtocol: "-1" CidrIp: 0.0.0.0/0 Tags: - Key: Name Value: sgweb sgwebfromclient: Type: AWS::EC2::SecurityGroupIngress Properties: IpProtocol: tcp FromPort: 80 ToPort: 80 SourceSecurityGroupId: !Ref sgclient GroupId: !Ref sgweb Description: "web from client" # IAM Role # ------------------------------------------------------------# roleecstaskexec: Type: AWS::IAM::Role Properties: Path: / RoleName: "role-ecs-taskexec" AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - sts:AssumeRole Principal: Service: - ecs-tasks.amazonaws.com ManagedPolicyArns: - "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" Policies: - PolicyName: roleecstaskexecpolicy PolicyDocument: Statement: - Effect: Allow Action: - logs:CreateLogGroup Resource: '*' roleecstask: Type: AWS::IAM::Role Properties: Path: / RoleName: "role-ecstask" AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - sts:AssumeRole Principal: Service: - ecs-tasks.amazonaws.com Policies: - PolicyName: roleecstaskpolicy PolicyDocument: Statement: - Effect: Allow Action: - ssmmessages:CreateControlChannel - ssmmessages:CreateDataChannel - ssmmessages:OpenControlChannel - ssmmessages:OpenDataChannel - logs:CreateLogGroup Resource: '*' # LogGroup # ------------------------------------------------------------# ecslogs: Type: AWS::Logs::LogGroup DeletionPolicy: Delete UpdateReplacePolicy: Delete Properties: LogGroupName: "/ecs/logs" RetentionInDays: 30 # Cluster # ------------------------------------------------------------# cluster: Type: AWS::ECS::Cluster Properties: ClusterName: cluster ServiceConnectDefaults: Namespace: local # TaskDefinition # ------------------------------------------------------ taskdefinition: Type: AWS::ECS::TaskDefinition Properties: Family: taskdefinition Cpu: 512 Memory: 1024 ExecutionRoleArn: !GetAtt roleecstaskexec.Arn NetworkMode: awsvpc RequiresCompatibilities: - FARGATE TaskRoleArn: !GetAtt roleecstask.Arn ContainerDefinitions: - Name: container Essential: True Image: "public.ecr.aws/amazonlinux/amazonlinux:2023.0.20230517.1" Command: - "/bin/sh -c \"yum install -y httpd && echo '<html> <head> <title>Sample</title> </head> <body> TEST </body> </html>' > /var/www/html/index.html && /usr/sbin/httpd -D FOREGROUND\"" EntryPoint: - "sh" - "-c" Memory: 512 PortMappings: - ContainerPort: 80 Protocol: TCP AppProtocol: http Name: website LogConfiguration: LogDriver: awslogs Options: awslogs-group: "/ecs/logs" awslogs-region: "ap-northeast-1" awslogs-stream-prefix: ecs awslogs-create-group: true # Service # ------------------------------------------------------------# serviceweb: Type: AWS::ECS::Service Properties: ServiceName: "service-web" Cluster: !Ref cluster DesiredCount: 2 LaunchType: FARGATE EnableExecuteCommand: True TaskDefinition: !Ref taskdefinition PlatformVersion: LATEST ServiceConnectConfiguration: Enabled: true Namespace: local Services: - DiscoveryName: web ClientAliases: - DnsName: "web.local" Port: 80 PortName: website NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: ENABLED SecurityGroups: - !Ref sgweb Subnets: - !Ref snpublica - !Ref snpublicc serviceclient: Type: AWS::ECS::Service Properties: ServiceName: "service-client" Cluster: !Ref cluster DesiredCount: 2 LaunchType: FARGATE EnableExecuteCommand: True TaskDefinition: !Ref taskdefinition PlatformVersion: LATEST ServiceConnectConfiguration: Enabled: true Namespace: local Services: - DiscoveryName: client ClientAliases: - DnsName: "client.local" Port: 80 PortName: website NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: ENABLED SecurityGroups: - !Ref sgclient Subnets: - !Ref snpublica - !Ref snpublicc
最後に
今回はAmazon ECS Service ConnectをAWS CloudFormationで実装してみました。実装する際に気になったことを調べてみたり、実際に試してみることで理解度が深まりました。
本記事がどなたかのお役に立てれば幸いです。
(※2023/6/6追記) 本記事内のテンプレートはスタック削除時に名前空間が削除されずに残っていました。それを解消したものを以下の記事に掲載しています。