71 lines
2.4 KiB
Markdown
71 lines
2.4 KiB
Markdown
---
|
|
title: "Deploying to AWS ECS with Terraform"
|
|
date: 2026-05-14T10:00:00+08:00
|
|
draft: false
|
|
description: "Infrastructure as code from scratch: VPC, subnets, security groups, ECR repository, ECS service with Fargate, and an ALB in front. Every resource explained."
|
|
tags:
|
|
- AWS
|
|
- Terraform
|
|
- DevOps
|
|
---
|
|
|
|
ECS with Fargate is the sweet spot between managing servers and going full serverless. Terraform makes it reproducible.
|
|
|
|
## The Stack
|
|
|
|
Here's what we need to provision:
|
|
|
|
| Resource | Purpose |
|
|
|----------|---------|
|
|
| VPC | Isolated network |
|
|
| Public & private subnets | Multi-AZ placement |
|
|
| Security groups | Network access control |
|
|
| ECR repository | Container image storage |
|
|
| ECS cluster + service | Run containers |
|
|
| ALB + target group | Traffic distribution |
|
|
|
|
## Security Groups: Getting Them Right
|
|
|
|
The key is getting the security groups right — one misconfigured rule and your containers can't talk to each other, or worse, your database is exposed to the internet.
|
|
|
|
```hcl
|
|
resource "aws_security_group" "ecs_service" {
|
|
name = "ecs-service-sg"
|
|
vpc_id = aws_vpc.main.id
|
|
|
|
ingress {
|
|
from_port = 3000
|
|
to_port = 3000
|
|
protocol = "tcp"
|
|
security_groups = [aws_security_group.alb.id]
|
|
}
|
|
|
|
egress {
|
|
from_port = 0
|
|
to_port = 0
|
|
protocol = "-1"
|
|
cidr_blocks = ["0.0.0.0/0"]
|
|
}
|
|
}
|
|
```
|
|
|
|
The principle: **allow the minimum**. The ALB security group allows inbound from the internet on 80/443. The ECS security group only allows inbound from the ALB. The database security group only allows inbound from ECS.
|
|
|
|
## Fargate Configuration
|
|
|
|
Fargate abstracts the EC2 instances. You specify CPU and memory, and AWS handles the rest. For most web services, 0.25 vCPU / 512 MB is a good starting point — scale up based on load testing, not guessing.
|
|
|
|
## CI/CD Integration
|
|
|
|
After Terraform creates the infrastructure, your CI pipeline needs to:
|
|
|
|
1. Build and tag the Docker image
|
|
2. Push to ECR
|
|
3. Update the ECS service with the new task definition
|
|
|
|
Use a Terraform backend with state locking — S3 + DynamoDB is the standard approach. Without state locking, two people running `terraform apply` at the same time can corrupt your infrastructure state.
|
|
|
|
## Cost Notes
|
|
|
|
Fargate is more expensive per vCPU-hour than raw EC2, but you save the operational cost of patching instances and managing capacity. For small-to-medium workloads, the difference is negligible compared to the engineering time saved.
|