Zero-Downtime Infrastructure as Code: Terraform & Ansible on AWS
1. Konteks Industri & Analisis Akar Masalah
Dalam lanskap enterprise modern, ketersediaan tinggi (high availability) dan zero-downtime deployment bukan lagi fitur sekunder, melainkan mandat fundamental bisnis. Kegagalan infrastruktur atau gangguan sesaat saat pembaruan aplikasi berpotensi menyebabkan kerugian finansial masif serta rusaknya reputasi merek. Namun, realitas operasional di lapangan sering kali memperlihatkan kebalikannya: provisioning manual, inkonsistensi konfigurasi antar lingkungan (environment drift), dan proses rollback yang lambat serta rentan kesalahan manusia (human error).
Secara historis, administrator sistem mengandalkan skrip shell ad-hoc atau konfigurasi manual melalui konsol AWS untuk membangun infrastruktur. Pendekatan ini menciptakan snowflake serversβserver unik yang tidak dapat direproduksi secara persis, membuat proses troubleshooting menjadi mimpi buruk. Di sisi lain, aplikasi web modern menuntut ekosistem yang kompleks: mulai dari web server Nginx/Caddy, backend Go/Node.js/PHP-FPM, sistem queue berbasis Supervisor, penyimpanan objek S3, hingga basis data MySQL/MariaDB dan caching layer Redis. Mengubah satu parameter konfigurasi sistem operasi secara manual di tengah produksi dapat merusak koneksi socket, menyebabkan kebocoran memori, atau memicu kegagalan autentikasi TLS.
Sebelum adopsi Infrastructure as Code (IaC) yang matang, organisasi sering kali mengalami:
- Downtime Terjadwal & Tidak Terjadwal: Proses deployment memerlukan waktu henti (maintenance window) yang mengorbankan pengalaman pengguna.
- Konfigurasi yang Menyimpang (Drift): Server staging dan production berbeda, menyebabkan bug yang hanya muncul setelah rilis.
- Proses Rollback Manual: Saat terjadi kegagalan, tim harus melakukan ssh ke setiap instans, mengembalikan berkas konfigurasi secara manual, yang memakan waktu hingga berjam-jam.
Dengan memadukan Terraform untuk orkestrasi infrastruktur imajiner menjadi nyata dan Ansible untuk konfigurasi serta manajemen deployment bebas gangguan (zero-downtime), organisasi dapat mewujudkan otomatisasi dari tingkat logam (bare metal / cloud VM) hingga lapisan aplikasi web paling atas tanpa intervensi manual.
2. Konsep Inti & Deep Dive Arsitektur
Arsitektur Zero-Downtime Infrastructure as Code bertumpu pada konsep Immutable Infrastructure. Alih-alih memperbarui (in-place update) server yang sedang berjalan, kita membuat infrastruktur baru, memvalidasinya, mengalihkan lalu lintas secara mulus, lalu memusnahkan infrastruktur lama.
Berikut adalah representasi alur kerja end-to-end dalam bentuk diagram ASCII:
+------------------+ +-----------------------+ +------------------------+
| Developer / CI | ------->| Terraform Cloud/State | ------>| AWS Cloud (VPC, ASG, |
| Git Push / PR | | (Remote Backend S3/DB)| | ALB, EC2, RDS, ElastiC)|
+------------------+ +-----------------------+ +------------------------+
|
v
+------------------+ +-----------------------+ +------------------------+
| Health Check & |<------- | Ansible Provisioning |<-------| Launch Template / AMI |
| Traffic Shift | | (Nginx, PHP, Redis) | | Auto Scaling Group |
+------------------+ +-----------------------+ +------------------------+
Komponen Arsitektur Utama:
- Terraform State Management: Menyimpan peta aktual infrastruktur di AWS dalam remote backend (Amazon S3 dengan penguncian DynamoDB) untuk mencegah kondisi balapan (race condition) saat beberapa insinyur atau pipeline CI/CD melakukan eksekusi secara paralel.
- AWS Auto Scaling Group (ASG) & Launch Template: Memungkinkan rolling update otomatis. Saat AMI atau skrip konfigurasi berubah, ASG membuat instans baru terlebih dahulu sebelum menghentikan instans lama.
- Application Load Balancer (ALB) & Health Check: Mengarahkan lalu lintas hanya ke instans yang lulus uji kesehatan HTTP/TCP (health check), memastikan pengguna tidak pernah melihat halaman error 502/504.
- Ansible Configuration Engine: Dijalankan secara otomatis saat instans EC2 melakukan booting (melalui Cloud-Init /
user_data) atau dipicu oleh Terraformremote-exec/ AWS Systems Manager (SSM), untuk mengonfigurasi Nginx, PHP-FPM, Supervisor, dan modul keamanan.
3. Perbandingan Komparatif / Analisis Pendekatan
Untuk memahami mengapa pendekatan Terraform + Ansible di AWS menjadi standar emas enterprise, mari kita bandingkan dengan metode konvensional.
| Parameter Perbandingan | Manual / Console AWS | Bash Scripting + Cron | Terraform + Ansible (Zero-Downtime) |
|---|---|---|---|
| Reproduksibilitas | Rendah (Sangat bergantung individu) | Sedang (Sering gagal di OS berbeda) | Tinggi (Deklaratif & Konsisten) |
| Waktu Recovery (RTO) | Berjam-jam (Manual troubleshooting) | 30 - 60 Menit | < 3 Menit (Automated ASG Replacement) |
| Risiko Human Error | Sangat Tinggi | Tinggi | Sangat Rendah (Code Review & Test) |
| Manajemen State | Tidak ada | Manual / Berkas lokal | Terpusat, Terenkripsi, Terkunci (S3+DynamoDB) |
| Dampak Latensi & CPU | Tidak terukur | Sering membebani CPU saat skrip salah | Efisien, penjadwalan teroptimasi di Cloud |
[!IMPORTANT] Jangan pernah menyimpan berkas state Terraform (
terraform.tfstate) di repositori Git lokal jika Anda bekerja dalam tim. Gunakan remote backend dengan enkripsi at-rest menggunakan AWS KMS.
4. Prasyarat Sistem & Persiapan Lingkungan
Sebelum menulis kode infrastruktur, pastikan workstation atau CI/CD runner Anda memiliki perangkat lunak dan hak akses berikut:
- Terraform: Versi
>= 1.5.0 - Ansible: Versi
>= 2.15 - AWS CLI: Terkonfigurasi dengan kredensial IAM yang memiliki izin penuh administrator (atau kebijakan spesifik EC2, VPC, IAM, S3, RDS)
- Git & Make: Untuk otomatisasi perintah operasional
Verifikasi instalasi dengan perintah berikut di terminal:
terraform -version
ansible --version
aws sts get-caller-identity
Struktur direktori proyek enterprise yang direkomendasikan:
infra-zero-downtime/
βββ terraform/
β βββ main.tf
β βββ variables.tf
β βββ outputs.tf
β βββ vpc.tf
β βββ ec2.tf
β βββ alb.tf
βββ ansible/
β βββ playbook.yml
β βββ roles/
β βββ common/
β βββ webserver/
β βββ database/
βββ Makefile
5. Panduan Implementasi Langkah demi Langkah
Langkah 1: Konfigurasi Terraform - Remote Backend & VPC
Buat file terraform/main.tf untuk mendefinisikan penyedia AWS dan backend penyimpanan state.
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "enterprise-terraform-state-bucket-prod"
key = "prod/infrastructure/terraform.tfstate"
region = "ap-southeast-1"
dynamodb_table = "terraform-lock-table"
encrypt = true
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Environment = "Production"
ManagedBy = "Terraform"
Project = "ZeroDowntimeWeb"
}
}
}
Buat file terraform/vpc.tf untuk infrastruktur jaringan yang aman dengan Public dan Private Subnet.
variable "aws_region" {
default = "ap-southeast-1"
}
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_subnet" "public_1" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-southeast-1a"
map_public_ip_on_launch = true
tags = {
Name = "prod-public-1"
}
}
resource "aws_subnet" "public_2" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
availability_zone = "ap-southeast-1b"
map_public_ip_on_launch = true
tags = {
Name = "prod-public-2"
}
}
resource "aws_subnet" "private_1" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.10.0/24"
availability_zone = "ap-southeast-1a"
tags = {
Name = "prod-private-1"
}
}
resource "aws_subnet" "private_2" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.20.0/24"
availability_zone = "ap-southeast-1b"
tags = {
Name = "prod-private-2"
}
}
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "prod-igw"
}
}
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-route-table-public"
}
}
resource "aws_route_table_association" "pub_1" {
subnet_id = aws_subnet.public_1.id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "pub_2" {
subnet_id = aws_subnet.public_2.id
route_table_id = aws_route_table.public.id
}
Langkah 2: Konfigurasi Load Balancer & Auto Scaling Group
Buat file terraform/alb.tf untuk menangani lalu lintas masuk secara seimbang.
resource "aws_security_group" "alb" {
name = "prod-alb-sg"
description = "Allow HTTP/HTTPS inbound traffic"
vpc_id = aws_vpc.main.id
ingress {
description = "HTTP from anywhere"
from_port = 80
to_port = 80
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_lb" "web" {
name = "prod-web-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = [aws_subnet.public_1.id, aws_subnet.public_2.id]
}
resource "aws_lb_target_group" "web" {
name = "prod-web-tg"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.main.id
health_check {
path = "/health.php"
protocol = "HTTP"
matcher = "200"
interval = 15
timeout = 5
healthy_threshold = 2
unhealthy_threshold = 3
}
}
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.web.arn
port = "80"
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.web.arn
}
}
Buat file terraform/ec2.tf untuk mendefinisikan instans web server di dalam Auto Scaling Group dengan kebijakan rolling update.
resource "aws_security_group" "ec2" {
name = "prod-ec2-sg"
description = "Allow traffic from ALB"
vpc_id = aws_vpc.main.id
ingress {
description = "HTTP from ALB"
from_port = 80
to_port = 80
protocol = "tcp"
security_groups = [aws_security_group.alb.id]
}
ingress {
description = "SSH Admin"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["103.21.244.0/22"] # Contoh IP kantor aman
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
aws_account_id = "099720109477" # Canonical
}
resource "aws_launch_template" "web" {
name_prefix = "prod-web-lt-"
image_id = data.aws_ami.ubuntu.id
instance_type = "t3.medium"
key_name = "prod-key"
network_interfaces {
associate_public_ip_address = true
security_groups = [aws_security_group.ec2.id]
}
user_data = base64encode(<<-EOF
#!/bin/bash
apt-get update -y
apt-get install -y ansible git python3-pip
echo "localhost ansible_connection=local" > /etc/ansible/hosts
git clone https://github.com/your-org/infra-zero-downtime.git /opt/infra
cd /opt/infra/ansible
ansible-playbook playbook.yml
EOF
)
lifecycle {
create_before_destroy = true
}
}
resource "aws_autoscaling_group" "web" {
name_prefix = "prod-asg-"
desired_capacity = 2
max_size = 6
min_size = 2
target_group_arns = [aws_lb_target_group.web.arn]
vpc_zone_identifier = [aws_subnet.public_1.id, aws_subnet.public_2.id]
launch_template {
id = aws_launch_template.web.id
version = "$Latest"
}
instance_refresh {
strategy = "Rolling"
preferences {
min_healthy_percentage = 50
instance_warmup = 300
}
triggers = ["tag"]
}
lifecycle {
create_before_destroy = true
}
}
Langkah 3: Konfigurasi Ansible - Otomatisasi Nginx, PHP-FPM, & Rollback Handler
Buat file ansible/playbook.yml untuk mengonfigurasi tumpukan aplikasi web secara idempotent.
---
- name: Configure Production Web Server
hosts: localhost
become: yes
vars:
php_version: "8.2"
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install essential packages
apt:
name:
- curl
- git
- unzip
- software-properties-common
state: present
- name: Add PPA for PHP
apt_repository:
repo: ppa:ondrej/php
state: present
- name: Install Nginx and PHP-FPM Stack
apt:
name:
- nginx
- php{{ php_version }}-fpm
- php{{ php_version }}-cli
- php{{ php_version }}-mysql
- php{{ php_version }}-redis
- php{{ php_version }}-mbstring
- php{{ php_version }}-xml
state: present
- name: Configure Nginx virtual host
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/default
owner: root
group: root
mode: '0644'
notify: Restart Nginx
- name: Create health check endpoint
copy:
content: "<?php echo 'OK'; ?>"
dest: /var/www/html/health.php
owner: www-data
group: www-data
mode: '0644'
- name: Ensure Nginx and PHP-FPM are running and enabled
systemd:
name: "{{ item }}"
state: started
enabled: yes
loop:
name:
- nginx
- "php{{ php_version }}-fpm"
handlers:
- name: Restart Nginx
service:
name: nginx
state: restarted
- name: Restart PHP-FPM
service:
name: "php{{ php_version }}-fpm"
state: restarted
Buat template Nginx ansible/templates/nginx.conf.j2.
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.php index.html index.htm;
server_name _;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
[!TIP] Gunakan modul
systemddan handlers dalam Ansible agar layanan hanya direstart ketika file konfigurasi benar-benar mengalami perubahan (idempotensi penuh).
6. Bedah Kode & Penjelasan Detail Baris Kunci
lifecycle { create_before_destroy = true }di Terraform: Baris ini sangat krusial untuk zero-downtime. Saat Anda mengubah Launch Template, Terraform akan membuat sumber daya baru terlebih dahulu sebelum menghapus yang lama. Tanpa baris ini, Terraform akan mematikan server lama terlebih dahulu, mengakibatkan outage instan.instance_refreshdi ASG: Mengatur agar AWS secara otomatis mengganti instans satu per satu (rolling) dengan batas kesehatan minimum (min_healthy_percentage = 50), memastikan kapasitas layanan tidak pernah kosong selama pembaruan.user_datadi Launch Template: Menjalankan skrip bootstrap otomatis saat instans menyala, mengunduh Ansible, dan menjalankan playbook secara lokal, menghasilkan server yang siap melayani trafik tanpa sentuhan manusia.
7. Skenario Produksi & Studi Kasus Dunia Nyata
Dalam kasus nyata penanganan flash sale e-commerce dengan lonjakan lalu lintas dari 1.00ΰ¦ΰ§ requests per second (RPS) menjadi 25.000 RPS:
- Auto Scaling Policy: Diatur berdasarkan metrik CloudWatch
CPUUtilization > 70%atauALBTargetResponseTime > 0.5s. - Database Scaling: Menggunakan Amazon Aurora MySQL Multi-AZ dengan Read Replicas otomatis yang diorkestrasi oleh Terraform.
- Caching Layer: Redis Cluster terdistribusi yang dikonfigurasi melalui Ansible untuk menangani sesi pengguna dan kueri berat secara in-memory.
Ketika pembaruan kode aplikasi dikirim ke repositori, pipeline CI/CD memicu pembaruan versi AMI. ASG melakukan instance refresh secara bertahap tanpa menjatuhkan koneksi aktif (persistent keep-alive connection) berkat penanganan graceful shutdown di Nginx (worker_shutdown_timeout 10s).
8. Common Pitfalls, Anti-Patterns & Solusi
- Anti-Pattern 1: Menggunakan skrip Ansible untuk memodifikasi infrastruktur cloud (seperti membuat VPC atau RDS). Solusi: Gunakan Terraform khusus untuk penyediaan infrastruktur (infrastructure provisioning) dan Ansible khusus untuk konfigurasi sistem operasi serta aplikasi (configuration management).
- Anti-Pattern 2: Mengabaikan penguncian State Terraform (
dynamodb_table). Solusi: Selalu aktifkan DynamoDB locking agar dua developer tidak menjalankanterraform applysecara bersamaan yang dapat merusak state file. - Pitfall 3: Timeout pada Health Check saat instance warmup terlalu singkat.
Solusi: Berikan waktu
instance_warmupyang memadai (misalnya 300 detik) agar aplikasi PHP dan cache Redis sempat melakukan preload sebelum ALB mulai mengirimkan trafik.
9. Benchmark, Metrik Kinerja & Tuning Performa
Untuk memastikan infrastruktur yang dibangun siap menghadapi beban produksi, lakukan pengujian beban (load testing) menggunakan k6 atau wrk.
Contoh skrip pengujian k6 (load-test.js):
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 1000 }, // Ramp-up ke 1000 pengguna
{ duration: '5m', target: 5000 }, // Tahan di 5000 pengguna
{ duration: '2m', target: 0 }, // Ramp-down
],
};
export default function () {
const res = http.get('http://prod-web-alb-123456789.ap-southeast-1.elb.amazonaws.com/');
check(res,
{ 'status is 200': (r) => r.status === 200 },
{ 'response time < 200ms': (r) => r.timings.duration < 200 }
);
sleep(1);
}
Jalankan pengujian:
k6 run load-test.js
Tuning Kernel Linux (/etc/sysctl.conf) via Ansible untuk Handle High Concurrency:
# Tingkatkan jumlah maksimal open files
fs.file-max = 2097152
# Optimasi TCP backlog untuk koneksi masuk masif
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# Reuse port TIME_WAIT
net.ipv4.tcp_tw_reuse = 1
10. Checklist Penerapan Produksi & Kesimpulan
Sebelum meluncurkan infrastruktur ini ke lingkungan produksi live, pastikan Anda telah memeriksa daftar periksa berikut:
- Remote backend S3 terenkripsi dan terlindungi dengan DynamoDB State Locking.
- Seluruh variabel rahasia (kunci API, kredensial database) dikelola melalui AWS Secrets Manager / Parameter Store, bukan hardcode di Terraform/Ansible.
- Konfigurasi
create_before_destroy = trueaktif pada Launch Template dan ASG. - Aturan Security Group AWS mengikuti prinsip least privilege (hanya port yang diperlukan yang dibuka).
- Pengujian rolling update dan simulasi failure instance telah berhasil dilakukan di lingkungan staging.
Dengan menggabungkan kekuatan deklaratif Terraform dan fleksibilitas konfigurasi Ansible di atas fondasi AWS yang resilien, tim Anda kini memiliki kapabilitas penuh untuk menjalankan deployment kapan saja di siang hari tanpa rasa cemas, mewujudkan standar keandalan kelas dunia yang sejati.