L
Infrastructure as Code (IaC) β€’ Literally Web ID

Arsitektur Zero-Downtime Infrastructure as Code Menggunakan Terraform & AWS

⏱️ 15 menit baca β€’ πŸ“… 12 Oct 2026 β€’ πŸ‘οΈ 1 views
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:

  1. Terraform membaca konfigurasi deklaratif dan memeriksa Remote State yang dikunci oleh DynamoDB untuk menghindari konflik konkurensi.
  2. ASG meluncurkan instance "Green" baru yang menjalankan versi aplikasi terbaru.
  3. Skrip user_data pada instance baru secara otomatis mengonfigurasi Nginx, menghubungkan worker Supervisor, memuat sertifikat SSL, dan melakukan health check internal.
  4. Setelah instance Green lolos health check ALB, Application Load Balancer secara bertahap mengalihkan traffic dari Blue ke Green.
  5. 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.x dengan 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 pada aws_launch_template atau aws_autoscaling_group, Terraform akan memastikan bahwa sumber daya baru dibuat dan siap sepenuhnya sebelum sumber daya lama dihancurkan.
  • instance_refresh pada 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 menjalankan terraform apply secara 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):

  1. Edge Caching: Cloudflare menangani static assets (CSS, JS, Gambar) di Edge locations, mengurangi beban ke ALB hingga 70%.
  2. 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.
  3. 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_ami untuk mengambil AMI terbaru secara dinamis.
  • Pitfall 3: Mengabaikan Graceful Shutdown pada Aplikasi Node.js/PHP. Solusi: Pastikan web server menangani sinyal SIGTERM dengan 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 = true diterapkan pada seluruh komponen Launch Template.
  • Health check ALB mengarah ke endpoint valid (/health.php).
  • Secret seperti db_password dikelola 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.

🏷️ Tags: #Nginx #High Concurrency #Performance #Sysadmin #Web Server