This reference architecture provides a set of YAML templates for deploying micro-services to Amazon EC2 Container Service (Amazon ECS) using the AWS Fargate launchtype with AWS CloudFormation.
You can launch this CloudFormation stack in your account:
AWS Region | Short name | |
---|---|---|
US East (N. Virginia) | us-east-1 |
The repository consists of a set of nested templates that deploy the following:
- A tiered VPC with public and private subnets, spanning an AWS region.
- An ECS cluster that your Fargate tasks will be launched into.
- A pair of NAT gateways (one in each AZ) to handle outbound traffic.
- Two interconnecting micro-services deployed as ECS services (website-service and product-service).
- An Application Load Balancer (ALB) to the public subnets to handle inbound traffic.
- ALB path-based routes for each ECS service to route the inbound traffic to the correct service.
- Centralized container logging with Amazon CloudWatch Logs.
Using CloudFormation to deploy and manage services with ECS has a number of nice benefits over more traditional methods (AWS CLI, scripting, etc.).
A template can be used repeatedly to create identical copies of the same stack (or to use as a foundation to start a new stack). Templates are simple YAML- or JSON-formatted text files that can be placed under your normal source control mechanisms, stored in private or public locations such as Amazon S3, and exchanged via email. With CloudFormation, you can see exactly which AWS resources make up a stack. You retain full control and have the ability to modify any of the AWS resources created as part of a stack.
Fed up with outdated documentation on your infrastructure or environments? Still keep manual documentation of IP ranges, security group rules, etc.?
With CloudFormation, your template becomes your documentation. Want to see exactly what you have deployed? Just look at your template. If you keep it in source control, then you can also look back at exactly which changes were made and by whom.
CloudFormation not only handles the initial deployment of your infrastructure and environments, but it can also manage the whole lifecycle, including future updates. During updates, you have fine-grained control and visibility over how changes are applied, using functionality such as change sets, rolling update policies and stack policies.
The templates below are included in this repository and reference architecture:
Template | Description |
---|---|
master.yaml | This is the master template - deploy it to CloudFormation and it deploys all of the others automatically. |
infrastructure/vpc.yaml | This template deploys a VPC with a pair of public and private subnets spread across two Availability Zones. It deploys an Internet gateway, with a default route on the public subnets. It deploys a pair of NAT gateways (one in each AZ), and default routes for them in the private subnets. |
infrastructure/security-groups.yaml | This template contains the security groups required by the entire stack. They are created in a separate nested template so that they can be referenced by all of the other nested templates. |
infrastructure/load-balancers.yaml | This template deploys an ALB to the public subnets, which exposes the various ECS services. It is created in in a separate nested template so that it can be referenced by all of the other nested templates and so that the various ECS services can register with it. |
infrastructure/ecs-cluster.yaml | This template deploys an ECS cluster and creates an ECS Task Execution IAM Role. |
services/product-service/service.yaml | This is an example of a long-running ECS service that serves a JSON API of products. For the full source for the service, see services/product-service/src. |
services/website-service/service.yaml | This is an example of a long-running ECS service that needs to connect to another service (product-service) via the load-balanced URL. We use an environment variable to pass the product-service URL to the containers. For the full source for this service, see services/website-service/src. |
After the CloudFormation templates have been deployed, the stack outputs contain a link to the load-balanced URLs for each of the deployed micro-services.
You can launch this CloudFormation stack in your account:
AWS Region | Short name | |
---|---|---|
US East (N. Virginia) | us-east-1 |
- Fork this GitHub repository.
- Clone the forked GitHub repository to your local machine.
- Modify the templates.
- Upload them to an Amazon S3 bucket of your choice.
- Either create a new CloudFormation stack by deploying the master.yaml template, or update your existing stack with your version of the templates.
- Push your container to a registry somewhere (e.g., Amazon ECR).
- Copy one of the existing service templates in services/*.
- Update the
ContainerName
andImage
parameters to point to your container image instead of the example container. - Increment the
ListenerRule
priority number (no two services can have the same priority number - this is used to order the ALB path based routing rules). - Copy one of the existing service definitions in master.yaml and point it at your new service template. Specify the HTTP
Path
at which you want the service exposed. - Deploy the templates as a new stack, or as an update to an existing stack.
By default, the containers in your ECS tasks/services are already configured to send log information to CloudWatch Logs and retain them for 365 days. Within each service's template (in services/*), a LogGroup is created that is named after the CloudFormation stack. All container logs are sent to that CloudWatch Logs log group, using a prefix of the form Stack-Name/Container-Name/ECS-Task-ID
.
You can view the logs by looking in your CloudWatch Logs console (make sure you are in the correct AWS region).
ECS Fargate only supports the awslogs
logging driver, and you can find more information in the Using the awslogs Log Driver documentation. You can also adjust the log retention period from the default 365 days by tweaking the RetentionInDays
parameter.
For more information, see the LogConfiguration API operation.
AWS Fargate introduced a new concept known as task-level resources, which allows you to set a ceiling for the total amount of resources a task can consume. This enables Fargate to allocate capacity on your behalf when launching a task. Task-level resources are required parameters for Fargate tasks, and must be defined according to one of the many supported configurations.
These resources are defined in the master.yaml templates and can be changed by modifying the ProductService
or WebsiteService
sections as illustrated below:
ProductService:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: ...
Parameters:
...
TaskCpu: 512 # cpu-units
TaskMemory: 1024 # megabyes
...
As well as defining compute resources at the task level, you can continue to define these resources at a container level within your tasks. This is useful if you have multiple containers running within your task which require unequal shares of the task-level resources, or if it is more important to allocate resources to some containers relevant to others within a task.
There are three parameters you can define at the container level: CPU, Memory, and MemoryReservation. Cpu
and MemoryReservation
both set the amount of resource to reserve for the container, so can be used as a way to guarantee container performance. Memory
sets a hard limit of memory to present to a container.
These resources are defined in the relevant service template (in services/*/service.yaml) and can be defined using the Cpu
, Memory
, and MemoryReservation
parameters:
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: ...
ContainerDefinitions:
- Name: ...
# CPU and Memory reservations are set to 50% of the default values specified above
Cpu: 256 # cpu-units
Memory: 1024 # megabytes
MemoryReservation: 512 # megabytes
...
Deploy another CloudFormation stack from the same set of templates to create a new environment. The stack name provided when deploying the stack is prefixed to all taggable resources (e.g., EC2 instances, VPCs, etc.) so you can distinguish the different environment resources in the AWS Management Console.
This set of templates deploys the following network design:
Item | CIDR Range | Usable IPs | Description |
---|---|---|---|
VPC | 10.180.0.0/16 | 65,536 | The whole range used for the VPC and all subnets |
Public Subnet | 10.180.8.0/21 | 2,041 | The public subnet in the first Availability Zone |
Public Subnet | 10.180.16.0/21 | 2,041 | The public subnet in the second Availability Zone |
Private Subnet | 10.180.24.0/21 | 2,041 | The private subnet in the first Availability Zone |
Private Subnet | 10.180.32.0/21 | 2,041 | The private subnet in the second Availability Zone |
You can adjust the CIDR ranges used in this section of the master.yaml template:
VPC:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Sub ${TemplateLocation}/infrastructure/vpc.yaml
Parameters:
EnvironmentName: !Ref AWS::StackName
VpcCIDR: 10.180.0.0/16
PublicSubnet1CIDR: 10.180.8.0/21
PublicSubnet2CIDR: 10.180.16.0/21
PrivateSubnet1CIDR: 10.180.24.0/21
PrivateSubnet2CIDR: 10.180.32.0/21
If you found yourself wishing this set of frequently asked questions had an answer for a particular problem, please submit a pull request. The chances are that others will also benefit from having the answer listed here.
Please create a new GitHub issue for any feature requests, bugs, or documentation improvements.
Where possible, please also submit a pull request for the change.
Copyright 2011-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at
http://aws.amazon.com/apache2.0/
or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.