Making the Most of Your AWS EC2 Free Tier

Get load balancing and zero downtime deploys from a single EC2 instance.


I am cheap and when I work on a side project that gets no users I'd like to not lose money. To help with this I use the EC2 free tier as efficiently as possible. In this article I will explain how.

Contents

In this article we'll cover:

  • Load balancing using a single EC2 instance
  • Doing 0 downtime deploys with a single EC2 instance

Prerequisite Knowledge

This article will focus on tuning an existing ECS cluster that uses EC2 to host its containers, so an understanding of EC2 & ECS along with its subcomponents is required. We will be using IaC to manage our infrastructure, so a general understanding of Terraform will be helpful as well.

Our containers will be running a Node.js application, but theoretically any single threaded runtime could benefit from this.

Load Balancing Using a Single EC2 instance

Since Node.js is single threaded, it's a common pattern to scale web services horizontally. We're able to scale horizontally within a single free tier EC2 Instance since a t3.micro has 2 vCPus. We can run two containers within the instance to gain more concurrent computing power.

Making the Most of Our Free Resources

The best EC2 instance available within the free tier is the t3.micro, which has 2 vCPUs and 1 GB of memory. Naturally we want to allocate as much of these resources as possible to our containers, but there is a balancing act where we need to leave enough resources for the host OS to run.

When allocating resources for two containers within a single t3.micro instance, I found the sweet spot to be:

cpu    = 820 # 820 vCPU credits - about 80% of a single vCPU
memory = 410 # 410 MiB

410 MiB of memory is not a lot, but for my use case as a stateless backend web service it is enough.

Running Two Containers in a Single EC2 Instance

Now that we know how much of our t3.micro's resource we can allocate, let's set up load balancing between the containers.

Enabling Bridge Mode

In order to do this we will be using EC2's bridge network mode. Bridge mode enables a mapping of containerPort to hostPost. This allows us to run multiple containers on the same port within an EC2 instance. Here is a diagram:

Overall bridge mode maps containers' ports to a different external port within the EC2 instance. This enables us to send requests to each container individually.

Here is example of this configuration in Terraform:

resource "aws_ecs_task_definition" "ecs-task-definition" {
  ...
  network_mode             = "bridge"
  container_definitions = jsonencode([
    {
      ...
      portMappings = [
        {
          containerPort = 3000
          # hostPort    = N/A
        }
      ]
      ...
    }
  ])
}

Notice that the hostPort is not applicable here, that is because bridge mode automatically handles creating that port number for us.

Configure ECS to Spin Up Two Containers On Our EC2 Instance

We have enabled bridge mode on our EC2 instance and configured resources to be shared between two containers. Now we must configure ECS to spin up multiple containers within the instance.

ECS does the container orchestration for us, as the name implies, so we just need to configure the correct desired_count.

resource "aws_ecs_service" "ecs-service" {
  ...
  desired_count = 2
  ...
}

Now we have two containers running in a single EC2 instance. 🎉

Sharing Traffic

So we have two containers in a single EC2 instance, but that doesn't help much unless they're both receiving traffic.

ECS does the magic of tracking the hostPorts that were created dynamically and distributing traffic between them (source). So all you need is the following target group associated with your load balancer (this most likely already exists if you were using a load balancer with your ECS cluster).

resource "aws_alb_target_group" "alb-target-group" {
  ...
  port     = 3000
  protocol = "HTTP"
  ...
}

You can confirm ECS is dynamically updating the target group by going to EC2 > Target groups. You should see something like the following:

So sweet! Now we have two containers able to handle requests in single free tier instance. There is the advantange of having two instances of our web service running concurrently, but there is also the major advantange our service is now capable of zero downtime deploys.

Zero Downtime Deploys Using a Single EC2 Instance

Typically when you have just one EC2 instance you'd have to spin up another instance to do a rolling deploy. This is no longer the case for us since we now have one instance with two containers.

In order to allow ECS to replace one container at a time we must configure our ECS service to only require 50% healthy containers. This allows ECS to spin down one container and spin up a new container with the updated task definition. Then subsequently replace the old versioned container with the new version.

resource "aws_ecs_service" "ecs-service" {
  ...
  desired_count                      = 2
  deployment_minimum_healthy_percent = 50
  ...
}

With this in place all following task definitions will be done with a rolling deploy (i.e. no downtime)!

Conclusion

We are now effectively using our single t3.micro EC2 instance! If you have any other tips feel free to reach out to me on Linkedin.