Infrastructure as Code (IaC) dengan Terraform & AWS untuk Zero-Downtime Deployment
1. Konteks Industri & Analisis Akar Masalah
Dalam lanskap pengembangan web modern, kecepatan iterasi dan stabilitas infrastruktur adalah dua pilar penentu keberhasilan bisnis digital. Namun, mengelola infrastruktur secara manual melalui konsol AWS (ClickOps) sering kali menjadi sumber dari berbagai kegagalan sistem yang merugikan. Masalah klasik yang sering dihadapi oleh Infrastructure Engineers dan DevOps Specialists meliputi penyimpangan konfigurasi (configuration drift), human error saat pembaruan manual, ketidakmampuan untuk mereplikasi environment secara presisi (Development vs Staging vs Production), serta waktu downtime yang tinggi saat melakukan pembaruan infrastruktur atau deployment aplikasi.
Sebelum adopsi Infrastructure as Code (IaC) berbasis Terraform dan arsitektur Zero-Downtime Deployment di AWS, sebuah tim biasanya mengalami masalah berikut:
- Inkonsistensi Environment: Server staging menggunakan spesifikasi instance yang berbeda dengan production, menyebabkan bug lolos ke production.
- Downtime Signifikan: Pembaruan instance web server (Nginx/Apache) atau aplikasi backend (PHP-FPM/Go/Node.js) dilakukan dengan cara menghentikan layanan yang sedang berjalan, menghasilkan error 502/504 bagi pengguna akhir.
- Kurangnya Keterlacakan (Traceability): Perubahan rule Security Group atau routing tabel Route 53 dilakukan secara ad-hoc tanpa audit trail yang jelas.
Setelah menerapkan Terraform untuk manajemen infrastruktur dan strategi Rolling Updates atau Blue/Green Deployments via Auto Scaling Groups dan Application Load Balancers (ALB), organisasi mendapatkan keuntungan transformasional:
- Infrastruktur sebagai Kode (Immature Infrastructure): Seluruh perubahan terekam dalam version control (Git), memastikan auditabilitas penuh.
- Zero-Downtime mutlak: Load balancer secara otomatis mendeteksi kesehatan instance baru dan mengalihkan traffic secara mulus sebelum instance lama dihentikan.
- Pemulihan Cepat (Disaster Recovery): Environment baru dapat di-spin up dari awal dalam hitungan menit menggunakan skrip Terraform yang modular.
2. Konsep Inti & Deep Dive Arsitektur
Arsitektur yang kita bangun dirancang untuk menangani beban tinggi secara high-availability dengan mendistribusikan beban kerja ke beberapa Availability Zones (AZ) di AWS. Sistem ini mengintegrasikan seluruh lapisan: DNS via Cloudflare, Load Balancer (ALB), Auto Scaling Group (ASG) yang menjalankan web server Nginx dan backend PHP-FPM/Node.js, serta koneksi ke database terkelola (Amazon Aurora MySQL/MariaDB) dan caching layer (Amazon ElastiCache Redis).
[ Cloudflare DNS / WAF ]
│
▼
┌────────────────────────────────────────┐
│ AWS Application Load Balancer │
│ (Multi-AZ) │
└───────────────────┬────────────────────┘
│
┌──────────┴──────────┐
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ AZ-A: EC2/ASG │ │ AZ-B: EC2/ASG │
│ Nginx + Node │ │ Nginx + Node │
└────────┬────────┘ └────────┬────────┘
│ │
└──────────┬──────────┘
│
┌──────────┴──────────┐
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Amazon Aurora │ │ ElastiCache │
│ MySQL (Primary) │ │ Redis (Cluster) │
└─────────────────┘ └─────────────────┘
Alur Eksekusi Permintaan (Request Lifecycle)
- Klien mengirimkan HTTP/HTTPS request ke domain, yang diproses melalui Cloudflare (menyediakan Edge Caching, SSL/TLS, dan WAF).
- Traffic diteruskan ke AWS Application Load Balancer yang berada di public subnet di dua Availability Zones berbeda.
- ALB mendistribusikan request ke target group yang berisi instance EC2 dalam Auto Scaling Group melalui private subnet.
- Nginx pada EC2 menerima request, bertindak sebagai reverse proxy, meneruskan request dinamis ke backend (misalnya Node.js atau PHP-FPM melalui UNIX socket), dan melayani asset statis secara langsung.
- Backend berinteraksi dengan Amazon Aurora MySQL untuk persistensi data dan Amazon ElastiCache Redis untuk sesi serta caching.
3. Perbandingan Komparatif / Analisis Pendekatan
Berikut adalah analisis mendalam mengenai pendekatan manajemen infrastruktur dan deployment:
| Parameter / Fitur | ClickOps (Manual AWS Console) | Terraform + ASG / ALB (Pendekatan Kita) |
|---|---|---|
| Reproduksibilitas | Rendah (Rentan kesalahan manusia) | Tinggi (100% didefinisikan lewat kode) |
| Deployment Downtime | Tinggi (Manual restart/replace) | Nol (Zero-Downtime via Target Group draining) |
| Audit & Governance | Tidak ada (Harus cek CloudTrail manual) | Sempurna via Git Commit History & Terraform State |
| Resource Cost Efficiency | Boros (Sering lupa mematikan resource uji) | Efisien (Auto Scaling & Lifecycle Policies) |
| Waktu Recovery (DR) | Berjam-jam hingga hari | Hitungan menit (terraform apply) |
[!WARNING] Jangan pernah menyimpan file state Terraform (
terraform.tfstate) yang berisi sensitif data di penyimpanan lokal tanpa enkripsi saat bekerja dalam tim. Gunakan backend terpusat seperti Amazon S3 dengan State Locking via DynamoDB.
4. Prasyarat Sistem & Persiapan Lingkungan
Sebelum menjalankan konfigurasi Terraform, pastikan environment lokal atau CI/CD runner Anda memiliki toolchain berikut terinstal:
- Terraform: Versi >= 1.5.0
- AWS CLI: Versi 2.x dengan konfigurasi kredensial yang valid (
aws configureatau IAM Instance Profile) - Git: Untuk manajemen modul dan version control
Verifikasi instalasi dengan perintah berikut:
terraform -version
aws sts get-caller-identity
5. Panduan Implementasi Langkah demi Langkah
Kita akan membuat struktur direktori modular untuk Terraform:
terraform-aws-infra/
├── main.tf
├── variables.tf
├── outputs.tf
├── provider.tf
└── modules/
├── vpc/
├── alb/
└── compute/
A. File provider.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "production-terraform-state-bucket-xyz"
key = "infrastructure/state.tfstate"
region = "ap-southeast-1"
dynamodb_table = "terraform-lock-table"
encrypt = true
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Environment = var.environment
ManagedBy = "Terraform"
}
}
}
B. Modul VPC (modules/vpc/main.tf)
variable "vpc_cidr" { type = string }
variable "azs" { type = list(string) }
variable "public_subnets" { type = list(string) }
variable "private_subnets" { type = list(string) }
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "production-vpc"
}
}
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "production-igw"
}
}
resource "aws_subnet" "public" {
count = length(var.public_subnets)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnets[count.index]
availability_zone = var.azs[count.index]
map_public_ip_on_launch = true
tags = {
Name = "production-public-subnet-${count.index}"
}
}
resource "aws_subnet" "private" {
count = length(var.private_subnets)
vpc_id = aws_vpc.main.id
cidr_block = var.private_subnets[count.index]
availability_zone = var.azs[count.index]
tags = {
Name = "production-private-subnet-${count.index}"
}
}
resource "aws_eip" "nat" {
domain = "vpc"
depends_on = [aws_internet_gateway.gw]
}
resource "aws_nat_gateway" "nat" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public[0].id
tags = {
Name = "production-nat-gw"
}
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gw.id
}
tags = { Name = "production-public-rt\