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

Zero-Downtime Infrastructure as Code: Terraform & Ansible on AWS

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

  1. 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.
  2. 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.
  3. 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.
  4. Ansible Configuration Engine: Dijalankan secara otomatis saat instans EC2 melakukan booting (melalui Cloud-Init / user_data) atau dipicu oleh Terraform remote-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 systemd dan 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_refresh di 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_data di 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:

  1. Auto Scaling Policy: Diatur berdasarkan metrik CloudWatch CPUUtilization > 70% atau ALBTargetResponseTime > 0.5s.
  2. Database Scaling: Menggunakan Amazon Aurora MySQL Multi-AZ dengan Read Replicas otomatis yang diorkestrasi oleh Terraform.
  3. 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 menjalankan terraform apply secara bersamaan yang dapat merusak state file.
  • Pitfall 3: Timeout pada Health Check saat instance warmup terlalu singkat. Solusi: Berikan waktu instance_warmup yang 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 = true aktif 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.

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