Membawa Ansible ke level production dengan pipeline CI/CD otomatis menggunakan GitHub Actions dan GitLab CI, serta memahami paradigma GitOps & Infrastructure as Code dan kolaborasi Ansible dengan Terraform.

Setelah di episode 18 sebelumnya kita membahas bagaimana menjaga kualitas kode Ansible menggunakan ansible-lint dan mengujinya secara terisolasi dengan Molecule, kalian sekarang punya senjata yang kuat untuk memastikan kode infrastruktur aman sebelum berdampak. Namun ada satu pertanyaan yang belum terjawab: siapa yang menjalankan semua itu, dan kapan?
Jika jawabannya adalah "kita menjalankannya manual di laptop masing-masing", maka kita baru setengah jalan. Di dunia nyata, tim infrastruktur yang sehat tidak menyerahkan kualitas pada kedisiplinan individual, karena manusia pasti lupa. Jawaban yang benar adalah pipeline CI/CD: sebuah mesin yang menjalankan linting, testing, dan deployment secara otomatis setiap kali kode berubah.
Episode 19 ini akan membawa kalian memahami paradigma Infrastructure as Code (IaC) dan GitOps dalam konteks Ansible. Kita akan membangun pipeline menggunakan GitHub Actions dan GitLab CI, membahas bagaimana menangani secret di CI dengan aman, dan terakhir mempelajari kolaborasi antara Ansible dan Terraform — dua raksasa automasi yang sebenarnya saling melengkapi, bukan bersaing. Ini adalah materi yang akan kalian pakai hampir setiap hari sebagai engineer infrastruktur.
Marilah kita mulai dari pertanyaan paling fundamental. Apa yang berubah jika kita menjalankan ansible-playbook dari laptop?
Masalah pertama: konsistensi. Playbook yang dijalankan dari laptop A bisa berbeda hasilnya dari laptop B, hanya karena versi Ansible, Python, atau collections yang berbeda. Masalah ini akan kita kupas tuntas di episode 20 dengan konsep execution environments.
Masalah kedua: tidak ada jejak (audit trail). Ketika seorang engineer menjalankan ansible-playbook -i production.yml deploy.yml dari laptopnya pukul 2 pagi, tidak ada catatan siapa yang melakukannya, perubahan apa yang dipicu, dan apakah ada tahap review sebelumnya.
Masalah ketiga: tidak ada gerbang kualitas. Tanpa pipeline, tidak ada yang memaksa kode melewati ansible-lint dan Molecule sebelum sampai ke produksi.
Dengan pipeline CI/CD, semua masalah ini teratasi. Kode berubah → CI menjalankan kualitas → kode lolos → deployment berjalan → semua tercatat. Inilah inti dari paradigma GitOps: Git adalah satu-satunya sumber kebenaran (single source of truth). Tidak ada konfigurasi yang berubah di server kecuali perubahan itu berasal dari repository Git.
Skenario GitOps yang paling umum untuk Ansible terlihat seperti ini:
ansible-lint + Molecule test.main.Kuncinya ada di poin 5: deployment hanya terjadi ketika ada merge ke main, bukan ketika seseorang mengetik perintah secara manual. Semua proses terekam di history Git dan log pipeline.
| Tahap | Kapan Dijalankan | Tujuan |
|---|---|---|
| Lint | Setiap push / PR | Menegakkan best practice dan style (ansible-lint) |
| Test | Setiap push / PR | Menguji role di container terisolasi (Molecule) |
| Review | Manusia | Menilai logika dan dampak perubahan |
| Deploy Staging | Merge ke main | Deploy ke environment uji terlebih dahulu |
| Deploy Production | Merge ke main (+ approval opsional) | Deploy ke environment nyata |
Note
Konsep "deploy setelah merge" ini adalah ciri khas GitOps dan merupakan praktik standar di tim yang mengelola infrastruktur sebagai kode. Nanti di episode 20, paradigma yang sama akan diterapkan pada skala enterprise melalui AWX/Ansible Automation Platform.
GitHub Actions adalah pilihan paling populer untuk proyek yang repositorinya di GitHub. Workflow didefinisikan sebagai YAML di direktori .github/workflows/. Berikut contoh workflow lengkap yang menjalankan lint → test → deploy dengan trigger otomatis saat PR dan saat merge ke main:
name: Ansible CI/CD
on:
pull_request:
paths:
- "ansible/**"
push:
branches: [main]
paths:
- "ansible/**"
jobs:
lint:
name: Lint playbooks & roles
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install ansible-lint
run: pip install ansible-lint
- name: Run ansible-lint
run: ansible-lint
molecule:
name: Molecule test (Docker)
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install Molecule
run: pip install "molecule[docker]"
- name: Run molecule test
run: molecule test
deploy:
name: Deploy to production
runs-on: ubuntu-latest
needs: [lint, molecule]
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Setup SSH key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.DEPLOY_SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan github.com >> ~/.ssh/known_hosts
- name: Install Ansible & collections
run: |
pip install ansible-core
ansible-galaxy collection install -r requirements.yml
- name: Run ansible-playbook
run: |
ansible-playbook \
-i inventory/production.yml \
--vault-password-file <(echo "${{ secrets.VAULT_PASSWORD }}") \
playbooks/deploy.ymlMari kita bedah workflow di atas:
on) didefinisikan agar pipeline berjalan pada dua kejadian: setiap PR yang mengubah file di direktori ansible/, dan setiap push ke branch main. Trigger paths ini penting agar pipeline tidak membuang resource untuk perubahan yang tidak menyentuh kode Ansible.lint menjalankan ansible-lint dari root proyek. Jika ada pelanggaran error, job ini gagal dan PR tidak boleh di-merge.molecule menjalankan molecule test dengan Docker. Ia menunggu job lint selesai (needs: lint) agar urutan pengujian terstruktur.deploy hanya berjalan saat github.ref == 'refs/heads/main' — yaitu setelah PR di-merge, bukan saat PR dibuka. Ini persis pola GitOps yang kita bahas.${{ secrets.NAMA_SECRET }}. SSH private key ditulis ke ~/.ssh dengan permission yang benar, dan password Vault disuntikkan sebagai password file.Warning
Jangan pernah menulis secret secara hardcoded di dalam file workflow, dan jangan pernah men-log nilai secret. GitHub Actions otomatis menyensor secret di log, tapi itu bukan alasan untuk lengah: pastikan secret hanya masuk ke proses yang membutuhkannya. Simpan semua kredensial di GitHub Secrets (atau secret manager serupa), dan gunakan Ansible Vault untuk data sensitif di dalam repository — sesuai yang kita pelajari di episode 14.
Untuk tim yang menggunakan GitLab, alur yang sama dibangun dengan file .gitlab-ci.yml. GitLab CI menggunakan konsep stages dan jobs, dan bisa menjalankan DinD (Docker in Docker) untuk kebutuhan Molecule:
---
stages:
- lint
- test
- deploy
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
cache:
paths:
- .cache/pip
lint:
stage: lint
image: python:3.12-slim
script:
- pip install --no-cache-dir ansible-lint
- ansible-lint
only:
- merge_requests
- main
test:
stage: test
image: docker:27
services:
- docker:27-dind
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: /certs
script:
- pip install --no-cache-dir "molecule[docker]"
- molecule test
only:
- merge_requests
- main
deploy:
stage: deploy
image: python:3.12-slim
script:
- apk add --no-cache openssh-client
- pip install --no-cache-dir ansible-core
- ansible-galaxy collection install -r requirements.yml
- echo "$VAULT_PASSWORD" > .vault-pass
- chmod 600 .vault-pass
- ansible-playbook -i inventory/production.yml --vault-password-file .vault-pass playbooks/deploy.yml
only:
- main
when: manualPerhatikan beberapa perbedaan dan persamaan dengan GitHub Actions:
test menggunakan service Docker-in-Docker agar perintah docker dan molecule bisa berjalan di dalam runner.deploy didefinisikan dengan only: main dan when: manual, yang berarti ia menunggu approval manual dari engineer sebelum benar-benar menjalankan deployment ke produksi. Ini pola yang sangat umum untuk production: automasi penuh sampai staging, tapi produksi butuh persetujuan manusia.VAULT_PASSWORD diambil dari CI/CD Variables GitLab.Tip
Perbedaan budaya antara GitHub Actions dan GitLab CI tidak seberapa dibanding kesamaan prinsipnya: lint harus lolos sebelum test, test harus lolos sebelum deploy, dan deploy ke produksi idealnya membutuhkan trigger yang jelas (merge ke main atau approval manual). Prinsip inilah yang jauh lebih penting daripada platform yang kalian pilih.
| Aspek | GitHub Actions | GitLab CI |
|---|---|---|
| Definisi pipeline | YAML di .github/workflows/ | YAML .gitlab-ci.yml di root repo |
| Trigger produksi | push ke main / tag | only: main / rules |
| Persetujuan manual | Environment + reviewers | when: manual |
| Docker untuk Molecule | Runner bawaan Ubuntu | DinD service |
| Secrets | Repository/Environment Secrets | CI/CD Variables |
Secret adalah bagian paling sensitif dari pipeline. Pola terbaik yang perlu kalian terapkan:
production terpisah dengan protection rules; secret produksi tidak boleh terlihat oleh job PR.Caution
Hati-hati dengan pola <(echo "${{ secrets.VAULT_PASSWORD }}"). Proses substitution ini aman selama secret tidak tercetak, tapi beberapa tool bisa menerima password lewat environment variable atau file. Pilih mekanisme yang paling sedikit meninggalkan jejak di log. Yang terpenting: jika secret pernah bocor di log CI, langsung rotate secret tersebut — jangan menunggu.
Sekarang kita masuk ke bagian favorit banyak engineer: bagaimana Terraform dan Ansible bekerja sama. Sering ada kebingungan apakah kedua tools ini bersaing. Jawabannya: tidak. Mereka menyelesaikan masalah yang berbeda, dan dalam tim produksi yang sehat, keduanya dipakai bersama.
Perbedaan peran yang paling mudah diingat:
| Pertanyaan | Terraform | Ansible |
|---|---|---|
| "Apa yang harus ada?" (VM, VPC, subnet, security group) | Ya | Tidak |
| "Bagaimana VM itu dikonfigurasi?" (install nginx, setup app, deploy kode) | Terbatas | Ya |
| Idempotent terhadap infrastruktur cloud | Sangat kuat | Tidak dirancang untuk ini |
| Idempotent terhadap state di dalam server | Terbatas | Sangat kuat |
| Kecepatan untuk perubahan kecil | Lambat (provisioning) | Cepat (config) |
Analogi yang pas: Terraform itu seperti kontraktor yang membangun gedung (membuat fondasi, struktur, instalasi listrik), sedangkan Ansible itu seperti tim interior yang menata isi ruangan (memasang furniture, mengatur dekorasi, memastikan semua fungsional). Masing-masing ahli di bidangnya dan hasil terbaik didapat saat keduanya bekerja berurutan.
Important
Jangan mencoba menjadikan Terraform sebagai configuration management, dan jangan mencoba menjadikan Ansible sebagai tool provisioning infrastruktur cloud (walaupun Ansible punya modul cloud). Di skala enterprise, keduanya punya kelebihan masing-masing yang justru saling menguatkan — seperti yang akan kita lihat pada alur kerja berikut.
Alur kolaborasi yang paling umum adalah provisioning dulu, konfigurasi setelahnya:
apply — Membuat infrastruktur: VPC, subnet, security group, dan instance VM.output — Mengekspor informasi penting, paling utama IP address dari resource yang baru dibuat.Kunci dari kolaborasi ini adalah bagaimana Ansible mendapatkan IP dari Terraform. Ada beberapa cara, dan kita akan melihat yang paling sederhana dan paling banyak dipakai.
Pertama, definisikan output di file Terraform. Contoh untuk resource instance AWS:
output "web_public_ip" {
description = "Public IP dari instance web"
value = aws_instance.web.public_ip
}
output "web_private_ip" {
description = "Private IP dari instance web"
value = aws_instance.web.private_ip
}Setelah terraform apply selesai, kalian bisa melihat hasil output:
terraform outputOutput yang tampil:
web_private_ip = "10.0.1.10"
web_public_ip = "203.0.113.10"Untuk konsumsi oleh script/CI, gunakan format JSON:
terraform output -json{
"web_public_ip": {
"sensitive": false,
"type": "string",
"value": "203.0.113.10"
}
}Ada tiga pendekatan umum untuk menghubungkan output Terraform dengan Ansible:
1. Extra Vars (paling sederhana). Ambil IP sebagai extra var saat menjalankan playbook:
ansible-playbook -i inventory/production.yml \
-e "web_public_ip=$(terraform output -raw web_public_ip)" \
-e "web_private_ip=$(terraform output -raw web_private_ip)" \
playbooks/deploy-app.ymlDi dalam playbook, IP tersebut bisa dipakai untuk add_host supaya server yang baru dibuat menjadi bagian dari inventory runtime:
---
- name: Registrasi server baru ke inventory runtime
hosts: localhost
gather_facts: false
tasks:
- name: Tambahkan web server yang baru dibuat
ansible.builtin.add_host:
name: web01
ansible_host: "{{ web_public_ip }}"
ansible_user: ubuntu
groups:
- webservers
- name: Konfigurasi & deploy aplikasi ke webservers
hosts: webservers
become: true
tasks:
- name: Install Nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
- name: Deploy aplikasi
ansible.builtin.copy:
src: files/app/
dest: /var/www/html
mode: "0644"2. Generate inventory file. Simpan output ke file inventory secara dinamis, cocok untuk skenario yang membutuhkan inventory permanen:
cat > inventory/generated.yml <<EOF
all:
children:
webservers:
hosts:
web01:
ansible_host: "$(terraform output -raw web_public_ip)"
ansible_user: ubuntu
EOF3. Dynamic inventory plugin. Di episode 21 nanti, kita akan membahas dynamic inventory secara mendalam, termasuk mengintegrasikan data dari cloud provider langsung tanpa harus mengekspor output secara manual.
Tip
Untuk skenario "server baru lahir lalu langsung dikonfigurasi" dalam satu alur otomatis, pendekatan extra vars + add_host adalah yang paling umum. Untuk skenario di mana inventory harus bertahan lama dan bisa dibaca oleh banyak playbook, generate file inventory lebih cocok. Pilih sesuai kebutuhan, bukan karena kebiasaan.
Kolaborasi Terraform–Ansible punya beberapa jebakan yang wajib kalian pahami:
1. Ansible tidak boleh berjalan sebelum infrastruktur siap. Jika ansible-playbook dijalankan sebelum terraform apply selesai, IP yang dipakai belum ada dan koneksi SSH akan gagal. Pastikan di pipeline, job Ansible didefinisikan setelah job Terraform selesai (needs: di GitHub Actions atau stage yang lebih akhir di GitLab CI).
2. Jangan memakai terraform output -raw tanpa state yang valid. Output hanya mencerminkan state terakhir Terraform. Jika state kedaluwarsa (misal instance dihapus di luar Terraform), IP yang dihasilkan tidak berguna.
3. Keamanan SSH key. Server baru biasanya hanya punya key yang sudah di-inject saat provisioning. Pastikan Ansible memakai key yang benar melalui ansible_ssh_private_key_file atau SSH agent di CI.
4. Idempotency tetap berlaku. Menjalankan ulang playbook Ansible di server yang sudah dikonfigurasi harus tetap menghasilkan status ok, bukan changed berulang — persis prinsip yang kita pelajari sejak episode 5 dan kita uji dengan Molecule di episode 18.
Jika digabungkan dengan pipeline dari awal episode, workflow lengkap Infrastructure as Code di sebuah organisasi modern terlihat seperti ini:
terraform fmt/terraform plan untuk perubahan infrastruktur, dan ansible-lint + Molecule untuk perubahan Ansible.main.terraform apply (membuat/mengubah infrastruktur).ansible-playbook ke server yang baru/berubah.Alur ini menghilangkan "orang yang mengeksekusi dari laptop", menutup celah manusia, dan menjadikan seluruh infrastruktur benar-benar as code.
Pada episode 19 ini, kita telah belajar bahwa automasi Ansible baru bernilai penuh ketika diintegrasikan ke dalam pipeline CI/CD. Kita sudah memahami paradigma GitOps: Git sebagai single source of truth, di mana perubahan hanya berdampak setelah di-merge ke branch main. Kita juga telah membangun pipeline lengkap dengan GitHub Actions dan GitLab CI, mempelajari cara menangani secrets dengan aman, dan memetakan pembagian peran yang jelas antara Terraform (provisioning infrastruktur) dan Ansible (configuration management & deployment aplikasi).
Poin kunci yang perlu kalian bawa pulang:
main untuk staging, approval manual untuk produksi.Di episode 20 selanjutnya kita akan membahas salah satu topik yang paling ditunggu: Modern Execution Environments. Kita akan menelusuri masalah "works on my machine" yang menjadi momok di tim besar, lalu belajar menggunakan ansible-builder untuk membuat image container berisi environment Ansible yang konsisten, ansible-navigator untuk menjalankan automasi berbasis container, dan mengenal AWX/Ansible Automation Platform untuk skala enterprise dengan RBAC, job scheduling, dan audit logging. Pastikan tetap semangat!