In this post we will review a CloudFormation stack to create an ECS service.
Using ECS we can quickly deploy our docker based service on the cloud. In this example we will use the ECS Fargate mode which is a serverless deployment.
We start with a VPC deployment, including 2 subnets and and internet gateway allowing access to the internet.
vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
Tags:
- Key: Name
Value: backend-vpc
subnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref vpc
CidrBlock: 10.0.1.0/24
AvailabilityZone: us-east-1a
Tags:
- Key: Name
Value: backend-subnet1
subnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref vpc
CidrBlock: 10.0.2.0/24
AvailabilityZone: us-east-1b
Tags:
- Key: Name
Value: backend-subnet2
internetGateway:
Type: AWS::EC2::InternetGateway
DependsOn: vpc
Properties:
Tags:
- Key: Name
Value: backend-igw
attachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref vpc
InternetGatewayId: !Ref internetGateway
routeTable1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref vpc
Tags:
- Key: Name
Value: cuustomer-route-table1
routeTable2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref vpc
Tags:
- Key: Name
Value: backend-route-table2
routeTableAssociate1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref subnet1
RouteTableId: !Ref routeTable1
routeTableAssociate2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref subnet2
RouteTableId: !Ref routeTable2
publicRoute1:
Type: AWS::EC2::Route
DependsOn: attachGateway
Properties:
RouteTableId: !Ref routeTable1
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref internetGateway
publicRoute2:
Type: AWS::EC2::Route
DependsOn: attachGateway
Properties:
RouteTableId: !Ref routeTable2
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref internetGateway
vpcSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref vpc
GroupDescription: vpcSecurityGroup
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: backend-vpc-security-group
An optional, but recommended step, is to add a bastion server, that is a server we can bash into, and check connections within the vpc.
Parameters:
LatestAmiId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
Resources:
bastionSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref vpc
GroupDescription: bastionSecurityGroup
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: bastion-vpc-security-group
bastionServer1:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: !Ref LatestAmiId
SubnetId: !Ref subnet1
KeyName: ec2
SecurityGroupIds:
- !Ref bastionSecurityGroup
Tags:
- Key: Name
Value: bastion-server1
elasticIP1:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
InstanceId: !Ref bastionServer1
Tags:
- Key: Name
Value: bastion-elastic-ip1
The last thing we need to do is to create a load balancer allowing access to the service, and an ECS service that maintains our desired amount of containers. Note that the task definition includes the tag of the image that we want to run. In this case we use an image from AWS ECR. To view the logs of the containers, we configure a CloudWatch log group.
Notice that the security group enables access to the containers both from the load balancer, and from the bastion server.
loadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: loadBalancerSecurityGroup
VpcId: !Ref vpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
loadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: backend-alb
Scheme: internet-facing
SecurityGroups:
- !Ref loadBalancerSecurityGroup
Subnets:
- !Ref subnet1
- !Ref subnet2
targetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: backend-vpc-target-group
Port: 8080
Protocol: HTTP
TargetType: ip
VpcId: !Ref vpc
loadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref loadBalancer
Port: 80
Protocol: HTTP
DefaultActions:
- TargetGroupArn: !Ref targetGroup
Type: forward
taskExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: backend-task-role
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: 'sts:AssumeRole'
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
cloudwatchLogsGroup:
Type: 'AWS::Logs::LogGroup'
Properties:
LogGroupName: backend-log-group
RetentionInDays: 3
taskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
NetworkMode: awsvpc
ExecutionRoleArn: !Ref taskExecutionRole
RequiresCompatibilities:
- FARGATE
Cpu: 256
Memory: 512
ContainerDefinitions:
- Name: origin
Image: MY-ACCOUNT-NUMBER.dkr.ecr.us-east-1.amazonaws.com/MY-IMAGE:latest
PortMappings:
- ContainerPort: 8080
Protocol: tcp
Environment:
- Name: PORT
Value: 8080
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref cloudwatchLogsGroup
awslogs-region: us-east-1
awslogs-stream-prefix: origin
ecsCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: backend-cluster
CapacityProviders:
- FARGATE
- FARGATE_SPOT
DefaultCapacityProviderStrategy:
- CapacityProvider: FARGATE
Weight: 1
- CapacityProvider: FARGATE_SPOT
Weight: 1
containerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: containerSecurityGroup
VpcId: !Ref vpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 8080
ToPort: 8080
SourceSecurityGroupId: !Ref loadBalancerSecurityGroup
- IpProtocol: tcp
FromPort: 8080
ToPort: 8080
SourceSecurityGroupId: !Ref bastionSecurityGroup
ecsService:
Type: AWS::ECS::Service
DependsOn: loadBalancerListener
Properties:
Cluster: !Ref ecsCluster
DesiredCount: 2
TaskDefinition: !Ref taskDefinition
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
Subnets:
- !Ref subnet1
- !Ref subnet2
SecurityGroups:
- !Ref containerSecurityGroup
LoadBalancers:
- ContainerName: origin
ContainerPort: 8080
TargetGroupArn: !Ref targetGroup
No comments:
Post a Comment