Membangun Infrastruktur Immutable Menggunakan Terraform dan Ansible di AWS
1. Konteks Industri & Analisis Akar Masalah
Dalam lanskap pengembangan web dan infrastruktur modern, pengelolaan server tradisional berbasis Mutable Infrastructure (infrastruktur yang dapat diubah secara langsung) telah lama menjadi sumber utama kegagalan sistem di lingkungan produksi. Ketika sebuah aplikasi web skala enterprise mengalami lonjakan trafik, atau ketika sebuah patch keamanan kritis untuk Nginx, PHP-FPM, atau kernel Linux harus diterapkan, pendekatan konvensional biasanya melibatkan proses SSH ke dalam server produksi dan menjalankan perintah manual seperti apt update, systemctl restart, atau mengubah konfigurasi secara langsung (hot-patching).
Pendekatan ini melahirkan fenomena klasik yang dikenal sebagai Configuration Drift (penyimpangan konfigurasi). Seiring waktu, tidak ada dua server di dalam satu pool kluster yang memiliki konfigurasi identik. Hal ini memicu bug yang sulit direproduksi ("it works on my staging server, but fails in production"), waktu pemulihan insiden (Recovery Time Objective) yang lama, serta kerentanan keamanan yang terlewatkan akibat inkonsistensi environment.
Sebaliknya, Immutable Infrastructure membalikkan paradigma ini sepenuhnya. Dalam model immutable, server tidak pernah diperbarui (patched) secara langsung. Setiap perubahan—mulai dari versi kode Go/Node.js, pembaruan aturan firewall UFW, konfigurasi VirtualHost Nginx, hingga parameter PHP-FPM—diperlakukan sebagai build artefak baru. Server lama dihancurkan (terminated) dan digantikan sepenuhnya oleh instance baru yang bersih dari golden image.
Kondisi Sebelum vs Sesudah Penerapan
| Aspek | Mutable Infrastructure (Tradisional) | Immutable Infrastructure (Terraform & Ansible) | Dampak Bisnis / Operasional |
|---|---|---|---|
| Deploy & Patch | SSH manual, git pull, composer install di server live. |
Automasi penuh, Golden Image (AMI) baru, Zero-downtime rolling replacement. | Meniadakan human error, mempercepat rilis dari hari ke menit. |
| Configuration Drift | Tinggi. Konfigurasi antar server berbeda karena patch manual. | Nol. Seluruh node diinisialisasi dari basis AMI yang identik. | Stabilitas sistem meningkat drastis, debugging lebih cepat. |
| Disaster Recovery | Lambat. Harus membangun ulang server dari awal secara manual. | Instan. Terraform mendestroy dan mereprovisioning infrastruktur dalam hitungan menit. | RTO (Recovery Time Objective) turun drastis dari jam ke menit. |
| Auditabilitas | Rendah. Jejak perubahan hilang di dalam log terminal sysadmin. | Tinggi. Semua infrastruktur didefinisikan secara deklaratif dalam bentuk kode (IaC). | Kepatuhan terhadap standar keamanan (SOC2, ISO27001) lebih mudah dipenuhi. |
[!WARNING] Jangan pernah melakukan SSH-driven development atau mengubah file konfigurasi di server produksi. Segala perubahan manual akan hilang saat instance di-recycle dan merusak integritas Infrastructure as Code Anda.
2. Konsep Inti & Deep Dive Arsitektur
Arsitektur infrastruktur immutable yang robust mengandalkan kombinasi dua pilar utama: Terraform untuk Orkestrasi dan Provisioning lapisan fondasi (VPC, Subnet, Security Group, IAM, EC2 Instance, EBS Volume, Load Balancer) secara deklaratif, dan Ansible untuk Konfigurasi Manajemen (instalasi Runtime Go/Node/PHP, konfigurasi Web Server Nginx, pengaturan SSL Let's Encrypt, optimasi Redis dan MySQL, serta manajemen service Supervisor).
Alur kerja end-to-end (pipeline execution flow) dari pembuatan infrastruktur hingga aplikasi siap melayani pengguna dapat divisualisasikan melalui diagram arsitektur berikut:
+-------------------------------------------------------------------------+
| Developer Workstation |
| +--------------------+ +--------------------+ |
| | Terraform Apply | ----------> | Ansible Playbook | |
| +--------------------+ +--------------------+ |
+-----------------------------------------------|-------------------------+
| (Provision & Configure)
v
+-------------------------------------------------------------------------+
| AWS Cloud |
| |
| +-------------------------------------------------------------------+ |
| | VPC (10.0.0.0/16) | |
| | | |
| | +-----------------------+ +-------------------------+ | |
| | | Public Subnet (ALB) | | Private Subnet (Web/App)| | |
| | | 10.0.1.0/24 | | 10.0.10.0/24 | | |
| | | | | | | |
| | | [Application Load | --------> | [EC2: Nginx + PHP-FPM] | | |
| | | Balancer] | | [Go / Node.js Backend] | | |
| | +-----------------------+ +-------------------------+ | |
| | | | |
| | v | |
| | +-------------------------+ | |
| | | Database Subnet | | |
| | | 10.0.20.0/24 | | |
| | | [MySQL / MariaDB + Redis| | |
| | +-------------------------+ | |
| |___________________________________________________________________| |
+-------------------------------------------------------------------------+
Alur Eksekusi Data & Eksekusi:
- Inisialisasi Fondasi (Terraform): Terraform membaca file konfigurasi
.tf, membuat jaringan privat virtual (VPC), subnet publik/privat, route table, internet gateway, security group, serta memprovisikan instance EC2. - Konfigurasi Software (Ansible): Setelah instance EC2 berstatus
running, Terraform secara otomatis (melaluiremote-execatau trigger inventory dinamis) memanggil Ansible Playbook. - Eksekusi Playbook: Ansible terhubung ke instance melalui SSH, menginstal paket sistem operasi, mendownload source code aplikasi web, mengkonfigurasi Nginx, menyetel izin file (permissions), mengaktifkan supervisor untuk queue worker, dan mengamankan SSL via Certbot.
- Edge & Traffic Routing (Cloudflare & ALB): Trafik publik masuk melalui Cloudflare (WAF & CDN), diteruskan ke AWS Application Load Balancer, lalu didistribusikan secara aman ke instans EC2 di Private Subnet.
3. Perbandingan Komparatif / Analisis Pendekatan
Dalam membangun sistem otomatisasi cloud, terdapat beberapa pendekatan populer selain kombinasi Terraform + Ansible. Berikut adalah analisis mendalam untuk membantu Anda memilih arsitektur yang paling tepat berdasarkan kebutuhan enterprise:
| Kriteria Penilaian | Terraform + Ansible (Hybrid) | AWS CloudFormation + AWS Systems Manager | Packer + Terraform + AWS Auto Scaling | Pulumi + Python/TypeScript |
|---|---|---|---|---|
| Bahasa Deklarasi | HCL (Terraform) & YAML (Ansible) | JSON / YAML (CloudFormation) | HCL + JSON (Packer) | TypeScript, Python, Go, C# |
| Cloud Agnostic | Ya (Multi-cloud: AWS, GCP, Azure) | Tidak (Eksklusif AWS) | Ya (Multi-cloud AMI/Image builder) | Ya (Multi-cloud) |
| Kecepatan Deploy | Menengah (Provisioning + Config time) | Lambat hingga Menengah | Sangat Cepat (Karena menggunakan pre-baked AMI) | Cepat |
| Kompleksitas Kurva Belajar | Menengah (Menguasai dua tool berbeda) | Rendah hingga Menengah | Tinggi (Manajemen siklus image) | Tinggi (Butuh pemahaman OOP/Functional) |
| Manajemen State | Remote State (S3 + DynamoDB Lock) | Dikelola sepenuhnya oleh AWS Stack | S3 / Terraform Cloud State | Pulumi Service / S3 Backend |
[!TIP] Kombinasi Terraform + Ansible adalah standar industri paling fleksibel. Terraform sangat unggul dalam manajemen infrastruktur cloud abstrak, sementara Ansible adalah raja dalam konfigurasi sistem operasi tingkat lanjut dan manajemen software stack.
4. Prasyarat Sistem & Persiapan Lingkungan
Sebelum menjalankan kode implementasi, pastikan workstation atau CI/CD Runner Anda telah terinstal dependensi berikut dengan versi minimum yang disarankan:
- Terraform: Versi
>= 1.5.0 - Ansible: Versi
>= 2.15.0 - AWS CLI: Versi
>= 2.13.0yang sudah dikonfigurasi dengan kredensial (aws configure) yang memiliki hak akses penuh (AdministratorAccessatau policy kustom VPC/EC2/IAM). - SSH Key Pair: Key pair lokal yang didaftarkan ke AWS untuk akses Ansible (
~/.ssh/id_rsa.pub).
Verifikasi instalasi melalui terminal:
terraform -version
ansible --version
aws sts get-caller-identity
5. Panduan Implementasi Langkah demi Langkah
Struktur direktori proyek enterprise Anda harus dirancang secara modular seperti berikut:
infrastructure-project/
├── terraform/
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ └── terraform.tfvars
└── ansible/
├── playbook.yml
└── inventory.ini
Langkah 1: Provisioning Infrastruktur dengan Terraform
Buat file terraform/main.tf untuk mendefinisikan VPC, subnet, security group, dan instance EC2 web server.
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
}
# 1. Virtual Private Cloud (VPC)
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "enterprise-vpc"
Environment = var.environment
}
}
# 2. Internet Gateway
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "enterprise-igw"
}
}
# 3. Public Subnet
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 = "enterprise-public-subnet"
}
}
# 4. Route Table Public
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 = "enterprise-public-rt"
}
}
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
# 5. Security Group untuk Web Server
resource "aws_security_group" "web_sg" {
name = "web-server-sg"
description = "Allow HTTP, HTTPS and SSH"
vpc_id = aws_vpc.main.id
ingress {
description = "Allow SSH from Admin IP"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.admin_ip]
}
ingress {
description = "Allow HTTP from Anywhere"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "Allow 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 = "web-server-sg"
}
}
# 6. Key Pair AWS
resource "aws_key_pair" "deployer" {
key_name = "enterprise-deployer-key"
public_key = file(var.ssh_public_key_path)
}
# 7. EC2 Instance Web Server
resource "aws_instance" "web_server" {
ami = var.ubuntu_ami_id
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 = 40
volume_type = "gp3"
delete_on_termination = true
}
tags = {
Name = "enterprise-web-node-01"
Environment = var.environment
}
}
Buat file terraform/variables.tf:
variable "aws_region" {
type = string
default = "ap-southeast-1"
}
variable "environment" {
type = string
default = "production"
}
variable "admin_ip" {
type = string
description = "Your office/VPN CIDR for SSH access"
}
variable "ssh_public_key_path" {
type = string
default = "~/.ssh/id_rsa.pub"
}
variable "ubuntu_ami_id" {
type = string
description = "Ubuntu 22.04 LTS AMI ID for ap-southeast-1"
default = "ami-060e277c0d4cce553"
}
variable "instance_type" {
type = string
default = "t3.medium"
}
Buat file terraform/outputs.tf untuk mengekspor IP publik instance:
output "web_server_public_ip" {
value = aws_instance.web_server.public_ip
description = "Public IP address of the web server for Ansible inventory"
}
Buat file terraform/terraform.tfvars:
aws_region = "ap-southeast-1"
environment = "production"
admin_ip = "203.0.113.50/32"
ssh_public_key_path = "~/.ssh/id_rsa.pub"
instance_type = "t3.medium"
Inisialisasi dan jalankan Terraform:
cd terraform
terraform init
terraform plan -out=tfplan
terraform apply tfplan
Langkah 2: Konfigurasi Software Stack dengan Ansible Playbooks
Setelah Terraform selesai dan mengeluarkan IP publik server, buat file inventory Ansible ansible/inventory.ini. Anda dapat memasukkan IP output Terraform secara otomatis atau manual:
[webservers]
<MASUKKAN_PUBLIC_IP_TERRAFOM_DISINI> ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa
Buat file ansible/playbook.yml yang mencakup instalasi Nginx, PHP-FPM, Redis, konfigurasi Firewall UFW, penyiapan direktori web, dan VirtualHost Nginx produksi:
---
- name: Provision and Configure Web Server Stack
hosts: webservers
become: yes
vars:
app_user: "www-data"
php_version: "8.2"
tasks:
- name: Update apt cache and upgrade packages
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install essential system packages
apt:
name:
- curl
- git
- ufw
- unzip
- software-properties-common
state: present
- name: Add PPA for PHP (Ondrej Sury)
apt_repository:
repo: "ppa:ondrej/php"
state: present
- name: Install Nginx, PHP-FPM, and Database Client
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
- php{{ php_version }}-curl
- redis-server
state: present
update_cache: yes
- name: Configure UFW Firewall
ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- "22"
- "80"
- "443"
- name: Enable UFW
ufw:
state: enabled
policy: deny
- name: Ensure Nginx is running and enabled
service:
name: nginx
state: started
enabled: yes
- name: Ensure PHP-FPM is running and enabled
service:
name: "php{{ php_version }}-fpm"
state: started
enabled: yes
- name: Ensure Redis is running and enabled
service:
name: redis-server
state: started
enabled: yes
- name: Create web application root directory
file:
path: /var/www/html/public
state: directory
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: '0755'
- name: Deploy Production Nginx VirtualHost Configuration
copy:
dest: "/etc/nginx/sites-available/default"
content: |
server {
listen 80;
server_name _;
root /var/www/html/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
index index.php index.html index.htm;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php{{ php_version }}-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
notify:
- Restart Nginx
- name: Create a sample PHP info file for validation
copy:
dest: "/var/www/html/public/index.php"
content: "<?php echo '<h1>Enterprise Immutable Infrastructure Node Operational</h1>'; phpinfo(); ?>"
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: '0644'
handlers:
- name: Restart Nginx
service:
name: nginx
state: restarted
Jalankan Ansible Playbook:
cd ansible
ansible-playbook -i inventory.ini playbook.yml
6. Bedah Kode & Penjelasan Detail Baris Kunci
Mari kita bedah beberapa parameter krusial dalam file konfigurasi yang telah kita bangun:
-
delete_on_termination = truepada Blok EBS (terraform/main.tf): Parameter ini memastikan bahwa ketika instance EC2 dihancurkan (misalnya saat proses rolling update), volume disk root EBS ikut terhapus secara otomatis. Hal ini mencegah akumulasi penyimpanan yatim piatu (orphan volume) yang dapat membengkakkan tagihan AWS cloud Anda. -
try_files $uri $uri/ /index.php?$query_string;pada Nginx VirtualHost (ansible/playbook.yml): Baris ini sangat vital untuk arsitektur web modern berbasis Front Controller (seperti Laravel, Symfony, atau SPA router). Jika file atau direktori fisik tidak ditemukan di server, Nginx akan mengalihkan seluruh request keindex.phpsecara mulus tanpa mencetak error 404 Not Found. -
UFW Default Policy
denydengan Allowlist Eksplisit: Di dalam Ansible task, kami menerapkan kebijakan ketat menolak seluruh trafik masuk secara default (policy: deny), kecuali port SSH (22), HTTP (80), dan HTTPS (443). Ini adalah lini pertahanan pertama untuk mencegah akses ilegal dari bot scanner internet.
7. Skenario Produksi & Studi Kasus Dunia Nyata
Dalam skenario produksi nyata berbeaya tinggi (misalnya menangani flash sale e-commerce dengan 50.000 concurrent users), arsitektur single instance di atas perlu ditingkatkan menjadi Auto Scaling Group (ASG) di balik Application Load Balancer (ALB) dengan AWS RDS (Relational Database Service).
Alih-alih mengkonfigurasi EC2 mentah menggunakan Ansible secara langsung pada instance runtime, pendekatan enterprise terbaik adalah menggunakan HashiCorp Packer. Packer menggunakan skrip Ansible yang sama untuk membangun Golden AMI (Amazon Machine Image). Ketika ada pembaruan kode aplikasi:
- CI/CD Pipeline (GitHub Actions / GitLab CI) memicu Packer untuk membuat AMI baru yang sudah berisi kode terbaru.
- Terraform memperbarui
launch_templatedi ASG dengan AMI terbaru tersebut. - Terraform memicu perintah
aws autoscaling start-instance-refresh, yang secara otomatis melakukan rolling update (mengganti instance lama dengan instance baru satu per satu tanpa downtime).
8. Common Pitfalls, Anti-Patterns & Solusi
Berikut adalah kesalahan fatal yang sering dilakukan oleh engineer pemula hingga menengah saat mengimplementasikan Terraform dan Ansible:
- Anti-Pattern 1: Menyimpan Terraform State File secara Lokal (
terraform.tfstate)- Dampak: Jika dua orang engineer menjalankan
terraform applydari komputer lokal berbeda, state file akan korup dan terjadi konflik resource. - Solusi: Selalu gunakan Remote Backend seperti AWS S3 dengan enkripsi aktif dan DynamoDB untuk State Locking.
- Dampak: Jika dua orang engineer menjalankan
- Anti-Pattern 2: Menggunakan Ansible untuk Mengubah Infrastruktur Cloud
- Dampak: Menggunakan Ansible untuk membuat VPC atau Security Group di AWS. Ini adalah pelanggaran batas tanggung jawab (separation of concerns).
- Solusi: Serahkan seluruh provisioning infrastruktur cloud kepada Terraform, dan gunakan Ansible khusus untuk manajemen konfigurasi di dalam sistem operasi (OS Configuration).
- Anti-Pattern 3: Hardcoding Kredensial Sensitif di dalam Playbook atau TFVars
- Dampak: Kebocoran password database atau secret key di public repository GitHub.
- Solusi: Manfaatkan AWS Secrets Manager, Parameter Store, atau variabel lingkungan sensitif (environment variables).
9. Benchmark, Metrik Kinerja & Tuning Performa
Setelah infrastruktur immutable berdiri, lakukan pengujian beban (load testing) untuk memastikan server Nginx dan PHP-FPM mampu menangani konkurensi tinggi. Gunakan tool benchmarking modern seperti k6 atau Apache Bench (ab).
Contoh skrip uji beban sederhana menggunakan ab untuk 10.000 request dengan 100 konkurensi paralel:
ab -n 10000 -c 100 http://<YOUR_WEB_SERVER_PUBLIC_IP>/
Tuning Performa Lanjutan di Nginx & PHP-FPM:
- Nginx Worker Processes: Sesuaikan
worker_processes auto;di file/etc/nginx/nginx.confagar Nginx menggunakan seluruh inti CPU (vCPU) yang tersedia pada instance t3.medium. - PHP-FPM Process Manager: Pada
/etc/php/8.2/fpm/pool.d/www.conf, ubah daripm = dynamicmenjadipm = staticdengan jumlahpm.max_children = 50(tergantung alokasi RAM instance) untuk menghindari overhead pembuatan proses baru saat lonjakan trafik mendadak.
10. Checklist Penerapan Produksi & Kesimpulan
Sebelum meluncurkan infrastruktur immutable Anda ke lingkungan live/production, pastikan seluruh item dalam checklist berikut telah terpenuhi:
- Remote state Terraform diamankan di S3 dengan enkripsi AES-256 dan penguncian DynamoDB.
- SSH key root dinonaktifkan atau dibatasi ketat melalui pembatasan IP CIDR pada Security Group AWS.
- Seluruh file konfigurasi sensitif dikelola melalui enkripsi (Ansible Vault atau AWS Secrets Manager).
- Monitoring dan log centralizer (seperti CloudWatch Agent atau Prometheus/Grafana) telah terpasang di dalam Ansible Playbook.
- Uji coba Disaster Recovery (menghancurkan dan membangun ulang seluruh stack dari nol) telah berhasil dilakukan tanpa kendala.
Dengan mengintegrasikan Terraform dan Ansible secara disiplin dalam paradigma infrastruktur immutable, organisasi Anda tidak hanya mencapai otomatisasi penuh, tetapi juga membangun ketahanan sistem yang luar biasa, keamanan tingkat tinggi, dan skalabilitas tanpa batas di ekosistem AWS.