Amazon ECS & AWS Proton - Accelerated Global Delivery in IT-Projects

Sven Leiß
awsblackbelt
Published in
13 min readJun 26, 2023

--

Photo by Growtika on Unsplash

Amazon ECS and AWS Proton are powerful services offered by Amazon Web Services. Amazon ECS is a scalable, high-performance container orchestration service that supports Docker containers and allows you to run and scale containerized applications easily[1]. On the other hand, AWS Proton is a fully managed application delivery service for containerized and serverless applications. It provides consistent deployment, infrastructure provisioning, and monitoring across multiple teams and applications[7].

Figure 1: AWS ECS Overview (Source: https://aws.amazon.com/de/ecs/)

Amazon ECS

Components

  1. Clusters: Logical grouping of tasks or services, using either Fargate or EC2 instances for task execution[2].
  2. Tasks: Smallest deployable unit, representing a running set of containers on a single host.
  3. Services: Ensures that the specified number of tasks is consistently running and healthy. Integrates with Elastic Load Balancing for traffic distribution across multiple tasks[3].
  4. Task Definitions: Blueprint for the application, specifying Docker containers, CPU and memory specifications, networking setup, and IAM roles[4].

Advanced Task Management

Amazon ECS provides sophisticated scheduling capabilities, optimizing resource usage, enhancing application performance, and improving resilience through Spread, Binpack, and Random Placement strategies. It also enables task-level and container-level health checks, ensuring high availability and resilience of your application[5][6].

AWS Proton

Components

  1. Templates: Written in CloudFormation, define the resources and architecture for a particular type of service or environment[8].
  2. Services: Instance of a service template. Includes code (deployed via CodePipeline) and other resources defined by the service template.
  3. Environments: Logical grouping of services. Associated with an environment account and environment template[8].
Figure 2: AWS Proton Overview (Source: https://aws.amazon.com/de/proton/)

Advanced Application Delivery

AWS Proton provides centralized governance and management of service templates, ensures organizational adherence to established best practices and compliance requirements, and facilitates a service catalog for developers to deploy their code using pre-approved infrastructure setups. Proton’s integration with AWS CodeStar services and AWS X-Ray and CloudWatch ensures seamless CI/CD pipelines and effective application monitoring[9][10].

AWS Proton Example

AWS is providing a workshop to explain the fundamentals of AWS Proton.

As an Administrator, you create and register an Environment Template with AWS Proton, which defines the shared resources.

AWS Proton deploys one or more Environments, based on an Environment Template.

As an Administrator, you create and register a Service Template with AWS Proton, which defines the related infrastructure, monitoring, and CI/CD resources as well as compatible Environment Templates.

As a Developer, you select a registered Service Template and provide a link to your Source code repository.

AWS Proton provisions the Service with a CI/CD Pipeline for your Service instances.

AWS Proton provisions and manages the Service and the Service Instances that are running the Source code as was defined in the selected Service Template. A Service Instance is an instantiation of the selected Service Template in an Environment for a single stage of a Pipeline (for example Prod).

Based on the described components of AWS Proton AWS is providing the environment template and the service templates for the different tasks.

manifest.yaml

infrastructure:
templates:
- file: "cloudformation.yaml"
rendering_engine: jinja
template_language: cloudformation

Environment Schema

The file multi-svc-env/v1/schema/schema.yaml defines what input parameters are needed to create the environment. In this example; we would need 2 parameters, both of which are required: The VPC CIDR and the hostname for the Route53 Hosted Zone.

schema:                            # required
format: # required
openapi: "3.0.0" # required
# required defined by administrator
environment_input_type: "PublicEnvironmentInput"
types: # required
# defined by administrator
PublicEnvironmentInput:
type: object
description: "Input properties for my environment"
properties:
vpc_cidr: # parameter
type: string
description: "The CIDR range for your VPC"
default: 10.0.0.0/16
pattern: ([0-9]{1,3}\.){3}[0-9]{1,3}($|/(16|24))
dns_hostname: # parameter
type: string
description: "The hostname for the Route53 Hosted Zone"
default: protonworkshop.hosted.local

Infrastructure definition with CloudFormation

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Simple Lambda based HTTP service template

Mappings:
# The VPC and subnet configuration is passed in via the environment spec.
SubnetConfig:
VPC:
CIDR: '{{ environment.inputs.vpc_cidr}}' # customization parameter
DNS:
Hostname: '{{ environment.inputs.dns_hostname}}' # customization parameter

Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
EnableDnsSupport: true
EnableDnsHostnames: true
CidrBlock: !FindInMap ['SubnetConfig', 'VPC', 'CIDR']

PublicSubnetOne:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 0
- Fn::GetAZs: {Ref: 'AWS::Region'}
VpcId: !Ref 'VPC'
CidrBlock: !Select [ 0, !Cidr [ !FindInMap ['SubnetConfig', 'VPC', 'CIDR'], 3, 8 ]]
MapPublicIpOnLaunch: true

PublicSubnetTwo:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 1
- Fn::GetAZs: {Ref: 'AWS::Region'}
VpcId: !Ref 'VPC'
CidrBlock: !Select [ 1, !Cidr [ !FindInMap ['SubnetConfig', 'VPC', 'CIDR'], 3, 8 ]]
MapPublicIpOnLaunch: true

PublicSubnetThree:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 2
- Fn::GetAZs: {Ref: 'AWS::Region'}
VpcId: !Ref 'VPC'
CidrBlock: !Select [ 2, !Cidr [ !FindInMap ['SubnetConfig', 'VPC', 'CIDR'], 3, 8 ]]
MapPublicIpOnLaunch: true

# Setup networking resources for the public subnets. Containers
# in the public subnets have public IP addresses and the routing table
# sends network traffic via the internet gateway.
InternetGateway:
Type: AWS::EC2::InternetGateway
GatewayAttachement:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref 'VPC'
InternetGatewayId: !Ref 'InternetGateway'
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref 'VPC'
PublicRoute:
Type: AWS::EC2::Route
DependsOn: GatewayAttachement
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
PublicSubnetThreeRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetThree
RouteTableId: !Ref PublicRouteTable

# ECS Resources
ECSCluster:
Type: AWS::ECS::Cluster

# A security group for the containers we will run in Fargate.
# Rules are added to this security group based on what ingress you
# add for the cluster.
ContainerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Access to the ECS Containers
VpcId: !Ref 'VPC'

# This is a role to allows ECS container agent makes calls to
# the Amazon ECS API on your behalf.
ECSTaskExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service:
- ecs.amazonaws.com
- ecs-tasks.amazonaws.com
Action: ['sts:AssumeRole']
Path: /
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy'

DnsHostedZone:
Type: AWS::Route53::HostedZone
Properties:
Name: !FindInMap ['SubnetConfig', 'DNS', 'Hostname']
HostedZoneConfig:
Comment: Private hosted zone
VPCs:
- VPCId: !Ref VPC
VPCRegion: !Ref AWS::Region

# These output values are will be available to service templates, given the
# the 'environment' namespace, for example, environment.outputs.ClusterName.
Outputs:
ContainerSecurityGroup:
Description: Container Security group
Value: !Ref 'ContainerSecurityGroup'
ClusterName:
Description: The name of the ECS cluster
Value: !Ref 'ECSCluster'
ECSTaskExecutionRole:
Description: The ARN of the ECS task execution role
Value: !GetAtt 'ECSTaskExecutionRole.Arn'
VpcId:
Description: The ID of the VPC that this stack is deployed in
Value: !Ref 'VPC'
PublicSubnetOne:
Description: Public subnet one
Value: !Ref 'PublicSubnetOne'
PublicSubnetTwo:
Description: Public subnet two
Value: !Ref 'PublicSubnetTwo'
PublicSubnetThree:
Description: Public subnet three
Value: !Ref 'PublicSubnetThree'
HostedZoneId:
Description: The ID of the hosted zone
Value: !Ref 'DnsHostedZone'
VpcCIDR:
Description: The VPC CIDR
Value: '{{ environment.inputs.vpc_cidr }}'
DnsHostname:
Description: The DNS hostname of the hosted zone
Value: '{{ environment.inputs.dns_hostname }}'

Fargate Schema

schema:
format:
openapi: "3.0.0"
service_input_type: "LoadBalancedServiceInput"
pipeline_input_type: "PipelineInputs"

types:
LoadBalancedServiceInput:
type: object
description: "Input properties for a loadbalanced Fargate service"
properties:
port:
type: number
description: "The port to route traffic to"
default: 80
minimum: 0
maximum: 65535
desired_count:
type: number
description: "The default number of Fargate tasks you want running"
default: 1
minimum: 1
task_size:
type: string
description: "The size of the task you want to run"
enum: ["x-small", "small", "medium", "large", "x-large"]
default: "x-small"
scope:
type: string
description: "If the service will be public or private"
enum: ["public", "private"]
default: "private"
image:
type: string
description: "The name/url of the container image"
default: "public.ecr.aws/z9d2n7e1/nginx:1.19.5"
minLength: 1
maxLength: 200
env_vars:
type: string
description: "The Docker environment variables to use"
default: "ENV_NAME_1=ENV_VALUE_1;ENV_NAME_2=ENV_VALUE_2"
minLength: 1
maxLength: 200
PipelineInputs:
type: object
description: "Pipeline input properties"
properties:
dockerfile:
type: string
description: "The location of the Dockerfile to build"
default: "Dockerfile"
minLength: 1
maxLength: 100
unit_test_command:
type: string
description: "The command to run to unit test the application code"
default: "echo 'add your unit test command here'"
minLength: 1
maxLength: 200

Service Pipeline

Source: https://catalog.us-east-1.prod.workshops.aws/workshops/494f5025-4ae5-46b8-83a8-7c8a6ad7b9f8/en-US/module-1/services/define-the-service-pipeline-infrastructure

Pipeline manifest

infrastructure:
templates:
- file: "cloudformation.yaml"
rendering_engine: jinja
template_language: cloudformation

The service instance infrastructure file will define all the infrastructure pieces that need to be created. In this case we create:

  1. ECS Task Definition
  2. ECS Service
  3. Load Balancer and all the necessary resources to point the Load Balancer to the ECS tasks.

AWSTemplateFormatVersion: '2010-09-09'
Description: Deploy a service on AWS Fargate, hosted in a public subnet, and accessible via a load balancer.
Mappings:
TaskSize:
x-small:
Cpu: 256
Memory: 512
small:
Cpu: 512
Memory: 1024
medium:
Cpu: 1024
Memory: 2048
large:
Cpu: 2048
Memory: 4096
x-large:
Cpu: 4096
Memory: 8192
Resources:
# A log group for storing the stdout logs from this service's containers
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: '{{service.name}}/{{service_instance.name}}'

# The task definition. This is a simple metadata description of what
# container to run, and what resource requirements it has.
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: '{{service.name}}_{{service_instance.name}}'
Cpu: !FindInMap [TaskSize, '{{service_instance.inputs.task_size}}', Cpu]
Memory: !FindInMap [TaskSize, '{{service_instance.inputs.task_size}}', Memory]
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ExecutionRoleArn: '{{environment.outputs.ECSTaskExecutionRole}}'
TaskRoleArn: !Ref "AWS::NoValue"
ContainerDefinitions:
- Name: '{{service_instance.name}}'
Cpu: !FindInMap [TaskSize, '{{service_instance.inputs.task_size}}', Cpu]
Memory: !FindInMap [TaskSize, '{{service_instance.inputs.task_size}}', Memory]
Image: '{{service_instance.inputs.image}}'
Environment:
{% set env_vars = service_instance.inputs.env_vars.split(';') %}
{% for env_var in env_vars %}
{% set env_name, env_value = env_var.split('=') %}
- Name: '{{ env_name|trim }}'
Value: '{{ env_value|trim }}'
{% endfor %}
PortMappings:
- ContainerPort: '{{service_instance.inputs.port}}'
LogConfiguration:
LogDriver: 'awslogs'
Options:
awslogs-create-group: 'True'
awslogs-group: '{{service.name}}/{{service_instance.name}}'
awslogs-region: !Ref 'AWS::Region'
awslogs-stream-prefix: 'fargate/service/{{service.name}}'

# The service_instance.inputs. The service is a resource which allows you to run multiple
# copies of a type of task, and gather up their logs and metrics, as well
# as monitor the number of running tasks and replace any that have crashed
Service:
Type: AWS::ECS::Service
DependsOn: LoadBalancerRule
Properties:
ServiceName: '{{service.name}}_{{service_instance.name}}'
Cluster: '{{environment.outputs.ClusterName}}'
LaunchType: FARGATE
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 75
DesiredCount: '{{service_instance.inputs.desired_count}}'
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- '{{environment.outputs.ContainerSecurityGroup}}'
Subnets:
- '{{environment.outputs.PublicSubnetOne}}'
- '{{environment.outputs.PublicSubnetTwo}}'
- '{{environment.outputs.PublicSubnetThree}}'
TaskDefinition: !Ref 'TaskDefinition'
LoadBalancers:
- ContainerName: '{{service_instance.name}}'
ContainerPort: '{{service_instance.inputs.port}}'
TargetGroupArn: !Ref 'TargetGroup'

# A target group. This is used for keeping track of all the tasks, and
# what IP addresses / port numbers they have. You can query it yourself,
# to use the addresses yourself, but most often this target group is just
# connected to an application load balancer, or network load balancer, so
# it can automatically distribute traffic across all the targets.
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
TargetType: ip
HealthCheckIntervalSeconds: 10
HealthCheckPath: /health
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
Matcher:
HttpCode: 200-299
# Note that the Name property has a 32 character limit, which could be
# reached by using either {{service.name}}, {{service_instance.name}}
# or a combination of both as we're doing here, so we truncate the name to 29 characters
# plus an ellipsis different from '...' or '---' to avoid running into errors.
Name: '{{(service.name~"--"~service_instance.name)|truncate(29, true, "zzz")}}'
Port: '{{service_instance.inputs.port}}'
Protocol: HTTP
UnhealthyThresholdCount: 10
VpcId: '{{environment.outputs.VpcId}}'
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: '5'
- Key: slow_start.duration_seconds
Value: '60'

# Create a rule on the load balancer for routing traffic to the target group
LoadBalancerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
Actions:
- TargetGroupArn: !Ref 'TargetGroup'
Type: 'forward'
Conditions:
- Field: path-pattern
Values:
- '*'
ListenerArn: !Ref LoadBalancerListener
Priority: 1

EcsSecurityGroupIngressFromALB:
Type: AWS::EC2::SecurityGroupIngress
Properties:
Description: Ingress from the ALB
GroupId: '{{environment.outputs.ContainerSecurityGroup}}'
IpProtocol: -1
SourceSecurityGroupId: !Ref 'LoadBalancerSG'

# Load balancer, hosted in public subnets that is accessible
# to the public or internally depending on the scope. It is
# intended to route traffic to one or more public/private
# facing services.
LoadBalancerSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Access to the public facing load balancer
VpcId: '{{environment.outputs.VpcId}}'
SecurityGroupIngress:
{% if 'public' == service_instance.inputs.scope %}
# Allow access to ALB from anywhere on the internet
- CidrIp: 0.0.0.0/0
IpProtocol: -1
{% else %}
# Allow access only from the VPC CIDR
- CidrIp: '{{environment.outputs.VpcCIDR}}'
IpProtocol: -1
FromPort: '{{service_instance.inputs.port}}'
ToPort: '{{service_instance.inputs.port}}'
{% endif %}

{% if 'public' == service_instance.inputs.scope %}
{% set scheme = 'internet-facing' %}
{% set port = '80' %}
{% else %}
{% set scheme = 'internal' %}
{% set port = service_instance.inputs.port %}
{% endif %}
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Scheme: '{{scheme}}'
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: '30'
Subnets:
# The load balancer is placed into the public subnets, so that traffic
# from the internet can reach the load balancer directly via the internet gateway
- '{{environment.outputs.PublicSubnetOne}}'
- '{{environment.outputs.PublicSubnetTwo}}'
- '{{environment.outputs.PublicSubnetThree}}'
SecurityGroups: [!Ref 'LoadBalancerSG']

LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
DependsOn:
- LoadBalancer
Properties:
DefaultActions:
- TargetGroupArn: !Ref 'TargetGroup'
Type: 'forward'
LoadBalancerArn: !Ref 'LoadBalancer'
Port: '{{port}}'
Protocol: HTTP

RecordSet:
Type: AWS::Route53::RecordSet
DependsOn:
- LoadBalancer
Properties:
AliasTarget:
HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID
DNSName: !GetAtt LoadBalancer.DNSName
HostedZoneId: '{{environment.outputs.HostedZoneId}}'
Name: '{{service.name}}.{{environment.outputs.DnsHostname}}'
Type: A

Outputs:
ServiceEndpoint:
Description: The URL to access the service
Value: !Sub "http://${LoadBalancer.DNSName}"

After you have completed all necessary steps we have created a first running application using Fargate and Proton.

Source: https://catalog.us-east-1.prod.workshops.aws/workshops/494f5025-4ae5-46b8-83a8-7c8a6ad7b9f8/en-US/module-1/services/create-the-service

Combining Amazon ECS and AWS Proton for Global Delivery

By aligning DevOps best practices with robust AWS services, Amazon ECS and AWS Proton significantly accelerate global delivery in the areas of advanced CI/CD, operational excellence, and security[11][13][14][15]. Furthermore, they are core components in a rich ecosystem of AWS services, including AWS Fargate[16], AWS Lambda[17], AWS CodeStar Services[18], AWS CloudFormation[19], Amazon RDS[20], DynamoDB[21], AWS IAM[22], AWS X-Ray, and CloudWatch, each designed to address specific needs. This integration with other AWS services enhances your global delivery approach.

Optimizing Global IT Projects with Multi-Account Deployments

When it comes to international IT projects, using a multi-cloud strategy with tools such as Amazon Elastic Container Service (ECS) and AWS Proton, along with Route 53[23] and CloudFront[24], can lead to more efficient deployments. Amazon ECS is designed to facilitate deployment across various cloud environments, while Route 53’s latency-based routing efficiently redirects users to the nearest operational service in their respective cloud, minimizing latency and enhancing the user experience.

In the complex landscape of multi-account management, AWS Proton stands out by simplifying the process. The abstraction layer provided by Proton over the management process lets developers focus more on their core tasks — creating high-quality applications and services — rather than being entangled in the intricacies of cloud infrastructure management.

Therefore, by utilizing a multi-account approach with these AWS tools, you can ensure that your services are swiftly and efficiently delivered to users worldwide, regardless of their chosen cloud platform. This not only improves their experience but also broadens your service accessibility across different cloud environments.

DevOps and AWS Best Practices

The combination of Amazon ECS and AWS Proton, supported by a DevOps culture, results in shorter development cycles, increased deployment frequency, more dependable releases, and faster time to market[25][26]. Furthermore, both Amazon ECS and AWS Proton align with the five pillars of the AWS Well-Architected Framework: Operational Excellence, Security, Reliability, Performance Efficiency, and Cost Optimization.

Facilitating Global Delivery

Amazon ECS and AWS Proton greatly facilitate a global delivery approach by enabling decentralized development, seamless collaboration, streamlined delivery, cost efficiency, 24/7 productivity, and risk mitigation. This makes AWS a suitable platform for managing nearshore and offshore collaboration [27].

Decentralized Development and Collaboration

With Amazon ECS and AWS Proton, decentralized development becomes highly effective. Teams, irrespective of their location, can work on the same project seamlessly. They can leverage Proton’s service catalog to deploy their code using pre-approved and standardized infrastructure setups, ensuring uniformity across development environments and reducing the likelihood of errors. This feature is particularly useful when working with offshore or nearshore resources where differences in development environments can often lead to complications.

Global Infrastructure and Reach

AWS has a vast global infrastructure, with data centers located across the globe. This allows for the deployment of applications in various AWS regions worldwide, reducing latency, and improving user experience. Organizations can deploy ECS clusters and Proton services across multiple regions, ensuring their applications are always close to their end-users. This also enables organizations to leverage resources in those regions more effectively, whether it’s using lower-cost resources or taking advantage of local expertise.

Cost Optimization

Cost is a significant factor when considering nearshore or offshore resources for project delivery. Different regions have different cost structures, and AWS’s pay-as-you-go model enables organizations to take advantage of these regional cost differences. By effectively managing and scaling resources according to application needs, Amazon ECS and AWS Proton help optimize cost, contributing to the overall cost-effectiveness of the global delivery model.

24/7 Productivity

With a well-coordinated global delivery model, organizations can ensure round-the-clock productivity. The time zone differences between nearshore and offshore teams can be leveraged to ensure continuous progress on projects. The AWS cloud facilitates this by providing always-on, globally accessible services, allowing teams to collaborate and work effectively irrespective of their geographical location.

Risk Mitigation

Global delivery can come with its set of challenges, particularly in terms of security and compliance across different regions. AWS provides robust security features and adheres to global compliance protocols, ensuring data protection across geographical boundaries. Services like AWS IAM, CloudTrail, and Security Hub offer granular access control, comprehensive auditing, and centralized security management, helping mitigate risks associated with global delivery.

Summary

Amazon ECS and AWS Proton are powerful tools for accelerating global delivery of IT projects, driving significant improvements in operational excellence, security, reliability, performance efficiency, and cost optimization. They help organizations navigate the challenges of global delivery and seize new opportunities in the rapidly evolving digital landscape.

About the Author:

My name is Sven Leiss and I am an 5x certified AWS enthusiast and AWS Migration Blackbelt. I have been working in the AWS space for the past 7 years and have extensive knowledge of the AWS platform and its various services. I am passionate about helping customers get the most out of the cloud and have a great track record of successful implementations.

I have extensive experience in designing and implementing cloud architectures using AWS services such as EC2, S3, Lambda and more. I am also well versed in DevOps and AWS cloud migration journeys.

If you are looking for an experienced AWS expert, I would be more than happy to help. Feel free to contact me to discuss your cloud needs and see how I can help you get the most out of the cloud.

References

  1. https://aws.amazon.com/ecs/
  2. https://docs.aws.amazon.com/AmazonECS/latest/developerguide/clusters.html
  3. https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_services.html
  4. https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html
  5. https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_placement_strategies.html
  6. https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service-load-balancing.html
  7. https://docs.aws.amazon.com/proton/latest/userguide/Welcome.html
  8. https://docs.aws.amazon.com/proton/latest/userguide/ag-templates.html
  9. https://docs.aws.amazon.com/proton/latest/userguide/ug-service-catalog.html
  10. https://docs.aws.amazon.com/proton/latest/userguide/ag-troubleshooting.html
  11. https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_GetStarted.html
  12. https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_cwet.html
  13. https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_security.html
  14. https://docs.aws.amazon.com/proton/latest/userguide/ug-manage-infrastructure.html
  15. https://aws.amazon.com/fargate/
  16. https://aws.amazon.com/lambda/
  17. https://aws.amazon.com/codestar/
  18. https://aws.amazon.com/cloudformation/
  19. https://aws.amazon.com/rds/
  20. https://aws.amazon.com/dynamodb/
  21. https://aws.amazon.com/iam/
  22. https://aws.amazon.com/route53/
  23. https://aws.amazon.com/cloudfront/
  24. https://aws.amazon.com/devops/
  25. https://aws.amazon.com/architecture/well-architected/
  26. https://aws.amazon.com/about-aws/global-infrastructure/

--

--