AWS Fargate Microservices with CloudFormation | basic infrastructure

Stefano Monti
AWS Infrastructure
Published in
4 min readDec 28, 2022

In this article, we are going to deploy a microservices architecture using Docker, AWS Fargate, and Cloudformation.

Prerequisites

You must have an AWS account and aws-cli installed on your machine. Here you can find instructions to satisfy the prerequisites for this tutorial.

Let’s Start!!

Here you can find the templates for the deployment.
This is what we have:

— vpc stack that contains the vpc resources, this is basically where our application will run;
— cluster stack that contains the fargate cluster, the load balancer, and all the stuff needed to let our application work correctly;
— sh script with useful commands for running deploys from the command line (it uses AWS-CLI, if you don’t know what is it, check this).

VPC Stack

$ aws cloudformation create-stack --stack-name cf-infra-aws-ecs-microservices-vpc-stack --template-body file://./vpc-stack.yml

Read this article to understand what exactly this stack contains.
Pay attention to the Outputs section:


Outputs:
StwVpcId:
Description: Vpc Id
Value: !Ref StwVpc
Export:
Name: !Sub "${AWS::StackName}-Vpc-Id"
StwSubnetPublic1:
Description: SubnetPublic1 Id
Value: !Ref StwSubnetPublic1
Export:
Name: !Sub "${AWS::StackName}-SubnetPublic1-Id"
StwSubnetPrivate1:
Description: SubnetPrivate1 Id
Value: !Ref StwSubnetPrivate1
Export:
Name: !Sub "${AWS::StackName}-SubnetPrivate1-Id"
StwSubnetPrivate2:
Description: SubnetPrivate2 Id
Value: !Ref StwSubnetPrivate2
Export:
Name: !Sub "${AWS::StackName}-SubnetPrivate2-Id"

Network stack outputs will be used by other stacks in the next steps so, they are very important; if you want to learn more about outputs and import between cloudformation stacks read this.

The following is our actual situation:

To make it simple, route tables and nat gateway won’t be reported in the next schemas; so, this is our starting situation:

Cluster and Load Balancer

Let’s begin by creating a Fargate cluster:

  ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: 'stw-aws-ecs-microservices'

This initializes the cluster where our containers will run.

Now the Load Balancer; will be internal type, it will be possible to reach it from only inside the VPC (this could be an issue in case of testing the APIs, but is it possible to create an APIGateway integration).
It is also present in the template a load balancer listener with a default target group where will be routed all the calls that don’t match any rules.
Finally, has been inserted a security group with a rule to allow all ingress requests (this is could be a bad choice in the case of a public load balancer)

  LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Scheme: internal
Name: ecs-services
Subnets:
- Fn::ImportValue:
!Sub "${VPCStackName}-SubnetPrivate1-Id"
- Fn::ImportValue:
!Sub "${VPCStackName}-SubnetPrivate2-Id"
SecurityGroups:
- !Ref LoadBalancerSecurityGroup

LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref LoadBalancer
Protocol: HTTP
Port: 80
DefaultActions:
- Type: forward
TargetGroupArn: !Ref DefaultTargetGroup


DefaultTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: default
VpcId:
Fn::ImportValue:
!Sub "${VPCStackName}-Vpc-Id"
Protocol: 'HTTP'
Port: '80'

LoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for loadbalancer to services on ECS
VpcId:
Fn::ImportValue:
!Sub "${VPCStackName}-Vpc-Id"
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
IpProtocol: -1

Last but not least, we need
— a cloud watch log group for giving our container a channel of communication with us;
— a security group that allows the containers to receive requests only by the load balancer (everything will pass from the load balancer!!);
— a role to assign to the containers for downloading images from ecr and writing logs into the log group described before.

  CloudWatchLogsGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: 'stw-aws-ecs-microservices-lg'
RetentionInDays: 1

ContainerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId:
Fn::ImportValue:
!Sub "${VPCStackName}-Vpc-Id"
GroupDescription: for ecs containers
SecurityGroupIngress:
- SourceSecurityGroupId: !Ref 'LoadBalancerSecurityGroup'
IpProtocol: -1

ECSTaskExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ecs-tasks.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: AmazonECSTaskExecutionRolePolicy
PolicyDocument:
Statement:
- Effect: Allow
Action:
# ECS Tasks to download images from ECR
- 'ecr:GetAuthorizationToken'
- 'ecr:BatchCheckLayerAvailability'
- 'ecr:GetDownloadUrlForLayer'
- 'ecr:BatchGetImage'
# ECS tasks to upload logs to CloudWatch
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Resource: '*'

All these 3 resources will be exported to permit the single services to use them.

Output section:

Outputs:

Cluster:
Value: !Ref ECSCluster
Export:
Name: !Sub ${AWS::StackName}-Cluster

Listener:
Description: listener port 80
Value: !Ref LoadBalancerListener
Export:
Name: !Sub ${AWS::StackName}-Listener

ContainerSecurityGroup:
Description: container security group
Value: !Ref ContainerSecurityGroup
Export:
Name: !Sub ${AWS::StackName}-ContainerSecurityGroup

LoadBalancerDNS:
Description: Domain name for the loadbalancer
Value: !GetAtt LoadBalancer.DNSName
Export:
Name: !Sub ${AWS::StackName}-LoadBalancerDNS

ECSTaskExecutionRoleArn:
Description: ECS Task Execution Role
Value: !GetAtt 'ECSTaskExecutionRole.Arn'
Export:
Name: !Sub ${AWS::StackName}-ECSTaskExecutionRole

Well done!! Our common infrastructure is ready; remember to deploy it with the following command (is mandatory to have already deployed the network stack described above):

aws cloudformation create-stack --stack-name cf-infra-aws-ecs-microservices-cluster-stack --template-body file://./cluster-stack.yml --capabilities CAPABILITY_NAMED_IAM \
--parameters ParameterKey=VPCStackName,ParameterValue=cf-infra-aws-ecs-microservices-vpc-stack

Now it is time to create the singles microservices, follow this link if you want to know how.
See you there,
Bye 😘

To be continued …

--

--