Using Amazon ECS Fargate with AWS SAM CLI
How to deploy a simple public facing application on AWS Fargate using AWS SAM CLI
Dependencies
This pattern uses the AWS SAM CLI for deploying CloudFormation stacks on your AWS account.
You should follow the appropriate steps for installing SAM CLI .
You should follow the appropriate steps for installing SAM CLI .
About
AWS SAM CLI streamlines the deployment of your containerized applications and necessary infrastructure resources efficiently. An extension of AWS CloudFormation, SAM CLI simplifies serverless application deployment and management by allowing you to define infrastructure as code. This facilitates version control and reproducibility, simplifying the packaging and deployment of your application code, dependencies, and configurations.
This pattern will show how to deploy a simple nodeJS application to AWS Fargate using AWS SAM CLI. The following resources will be created as part of the provided templates:
- Fargate Cluster
- Amazon Elastic Container Registry
- Fargate Service
- Fargate Task Definition
- Amazon VPC
- Internet Gateway
- 2 Public Subnets
- 2 Private Subnets
- Application Load Balancer
Architecture
The following diagram shows the architecture that will be deployed:

Define your Infrastructure
The following AWS SAM CLI template.yml creates a simple Amazon ECS cluster using AWS Fargate.
As part of the
template.yml, the following resources will be created:- Amazon ECS Cluster
- ECR Repo
- Log Group
- All IAM related roles/policies
In addition to the
template.yml, you will also need a vpc.yml, where the necessary network resources are defined.Finally, AWS SAM CLI will also look for a samconfig file, which contains default parameters for your Infrastructure as Code.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File: template.yml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AWS Fargate with VPC, ALB, and ECR
Parameters:
ImageTag:
Description: tag name for image
Type: String
Default: latest
Globals:
Function:
Timeout: 3
MemorySize: 128
Resources:
VPC:
Type: AWS::Serverless::Application
Properties:
Location: ./vpc.yml
ECRRepo:
Type: AWS::ECR::Repository
Properties:
EmptyOnDelete: true
Cluster:
Type: AWS::ECS::Cluster
Properties:
CapacityProviders:
- FARGATE
Service:
Type: AWS::ECS::Service
Properties:
ServiceName: "hello-world"
Cluster: !Ref Cluster
LaunchType: FARGATE
EnableExecuteCommand: true
HealthCheckGracePeriodSeconds: 5
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
Subnets: [!GetAtt "VPC.Outputs.PublicSubnet1", !GetAtt VPC.Outputs.PublicSubnet2]
SecurityGroups: [!GetAtt VPC.Outputs.SG]
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 50
DesiredCount: 1
TaskDefinition: !Ref "TaskDefinition"
LoadBalancers:
- ContainerName: "hello-world"
ContainerPort: 3000
TargetGroupArn: !GetAtt VPC.Outputs.LB
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: HelloWorld
Cpu: 1024
Memory: 8192
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn
TaskRoleArn: !Ref ECSTaskRole
RuntimePlatform:
CpuArchitecture: X86_64
ContainerDefinitions:
- Name: hello-world
Cpu: 1024
Memory: 8192
Image: !Sub
- ${RepoUrl}:${ImageTag}
- RepoUrl: !GetAtt ECRRepo.RepositoryUri
PortMappings:
- ContainerPort: 3000
LogConfiguration:
LogDriver: awslogs
Options:
mode: non-blocking
max-buffer-size: 25m
awslogs-group: !Ref LogGroup
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: containerlog
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /fargatelogs
ECSTaskExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ecs-tasks.amazonaws.com]
Action: ['sts:AssumeRole']
Condition:
ArnLike:
aws:SourceArn: !Sub arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:*
StringEquals:
aws:SourceAccount: !Ref AWS::AccountId
Path: /
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']
Condition:
ArnLike:
aws:SourceArn: !Sub arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:*
StringEquals:
aws:SourceAccount: !Ref AWS::AccountId
Path: /
Outputs:
ClusterName:
Description: Amazon ECS Cluster Name
Value: !Ref Cluster
ServiceName:
Description: Amazon ECS Service Name
Value: !GetAtt Service.Name
FQDN:
Description: URL for your application
Value: !GetAtt VPC.Outputs.PublicLBFQDN
RepositoryUrl:
Description: URL of the repo
Value: !GetAtt ECRRepo.RepositoryUri1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File: template.yml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AWS Fargate with VPC, ALB, and ECR
Parameters:
ImageTag:
Description: tag name for image
Type: String
Default: latest
Globals:
Function:
Timeout: 3
MemorySize: 128
Resources:
VPC:
Type: AWS::Serverless::Application
Properties:
Location: ./vpc.yml
ECRRepo:
Type: AWS::ECR::Repository
Properties:
EmptyOnDelete: true
Cluster:
Type: AWS::ECS::Cluster
Properties:
CapacityProviders:
- FARGATE
Service:
Type: AWS::ECS::Service
Properties:
ServiceName: "hello-world"
Cluster: !Ref Cluster
LaunchType: FARGATE
EnableExecuteCommand: true
HealthCheckGracePeriodSeconds: 5
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
Subnets: [!GetAtt "VPC.Outputs.PublicSubnet1", !GetAtt VPC.Outputs.PublicSubnet2]
SecurityGroups: [!GetAtt VPC.Outputs.SG]
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 50
DesiredCount: 1
TaskDefinition: !Ref "TaskDefinition"
LoadBalancers:
- ContainerName: "hello-world"
ContainerPort: 3000
TargetGroupArn: !GetAtt VPC.Outputs.LB
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: HelloWorld
Cpu: 1024
Memory: 8192
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn
TaskRoleArn: !Ref ECSTaskRole
RuntimePlatform:
CpuArchitecture: X86_64
ContainerDefinitions:
- Name: hello-world
Cpu: 1024
Memory: 8192
Image: !Sub
- ${RepoUrl}:${ImageTag}
- RepoUrl: !GetAtt ECRRepo.RepositoryUri
PortMappings:
- ContainerPort: 3000
LogConfiguration:
LogDriver: awslogs
Options:
mode: non-blocking
max-buffer-size: 25m
awslogs-group: !Ref LogGroup
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: containerlog
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /fargatelogs
ECSTaskExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ecs-tasks.amazonaws.com]
Action: ['sts:AssumeRole']
Condition:
ArnLike:
aws:SourceArn: !Sub arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:*
StringEquals:
aws:SourceAccount: !Ref AWS::AccountId
Path: /
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']
Condition:
ArnLike:
aws:SourceArn: !Sub arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:*
StringEquals:
aws:SourceAccount: !Ref AWS::AccountId
Path: /
Outputs:
ClusterName:
Description: Amazon ECS Cluster Name
Value: !Ref Cluster
ServiceName:
Description: Amazon ECS Service Name
Value: !GetAtt Service.Name
FQDN:
Description: URL for your application
Value: !GetAtt VPC.Outputs.PublicLBFQDN
RepositoryUrl:
Description: URL of the repo
Value: !GetAtt ECRRepo.RepositoryUriYou will also need an application you can deploy to your infrastructure. For the purpose of this demo, a simple Hello World nodeJS application is provided for you.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// File: index.js
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('Hello World!')
res.status(200).json
})
app.listen(port, () => {
console.log(`Access your application at`, `http://localhost:${port}`)
})
module.exports = app1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// File: index.js
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('Hello World!')
res.status(200).json
})
app.listen(port, () => {
console.log(`Access your application at`, `http://localhost:${port}`)
})
module.exports = appOnce you have the files downloaded, you can deploy your infrastructure using the following commands:
1
sam build && sam deployTest it Out
After the provided templates are deployed, you can find the the following AWS CloudFormation outputs using the following commands:
1
2
3
RepositoryUrl=$(aws cloudformation --region us-east-1 describe-stacks --stack-name nodejs-sam --query "Stacks[0].Outputs[?OutputKey=='RepositoryUrl'].OutputValue" --output text) && echo $RepositoryUrl
FQDN=$(aws cloudformation --region us-east-1 describe-stacks --stack-name nodejs-sam --query "Stacks[0].Outputs[?OutputKey=='FQDN'].OutputValue" --output text) && echo $FQDN⚠️ Warning: You will want to build and push your container image prior to deploying this application to Fargate.The default template will look for the image in your provisioned Amazon Elastic Container Registry with the tag "latest". Once the image exists, you will be able to test your application using the output retrieved from the FQDN command.
💡 Tip: In Production, it's not best practice to use thelatesttag for your containerized application images. Instead, you'll want to tag your images per release as part of your CI/CD workflow or pipeline.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article