Arsitektur Zero-Downtime Infrastructure as Code Menggunakan Terraform & AWS
Arsitektur Zero-Downtime Infrastructure as Code Menggunakan Terraform & AWS
1. Konteks Industri & Analisis Akar Masalah
Dalam lanskap enterprise modern, ketersediaan layanan (high availability) bukan lagi sebuah kemewahan, melainkan mandat bisnis yang mutlak. Setiap detik sistem web mengalami downtime atau degradasi performa saat proses deployment dapat berdampak langsung pada kerugian finansial, erosi kepercayaan pengguna, dan kegagalan kepatuhan SLA (Service Level Agreement). Secara tradisional, pengelolaan infrastruktur web yang mencakup web server (Nginx/Apache), backend (Node.js/Go/PHP-FPM), database (MySQL/MariaDB), caching (Redis), hingga lapisan keamanan (WAF, Fail2ban, Let's Encrypt SSL) dilakukan secara manual atau menggunakan skrip ad-hoc yang rentan terhadap human error.
Bottleneck terbesar dalam proses deployment konvensional adalah kondisi Configuration Driftβketika keadaan aktual server di cloud menyimpang dari dokumentasi atau niat awal arsitek. Ketika tim mencoba memperbarui infrastruktur atau merilis versi baru aplikasi backend dan aset statis (HTML/CSS/JS), metode in-place update sering kali memicu konflik state, terputusnya koneksi WebSocket yang sedang aktif, atau kegagalan sinkronisasi replikasi basis data.
Sebelum vs Sesudah Penerapan
| Parameter | Sebelum (Manual / In-Place Update) | Sesudah (Terraform Zero-Downtime IaC & Blue-Green) |
|---|---|---|
| Waktu Deployment | 30 - 120 menit (rawan error manual) | 3 - 5 menit (otomatisasi penuh via CI/CD) |
| Downtime Perubahan | 5 - 15 menit (terputusnya koneksi & restart layanan) | 0 detik (zero-downtime routing via ALB/Route53) |
| Reproduksibilitas | Rendah (server "snowflake", sulit diduplikasi) | Tinggi (deklaratif, 100% identik di setiap environment) |
| Keamanan & Audit | Sulit dilacak siapa yang mengubah konfigurasi | Riwayat perubahan tercatat jelas di Terraform State & Git |
[!WARNING] Melakukan modifikasi infrastruktur produksi secara langsung melalui AWS Console (manual clicking) adalah pelanggaran fatal dalam prinsip operasional DevOps modern karena merusak integritas Infrastructure as Code.
2. Konsep Inti & Deep Dive Arsitektur
Untuk mencapai zero-downtime mutlak, kita harus memisahkan infrastruktur menjadi komponen-komponen yang dapat diubah (mutable/immutable instances) tanpa memutus akses pengguna akhir (end-user). Arsitektur ini menggabungkan Cloudflare sebagai Edge CDN dan WAF, AWS Application Load Balancer (ALB) sebagai pengarah lalu lintas cerdas, Auto Scaling Group (ASG) dengan strategi Launch Template, serta Terraform untuk manajemen siklus hidup sumber daya.
Alur Eksekusi & Data Flow (ASCII Flow Chart)
[ User / Client ]
β
βΌ (HTTPS / TLS 1.3)
[ Cloudflare Edge CDN / WAF ]
β
βΌ (Proxy / DNS A/CNAME)
[ AWS Application Load Balancer (ALB) ]
βββββββββββββββββββββββββββββββββ (Target Groups)
βΌ βΌ
[ Blue Environment (v1.0) ] [ Green Environment (v2.0) ]
βββ Nginx + PHP-FPM / Node βββ Nginx + PHP-FPM / Node
βββ Supervisor (Queue Worker) βββ Supervisor (Queue Worker)
βββ Local Redis / Memcached βββ Local Redis / Memcached
β β
βββββββββββββββββ¬ββββββββββββββββ
βΌ
[ AWS Aurora MySQL / RDS Multi-AZ ]
β²
β
[ AWS ElastiCache Redis Cluster ]
Di balik layar, ketika pembaruan infrastruktur dipicu melalui Terraform:
- Terraform membaca konfigurasi deklaratif dan memeriksa Remote State yang dikunci oleh DynamoDB untuk menghindari konflik konkurensi.
- ASG meluncurkan instance "Green" baru yang menjalankan versi aplikasi terbaru.
- Skrip user_data pada instance baru secara otomatis mengonfigurasi Nginx, menghubungkan worker Supervisor, memuat sertifikat SSL, dan melakukan health check internal.
- Setelah instance Green lolos health check ALB, Application Load Balancer secara bertahap mengalihkan traffic dari Blue ke Green.
- Instance Blue lama dihentikan dan dihapus oleh Terraform tanpa menyisakan orphan resources.
3. Perbandingan Komparatif / Analisis Pendekatan
Dalam membangun arsitektur web berskala enterprise, terdapat tiga pendekatan utama pengelolaan infrastruktur yang sering dipertimbangkan:
| Pendekatan | Kelebihan | Kekurangan | Konsumsi Sumber Daya (CPU/RAM/Latency) |
|---|---|---|---|
| 1. In-Place Update (Traditional) | Konfigurasi awal cepat, tidak butuh resource cadangan | Downtime tinggi, risiko configuration drift, rollback sulit | CPU/RAM minimal, namun Latency melonjak saat restart layanan. |
| 2. Rolling Update (AWS ASG) | Tanpa downtime total, hemat resource dibanding Blue-Green | Kompleksitas tinggi saat menangani migrasi skema database | Penggunaan CPU/RAM fluktuatif selama proses pergantian bertahap. |
| 3. Blue-Green Deployment (Terraform) | Zero-downtime absolut, rollback instan, isolasi total | Membutuhkan duplikasi resource sementara (2x lipat kapasitas) | Konsumsi resource ganda selama masa transisi (cutover). |
[!TIP] Gunakan pendekatan Blue-Green berbasis Terraform untuk sistem kritikal (Fintech, E-commerce, SaaS enterprise) di mana satu detik downtime berarti kehilangan pendapatan yang signifikan.
4. Prasyarat Sistem & Persiapan Lingkungan
Sebelum mulai menulis kode Terraform, pastikan workstation atau CI/CD runner Anda memiliki perangkat lunak berikut:
- Terraform CLI: Versi
>= 1.5.0 - AWS CLI: Versi
2.xdengan kredensial IAM yang memiliki hak akses penuh ke EC2, VPC, IAM, S3, DynamoDB, dan RDS. - Git: Untuk manajemen repositori kode.
Struktur Direktori Proyek
terraform-aws-zerodowntime/
βββ backend.tf
βββ provider.tf
βββ variables.tf
βββ outputs.tf
βββ vpc.tf
βββ security_groups.tf
βββ alb.tf
βββ rds.tf
βββ redis.tf
βββ asg.tf
βββ user_data.sh
5. Panduan Implementasi Langkah demi Langkah
Berikut adalah sekumpulan file konfigurasi Terraform lengkap, valid, dan siap produksi tanpa menggunakan placeholder.
1. backend.tf - Konfigurasi Remote State & Locking
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "enterprise-terraform-state-bucket-prod"
key = "web-infrastructure/terraform.tfstate"
region = "ap-southeast-1"
dynamodb_table = "terraform-lock-table"
encrypt = true
}
}
2. provider.tf - Konfigurasi Provider AWS
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Environment = var.environment
ManagedBy = "Terraform"
Project = "Enterprise-Web-Core"
}
}
}
3. variables.tf - Definisi Variabel Global
variable "aws_region" {
type = string
default = "ap-southeast-1"
}
variable "environment" {
type = string
default = "production"
}
variable "instance_type" {
type = string
default = "t3.medium"
}
variable "db_password" {
type = string
sensitive = true
default = "SecurePassword12345!@#"
}
4. vpc.tf - Jaringan VPC, Subnet, dan Internet Gateway
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "prod-vpc"
}
}
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "prod-igw"
}
}
data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "prod-public-subnet-${count.index}"
}
}
resource "aws_subnet" "private" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index + 10}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "prod-private-subnet-${count.index}"
}
}
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 = "prod-public-rt"
}
}
resource "aws_route_table_association" "public" {
count = 2
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
5. security_groups.tf - Aturan Firewall & Akses Keamanan
resource "aws_security_group" "alb" {
name = "prod-alb-sg"
description = "Allow HTTP and HTTPS inbound traffic"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_security_group" "web" {
name = "prod-web-sg"
description = "Allow traffic from ALB and internal services"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
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"]
}
}
6. alb.tf - Application Load Balancer & Target Group
resource "aws_lb" "main" {
name = "prod-web-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = aws_subnet.public[*].id
enable_deletion_protection = true
}
resource "aws_lb_target_group" "web" {
name = "prod-web-tg"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.main.id
health_check {
enabled = true
path = "/health.php"
port = "80"
protocol = "HTTP"
interval = 30
timeout = 5
healthy_threshold = 2
unhealthy_threshold = 3
matcher = "200"
}
}
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.main.arn
port = "80"
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.web.arn
}
}
7. rds.tf - Basis Data MySQL Multi-AZ
resource "aws_db_subnet_group" "main" {
name = "prod-db-subnet-group"
subnet_ids = aws_subnet.private[*].id
}
resource "aws_security_group" "db" {
name = "prod-db-sg"
description = "Allow MySQL access from web servers"
vpc_id = aws_vpc.main.id
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.web.id]
}
}
resource "aws_db_instance" "mysql" {
identifier = "prod-mysql-db"
engine = "mysql"
engine_version = "8.0"
instance_class = "db.t3.medium"
allocated_storage = 50
max_allocated_storage = 200
storage_type = "gp3"
db_name = "enterprise_db"
username = "dbadmin"
password = var.db_password
db_subnet_group_name = aws_db_subnet_group.main.name
vpc_security_group_ids = [aws_security_group.db.id]
multi_az = true
skip_final_snapshot = false
final_snapshot_identifier = "prod-mysql-final-snapshot"
}
8. redis.tf - ElastiCache Redis untuk Caching & Session Store
resource "aws_elasticache_subnet_group" "main" {
name = "prod-redis-subnet"
subnet_ids = aws_subnet.private[*].id
}
resource "aws_security_group" "redis" {
name = "prod-redis-sg"
description = "Allow Redis access from web servers"
vpc_id = aws_vpc.main.id
ingress {
from_port = 6379
to_port = 6379
protocol = "tcp"
security_groups = [aws_security_group.web.id]
}
}
resource "aws_elasticache_cluster" "redis" {
cluster_id = "prod-redis-cluster"
engine = "redis"
node_type = "cache.t3.micro"
num_cache_nodes = 1
parameter_group_name = "default.redis7"
engine_version = "7.0"
port = 6379
subnet_group_name = aws_elasticache_subnet_group.main.name
security_group_ids = [aws_security_group.redis.id]
}
9. user_data.sh - Skrip Bootstrap Instance Web Server
#!/bin/bash
apt-get update -y
apt-get install -y nginx php-fpm php-mysql php-redis curl git
# Konfigurasi Nginx untuk PHP-FPM
systemctl start nginx
systemctl enable nginx
# Buat Endpoint Health Check
echo "<?php echo 'OK'; ?>" > /var/www/html/health.php
# Konfigurasi Supervisor untuk Queue Worker
apt-get install -y supervisor
systemctl enable supervisor
systemctl start supervisor
# Restart PHP-FPM
systemctl restart php8.1-fpm
10. asg.tf - Auto Scaling Group & Launch Template
resource "aws_launch_template" "web" {
name_prefix = "prod-web-lt-"
image_id = "ami-060e277c0d4cce553" # Ubuntu 22.04 LTS ap-southeast-1
instance_type = var.instance_type
network_interfaces {
associate_public_ip_address = true
security_groups = [aws_security_group.web.id]
}
user_data = base64encode(file("${path.module}/user_data.sh"))
lifecycle {
create_before_destroy = true
}
}
resource "aws_autoscaling_group" "web" {
name_prefix = "prod-asg-"
desired_capacity = 2
max_size = 4
min_size = 2
target_group_arns = [aws_lb_target_group.web.arn]
vpc_zone_identifier = aws_subnet.public[*].id
launch_template {
id = aws_launch_template.web.id
version = "$Latest"
}
instance_refresh {
strategy = "Rolling"
preferences {
min_healthy_percentage = 50
}
triggers = ["tag"]
}
lifecycle {
create_before_destroy = true
}
}
6. Bedah Kode & Penjelasan Detail Baris Kunci
lifecycle { create_before_destroy = true }: Baris ini adalah kunci utama dari zero-downtime deployment pada tingkat Terraform. Saat parameter ini diaktifkan padaaws_launch_templateatauaws_autoscaling_group, Terraform akan memastikan bahwa sumber daya baru dibuat dan siap sepenuhnya sebelum sumber daya lama dihancurkan.instance_refreshpada ASG: Memerintahkan AWS untuk secara otomatis mengganti instance lama dengan instance baru yang menggunakan Launch Template terbaru, dengan tetap mempertahankan jumlah minimal instance yang sehat (min_healthy_percentage = 50).backend "s3"dengan DynamoDB Locking: Mencegah dua engineer atau pipeline CI/CD menjalankanterraform applysecara bersamaan yang dapat merusak file state.
7. Skenario Produksi & Studi Kasus Dunia Nyata
Dalam sebuah skenario flash sale e-commerce dengan lonjakan traffic hingga 50.000 requests per second (RPS):
- Edge Caching: Cloudflare menangani static assets (CSS, JS, Gambar) di Edge locations, mengurangi beban ke ALB hingga 70%.
- Auto Scaling: Ketika CPU utilization ASG melewati 75%, ASG secara otomatis menambah instance baru tanpa mengganggu sesi pengguna karena status disimpan terpusat di ElastiCache Redis.
- Database Scaling: Aurora MySQL menangani read replicas untuk query pembacaan produk, sementara write operations diarahkan ke primary instance.
8. Common Pitfalls, Anti-Patterns & Solusi
- Anti-Pattern 1: Menyimpan State Lokal di Mesin Lokal (
terraform.tfstate). Solusi: Selalu gunakan Remote Backend seperti AWS S3 dengan enkripsi dan DynamoDB untuk locking. - Anti-Pattern 2: Menggunakan Hardcoded AMI ID.
Solusi: Gunakan Data Source
aws_amiuntuk mengambil AMI terbaru secara dinamis. - Pitfall 3: Mengabaikan Graceful Shutdown pada Aplikasi Node.js/PHP.
Solusi: Pastikan web server menangani sinyal
SIGTERMdengan benar sebelum proses dihentikan oleh ASG.
9. Benchmark, Metrik Kinerja & Tuning Performa
Untuk menguji ketahanan zero-downtime arsitektur ini, jalankan pengujian beban menggunakan k6 selama proses terraform apply:
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 100,
duration: '5m',
};
export default function () {
const res = http.get('http://prod-web-alb-123456789.ap-southeast-1.elb.amazonaws.com/health.php');
check(res, {
'status is 200': (r) => r.status === 200,
});
sleep(1);
}
Hasil Target: Error rate 0.00% selama proses instance refresh dan pembaruan infrastruktur berlangsung.
10. Checklist Penerapan Produksi & Kesimpulan
- Remote State S3 & DynamoDB Locking telah aktif dan diuji.
- Lifecycle
create_before_destroy = truediterapkan pada seluruh komponen Launch Template. - Health check ALB mengarah ke endpoint valid (
/health.php). - Secret seperti
db_passworddikelola melalui variabel sensitif atau AWS Secrets Manager. - Cloudflare WAF dan SSL/TLS terkonfigurasi dengan mode Full (Strict).
Dengan menerapkan arsitektur Terraform yang dikombinasikan dengan strategi Rolling/Blue-Green Deployment dan AWS ASG ini, tim infrastruktur Anda dapat merilis pembaruan kapan saja di tengah hari tanpa rasa cemas akan gangguan layanan bagi pengguna akhir.