SSD Nodes Learn 🎉 VPS mulai $5.50/bln
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-08-07

Ansible Playbook vs Role: Kapan Menggunakan Masing-Masing

Pelajari kapan playbook Ansible sederhana sudah cukup dan kapan role diperlukan, termasuk struktur direktori, ansible-galaxy init, pemanggilan role, serta prioritas variabel.

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, July 30, 2026.

Perbedaan playbook dan role Ansible

Ansible playbook adalah file yang Anda jalankan dengan ansible-playbook. File ini memetakan sekelompok host ke pekerjaan yang harus dilakukan. Ansible role adalah direktori dengan struktur tetap yang berisi task, template, handler, dan variabel default. Playbook memanggil role berdasarkan namanya. Sintaks task di dalam keduanya sama. Jadi, perbedaannya bukan pada hal yang dapat Anda ekspresikan. Perbedaannya terletak pada penggunaan ulang.

Mulailah dengan playbook sederhana dalam satu file. Satu site.yml yang berisi daftar tasks: merupakan bentuk yang tepat untuk automasi pertama Anda. Bentuk ini juga tetap sesuai lebih lama dari perkiraan kebanyakan orang. Ubah menjadi role ketika blok task yang sama harus dijalankan untuk kelompok host kedua, atau ketika file berkembang hingga lebih dari sekitar 100 baris dan Anda tidak lagi dapat menemukan task hanya dengan menggulir.

Jika Anda belum pernah menulis playbook, mulailah dengan playbook pertama pada satu VPS dan kembali ke sini ketika ukurannya mulai bertambah.

Kapan flat playbook merupakan pilihan yang tepat

Flat playbook tepat digunakan ketika pekerjaan hanya dilakukan sekali, hanya pada satu host, atau tidak akan dibaca oleh orang lain. Provisioning satu application server atau melakukan patch pada sebuah host sebelum maintenance window tidak memerlukan struktur direktori bertingkat. Sebuah role menambahkan tujuh direktori dan satu lapisan indireksi. Jika satu-satunya pemanggilnya adalah playbook yang berada di sebelahnya, indireksi tersebut tidak memberikan manfaat dan hanya menambah satu langkah setiap kali Anda ingin membaca apa yang benar-benar dijalankan.

Flat playbook tidak lagi tepat pada momen tertentu, dan momen tersebut mudah dikenali. Anda menyalin satu blok task ke playbook kedua. Salinan itu merupakan tandanya. Sejak saat itu, setiap perbaikan harus dilakukan dua kali, dan suatu hari perbaikan tersebut hanya akan dilakukan satu kali.

Isi sebenarnya direktori role

roles/common/
  defaults/main.yml
  vars/main.yml
  tasks/main.yml
  handlers/main.yml
  templates/99-hardening.conf.j2
  files/
  meta/main.yml
  • tasks/main.yml adalah titik masuk. Ansible menjalankan file ini saat role dipanggil, sedangkan direktori lainnya bersifat opsional.
  • defaults/main.yml menyimpan variabel yang diharapkan dapat diganti oleh pemanggil. Ini adalah sumber dengan prioritas terendah di Ansible, sehingga hampir semua sumber lain akan mengalahkannya.
  • vars/main.yml menyimpan variabel yang tidak diharapkan untuk diganti oleh pemanggil. Prioritasnya lebih tinggi daripada inventory, sehingga keputusan ini harus dibuat dengan pertimbangan matang. Gunakan direktori ini sesekali saja.
  • handlers/main.yml menyimpan task yang dipicu oleh notify. Handler dijalankan satu kali di akhir play, terlepas dari berapa banyak task yang memberi notifikasi kepadanya.
  • files/ menyimpan file yang disalin apa adanya oleh modul copy, sedangkan templates/ menyimpan template Jinja2 yang dirender oleh modul template. Di dalam role, Anda merujuk keduanya hanya dengan nama file tanpa path karena Ansible lebih dahulu mencari di direktori milik role tersebut.
  • meta/main.yml mendeklarasikan dependensi role dan metadata yang dibaca oleh Ansible Galaxy.

Struktur ini bukan sekadar preferensi gaya. Ansible mencari file pada path yang persis tersebut. Jadi, template yang Anda letakkan di roles/common/template/ (tunggal) tidak akan pernah ditemukan.

Buat role umum dengan ansible-galaxy init

mkdir -p ~/infra/roles
cd ~/infra
ansible-galaxy init --init-path roles common

Perintah tersebut menulis seluruh kerangka di bawah roles/common, termasuk direktori yang tidak akan digunakan dan stub main.yml yang hanya berisi ---. Hapus direktori dan file yang dibiarkan kosong. vars/main.yml yang kosong tidak bermasalah bagi Ansible, tetapi menyamarkan file mana dalam role yang benar-benar penting.

Sekarang isi file yang menjalankan tugasnya. Mulai dari defaults karena bagian ini merupakan antarmuka publik role.

# roles/common/defaults/main.yml
---
common_packages:
  - ufw
  - fail2ban
  - unattended-upgrades
common_admin_group: admins
common_permit_root_login: "no"
common_password_authentication: "no"

Gunakan tanda kutip pada "no" dan "yes". Ansible mengurai YAML dengan PyYAML, yang membaca no tanpa tanda kutip sebagai boolean false. Akibatnya, baris konfigurasi yang dirender menjadi PermitRootLogin False dan sshd menolaknya. Tanda kutip mempertahankan nilai tersebut sebagai string.

# roles/common/tasks/main.yml
---
- name: Install the base packages
  ansible.builtin.apt:
    name: "{{ common_packages }}"
    state: present
    update_cache: true
    cache_valid_time: 3600

- name: Create the admin group
  ansible.builtin.group:
    name: "{{ common_admin_group }}"
    state: present

- name: Install the sshd hardening drop-in
  ansible.builtin.template:
    src: 99-hardening.conf.j2
    dest: /etc/ssh/sshd_config.d/99-hardening.conf
    owner: root
    group: root
    mode: "0644"
    validate: /usr/sbin/sshd -t -f %s
  notify: Restart sshd
# roles/common/handlers/main.yml
---
- name: Restart sshd
  ansible.builtin.service:
    name: ssh
    state: restarted
# roles/common/templates/99-hardening.conf.j2
# Managed by Ansible. Local edits are overwritten on the next run.
PermitRootLogin {{ common_permit_root_login }}
PasswordAuthentication {{ common_password_authentication }}

Pada Debian dan Ubuntu, unit systemd bernama ssh, sedangkan pada sistem keluarga RHEL namanya sshd. Handler yang menyebut nama unit yang salah hanya gagal saat sesuatu benar-benar mengubah template. Karena itu, masalah ini biasanya baru muncul beberapa minggu kemudian.

Baris validate adalah bagian paling berguna dalam task tersebut. Ansible merender template ke file sementara, mengganti %s dengan path file tersebut, lalu menjalankan perintah. File tujuan hanya diganti jika perintah selesai dengan kode keluar 0. Tambahkan direktif yang tidak valid ke dalam template, lalu jalankan kembali: task gagal dengan failed to validate, /etc/ssh/sshd_config.d/99-hardening.conf yang sebenarnya tetap tidak berubah, dan Anda masih memiliki server yang dapat digunakan untuk login. Perhatikan bahwa pemeriksaan ini menguji lebih dari sekadar sintaks. Jika sshd -t tidak dapat membaca host key, perintah tersebut selesai dengan sshd: no hostkeys available -- exiting. dan Ansible melaporkan failed to validate yang sama. Karena itu, baca msg modul sebelum menyalahkan template.

Cara playbook memanggil role

# site.yml
---
- name: Base configuration for every server
  hosts: all
  become: true
  roles:
    - common
# inventory.ini
[local]
localhost ansible_connection=local
ansible-playbook -i inventory.ini site.yml

Play harus diakhiri dengan failed=0 dalam ringkasan hasil. Teruskan parameter pada lokasi pemanggilan menggunakan bentuk yang diperluas. Dengan cara ini, satu role dapat digunakan untuk dua kelompok host:

  roles:
    - role: common
      common_admin_group: ops
      common_permit_root_login: prohibit-password

Ada satu aturan urutan yang mengejutkan hampir semua orang. Sebuah play dapat memuat pre_tasks, roles, tasks, dan post_tasks. Ansible menjalankannya dalam urutan tersebut, apa pun urutan penulisannya dalam file. Letakkan tasks: di atas roles:, tetapi role tetap dijalankan terlebih dahulu. Jadi, jika sesuatu harus dijalankan sebelum sebuah role, letakkan di pre_tasks:, bukan di bagian atas tasks:.

- name: Ordering demonstration
  hosts: local
  gather_facts: false
  pre_tasks:
    - name: Runs first
      ansible.builtin.debug:
        msg: pre
  roles:
    - common
  tasks:
    - name: Runs after the role
      ansible.builtin.debug:
        msg: task
  post_tasks:
    - name: Runs last
      ansible.builtin.debug:
        msg: post

Untuk memanggil role dari dalam daftar task, bukan menggunakan key roles:, gunakan import_role atau include_role.

  tasks:
    - name: Static, read when the playbook is parsed
      ansible.builtin.import_role:
        name: common

    - name: Dynamic, resolved when the task runs
      ansible.builtin.include_role:
        name: postgres
      when: "'db' in group_names"

import_role bersifat statis. Ansible membaca role pada waktu parsing, lalu task-nya menjadi bagian dari play. Karena itu, ansible-playbook --list-tasks site.yml mencantumkan task tersebut, dan tag pada import berlaku untuk setiap task di dalamnya. include_role bersifat dinamis. Tidak ada yang dibaca sampai task dijalankan. Hal ini memungkinkan Anda menentukan nama role dari variabel atau loop. Konsekuensinya, task tersebut tidak terlihat oleh --list-tasks dan --start-at-task.

Ada satu jebakan di sini. when: pada task include_role dievaluasi sebelum defaults/main.yml dari role yang disertakan berada dalam cakupan. Jika menulis when: common_packages | length > 0 pada include, eksekusi berhenti dengan 'common_packages' is undefined, meskipun variabel tersebut didefinisikan di dalam role yang sedang disertakan. Solusinya adalah memindahkan toggle ke luar role. Letakkan toggle tersebut di group_vars/all.yml agar berada dalam cakupan di semua tempat, dan biarkan default role digunakan untuk nilai yang dikonsumsi oleh role itu sendiri.

Variabel mana yang menang: defaults, group_vars, vars, extra vars

Ansible mendokumentasikan lebih dari dua puluh tingkat prioritas variabel. Empat di antaranya mencakup hampir setiap perdebatan nyata. Berikut urutannya dari yang paling lemah hingga paling kuat.

  • roles/<name>/defaults/main.yml berada di tingkat bawah. Hampir semua nilai yang Anda tetapkan di tempat lain akan mengalahkannya. Karena itu, roles/<name>/defaults/main.yml adalah tempat yang tepat untuk parameter yang dapat disesuaikan pada sebuah role.
  • group_vars/ dan host_vars/ berada di tingkat tengah. Di sinilah nilai khusus untuk situs Anda ditetapkan. Keduanya dapat menggantikan default role dengan jelas.
  • roles/<name>/vars/main.yml berada di atas host_vars. Nilai yang Anda tetapkan di sini tidak dapat digantikan dari inventory. Gunakan tingkat ini untuk hal-hal yang harus tetap konsisten secara internal di dalam role, seperti nama paket yang harus sesuai dengan nama service.
  • Parameter role yang diteruskan saat pemanggilan mengalahkan vars/main.yml. Sementara itu, -e pada command line mengalahkan semuanya, termasuk parameter role.

Anda dapat melihat proses ini dalam waktu sekitar satu menit. Berikan sebuah role kecil satu default dan satu role var, lalu tetapkan nama yang sama di host_vars.

# roles/prec/defaults/main.yml
---
prec_tunable: from-defaults
prec_internal: from-defaults
# roles/prec/vars/main.yml
---
prec_internal: from-rolevars
# host_vars/localhost.yml
---
prec_tunable: from-hostvars
prec_internal: from-hostvars
# roles/prec/tasks/main.yml
---
- name: Show which value survived
  ansible.builtin.debug:
    msg: "tunable={{ prec_tunable }} internal={{ prec_internal }}"
ansible-playbook -i inventory.ini prec.yml
ansible-playbook -i inventory.ini prec.yml -e prec_internal=from-cli

Proses pertama mencetak tunable=from-hostvars internal=from-rolevars. Inventory mengalahkan default role, tetapi kalah dari role var. Proses kedua mencetak internal=from-cli karena extra vars berada di tingkat paling atas dan tidak dapat digantikan oleh tingkat mana pun di bawahnya. Karena itu, -e cocok untuk proses satu kali, tetapi tidak tepat digunakan dalam script yang dipertahankan. Nilai tersebut secara diam-diam mengalahkan semua keputusan yang dipertimbangkan di repository Anda.

Aturan praktisnya: jika Anda ingin sebuah nilai dapat diatur, letakkan nilai tersebut di defaults/. Menempatkannya di vars/ memberi tahu setiap pengguna role di masa mendatang bahwa inventory tidak boleh mengubahnya. Terkadang memang itu yang Anda maksudkan, tetapi biasanya hal tersebut terjadi secara tidak sengaja.

Buktikan bahwa role bersifat idempotent: jalankan dua kali

Eksekusi Ansible yang dapat dipercaya menghasilkan keadaan yang sama pada eksekusi kedua dan melaporkan bahwa tidak ada perubahan. Jalankan playbook dua kali, lalu baca ringkasannya.

ansible-playbook -i inventory.ini site.yml
ansible-playbook -i inventory.ini site.yml

Ringkasan kedua seharusnya terlihat seperti ini:

PLAY RECAP *********************************************************************
localhost   : ok=4  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0

changed=0 berarti setiap modul memeriksa keadaan saat ini dan menemukan bahwa pekerjaan tersebut sudah selesai. changed=2 pada eksekusi kedua berarti dua task tidak dapat membedakan keadaannya, sehingga keduanya akan terus menulis ulang file dan me-restart service tanpa henti. Penyebab yang umum adalah command atau shell, karena Ansible tidak memiliki cara untuk mengetahui hasil dari perintah arbitrer.

# traps.yml
---
- name: Command modules do not know what they changed
  hosts: local
  gather_facts: false
  tasks:
    - name: This appends a line on every run
      ansible.builtin.shell: "echo run >> /tmp/grow.txt"

    - name: This appends a line only once
      ansible.builtin.shell: "echo run >> /tmp/guarded.txt"
      args:
        creates: /tmp/guarded.txt

Jalankan playbook tersebut dua kali, lalu hitung baris yang berisi wc -l /tmp/grow.txt /tmp/guarded.txt. /tmp/grow.txt berisi dua baris dan /tmp/guarded.txt berisi satu baris. Pada eksekusi kedua, task yang dilindungi tidak dijalankan sama sekali, dan hasilnya berisi pesan skipped, since /tmp/guarded.txt exists, karena creates memberikan modul hasil yang dapat diamati untuk dicari terlebih dahulu. Jika sebuah perintah tidak menghasilkan hasil semacam itu, daftarkan output-nya dan tentukan sendiri dengan changed_when.

ansible-playbook --check --diff site.yml memperkirakan perubahan tanpa menerapkannya, sedangkan --diff mencetak baris persis yang akan ditulis ulang oleh template. Baca output tersebut dengan mengingat satu pengecualian: task shell dan command dilewati dalam check mode, sehingga rencana yang terlihat bersih masih dapat menyembunyikan pekerjaan yang perlu dilakukan.

Mengapa Ansible menyatakan bahwa role tidak ditemukan

Ansible mencari direktori roles/ di sebelah file playbook, lalu di roles_path. Pencarian mengikuti playbook, bukan shell Anda.

ERROR! the role 'common' was not found in /home/deploy/lonely/roles:/home/deploy/lonely

Pesan tersebut berarti site.yml dan roles/ sudah tidak sinkron. Pesan itu juga menampilkan path yang telah dicoba. Simpan keduanya dalam direktori yang sama. Menjalankan perintah dari direktori induk tidak masalah karena yang menentukan adalah path playbook:

ansible-playbook -i infra/inventory.ini infra/site.yml

Ada bentuk masalah yang sama, tetapi lebih sulit terlihat. Ansible mengabaikan ansible.cfg di direktori saat ini jika direktori tersebut dapat ditulis oleh semua pengguna, karena pengguna mana pun pada server dapat menempatkan konfigurasi di sana dan mengubah hasil eksekusi Anda.

[WARNING]: Ansible is being run in a world writable directory (/tmp/infra), ignoring it as an ansible.cfg source.

Dengan demikian, pengaturan roles_path dan inventory tidak tersedia tanpa pesan apa pun, lalu pencarian role gagal karena alasan yang tidak berkaitan dengan role. ansible --version menampilkan config file yang benar-benar dimuatnya, sedangkan ansible-config dump --only-changed menampilkan setiap pengaturan yang berbeda dari default bawaan. Periksa keduanya setiap kali eksekusi berjalan seolah-olah konfigurasi Anda tidak ada.

Berbagi role: requirements.yml dan versi yang dikunci

Role yang ditulis orang lain diinstal, bukan disalin. Deklarasikan role tersebut satu kali:

# requirements.yml
---
roles:
  - name: postgres
    src: https://github.com/example/ansible-role-postgres
    scm: git
    version: v1.4.0
ansible-galaxy install -r requirements.yml -p galaxy_roles

Selalu tetapkan version. Tanpa pengaturan ini, Anda akan mendapatkan isi branch default pada hari ketika perintah dijalankan. Akibatnya, deployment yang berhasil bulan lalu dapat gagal tanpa perubahan apa pun pada repository Anda sendiri. Arahkan roles_path ke direktori unduhan, dan jangan masukkan direktori tersebut ke git:

# ansible.cfg
[defaults]
inventory = inventory.ini
roles_path = ./galaxy_roles

Role di roles/ yang berada di sebelah playbook tetap ditemukan karena path tersebut selalu dicari selain roles_path. Dengan demikian, role Anda sendiri tetap di-commit dan ditinjau, sedangkan role pihak ketiga menjadi unduhan yang dapat direproduksi dan dikunci pada tag.

Saat role bukan lagi jawabannya

Role adalah unit penggunaan ulang dalam satu eksekusi Ansible. Role tidak membuat server atau catatan DNS pada provider Anda. Memaksa role melakukan hal tersebut akan membuat playbook sulit dipelihara. Sebelum memulai, baca pembagian tugas antara Ansible dan Terraform. Role juga tidak menggantikan desain inventory. Setelah jumlah mesin bertambah, cara mengelompokkan dan mengakses server tersebut lebih penting daripada cara tugas-tugas disimpan.

Hardening yang dipasang oleh role common ini juga memerlukan keputusan tersendiri. Drop-in di atas hanya menetapkan dua direktif. Karena itu, baca pengaturan SSH yang benar-benar perlu diubah dan cara membuat Ubuntu menerapkan pembaruan keamanan secara otomatis sebelum menentukan hal-hal yang harus dimasukkan ke dalam role untuk setiap host yang Anda miliki.

FAQ

Kapan playbook Ansible harus diubah menjadi role?

Saat blok task yang sama harus dijalankan dalam play kedua atau terhadap grup host kedua. Menyalin task antar-playbook adalah tandanya, karena sejak saat itu setiap perbaikan harus diterapkan dua kali dan suatu hari hanya akan diterapkan sekali. Satu playbook dengan panjang kurang lebih 100 baris yang selalu menargetkan satu grup tidak memperoleh manfaat dari role, dan direktori tambahan justru membuatnya lebih sulit dibaca.

Apakah role dijalankan sebelum task dalam play yang sama?

Ya. Ansible menjalankan pre_tasks, lalu semua yang tercantum dalam roles:, kemudian tasks:, dan setelah itu post_tasks:. Ansible mengabaikan urutan kemunculan key tersebut dalam file Anda. Menulis tasks: di atas roles: tidak membuat task tersebut dijalankan lebih dahulu. Jika sesuatu harus terjadi sebelum role, letakkan di pre_tasks:.

Mengapa nilai group_vars saya tidak menimpa role?

Periksa apakah variabel tersebut ditetapkan dalam vars/main.yml milik role, bukan dalam defaults/main.yml. vars/ memiliki prioritas lebih tinggi daripada group_vars dan host_vars dalam urutan precedence Ansible, sehingga inventory tidak dapat menimpanya. Pindahkan variabel tersebut ke defaults/main.yml, yang berada dekat bagian bawah urutan dan merupakan tempat yang tepat untuk apa pun yang harus dapat diubah oleh pemanggil. Untuk memastikan bahwa penyebabnya adalah precedence, bukan kesalahan pengetikan, jalankan sekali dengan -e name=value, yang memiliki prioritas lebih tinggi daripada semua sumber lainnya.

Mengapa Ansible menyatakan bahwa role tidak ditemukan?

Pencarian dimulai dari lokasi yang sama dengan file playbook, sehingga site.yml dan roles/ harus berada dalam direktori yang sama. Error tersebut menampilkan path yang dicoba, seperti pada the role 'common' was not found in /home/deploy/lonely/roles:/home/deploy/lonely. Menjalankan playbook dari direktori induk tidak masalah, karena pencarian mengikuti path playbook, bukan working directory shell Anda. Jika Anda bergantung pada roles_path dari ansible.cfg, pastikan file tersebut dimuat dengan ansible --version, karena working directory yang dapat ditulis oleh semua pengguna membuat Ansible mengabaikannya.

Apakah saya memerlukan ansible-galaxy init untuk membuat role?

Tidak. Role hanya terdiri atas direktori dengan nama yang diharapkan, sehingga mkdir -p roles/common/tasks ditambah tasks/main.yml sudah merupakan role yang berfungsi. ansible-galaxy init --init-path roles common menghemat pengetikan dan menyediakan skeleton lengkap, termasuk meta/main.yml dan stub README. Hapus direktori yang dibiarkan kosong, karena vars/main.yml yang kosong menyamarkan file mana dalam role yang benar-benar melakukan sesuatu.

#ansible#roles#playbook#structure#automation