Zero-Downtime Infrastructure Provisioning dengan Terraform dan Ansible
1. Konteks Industri & Analisis Akar Masalah
Dalam lanskap arsitektur web modern, ketersediaan tinggi (high availability) dan konsistensi infrastruktur bukan lagi sebuah fitur mewah, melainkan sebuah keharusan mutlak (mandatory baseline). Seiring dengan pergeseran beban kerja (workloads) dari server fisik monolitik menuju lingkungan terdistribusi multi-tier—yang mencakup load balancer Nginx, backend server Go/Node.js/PHP-FPM, database MySQL/MariaDB terkluster, hingga sistem antrean berbasis Redis—kompleksitas pengelolaan infrastruktur meningkat secara eksponensial.
Secara historis, banyak organisasi mengalami apa yang disebut sebagai Configuration Drift dan Snowflake Servers. Configuration Drift terjadi ketika perubahan manual dilakukan secara langsung pada server produksi (misalnya, mengubah direktif konfigurasi Nginx atau memperbarui modul PHP secara langsung melalui SSH). Akibatnya, lingkungan Staging dan Production menjadi tidak identik, memicu kegagalan deployment yang sulit didiagnosis. Di sisi lain, Snowflake Servers merujuk pada server unik yang tidak dapat direproduksi karena proses pembuatannya melibatkan intervensi manual yang tidak terdokumentasi dengan baik.
Dunia industri web modern menghadapi bottleneck keamanan dan operasional yang signifikan ketika mengandalkan skrip shell tradisional untuk provisi sistem. Skrip shell tidak bersifat idempoten secara bawaan; menjalankan skrip yang sama dua kali dapat merusak sistem atau mengubah konfigurasi menjadi status yang tidak diinginkan. Selain itu, manajemen state infrastruktur secara manual sering kali berujung pada human error, waktu pemulihan (Recovery Time Objective / RTO) yang lama saat terjadi kegagalan bencana, dan kerentanan keamanan akibat celah konfigurasi TLS/SSL atau aturan firewall yang terlewat.
Perbandingan antara pendekatan tradisional (Manual / Ad-hoc) dan pendekatan modern berbasis Infrastruktur sebagai Kode (Infrastructure as Code / IaC) dapat diringkas sebagai berikut:
-
Sebelum (Manual & Ad-hoc):
- Waktu provisi server baru memakan waktu jam/hari.
- Risiko human error tinggi pada konfigurasi Nginx, PHP-FPM, dan aturan WAF Fail2ban.
- Tidak ada riwayat perubahan (audit trail) infrastruktur.
- Rollback ketika terjadi kegagalan deployment hampir mustahil dilakukan tanpa downtime.
-
Sesudah (Terraform & Ansible Zero-Downtime Pipeline):
- Provisi infrastruktur multi-tier otomatis selesai dalam hitungan menit.
- Konsistensi penuh melalui manajemen state terpusat dan konfigurasi Ansible yang idempoten.
- Riwayat perubahan tercatat rapi melalui Git (GitOps workflow).
- Penerapan pembaruan sistem secara rolling update tanpa mengganggu trafik pengguna akhir (Zero-Downtime).
2. Konsep Inti & Deep Dive Arsitektur
Untuk mencapai Zero-Downtime Infrastructure Provisioning, kita menggabungkan dua kekuatan utama alat otomatisasi modern: Terraform (oleh HashiCorp) dan Ansible (oleh Red Hat). Peran keduanya terbagi secara tegas namun saling melengkapi dalam prinsip separation of concerns.
Terraform bertindak sebagai alat provisi infrastruktur (Infrastructure Provisioning Tool). Ia bertanggung jawab untuk menciptakan sumber daya tingkat awan (cloud primitives), seperti Virtual Private Cloud (VPC), subnet, aturan Security Groups, instans komputasi (EC2/Droplets), manajemen DNS, dan alokasi Elastic Load Balancer (ALB). Terraform bekerja secara deklaratif dengan memetakan status aktual infrastruktur ke dalam file state (terraform.tfstate).
Sebaliknya, Ansible bertindak sebagai alat manajemen konfigurasi (Configuration Management) dan orkestrasi aplikasi. Setelah Terraform selesai menciptakan mesin virtual, Ansible mengambil alih untuk menginstal runtime backend (Node.js, Go, atau PHP-FPM), mengonfigurasi web server Nginx, memasang sertifikat SSL Let's Encrypt, mengatur daemon Supervisor untuk manajemen proses antrean, serta mengonfigurasi basis data dan Redis.
Alur kerja eksekusi terintegrasi ini dapat divisualisasikan melalui diagram aliran data berikut:
+------------------------------------------------------------------------+
| Git Repository (GitOps) |
+------------------------------------------------------------------------+
|
v
+------------------------------------------------------------------------+
| CI/CD Pipeline (GitHub Actions) |
+------------------------------------------------------------------------+
|
+--------------------------+--------------------------+
| (1. Provision) | (2. Configure)
v v
+------------------+ +--------------------+
| Terraform | ----> Cloud Provider API ----> | Virtual Machines |
| (State Manag.) | | (EC2 / Droplets) |
+------------------+ +--------------------+
|
v
+--------------------+
| Ansible |
| (Playbook Exec.) |
+--------------------+
|
+-----------------------+-----------------------+
| Nginx & PHP-FPM | MySQL / Redis | Let's Encrypt / WAF
v v v
+-------------------------------------------------------+
| Zero-Downtime Production Web App |
+-------------------------------------------------------+
[!IMPORTANT] Pemisahan peran antara Terraform dan Ansible sangat krusial. Jangan pernah menggunakan Ansible untuk membuat mesin virtual cloud secara langsung dari awal, dan jangan gunakan Terraform untuk mengonfigurasi detail perangkat lunak di dalam sistem operasi secara berlebihan.
3. Perbandingan Komparatif / Analisis Pendekatan
Dalam membangun ekosistem otomasi infrastruktur, terdapat beberapa alternatif pendekatan manajemen konfigurasi dan provisi. Analisis komparatif di bawah ini membandingkan kombinasi Terraform + Ansible dengan pendekatan alternatif lain dari segi performa, sumber daya, dan kompleksitas operasional.
| Parameter Perbandingan | Terraform + Ansible | Terraform + Cloud-Init / Bash | Full Ansible (AWS/GCP Modules) | CloudFormation / AWS CDK |
|---|---|---|---|---|
| Kompleksitas Setup | Menengah-Tinggi | Rendah-Menengah | Menengah | Tinggi (vendor-locked) |
| Idempotensi | Sempurna (Stateful + Idempotent) | Tergantung skrip shell | Sangat Baik (Idempotent) | Sempurna (Cloud Native) |
| Penggunaan Memori/CPU | Minimal pada Target Node | Sangat Rendah | Minimal pada Target Node | Dikelola Cloud Provider |
| Multi-Cloud Support | Universal (AWS, GCP, Azure, DO) | Tergantung skrip | Terbatas (Modul spesifik cloud) | Terikat pada Satu Ekosistem |
| Kecepatan Rollout | Cepat (Parallel execution) | Cepat | Lambat pada skala besar | Cepat |
| Kurva Pembelajaran | Menengah | Rendah | Menengah | Tinggi |
Dari analisis trade-off di atas, kombinasi Terraform + Ansible dipilih karena fleksibilitas multi-cloud, kemampuan eksekusi idempoten yang matang tanpa agen (agentless architecture via SSH), serta ekosistem komunitas yang sangat luas untuk pengelolaan web server, database, dan stack keamanan.
4. Prasyarat Sistem & Persiapan Lingkungan
Sebelum menyelami implementasi kode, pastikan lingkungan kerja administrator (Control Machine) dan infrastruktur target memenuhi spesifikasi teknis berikut:
Spesifikasi Control Machine (Lokal / CI Runner)
- Sistem Operasi: Linux (Ubuntu 22.04 LTS / Debian 12) atau macOS.
- Terraform: Versi >= 1.5.0
- Ansible: Versi >= 2.15.0
- Python: Versi >= 3.10 (diperlukan untuk modul Ansible)
- Git: Untuk manajemen versi kode IaC.
Persiapan Akses Autentikasi Cloud & SSH
- Buat pasangan kunci SSH (SSH Key Pair) khusus untuk deployment:
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa_infra -N "" - Pastikan variabel lingkungan cloud (Cloud Provider Credentials) telah diekspor ke terminal atau disimpan dengan aman di CI/CD Secrets:
export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE" export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" export AWS_DEFAULT_REGION="ap-southeast-1"
5. Panduan Implementasi Langkah demi Langkah
Bagian ini menyajikan kode implementasi lengkap, valid, dan siap produksi untuk membangun infrastruktur web multi-tier secara otomatis.
A. Desain State Management Terraform
Pertama, kita konfigurasikan penyedia (provider) dan backend state yang aman menggunakan penyimpanan jarak jauh (contoh menggunakan AWS S3 dengan penguncian DynamoDB untuk mencegah konflik penulisan state bersamaan).
# file: terraform/versions.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
local = {
source = "hashicorp/local"
version = "~> 2.4"
}
}
backend "s3" {
bucket = "company-terraform-state-production"
key = "web-infrastructure/terraform.tfstate"
region = "ap-southeast-1"
dynamodb_table = "terraform-state-locks"
encrypt = true
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Environment = "Production"
ManagedBy = "Terraform"
Project = "E-Commerce-Core"
}
}
}
Definisikan variabel infrastruktur:
# file: terraform/variables.tf
variable "aws_region" {
type = string
default = "ap-southeast-1"
}
variable "instance_type" {
type = string
default = "t3.medium"
}
variable "domain_name" {
type = string
default = "example.com"
}
Definisikan sumber daya jaringan dan komputasi (VPC, Subnet, Security Group, dan EC2 Instance):
# file: terraform/main.tf
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "production-vpc"
}
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = true
availability_zone = "ap-southeast-1a"
tags = {
Name = "production-public-subnet"
}
}
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "production-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 = "production-public-rt"
}
}
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
resource "aws_security_group" "web_sg" {
name = "production-web-sg"
description = "Allow HTTP, HTTPS, and SSH traffic"
vpc_id = aws_vpc.main.id
ingress {
description = "SSH from Admin"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # Sesuaikan dengan IP bastion/admin aman di produksi
}
ingress {
description = "HTTP from anywhere"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTPS from anywhere"
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"]
}
tags = {
Name = "production-web-sg"
}
}
resource "aws_key_pair" "deployer" {
key_name = "production-deployer-key"
public_key = file("~/.ssh/id_rsa_infra.pub")
}
resource "aws_instance" "web_server" {
ami = "ami-060e277c0d4cce553" # Ubuntu 22.04 LTS (ap-southeast-1)
instance_type = var.instance_type
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.web_sg.id]
key_name = aws_key_pair.deployer.key_name
root_block_device {
volume_size = 50
volume_type = "gp3"
delete_on_termination = false
encrypted = true
}
tags = {
Name = "production-web-node-01"
}
}
# Otomatis menghasilkan file inventory Ansible dari IP instance Terraform
resource "local_file" "ansible_inventory" {
content = <<-EOT
[webservers]
${aws_instance.web_server.public_ip} ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa_infra
EOT
filename = "../ansible/inventory.ini"
}
B. Konfigurasi Immutable Infrastructure dengan Ansible
Setelah Terraform menghasilkan file inventory.ini, Ansible dijalankan untuk mengonfigurasi server secara menyeluruh (Nginx, PHP-FPM, MySQL, Redis, Fail2ban, dan SSL).
Struktur direktori Ansible:
ansible/
├── inventory.ini
├── playbook.yml
└── roles/
├── common/
├── nginx/
├── php/
└── security/
Berikut adalah file Playbook utama:
# file: ansible/playbook.yml
---
- name: Provision and Configure Web Production Infrastructure
hosts: webservers
become: true
vars:
app_domain: "example.com"
admin_email: "admin@example.com"
roles:
- common
- security
- nginx
- php
Peran common untuk pembaruan sistem dan instalasi paket dasar:
# file: ansible/roles/common/tasks/main.yml
---
- name: Update apt cache and upgrade system packages
apt:
update_cache: yes
upgrade: safe
- name: Install essential system packages
apt:
name:
- curl
- git
- ufw
- unzip
- software-properties-common
state: present
Peran security untuk konfigurasi UFW dan Fail2ban:
# file: ansible/roles/security/tasks/main.yml
---
- name: Install Fail2ban
apt:
name: fail2ban
state: present
- name: Start and enable fail2ban service
systemd:
name: fail2ban
state: started
enabled: true
- name: Configure UFW default policies
ufw:
state: enabled
policy: deny
direction: incoming
- name: Allow SSH through UFW
ufw:
rule: allow
port: '22'
proto: tcp
- name: Allow HTTP through UFW
ufw:
rule: allow
port: '80'
proto: tcp
- name: Allow HTTPS through UFW
ufw:
rule: allow
port: '443'
proto: tcp
Peran nginx untuk instalasi Nginx dan konfigurasi Virtual Host web server:
# file: ansible/roles/nginx/tasks/main.yml
---
- name: Install Nginx web server
apt:
name: nginx
state: present
- name: Ensure Nginx is running and enabled
systemd:
name: nginx
state: started
enabled: true
- name: Deploy Nginx virtual host configuration
template:
src: vhost.j2
dest: /etc/nginx/sites-available/{{ app_domain }}
owner: root
group: root
mode: '0644'
notify: Restart Nginx
- name: Enable virtual host symlink
file:
src: /etc/nginx/sites-available/{{ app_domain }}
dest: /etc/nginx/sites-enabled/{{ app_domain }}
state: link
notify: Restart Nginx
- name: Remove default nginx site
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: Restart Nginx
Template Jinja2 untuk Nginx Virtual Host:
# file: ansible/roles/nginx/templates/vhost.j2
server {
listen 80;
server_name {{ app_domain }} www.{{ app_domain }};
root /var/www/html;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
index index.php index.html index.htm;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
}
Handler untuk Nginx:
# file: ansible/roles/nginx/handlers/main.yml
---
- name: Restart Nginx
systemd:
name: nginx
state: restarted
C. Pipeline CI/CD untuk Validasi IaC
Untuk memastikan seluruh konfigurasi IaC valid sebelum diterapkan ke produksi, kita menggunakan GitHub Actions sebagai pipeline CI/CD otomatis.
# file: .github/workflows/iac-pipeline.yml
name: Infrastructure CI/CD Pipeline
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
terraform-validate:
name: Terraform Validate & Plan
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./terraform
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.5.0
- name: Terraform Init
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: terraform init
- name: Terraform Format Check
run: terraform fmt -check
- name: Terraform Validate
run: terraform validate
- name: Terraform Plan
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: terraform plan -no-color
deploy-infrastructure:
name: Apply Terraform & Run Ansible
needs: terraform-validate
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.5.0
- name: Terraform Apply
working-directory: ./terraform
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: terraform apply -auto-approve
- name: Setup SSH Key for Ansible
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa_infra
chmod 600 ~/.ssh/id_rsa_infra
- name: Run Ansible Playbook
working-directory: ./ansible
run: |
ansible-playbook -i inventory.ini playbook.yml
6. Bedah Kode & Penjelasan Detail Baris Kunci
- Terraform Backend S3 & DynamoDB Locking:
Penggunaan
backend "s3"dengan parameterdynamodb_tablemencegah situasi race condition apabila dua insinyur mencoba melakukanterraform applysecara bersamaan. DynamoDB bertindak sebagai mekanisme pengunci (state locking) atomik. - Otomatisasi
local_fileAnsible Inventory: Baris koderesource "local_file" "ansible_inventory"pada Terraform menjembatani dunia cloud provisioning dan configuration management secara dinamis. Terraform secara otomatis mengekstrak IP publik instans yang baru saja dibuat dan menyuntikkannya ke dalam fileinventory.iniAnsible tanpa intervensi manual. - Idempotensi Ansible Playbook:
Setiap tugas dalam Ansible (seperti
apt,template,systemd) dirancang untuk memeriksa status sistem saat ini sebelum melakukan perubahan. Jika konfigurasi sudah sesuai dengan deklarasi playbook, Ansible tidak akan melakukan perubahan apa pun (changed=0), menjamin keamanan operasional berulang.
7. Skenario Produksi & Studi Kasus Dunia Nyata
Dalam skenario penanganan lalu lintas tinggi (high concurrency), pendekatan server tunggal di atas ditingkatkan menjadi arsitektur multi-tier berbeban seimbang (Load Balanced Multi-Tier Architecture).
- Arsitektur: Menggunakan AWS Application Load Balancer (ALB) di depan dua atau lebih instans EC2 web server dalam Auto Scaling Group.
- Database & Session Storage: Basis data MySQL dipisahkan ke AWS RDS Multi-AZ, sementara manajemen sesi PHP/Node.js dipindahkan ke kluster Redis mandiri untuk menghindari kehilangan sesi pengguna saat terjadi failover instans.
- Zero-Downtime Deployment: Ketika pembaruan kode aplikasi dikirimkan, pipeline CI/CD melakukan proses Rolling Update—mengeluarkan satu per satu instans web server dari target group ALB, memperbarui kodenya melalui Ansible, melakukan uji kesehatan (health check), dan mengembalikannya ke rotasi trafik secara mulus.
8. Common Pitfalls, Anti-Patterns & Solusi
- Anti-Pattern 1: Menyimpan Kredensial Sensitif di Git.
Solusi: Selalu gunakan sistem manajemen rahasia seperti AWS Secrets Manager, Vault, atau GitHub Actions Secrets. Enkripsi file variabel sensitif menggunakan
ansible-vaultjika disimpan di repositori. - Anti-Pattern 2: Ketergantungan Eksternal tanpa Pin Versi.
Solusi: Selalu tentukan versi spesifik (pinned version) untuk provider Terraform (
version = "~> 5.0") dan modul Ansible untuk mencegah kegagalan mendadak akibat pembaruan otomatis paket pihak ketiga. - Anti-Pattern 3: Mengabaikan Validasi State Drift.
Solusi: Jadwalkan eksekusi
terraform plansecara berkala (misalnya setiap hari melalui cron job CI) untuk mendeteksi apakah ada perubahan manual yang terjadi di luar kendali Terraform (drift detection).
9. Benchmark, Metrik Kinerja & Tuning Performa
Untuk memverifikasi performa infrastruktur web yang telah diprovisi, lakukan pengujian beban menggunakan perangkat tolok ukur k6 atau wrk.
Contoh skrip pengujian beban menggunakan wrk untuk menguji 100 koneksi simultan selama 30 detik:
wrk -t12 -c100 -d30s https://example.com/
Parameter Tuning Lanjutan di Nginx & Linux Kernel (/etc/sysctl.conf):
# Meningkatkan batas file descriptor maksimal untuk menangani ribuan koneksi konkuren
fs.file-max = 2097152
# Optimasi TCP socket buffer untuk web server berkinerja tinggi
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.core.somaxconn = 65535
Terapkan perubahan kernel dengan perintah:
sysctl -p
10. Checklist Penerapan Produksi & Kesimpulan
Sebelum merilis infrastruktur ke lingkungan produksi penuh, pastikan Anda telah menyelesaikan checklist berikut:
- State Terraform disimpan di remote storage yang terenkripsi dengan DynamoDB locking aktif.
- Semua kredensial SSH, database, dan token API dikelola melalui Secret Manager yang aman.
- Aturan Firewall (UFW / Security Groups) dikunci secara ketat (prinsip least privilege).
- Fail2ban dan pembaruan keamanan otomatis telah dikonfigurasi di setiap node server.
- Pemantauan (Monitoring) dan sistem log terpusat telah terhubung ke instans.
- Uji coba pemulihan bencana (Disaster Recovery Drill) dan proses rollback telah divalidasi.
Dengan mengombinasikan Terraform untuk manajemen infrastruktur deklaratif dan Ansible untuk orkestrasi konfigurasi idempoten, tim infrastruktur Anda dapat mencapai siklus hidup rilis yang dapat diprediksi, konsisten, aman, dan sepenuhnya bebas dari downtime.