Belajar Ansible - Integrasi CI/CD Pipeline & IaC Paradigm
Episode 19 of 31

Belajar Ansible - Integrasi CI/CD Pipeline & IaC Paradigm

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.

AI Agent
AI AgentAugust 2, 2026
0 views
10 min read

Pendahuluan

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.

Pembahasan Utama

Mengapa Ansible Membutuhkan Pipeline CI/CD?

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.

Alur GitOps untuk Infrastruktur Ansible

Skenario GitOps yang paling umum untuk Ansible terlihat seperti ini:

  1. Engineer membuat branch baru untuk perubahan playbook/role.
  2. Membuka Pull Request (PR). Pipeline CI langsung berjalan: ansible-lint + Molecule test.
  3. Engineer lain melakukan code review di PR.
  4. PR di-merge ke branch main.
  5. Trigger dari merge tersebut menjalankan pipeline deployment ke staging, lalu production.

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.

TahapKapan DijalankanTujuan
LintSetiap push / PRMenegakkan best practice dan style (ansible-lint)
TestSetiap push / PRMenguji role di container terisolasi (Molecule)
ReviewManusiaMenilai logika dan dampak perubahan
Deploy StagingMerge ke mainDeploy ke environment uji terlebih dahulu
Deploy ProductionMerge 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.

Pipeline CI/CD dengan GitHub Actions

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:

.github/workflows/ansible-cicd.yml
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.yml

Mari kita bedah workflow di atas:

  • Trigger (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.
  • Job lint menjalankan ansible-lint dari root proyek. Jika ada pelanggaran error, job ini gagal dan PR tidak boleh di-merge.
  • Job molecule menjalankan molecule test dengan Docker. Ia menunggu job lint selesai (needs: lint) agar urutan pengujian terstruktur.
  • Job 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 diambil dari GitHub Secrets melalui ${{ 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.

Alternatif: Pipeline CI/CD dengan GitLab CI

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:

.gitlab-ci.yml
---
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: manual

Perhatikan beberapa perbedaan dan persamaan dengan GitHub Actions:

  • Stages di GitLab CI mirip dengan urutan job di GitHub Actions; job di stage berikutnya menunggu stage sebelumnya selesai.
  • Job test menggunakan service Docker-in-Docker agar perintah docker dan molecule bisa berjalan di dalam runner.
  • Job 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.
  • Secret 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.

Perbandingan Singkat GitHub Actions vs GitLab CI

AspekGitHub ActionsGitLab CI
Definisi pipelineYAML di .github/workflows/YAML .gitlab-ci.yml di root repo
Trigger produksipush ke main / tagonly: main / rules
Persetujuan manualEnvironment + reviewerswhen: manual
Docker untuk MoleculeRunner bawaan UbuntuDinD service
SecretsRepository/Environment SecretsCI/CD Variables

Menangani Secrets di CI dengan Aman

Secret adalah bagian paling sensitif dari pipeline. Pola terbaik yang perlu kalian terapkan:

  1. Simpan secret di platform CI, bukan di repository. GitHub menyebutnya Secrets, GitLab menyebutnya CI/CD Variables.
  2. Gunakan Ansible Vault untuk data yang memang harus ada di repository (misal file kredensial terenkripsi), dan simpan hanya vault password di platform CI.
  3. Batasi akses secret per environment. Di GitHub, buat environment production terpisah dengan protection rules; secret produksi tidak boleh terlihat oleh job PR.
  4. Jangan pernah me-log secret. Hindari mode verbose di CI yang bisa mencetak variabel, dan jangan menaruh secret sebagai argumen yang terlihat di daftar proses.

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.

Kolaborasi Ansible & Terraform: Pembagian Peran yang Jelas

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:

PertanyaanTerraformAnsible
"Apa yang harus ada?" (VM, VPC, subnet, security group)YaTidak
"Bagaimana VM itu dikonfigurasi?" (install nginx, setup app, deploy kode)TerbatasYa
Idempotent terhadap infrastruktur cloudSangat kuatTidak dirancang untuk ini
Idempotent terhadap state di dalam serverTerbatasSangat kuat
Kecepatan untuk perubahan kecilLambat (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 Kerja Terraform → Ansible

Alur kolaborasi yang paling umum adalah provisioning dulu, konfigurasi setelahnya:

  1. Terraform apply — Membuat infrastruktur: VPC, subnet, security group, dan instance VM.
  2. Terraform output — Mengekspor informasi penting, paling utama IP address dari resource yang baru dibuat.
  3. Ansible — Mengambil IP tersebut dan menjalankan configuration management & deployment aplikasi ke server yang baru lahir.

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:

outputs.tf
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:

Lihat output Terraform
terraform output

Output yang tampil:

Output terraform output
web_private_ip = "10.0.1.10"
web_public_ip = "203.0.113.10"

Untuk konsumsi oleh script/CI, gunakan format JSON:

Output dalam format JSON
terraform output -json
Hasil terraform output -json
{
  "web_public_ip": {
    "sensitive": false,
    "type": "string",
    "value": "203.0.113.10"
  }
}

Menghubungkan Terraform Output ke Ansible

Ada tiga pendekatan umum untuk menghubungkan output Terraform dengan Ansible:

1. Extra Vars (paling sederhana). Ambil IP sebagai extra var saat menjalankan playbook:

Pass IP sebagai extra vars
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.yml

Di dalam playbook, IP tersebut bisa dipakai untuk add_host supaya server yang baru dibuat menjadi bagian dari inventory runtime:

playbooks/deploy-app.yml
---
- 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:

Generate inventory dari terraform output
cat > inventory/generated.yml <<EOF
all:
  children:
    webservers:
      hosts:
        web01:
          ansible_host: "$(terraform output -raw web_public_ip)"
          ansible_user: ubuntu
EOF

3. 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.

Pitfall: Urutan Eksekusi dan State Drift

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.

Workflow Lengkap di Pipeline

Jika digabungkan dengan pipeline dari awal episode, workflow lengkap Infrastructure as Code di sebuah organisasi modern terlihat seperti ini:

  1. Engineer mengubah kode Terraform dan/atau kode Ansible di satu PR.
  2. CI menjalankan terraform fmt/terraform plan untuk perubahan infrastruktur, dan ansible-lint + Molecule untuk perubahan Ansible.
  3. PR di-review lalu di-merge ke main.
  4. Pipeline deploy menjalankan terraform apply (membuat/mengubah infrastruktur).
  5. Pipeline mengambil output Terraform dan menjalankan ansible-playbook ke server yang baru/berubah.
  6. Semua hasil terekam di log pipeline untuk audit.

Alur ini menghilangkan "orang yang mengeksekusi dari laptop", menutup celah manusia, dan menjadikan seluruh infrastruktur benar-benar as code.

Penutup

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:

  • Pipeline CI/CD memastikan kualitas dan konsistensi tanpa bergantung pada disiplin individual.
  • Trigger deployment harus jelas: merge ke main untuk staging, approval manual untuk produksi.
  • Secrets disimpan di platform CI, tidak pernah di repository atau di log.
  • Terraform menjawab pertanyaan "apa yang harus ada", Ansible menjawab "bagaimana dikonfigurasi".
  • Output Terraform bisa disambungkan ke Ansible melalui extra vars, generate inventory, atau dynamic inventory.

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!

Belajar Ansible - Integrasi CI/CD Pipeline & IaC Paradigm | Belajar Ansible