Enable GZIP Compression for Web Container on Amazon ECS

Fayas Akram
4 min readOct 30, 2021

--

The performance of the web apps plays a vital role in user experience and impacts their engagement. Though web apps need to be performant, as most of the time, they can be accessed on Desktop/Mobile devices short span of time.

Websitestes usually comprise static assets such as .js .css and .json text files. Enabling GZIP compression on web servers is one of the simplest and most efficient ways to achieve that. This helps to reduce the bandwidth requirement, speeding up the website rendering.

All modern browsers include support for GZIP compression by default. However, to serve the compressed resources to users with no hiccups, we must configure your server properly.

In this post, I will discuss how to compress AWS ECS-based Web Containers by implementing NGINX reverse proxy sidecar with the internet blazing fast.

Excited? Let’s decompress!

As you can refer to this architecture, Users request to the Application Load Balancer, which then distributes the traffic to the NGINX reverse proxy sidecars. The NGINX reverse proxy then forwards the request to the application server and returns its response to the client via the load balancer.

With this network design, we can achieve compression by enabling GZIP as well as filtering unwanted traffic to the web applications.

NGINX configuration.

worker_processes  1;

events {
worker_connections 1024;
}

http {

gzip on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;

upstream apps {
server app-container:3000; # Same Name as App Container
}

server {
listen 80;
server_name localhost;


location / {
proxy_pass http://apps;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache_bypass $http_upgrade;
}
}
}

upstream apps: Define same as container application container name.

GZIP is a file format that uses DEFLATE internally, along with some interesting blocking, filtering heuristics, a header, and a checksum. In general, the additional blocking and heuristics that GZIP uses give it better compression ratios than DEFLATE alone.

gzip_types: Define types of files for compressed.

Note: I always deploy an environment using IaaC to make my life easier. So, I have developed Terraform code to deploy Fargret ECS (Nginx and App), ALB, Target Group for ALB

Container Definition

[
{
"name": "app-container",
"secrets": [],
"image": "Your App ECR URL Here",
"cpu": 512,
"memory": 512,
"portMappings": [
{
"containerPort": ${app_port},
"protocol": "tcp",
"hostPort": 0
}
],
"essential": true
},
{
"name": "nginx-container",
"image": "Your Nginx ECR URL Here",
"memory": 256,
"cpu": 256,
"essential": true,
"portMappings": [
{
"containerPort": ${web_port},
"protocol": "tcp",
"hostPort": 0
}
],
"links": [
"app-container:app-container"
],
"logConfiguration": {
"logDriver": "awslogs",
"secretOptions": null,
"options": {
"awslogs-group": "/ecs/nginx-log",
"awslogs-region": "${region}",
"awslogs-stream-prefix": "ecs"
}
}
}
]

Main.tf

data "template_file" "template" {
template = file("./templates/container-definition.json")
vars = {
region = var.AWS_REGION
app_port = var.container_port
web_port = var.nginx_port
}
}

As you can see from the main.tf file container ports passed as variables. var.container_port = 3000 and var.nginx_port = 80

Task Definition

resource "aws_ecs_task_definition" "task_definition" {
container_definitions = data.template_file.template.rendered
family = "SideCar"
requires_compatibilities = ["FARGATE"]
network_mode = "bridge"
execution_role_arn = "arn:aws:iam::YourAccountID:role/ecsTaskExecutionRole"
task_role_arn = "arn:aws:iam::YourAccountID:role/ecsTaskExecutionRole"
}

requires_compatibilities = [“FARGATE”] — Define that container deployed as fargrate instance.

execution_role_arn = “arn:aws:iam::YourAccountID:role/ecsTaskExecutionRole” — AWS managed role for Task Execution, Please replay with your AWS Account ID

task_role_arn = “arn:aws:iam::YourAccountID:role/ecsTaskExecutionRole” — AWS managed role for Task Execution, Please replay with your AWS Account ID

ECS Service

resource "aws_ecs_service" "ecs_service" {
cluster = aws_ecs_cluster.cluster.id
name = "WebApp"
task_definition = aws_ecs_task_definition.task_definition.arn
iam_role = "arn:aws:iam::YourAccountID:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS"
load_balancer {
target_group_arn = aws_alb_target_group.target_group.arn
container_name = "nginx-container"
container_port = var.nginx_port
}
desired_count = var.desired_count
lifecycle {
ignore_changes = [desired_count]
}
}

iam_role = “arn:aws:iam::YourAccountID:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS” — AWS managed role for AWS Service, Please replay with your AWS Account ID

iam_role = “arn:aws:iam::YourAccountID:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS” — AWS managed role for AWS Service, Please replay with your AWS Account ID

cluster = aws_ecs_cluster.cluster.id — I have created aws cluster in terraform file and I have referred to it here

load_balancer — I have created Application Load Balancer and map to the Nginx Container port request comes from ALB will be forwarded to the Nginx Container

Load Balancer Listner Rule

resource "aws_alb_listener" "listener" {
load_balancer_arn = aws_alb.fargate.id
port = "80"
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_alb_target_group.target_group.arn
}
condition {
host_header {
values = ["webapp.com"]
}
}
condition {
path_pattern {
values = var.alb_rule
}
}
}

I have added Port 80 Listner to the Application Load Balancer and defined the Listener rule as follows.

When client request like webapp.com or the request path container /api, which will be passed to the Nginx container.

Find the Full code here

Conclusion

Gzip compresses, reduced hosting charges, and brings a better user experience.

--

--