CloudformationでFargateを構築する
こんにちは、コカコーラ大好きカジです。
VPC構築済みの環境や、同じVPC内に複数のFargateを複数構築するときに使えるCloudformationテンプレートを作成してみました。 Fargateに関連するCloudwatch LogsやAuto Scalingの設定も含まれています。 また、自分が使いたいFargateのサンプルが見つからなかったので作成しました。 どなたかのお役に立てれば光栄です。
前提条件
- 利用予定のアカウントで、AWS CLIが利用可能
- VPC、ALB Security Group、ECS Task Security Groupが構築済み
- Fargateを配置するサブネットは、NATゲートウェイ等経由でインターネット通信可能であること
- ALBのログ保存用S3バケットが構築済み
S3バケットが無い場合はこちらのブロクを元に作成してください。
構成図
Dockerfile準備と、ECRの作成
動作確認ができるよう、ECRの作成から記載します。理解している人はスキップしてください。 別アカウントのリポジトリからの取得も想定し、ECRの作成と、Fargateの作成のCloudformationを分離しています。 (ここら辺は個人的な趣味もあり、テンプレートを分割しています。)
まず、テスト用のDockerイメージを構築するためDockerfileを用意します。
$ cat Dockerfile FROM nginx:alpine
以下のCloudformationテンプレートを使ってCreate StackしてECRを構築します。
AWSTemplateFormatVersion: '2010-09-09' Description: Create ECR Parameters: ProjectName: Default: kaji-test Type: String Resources: ECR: Type: AWS::ECR::Repository Properties: RepositoryName: !Sub ${ProjectName}-ecr
作成したECRで、以下のところをクリックして表示される、Dockerイメージの構築と、Pushを行います。
さきほどのDockerfileのあるところで、実行します。 $(aws ecr get-login --no-include-email --region ap-northeast-1) docker build -t <ECRのプロジェクト名 例:kaji-test-ecr> . docker tag <ECRのプロジェクト名 例:kaji-test-ecr>:latest xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/<ECRのプロジェクト名 例:kaji-test-ecr>:latest docker push xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/<ECRのプロジェクト名 例:kaji-test-ecr>:latest
Pushし終わったら、Fargete作成時に必要なImage URLをコピーアンドメーストしてメモしておきます。
Fargateの構築
文末のCloudformationテンプレートでCreate Stackしてください。 Crate Stack時に、VPC、Subnet、Security Groupを指定するようにしています。そのほかはそのままでOKです。
途中、ECS用のIAM Roleや、Auto ScalingのIAM Roleを作成するためIAMの許可にチェックを入れます。
Create Stackが正常に終わると以下のようになります。正常にできない場合は、ネットワーク構成や指定しているVPC、サブネットに誤りがないか確認しましょう。
ALBのDNS名で外部からアクセスすると、以下のようにnginxが表示されます。
Cloudformationテンプレート
sample-fargate.yaml
AWSTemplateFormatVersion: "2010-09-09" Description: Fargate and ALB Create Metadata: "AWS::CloudFormation::Interface": ParameterGroups: - Label: default: "Project Name Prefix" Parameters: - ProjectName - Label: default: "InternetALB Configuration" Parameters: - InternetALBName - TargetGroupName - Label: default: "Fargate for ECS Configuration" Parameters: - ECSClusterName - ECSTaskName - ECSTaskCPUUnit - ECSTaskMemory - ECSContainerName - ECSImageName - ECSServiceName - ECSTaskDesiredCount - Label: default: "Netowork Configuration" Parameters: - VpcId - ALBSecurityGroupId - ALBSubnetId1 - ALBSubnetId2 - ECSSecurityGroupId - ECSSubnetId1 - ECSSubnetId2 - Label: default: "Scaling Configuration" Parameters: - ServiceScaleEvaluationPeriods - ServiceCpuScaleOutThreshold - ServiceCpuScaleInThreshold - TaskMinContainerCount - TaskMaxContainerCount ParameterLabels: InternetALBName: default: "InternetALBName" TargetGroupName: default: "TargetGroupName" ECSClusterName: default: "ECSClusterName" ECSTaskName: default: "ECSTaskName" ECSTaskCPUUnit: default: "ECSTaskCPUUnit" ECSTaskMemory: default: "ECSTaskMemory" ECSContainerName: default: "ECSContainerName" ECSImageName: default: "ECSImageName" ECSServiceName: default: "ECSServiceName" ECSTaskDesiredCount: default: "ECSTaskDesiredCount" # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: ProjectName: Default: kaji-test Type: String #VPCID VpcId: Description : "VPC ID" Type: AWS::EC2::VPC::Id #ALBSecurity Group ALBSecurityGroupId: Type: AWS::EC2::SecurityGroup::Id #ALBSubnet1 ALBSubnetId1: Description : "ALB Subnet 1st" Type : AWS::EC2::Subnet::Id #ALBSubnet2 ALBSubnetId2: Description : "ALB Subnet 2st" Type : AWS::EC2::Subnet::Id #ECSSecurity Group ECSSecurityGroupId: Type: AWS::EC2::SecurityGroup::Id #ECSSubnet1 ECSSubnetId1: Description : "ECS Subnet 1st" Type : AWS::EC2::Subnet::Id #ECSSubnet2 ECSSubnetId2: Description : "ECS Subnet 2st" Type : AWS::EC2::Subnet::Id #InternetALB InternetALBName: Type: String Default: "alb" #TargetGroupName TargetGroupName: Type: String Default: "tg" #ECSClusterName ECSClusterName: Type: String Default: "cluster" #ECSTaskName ECSTaskName: Type: String Default: "task" #ECSTaskCPUUnit ECSTaskCPUUnit: AllowedValues: [ 256, 512, 1024, 2048, 4096 ] Type: String Default: "256" #ECSTaskMemory ECSTaskMemory: AllowedValues: [ 256, 512, 1024, 2048, 4096 ] Type: String Default: "512" #ECSContainerName ECSContainerName: Type: String Default: "container" #ECSImageName ECSImageName: Type: String Default: "xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/kaji-test-ecr:latest" #ECSServiceName ECSServiceName: Type: String Default: "service" #ECSTaskDesiredCount ECSTaskDesiredCount: Type: Number Default: 1 # Scaling params ServiceScaleEvaluationPeriods: Description: The number of periods over which data is compared to the specified threshold Type: Number Default: 2 MinValue: 2 ServiceCpuScaleOutThreshold: Type: Number Description: Average CPU value to trigger auto scaling out Default: 50 MinValue: 0 MaxValue: 100 ConstraintDescription: Value must be between 0 and 100 ServiceCpuScaleInThreshold: Type: Number Description: Average CPU value to trigger auto scaling in Default: 25 MinValue: 0 MaxValue: 100 ConstraintDescription: Value must be between 0 and 100 TaskMinContainerCount: Type: Number Description: Minimum number of containers to run for the service Default: 1 MinValue: 1 ConstraintDescription: Value must be at least one TaskMaxContainerCount: Type: Number Description: Maximum number of containers to run for the service when auto scaling out Default: 2 MinValue: 1 ConstraintDescription: Value must be at least one Resources: # ------------------------------------------------------------# # Target Group # ------------------------------------------------------------# TargetGroup: Type: "AWS::ElasticLoadBalancingV2::TargetGroup" Properties: VpcId: !Ref VpcId Name: !Sub "${ProjectName}-${TargetGroupName}" Protocol: HTTP Port: 80 TargetType: ip # ------------------------------------------------------------# # Internet ALB # ------------------------------------------------------------# InternetALB: Type: "AWS::ElasticLoadBalancingV2::LoadBalancer" Properties: Name: !Sub "${ProjectName}-${InternetALBName}" Tags: - Key: Name Value: !Sub "${ProjectName}-${InternetALBName}" Scheme: "internet-facing" LoadBalancerAttributes: - Key: "deletion_protection.enabled" Value: false - Key: "idle_timeout.timeout_seconds" Value: 60 - Key: "access_logs.s3.enabled" Value: true - Key: "access_logs.s3.bucket" Value: !Sub "alb-log-${AWS::AccountId}" SecurityGroups: - !Ref ALBSecurityGroupId Subnets: - !Ref ALBSubnetId1 - !Ref ALBSubnetId2 ALBListener: Type: "AWS::ElasticLoadBalancingV2::Listener" Properties: DefaultActions: - TargetGroupArn: !Ref TargetGroup Type: forward LoadBalancerArn: !Ref InternetALB Port: 80 Protocol: HTTP # ------------------------------------------------------------# # ECS Cluster # ------------------------------------------------------------# ECSCluster: Type: "AWS::ECS::Cluster" Properties: ClusterName: !Sub "${ProjectName}-${ECSClusterName}" # ------------------------------------------------------------# # ECS LogGroup # ------------------------------------------------------------# ECSLogGroup: Type: "AWS::Logs::LogGroup" Properties: LogGroupName: !Sub "/ecs/logs/${ProjectName}-ecs-group" # ------------------------------------------------------------# # ECS Task Execution Role # ------------------------------------------------------------# ECSTaskExecutionRole: Type: AWS::IAM::Role Properties: RoleName: !Sub "${ProjectName}-ECSTaskExecutionRolePolicy" Path: / 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 # ------------------------------------------------------------# # ECS TaskDefinition # ------------------------------------------------------------# ECSTaskDefinition: Type: "AWS::ECS::TaskDefinition" Properties: Cpu: !Ref ECSTaskCPUUnit ExecutionRoleArn: !Ref ECSTaskExecutionRole Family: !Sub "${ProjectName}-${ECSTaskName}" Memory: !Ref ECSTaskMemory NetworkMode: awsvpc RequiresCompatibilities: - FARGATE #ContainerDefinitions ContainerDefinitions: - Name: !Sub "${ProjectName}-${ECSContainerName}" Image: !Ref ECSImageName LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref ECSLogGroup awslogs-region: !Ref "AWS::Region" awslogs-stream-prefix: !Ref ProjectName MemoryReservation: 128 PortMappings: - HostPort: 80 Protocol: tcp ContainerPort: 80 # ------------------------------------------------------------# # ECS Service # ------------------------------------------------------------# ECSService: Type: AWS::ECS::Service DependsOn: ALBListener Properties: Cluster: !Ref ECSCluster DesiredCount: !Ref ECSTaskDesiredCount LaunchType: FARGATE LoadBalancers: - TargetGroupArn: !Ref TargetGroup ContainerPort: 80 ContainerName: !Sub "${ProjectName}-${ECSContainerName}" NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: ENABLED SecurityGroups: - !Ref ECSSecurityGroupId Subnets: - !Ref ECSSubnetId1 - !Ref ECSSubnetId2 ServiceName: !Sub "${ProjectName}-${ECSServiceName}" TaskDefinition: !Ref ECSTaskDefinition # ------------------------------------------------------------# # Auto Scaling Service # ------------------------------------------------------------# ServiceAutoScalingRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: application-autoscaling.amazonaws.com Action: sts:AssumeRole Path: / Policies: - PolicyName: !Sub "${ProjectName}-${ECSContainerName}-autoscaling" PolicyDocument: Statement: - Effect: Allow Action: - application-autoscaling:* - cloudwatch:DescribeAlarms - cloudwatch:PutMetricAlarm - ecs:DescribeServices - ecs:UpdateService Resource: '*' ServiceScalingTarget: Type: AWS::ApplicationAutoScaling::ScalableTarget Properties: MinCapacity: !Ref TaskMinContainerCount MaxCapacity: !Ref TaskMaxContainerCount ResourceId: !Sub - service/${EcsClusterName}/${EcsDefaultServiceName} - EcsClusterName: !Ref ECSCluster EcsDefaultServiceName: !Sub "${ProjectName}-${ECSServiceName}" RoleARN: !GetAtt ServiceAutoScalingRole.Arn ScalableDimension: ecs:service:DesiredCount ServiceNamespace: ecs DependsOn: - ECSService - ServiceAutoScalingRole ServiceScaleOutPolicy: Type: AWS::ApplicationAutoScaling::ScalingPolicy Properties: PolicyName: !Sub "${ProjectName}-${ECSServiceName}-ScaleOutPolicy" PolicyType: StepScaling ScalingTargetId: !Ref ServiceScalingTarget StepScalingPolicyConfiguration: AdjustmentType: ChangeInCapacity Cooldown: 60 MetricAggregationType: Average StepAdjustments: - ScalingAdjustment: 1 MetricIntervalLowerBound: 0 DependsOn: ServiceScalingTarget ServiceScaleInPolicy: Type: AWS::ApplicationAutoScaling::ScalingPolicy Properties: PolicyName: !Sub "${ProjectName}-${ECSServiceName}-ScaleInPolicy" PolicyType: StepScaling ScalingTargetId: !Ref ServiceScalingTarget StepScalingPolicyConfiguration: AdjustmentType: ChangeInCapacity Cooldown: 60 MetricAggregationType: Average StepAdjustments: - ScalingAdjustment: -1 MetricIntervalUpperBound: 0 DependsOn: ServiceScalingTarget ServiceScaleOutAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: !Sub "${ProjectName}-${ECSServiceName}-ScaleOutAlarm" EvaluationPeriods: !Ref ServiceScaleEvaluationPeriods Statistic: Average TreatMissingData: notBreaching Threshold: !Ref ServiceCpuScaleOutThreshold AlarmDescription: Alarm to add capacity if CPU is high Period: 60 AlarmActions: - !Ref ServiceScaleOutPolicy Namespace: AWS/ECS Dimensions: - Name: ClusterName Value: !Ref ECSCluster - Name: ServiceName Value: !Sub "${ProjectName}-${ECSServiceName}" ComparisonOperator: GreaterThanThreshold MetricName: CPUUtilization DependsOn: - ECSService - ServiceScaleOutPolicy ServiceScaleInAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: !Sub "${ProjectName}-${ECSServiceName}-ScaleInAlarm" EvaluationPeriods: !Ref ServiceScaleEvaluationPeriods Statistic: Average TreatMissingData: notBreaching Threshold: !Ref ServiceCpuScaleInThreshold AlarmDescription: Alarm to reduce capacity if container CPU is low Period: 300 AlarmActions: - !Ref ServiceScaleInPolicy Namespace: AWS/ECS Dimensions: - Name: ClusterName Value: !Ref ECSCluster - Name: ServiceName Value: !Sub "${ProjectName}-${ECSServiceName}" ComparisonOperator: LessThanThreshold MetricName: CPUUtilization DependsOn: - ECSService - ServiceScaleInPolicy
参考元
以下の2つのブログを参考にさせていただきました。
CloudFormationを使ってAWS Fargateの環境を構築する
Github aws-samples/startup-kit-templates/templates/fargate.cfn.yml