Amazon ECS/Fargateを使用した、安全でスケーラブルなコンテナデプロイ環境のテンプレートです。マルチAZ対応、ALBによる負荷分散、最小特権のIAMロールが自動的にセットアップされます。
AWS CLIを使用してCloudFormationスタックをデプロイする場合は、以下のコマンドを実行します。
aws cloudformation create-stack \ --stack-name container-orchestration-stack \ --template-body file://container-orchestration.yaml \ --capabilities CAPABILITY_IAM
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Container Orchestration Pattern with ECS Fargate - Best Practices'
Parameters:
Environment:
Type: String
Default: production
AllowedValues: [production, staging, development]
Resources:
# VPC
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: true
EnableDnsSupport: true
# Public Subnets
PublicSubnetOne:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.0.0/24
AvailabilityZone: !Select [0, !GetAZs '']
MapPublicIpOnLaunch: true
PublicSubnetTwo:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.1.0/24
AvailabilityZone: !Select [1, !GetAZs '']
MapPublicIpOnLaunch: true
# Private Subnets
PrivateSubnetOne:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.2.0/24
AvailabilityZone: !Select [0, !GetAZs '']
PrivateSubnetTwo:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.3.0/24
AvailabilityZone: !Select [1, !GetAZs '']
# Internet Gateway
InternetGateway:
Type: AWS::EC2::InternetGateway
GatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
# NAT Gateways
NatGatewayOneEIP:
Type: AWS::EC2::EIP
DependsOn: GatewayAttachment
Properties:
Domain: vpc
NatGatewayTwoEIP:
Type: AWS::EC2::EIP
DependsOn: GatewayAttachment
Properties:
Domain: vpc
NatGatewayOne:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGatewayOneEIP.AllocationId
SubnetId: !Ref PublicSubnetOne
NatGatewayTwo:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGatewayTwoEIP.AllocationId
SubnetId: !Ref PublicSubnetTwo
# Route Tables
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PublicRoute:
Type: AWS::EC2::Route
DependsOn: GatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnetOneRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetOne
RouteTableId: !Ref PublicRouteTable
PublicSubnetTwoRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetTwo
RouteTableId: !Ref PublicRouteTable
PrivateRouteTableOne:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PrivateRouteOne:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTableOne
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGatewayOne
PrivateSubnetOneRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnetOne
RouteTableId: !Ref PrivateRouteTableOne
PrivateRouteTableTwo:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PrivateRouteTwo:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTableTwo
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGatewayTwo
PrivateSubnetTwoRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnetTwo
RouteTableId: !Ref PrivateRouteTableTwo
# VPC Endpoints for AWS Services
S3VPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !Ref VPC
ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
RouteTableIds:
- !Ref PrivateRouteTableOne
- !Ref PrivateRouteTableTwo
ECRApiVPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !Ref VPC
ServiceName: !Sub com.amazonaws.${AWS::Region}.ecr.api
VpcEndpointType: Interface
PrivateDnsEnabled: true
SubnetIds:
- !Ref PrivateSubnetOne
- !Ref PrivateSubnetTwo
SecurityGroupIds:
- !Ref VPCEndpointSecurityGroup
ECRDkrVPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !Ref VPC
ServiceName: !Sub com.amazonaws.${AWS::Region}.ecr.dkr
VpcEndpointType: Interface
PrivateDnsEnabled: true
SubnetIds:
- !Ref PrivateSubnetOne
- !Ref PrivateSubnetTwo
SecurityGroupIds:
- !Ref VPCEndpointSecurityGroup
CloudWatchLogsVPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !Ref VPC
ServiceName: !Sub com.amazonaws.${AWS::Region}.logs
VpcEndpointType: Interface
PrivateDnsEnabled: true
SubnetIds:
- !Ref PrivateSubnetOne
- !Ref PrivateSubnetTwo
SecurityGroupIds:
- !Ref VPCEndpointSecurityGroup
# Security Groups
VPCEndpointSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for VPC endpoints
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
SourceSecurityGroupId: !Ref FargateContainerSecurityGroup
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Access to the Application Load Balancer
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
FargateContainerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Access to Fargate containers
VpcId: !Ref VPC
EcsSecurityGroupIngressFromALB:
Type: AWS::EC2::SecurityGroupIngress
Properties:
Description: Ingress from ALB
GroupId: !Ref FargateContainerSecurityGroup
IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !Ref ALBSecurityGroup
EcsSecurityGroupIngressFromSelf:
Type: AWS::EC2::SecurityGroupIngress
Properties:
Description: Ingress from containers in same security group
GroupId: !Ref FargateContainerSecurityGroup
IpProtocol: -1
SourceSecurityGroupId: !Ref FargateContainerSecurityGroup
# Application Load Balancer
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Type: application
Scheme: internet-facing
IpAddressType: ipv4
Subnets:
- !Ref PublicSubnetOne
- !Ref PublicSubnetTwo
SecurityGroups:
- !Ref ALBSecurityGroup
ALBTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
TargetType: ip
Protocol: HTTP
Port: 80
VpcId: !Ref VPC
HealthCheckEnabled: true
HealthCheckProtocol: HTTP
HealthCheckPath: /health
HealthCheckIntervalSeconds: 30
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
UnhealthyThresholdCount: 3
Matcher:
HttpCode: 200
ALBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref ApplicationLoadBalancer
Protocol: HTTP
Port: 80
DefaultActions:
- Type: forward
TargetGroupArn: !Ref ALBTargetGroup
# ECS Cluster
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterSettings:
- Name: containerInsights
Value: enabled
Configuration:
ExecuteCommandConfiguration:
Logging: DEFAULT
# CloudWatch Log Group
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
RetentionInDays: 7
# ECR Repository
ECRRepository:
Type: AWS::ECR::Repository
Properties:
ImageScanningConfiguration:
ScanOnPush: true
EncryptionConfiguration:
EncryptionType: AES256
LifecyclePolicy:
LifecyclePolicyText: |
{
"rules": [{
"rulePriority": 1,
"description": "Keep last 10 images",
"selection": {
"tagStatus": "any",
"countType": "imageCountMoreThan",
"countNumber": 10
},
"action": { "type": "expire" }
}]
}
# IAM Roles
ECSTaskExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
ECSTaskRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
# ECS Task Definition
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: app-task
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: 256
Memory: 512
ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn
TaskRoleArn: !GetAtt ECSTaskRole.Arn
ContainerDefinitions:
- Name: app-container
Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ECRRepository}:latest
Essential: true
PortMappings:
- ContainerPort: 80
Protocol: tcp
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref LogGroup
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: ecs
Environment:
- Name: ENVIRONMENT
Value: !Ref Environment
# ECS Service
ECSService:
Type: AWS::ECS::Service
DependsOn: ALBListener
Properties:
Cluster: !Ref ECSCluster
LaunchType: FARGATE
TaskDefinition: !Ref TaskDefinition
DesiredCount: 2
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: DISABLED
Subnets:
- !Ref PrivateSubnetOne
- !Ref PrivateSubnetTwo
SecurityGroups:
- !Ref FargateContainerSecurityGroup
LoadBalancers:
- ContainerName: app-container
ContainerPort: 80
TargetGroupArn: !Ref ALBTargetGroup
HealthCheckGracePeriodSeconds: 60
EnableExecuteCommand: true
# Auto Scaling
ServiceScalingTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
Properties:
MaxCapacity: 10
MinCapacity: 2
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
ServiceScalingPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyName: cpu-scaling-policy
PolicyType: TargetTrackingScaling
ScalingTargetId: !Ref ServiceScalingTarget
TargetTrackingScalingPolicyConfiguration:
PredefinedMetricSpecification:
PredefinedMetricType: ECSServiceAverageCPUUtilization
TargetValue: 70.0
ScaleInCooldown: 300
ScaleOutCooldown: 60
# CloudWatch Alarms
HighCPUAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: Alert when CPU exceeds 80%
MetricName: CPUUtilization
Namespace: AWS/ECS
Statistic: Average
Period: 300
EvaluationPeriods: 2
Threshold: 80
ComparisonOperator: GreaterThanThreshold
Dimensions:
- Name: ClusterName
Value: !Ref ECSCluster
- Name: ServiceName
Value: !GetAtt ECSService.Name
HighMemoryAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: Alert when memory exceeds 80%
MetricName: MemoryUtilization
Namespace: AWS/ECS
Statistic: Average
Period: 300
EvaluationPeriods: 2
Threshold: 80
ComparisonOperator: GreaterThanThreshold
Dimensions:
- Name: ClusterName
Value: !Ref ECSCluster
- Name: ServiceName
Value: !GetAtt ECSService.Name
Outputs:
LoadBalancerURL:
Description: URL of the Application Load Balancer
Value: !Sub http://${ApplicationLoadBalancer.DNSName}
ECSClusterName:
Description: Name of the ECS Cluster
Value: !Ref ECSCluster
ECRRepositoryURI:
Description: URI of the ECR Repository
Value: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ECRRepository}