Skip to content

Instantly share code, notes, and snippets.

@pchmn
Last active May 17, 2024 13:10
Show Gist options
  • Save pchmn/f3291eb6ee667d6ac853927686e82684 to your computer and use it in GitHub Desktop.
Save pchmn/f3291eb6ee667d6ac853927686e82684 to your computer and use it in GitHub Desktop.
Setup an AWS ECS Cluster

Configuring AWS ECS

Configuration of ECS with EC2 instances

Setting up ECS

Create an IAM User

Official doc

Create a key pair

Official doc

Create ECS role

  • Go to IAM Console
  • Click on Roles from navigation bar
  • Select type AWS service and select use case EC2
  • Click on Next: Permissions
  • Choose AmazonECS_FullAccess as policy and click on Next: Tags
  • Click on Next: Review
  • Set a Role Name and click on Create role

Create a VPC

We will create a VPC with public and private subnets. Official doc

Create secondary public subnet

In order to use an application load balancer in your vpc, you need at least 2 publis subnets. Official doc

Use a different availability zone !

Create security groups

SG for databse private instance

In this private instance, we will host all our databses tasks. This instance will be only accessible from our apps private instance.

  • Inbound rules:
Type Protocol Port range Destination Source
All traffic All All Custom default security group
All traffic All All Custom apps private instance security group
SSH TCP 22 Custom bastion security group
  • Outbound rules:
Type Protocol Port range Destination Source
All traffic All All Custom 0.0.0.0/0

SG for apps private instance

In this private instance, we will host all our apps tasks. This instance is private and only accessible by our load balancer.

  • Inbound rules:
Type Protocol Port range Destination Source
All traffic All All Custom default security group
All traffic All All Custom load balancer security group
SSH TCP 22 Custom bastion security group
  • Outbound rules:
Type Protocol Port range Destination Source
All traffic All All Custom 0.0.0.0/0
All traffic All All Custom load balancer security group

SG for bastion instance

A bastion instance is a public instance which will act as a proxy in order to SHH to private instances.

  • Inbound rules:
Type Protocol Port range Destination Source
SSH TCP 22 Custom 0.0.0.0/0
  • Outbound rules:
Type Protocol Port range Destination Source
SSH TCP 22 Custom private instance 1 security group
SSH TCP 22 Custom private instance 2 security group

SG for Application Load Balancer

We will use an application load balancer in our vpc, so let's create a security group for it:

  • Inbound rules
Type Protocol Port range Destination Source
HTTP TCP 80 Custom 0.0.0.0/0
HTTPS TCP 443 Custom 0.0.0.0/0
  • Outbound rules
Type Protocol Port range Destination Source
All traffic All All Custom 0.0.0.0/0

SG for NAT instance

  • Inbound rules:
Type Protocol Port range Destination Source
HTTP TCP 80 Custom 10.0.1.0/24
HTTPS TCP 443 Custom 10.0.1.0/24
SSH TCP 22 Custom 0.0.0.0/0
  • Outbound rules:
Type Protocol Port range Destination Source
HTTP TCP 80 Custom 0.0.0.0/0
HTTPS TCP 443 Custom 0.0.0.0/0

Use NAT instance instead of NAT Gateways

During VPC creation, aws automatically create a NAT Gateways, so private instance (in private subnet) can do some outbound traffic to the internet, but can't receive inboud traffic from the internet. NAT Gateways can cost a lot of money, so we will use NAT instance (it can be used as free tier plan). NAT instance can do the same work ad NAT Gateways with a public instance that act as a bastion host.

Delete NAT Gateways

  • Go to VPC Console
  • Select NAT Gateways from navigation bar
  • Select the NAT Gateway automatically created, then Action -> Delete NAT Gateway

Create NAT instance

  • Go to EC2 Console
  • Select Instances from navigation bar
  • Click on Launch instances
Choose AMI
  • Select ami-0c91611c0eda570b1 amzn-ami-vpc-nat-2018.03.0.20200918.0-x86_64-ebs AMI of Community AMIs and choose t2.micro instance type
  • Click on Next: Configure Instance Details
Configure Instance Details
  • Select your VPC and your public Subnet
  • Set Auto-assign Public IP as Enabled
  • Select your ECS role as IAM role
  • Click on Next: Add Storage
Add Storage
  • Click on Next: Add tags
Add tags
  • Set your instance name
  • Click on Next: Configure Security Group
Configure Security Group
  • Select your NAT security group
  • Click on Review and Launch
Review Instance Launch
  • Click on Launch

Disabling source/destination checks

  • Go to EC2 Console
  • Select Instances from navigation bar
  • Select your NAT instance and then Actions -> Networking -> Change Source/Dest. Check
  • Tick Stop and click on Save

Update main route table

  • Go to VPC Console
  • Select Route Tables from navigation bar
  • Select your Main route table, click on Routes tab and then Edit routes
  • Click on Add route and set this route: Destination | Target --- | --- | 0.0.0.0/0 | your NAT instance
  • Click on Save routes

Update private subnet route table

  • Go to VPC Console
  • Select Subnet from navigation bar
  • Select your private subnet, then click on Route Table tab, then Edit route table association
  • Choose the Route Table ID you created before
  • Click on Save

Create Clusters

We will create 2 clusters, one for our db private instance, and one for our apps private instance.

Create a Cluster

  • Go to ECS Console
  • Select Clusters from navigation bar
  • Click on Create Cluster
  • Select EC2 Linux + Networking and click on Next Step

Configuration

  • Set a Cluster Name
  • Select t2.micro for EC2 instance type
  • Select the VPC you created during ECS Set up
  • Select your private Subnet(s)
  • Select Use subnet setting for Auto assign public IP
  • Select the Security Group according to your cluster (db security group or app security group)
  • Select the ECS role you created before for Container instance IAM role
  • You can change other values (like instances number for example), but default values are good
  • Click on Create

If you activate CloudWatch Container Insights, be aware that metrics collected are charged as custom metrics, and are not included in free tier usage (see doc and metrics pricing).

Create an EC2 instance

  • Go to EC2 Console
  • Select Instances from navigation bar
  • Click on Launch Instance

Configuration

Choose AMI

  • Select Amazon ECS-Optimized Amazon Linux 2 AMI AMI
  • Select t2.micro (by default selected)
  • Click on Next: Configure Instance Details

Configure Instance

  • Select the ECS role you created before
Advanced Details
  • Add this in User data to attach your instance to your cluster:
#!/bin/bash
echo ECS_CLUSTER=your_cluster >> /etc/ecs/ecs.config
  • Click on Review and Launch

Review

  • Click on Edit security groups
  • Select the Security group you created during ECS set up (same security group as the cluster)
  • Click on Review and Launch
  • Click on Edit tags
  • Add a tag with key = Name and value = the name you want to give to your instance
  • Click on Review and Launch
  • Click on Launch
  • Select the Key pair you created during ECS set up and click on Launch Instance

Create Application Load Balancer

  • Go to ECS Console
  • Click on Load Balancer from navigation bar
  • Click on Create Load Balancer
  • Choose Application Load Balancer

Configure Load Balancer

For now we will not add HTTPS listener, because we haven't configured the certificate yet.

  • Set a Name
  • Select the PVC and your 2 public Subnets
  • Click on Next: Configure Security Settings

Configure Security Settings

  • Click on Next: Configure Security Groups (we will add HTPPS after)

Configure Security Groups

  • Select load balancer security group you created before
  • Click on Next: Configure Routing

Configure Routing

  • Select New target group as Target Group
  • Set a target Name
  • Click on Next: Register targets

Register targets

  • Select the apps instance and click on Add to registered
  • Click on Next: Review

When creating the load balancer, the target group you created (or selected) is the default target group. So it will redirect to it by default. We are able to modify this afterwards. So if you are no service running at this step this is all right.

Review

  • Click on Create

Branch a domain to an Application Load Balancer with AWS Route 53

Get an Elastic IP for your EC2 instance

  • Go to EC2 Console
  • Click on Elastic IPs in Resources card
  • Click on Allocate Elastic IP address
  • Let default value and click on Allocate
  • Select Elastic IP created and click on Associate Elastic IP address action
  • Select your EC2 instance and click on Associate

Create an hosted zone in Route 53

  • Go to Route 53 Console
  • Click on Create a hosted zone
  • Set your Domain name
  • Click on Create hosted zone

After creation, in your hosted zone details, you should see an NS record name, with 4 name space servers. We will use them after

Create record and branch them to the load balancer

  • Click on Create record
  • Select Simple routing and click on Next
  • Click on Define simple record
  • Set a Subdomain if you want
  • In Value/Route traffic to select Alias to Application and Classic Load Balancer, then choose your region, then choose your load balancer
  • Click on Define simple record
  • Repeat this for each subdomain you want

Add Name Space Servers to your Domain provider

  • Go to your domain provider
  • Change DNS servers to use the 4 name space servers you saw before

Use your domain under HTTPS:

Add HTTPS listener to the load balancer

  • Go to EC2 Console
  • Click on Load Balancers and select your load balancer
  • In Listeners tab, click on Add listener
  • Choose HTTPS as Protocol
  • Set same default action as HTTP : 80 listener
  • Select your certificate created before as Default SSL certificate
  • Click on Save

Create a Service

A service use a task definition, se we have to create a task definition before.

Create Task Definition

  • Go to ECS Console
  • Click on Task Definitions from navigation bar
  • Click on Create new Task Difinition
  • Select EC2 then click on Nest step

Configuration

  • Choose a Tas Definition Name

Volume

If you want your task to persist data (for databses for ex), even when container restarts, you have to configure a Docker volume. See mongodb part

Container Definitions
  • Click on Add container
  • Choose a Container name
  • Set repository url of the docker image you want
  • Set a Memory Limits
  • For dynamic Port mappings:
    • Host port: 0
    • Container port: port your container is exposed to
  • In STORAGE AND LOGGING part, tick Auto-configure CloudWatch Logs
  • Click on Add
  • Click on Create

Create Service

  • Click on Clusters from navigation bar, and select your cluster
  • In Services tab, click on Create

Configure service

  • Select EC2 as Launch type
  • Select the Task definition you created
  • Select your Cmuster
  • Choose a Service name
  • Set a Number of tasks
  • Set Minimum healthy percent = 0 (useful for --force-deployment cmd)
  • Set Maximum percent = 100
  • Other values as default
  • Click on Next step

Configure network

Load balancing
  • Select Application Load Balancer as Load balancer type
  • Select the ecs service role you created as Service IAM role
  • Select your Load Balancer
Container to load balance
  • Click on Add to load balancer
  • Production listener port: select 80: HTTP (we can change listeners for 443: HTTPS later)
  • Target group name: let create new (we create new target group for each service)
  • Path pattern and Evaluation order: you can change path pattern (ex: /api), evaluation order is mandatory
  • Health check path: set /
Service discovery (optional)
  • Tick Enable service discovery integration
  • Create or select Namespace
  • Create or select Service discovery service
  • Other values as default
  • Click on Next step

Set Auto Scaling (optional)

  • Click on Next step

Review

  • Review and click on Create Service

After service is created, you should be able to access you service at domain.com/api url

Update Target Group Deregistration delay

Time (by default 300sec) before Application load balancer completes deregistration process, it can help in-flight requests to the target to complete.

  • Go to EC2 Console
  • Click on Target Groups from navigation bar
  • Select your target group
  • In Group details tab, under Attributes part, click on Edit
  • Change Deregistration delay to suit your needs (ex: 30sec)
  • Click on Save changes

Update Application Load Balancer listeners

For now we use path pattern (ex: domain.com/api) to redirect to appropriate services with the load balancer. To use host pattern (ex: api.domain.com) we have to change listeners.

  • Go to EC2 Console
  • Click on Load Balancers from navigation bar
  • Select your load balancer, and click on Listeners tab
  • Click on View/edit rules for HTTP: 80
  • Click on Edit rules icon and select the rule you want to change from path to host
  • Remove IF condition
  • Click on Add condition and select Host header...
  • Set the Host you want (ex: api.domain.com)
  • Don't modify THEN condition
  • Click on Update

If you want to use HTTPS, copy the rules of HTTP : 80 listener in HTTPS: 443 listener

After changes, you should be able to access your service at api.domain.com url (insted of domain.com/api)

Create a mongodb container with Docker volume

In order to persist data of the mongodb, even if container restart or is shut down, we will use a docker volume

Create mongodb Task Definition

  • Go to ECS Console
  • Click on Task Definitions from navigation bar
  • Click on Create new Task Difinition
  • Select EC2 then click on Nest step

Configuration

  • Choose a Task Definition Name
  • Set Task Role and Task execution role with the task role you created before
Volumes
  • Click on Add volume
  • Choose a Name
  • Select your File system ID and your Access point ID
  • Tick Encryption in transit
  • click on Add
Container Definitions
  • Click on Add container
  • Choose a Container name
  • Image: registry.hub.docker.com/library/mongo:tag
  • Set a Memory Limits
  • For dynamic Port mappings:
    • Host port: 0
    • Container port: 27017
ENVIRONMENT
  • Add MONGO_INITDB_ROOT_USERNAME and MONGO_INITDB_ROOT_PASSWORD environment variables (this will be your mongo admin user)

Use ValueFrom if yo want to use a secret

STORAGE AND LOGGING
  • Select your Mount points with the volume created before
  • Choose a Container path specific for your container
  • Tick Auto-configure CloudWatch Logs
  • Click on Add
  • Click on Create

Create Service

  • Click on Clusters from navigation bar, and select your cluster
  • In Services tab, click on Create

Configure service

  • Select EC2 as Launch type
  • Select the Task definition you created
  • Select your Cluster
  • Choose a Service name
  • Set a Number of tasks
  • Set Minimum healthy percent = 0 (useful for --force-deployment cmd)
  • Set Maximum percent = 100
  • Other values as default
  • Click on Next step

Configure network

Load balancing

Do this only for service in public subnet.

  • Select Application Load Balancer as Load balancer type
  • Select the ecs service role you created as Service IAM role
  • Select your Load Balancer
Container to load balance
  • Click on Add to load balancer
  • Production listener port: select 80: HTTP (we can change listeners for 443: HTTPS later)
  • Target group name: let create new (we create new target group for each service)
  • Path pattern and Evaluation order: you can change path pattern (ex: /mongo), evaluation order is mandatory
  • Health check path: set /
Service discovery (optional)
  • Tick Enable service discovery integration
  • Create or select Namespace
  • Create or select Service discovery service
  • Other values as default
  • Click on Next step

Set Auto Scaling (optional)

  • Click on Next step

Review

  • Review and click on Create Service

After service is created, you should be able to access you service at domain.com/mongo url

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment