AWS CLI 명령으로 CloudFormation용 Amazon ECS 리소스 생성
AWS CLI 명령으로 CloudFormation용 Amazon ECS 리소스 생성
Amazon ECS를 CloudFormation과 함께 사용하는 또 다른 방법은 AWS CLI를 통한 것이에요. 명령을 사용해 태스크 정의, 클러스터, 서비스 같은 Amazon ECS 구성 요소에 대한 CloudFormation 스택을 만들고 배포할 수 있어요. 다음 튜토리얼은 AWS CLI를 사용해 CloudFormation 템플릿으로 Amazon ECS 리소스를 만드는 방법을 보여 줘요.
출처: 문서
본문
사전 준비 (Prerequisites)
Set up to use Amazon ECS의 단계가 완료되었어요.- IAM 사용자가
AmazonECS_FullAccessIAM 정책 예제에 지정된 필수 권한을 갖고 있어요.
1단계: 스택 생성
ecs-tutorial-template.yaml이라는 파일에 저장된 AWS CLI로 스택을 만들려면 다음 명령을 실행하세요.
cat << 'EOF' > ecs-tutorial-template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: '[AWSDocs] ECS: load-balanced-web-application'
Parameters:
VpcCidr:
Type: String
Default: '10.0.0.0/16'
Description: CIDR block for the VPC
ContainerImage:
Type: String
Default: 'public.ecr.aws/ecs-sample-image/amazon-ecs-sample:latest'
Description: Container image to use in task definition
PublicSubnet1Cidr:
Type: String
Default: '10.0.1.0/24'
Description: CIDR block for public subnet 1
PublicSubnet2Cidr:
Type: String
Default: '10.0.2.0/24'
Description: CIDR block for public subnet 2
PrivateSubnet1Cidr:
Type: String
Default: '10.0.3.0/24'
Description: CIDR block for private subnet 1
PrivateSubnet2Cidr:
Type: String
Default: '10.0.4.0/24'
Description: CIDR block for private subnet 2
ServiceName:
Type: String
Default: 'tutorial-app'
Description: Name of the ECS service
ContainerPort:
Type: Number
Default: 80
Description: Port on which the container listens
DesiredCount:
Type: Number
Default: 2
Description: Desired number of tasks
MinCapacity:
Type: Number
Default: 1
Description: Minimum number of tasks for auto scaling
MaxCapacity:
Type: Number
Default: 10
Description: Maximum number of tasks for auto scaling
Resources:
# VPC and Networking
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCidr
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-vpc'
# Internet Gateway
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-igw'
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
# Public Subnets for ALB
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [0, !GetAZs '']
CidrBlock: !Ref PublicSubnet1Cidr
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-public-subnet-1'
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [1, !GetAZs '']
CidrBlock: !Ref PublicSubnet2Cidr
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-public-subnet-2'
# Private Subnets for ECS Tasks
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [0, !GetAZs '']
CidrBlock: !Ref PrivateSubnet1Cidr
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-private-subnet-1'
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [1, !GetAZs '']
CidrBlock: !Ref PrivateSubnet2Cidr
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-private-subnet-2'
# NAT Gateways for private subnet internet access
NatGateway1EIP:
Type: AWS::EC2::EIP
DependsOn: InternetGatewayAttachment
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-nat-eip-1'
NatGateway2EIP:
Type: AWS::EC2::EIP
DependsOn: InternetGatewayAttachment
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-nat-eip-2'
NatGateway1:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGateway1EIP.AllocationId
SubnetId: !Ref PublicSubnet1
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-nat-1'
NatGateway2:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGateway2EIP.AllocationId
SubnetId: !Ref PublicSubnet2
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-nat-2'
# Route Tables
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-public-routes'
DefaultPublicRoute:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet1
PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet2
PrivateRouteTable1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-private-routes-1'
DefaultPrivateRoute1:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable1
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway1
PrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable1
SubnetId: !Ref PrivateSubnet1
PrivateRouteTable2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-private-routes-2'
DefaultPrivateRoute2:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable2
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway2
PrivateSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable2
SubnetId: !Ref PrivateSubnet2
# Security Groups
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub '${AWS::StackName}-alb-sg'
GroupDescription: Security group for Application Load Balancer
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Description: Allow HTTP traffic from internet
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
Description: Allow all outbound traffic
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-alb-sg'
ECSSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub '${AWS::StackName}-ecs-sg'
GroupDescription: Security group for ECS tasks
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: !Ref ContainerPort
ToPort: !Ref ContainerPort
SourceSecurityGroupId: !Ref ALBSecurityGroup
Description: Allow traffic from ALB
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
Description: Allow all outbound traffic
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-ecs-sg'
# Application Load Balancer
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub '${AWS::StackName}-alb'
Scheme: internet-facing
Type: application
Subnets:
- !Ref PublicSubnet1
- !Ref PublicSubnet2
SecurityGroups:
- !Ref ALBSecurityGroup
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-alb'
ALBTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: !Sub '${AWS::StackName}-tg'
Port: !Ref ContainerPort
Protocol: HTTP
VpcId: !Ref VPC
TargetType: ip
HealthCheckIntervalSeconds: 30
HealthCheckPath: /
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
UnhealthyThresholdCount: 5
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-tg'
ALBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref ALBTargetGroup
LoadBalancerArn: !Ref ApplicationLoadBalancer
Port: 80
Protocol: HTTP
# ECS Cluster
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Sub '${AWS::StackName}-cluster'
CapacityProviders:
- FARGATE
- FARGATE_SPOT
DefaultCapacityProviderStrategy:
- CapacityProvider: FARGATE
Weight: 1
- CapacityProvider: FARGATE_SPOT
Weight: 4
ClusterSettings:
- Name: containerInsights
Value: enabled
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-cluster'
# IAM Roles
ECSTaskExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub '${AWS::StackName}-task-execution-role'
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
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-task-execution-role'
ECSTaskRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub '${AWS::StackName}-task-role'
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-task-role'
# CloudWatch Log Group
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub '/ecs/${AWS::StackName}'
RetentionInDays: 7
# ECS Task Definition
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: !Sub '${AWS::StackName}-task'
Cpu: '256'
Memory: '512'
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn
TaskRoleArn: !GetAtt ECSTaskRole.Arn
ContainerDefinitions:
- Name: !Ref ServiceName
Image: !Ref ContainerImage
PortMappings:
- ContainerPort: !Ref ContainerPort
Protocol: tcp
Essential: true
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref LogGroup
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: ecs
HealthCheck:
Command:
- CMD-SHELL
- curl -f http://localhost/ || exit 1
Interval: 30
Timeout: 5
Retries: 3
StartPeriod: 60
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-task'
# ECS Service
ECSService:
Type: AWS::ECS::Service
DependsOn: ALBListener
Properties:
ServiceName: !Sub '${AWS::StackName}-service'
Cluster: !Ref ECSCluster
TaskDefinition: !Ref TaskDefinition
DesiredCount: !Ref DesiredCount
LaunchType: FARGATE
PlatformVersion: LATEST
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: DISABLED
SecurityGroups:
- !Ref ECSSecurityGroup
Subnets:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
LoadBalancers:
- ContainerName: !Ref ServiceName
ContainerPort: !Ref ContainerPort
TargetGroupArn: !Ref ALBTargetGroup
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 50
DeploymentCircuitBreaker:
Enable: true
Rollback: true
EnableExecuteCommand: true # For debugging
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-service'
# Auto Scaling Target
ServiceScalingTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
Properties:
MaxCapacity: !Ref MaxCapacity
MinCapacity: !Ref MinCapacity
ResourceId: !Sub 'service/${ECSCluster}/${ECSService.Name}'
RoleARN: !Sub 'arn:aws:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService'
ScalableDimension: ecs:service:DesiredCount
ServiceNamespace: ecs
# Auto Scaling Policy - CPU Utilization
ServiceScalingPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyName: !Sub '${AWS::StackName}-cpu-scaling-policy'
PolicyType: TargetTrackingScaling
ScalingTargetId: !Ref ServiceScalingTarget
TargetTrackingScalingPolicyConfiguration:
PredefinedMetricSpecification:
PredefinedMetricType: ECSServiceAverageCPUUtilization
TargetValue: 70.0
ScaleOutCooldown: 300
ScaleInCooldown: 300
Outputs:
VPCId:
Description: VPC ID
Value: !Ref VPC
Export:
Name: !Sub '${AWS::StackName}-VPC-ID'
LoadBalancerURL:
Description: URL of the Application Load Balancer
Value: !Sub 'http://${ApplicationLoadBalancer.DNSName}'
Export:
Name: !Sub '${AWS::StackName}-ALB-URL'
ECSClusterName:
Description: Name of the ECS Cluster
Value: !Ref ECSCluster
Export:
Name: !Sub '${AWS::StackName}-ECS-Cluster'
ECSServiceName:
Description: Name of the ECS Service
Value: !GetAtt ECSService.Name
Export:
Name: !Sub '${AWS::StackName}-ECS-Service'
PrivateSubnet1:
Description: Private Subnet 1 ID
Value: !Ref PrivateSubnet1
Export:
Name: !Sub '${AWS::StackName}-Private-Subnet-1'
PrivateSubnet2:
Description: Private Subnet 2 ID
Value: !Ref PrivateSubnet2
Export:
Name: !Sub '${AWS::StackName}-Private-Subnet-2'
EOF
이 튜토리얼에서 사용한 템플릿은 Fargate에서 실행되는 두 태스크가 있는 Amazon ECS 서비스를 만들어요. 각 태스크는 샘플 Amazon ECS 애플리케이션을 실행해요. 템플릿은 또한 애플리케이션 트래픽을 분산하는 Application Load Balancer와 CPU 활용도에 따라 애플리케이션을 스케일링하는 Application Auto Scaling 정책을 만들어요. 템플릿은 애플리케이션 배포에 필요한 네트워킹 리소스, 컨테이너 로그용 로깅 리소스, Amazon ECS 태스크 실행 IAM 역할도 만들어요.