SSD Nodes Learn 🎉 VPS dari $5.50/bln
Panduan Matt ConnorOleh Matt Connor · Dikemas kini 2026-08-07

Ansible Playbook vs Role: Bila Perlu Gunakan Yang Mana?

Ketahui perbezaan antara Ansible playbook dan role. Gunakan playbook untuk automasi ringkas dan beralih kepada role apabila kod melebihi 100 baris atau perlu digunakan semula.

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

Perbezaan antara Ansible playbook dan role

Ansible playbook ialah fail yang anda jalankan dengan ansible-playbook. Ia memetakan sekumpulan hos kepada tugasan yang perlu dilaksanakan. Ansible role pula ialah direktori dengan struktur tetap yang mengandungi task, template, handler dan pemboleh ubah lalai, yang kemudiannya dipanggil oleh playbook mengikut nama. Sintaks task di dalam kedua-duanya adalah sama, jadi ini bukan persoalan tentang apa yang boleh anda lakukan. Ini adalah persoalan tentang kebolehgunaan semula.

Mulakan dengan playbook yang ringkas. Satu site.yml yang mengandungi senarai tasks: adalah bentuk yang sesuai untuk automasi pertama anda, dan ia kekal mencukupi untuk tempoh yang lebih lama daripada jangkaan kebanyakan orang. Tukar kepada role apabila blok task yang sama perlu dijalankan untuk kumpulan hos kedua, atau apabila fail tersebut melebihi kira-kira 100 baris dan anda tidak lagi dapat mencari task dengan menatal.

Jika anda belum menulisnya lagi, mulakan dengan playbook pertama terhadap satu VPS dan kembali semula apabila ia mula berkembang.

Apabila playbook rata adalah jawapan yang tepat

Playbook rata adalah pilihan yang tepat apabila tugasan hanya dilakukan sekali, atau pada satu hos, atau apabila tiada orang lain yang akan membacanya. Menyediakan satu pelayan aplikasi, atau menampal (patch) satu mesin sebelum tempoh penyelenggaraan: kedua-duanya tidak memerlukan struktur direktori yang kompleks. Satu role menambah tujuh direktori dan satu lapisan pengantara (indirection). Jika pemanggil tunggal hanyalah playbook yang berada di sebelahnya, pengantara tersebut tidak memberikan sebarang faedah malah menyukarkan anda kerana perlu melompat ke fail lain setiap kali ingin membaca arahan yang sebenar.

Playbook rata tidak lagi menjadi pilihan yang tepat pada satu ketika, dan saat itu mudah untuk dikesan. Anda menyalin satu blok tugas ke dalam playbook kedua. Salinan itulah tandanya. Bermula dari saat itu, setiap pembetulan perlu dibuat dua kali, dan suatu hari nanti, ia pasti hanya akan dibuat sekali sahaja.

Kandungan sebenar direktori peranan

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 ialah titik masuk. Ansible menjalankan fail ini apabila peranan dipanggil, dan setiap direktori lain adalah pilihan.
  • defaults/main.yml menyimpan pemboleh ubah yang dijangka akan ditindih oleh pemanggil. Ia merupakan sumber keutamaan paling rendah dalam Ansible, jadi hampir semua perkara lain mengatasi nilainya.
  • vars/main.yml menyimpan pemboleh ubah yang tidak dijangka akan ditindih oleh pemanggil. Ia berada di atas inventori dari segi keutamaan, yang merupakan pernyataan yang sangat kuat. Gunakannya dengan jarang.
  • handlers/main.yml menyimpan tugasan yang dicetuskan oleh notify. Pengendali (handler) dijalankan pada penghujung play, sekali sahaja, tidak kira berapa banyak tugasan yang memberitahunya.
  • files/ menyimpan fail yang disalin secara verbatim oleh modul copy, dan templates/ menyimpan templat Jinja2 yang dirender oleh modul template. Di dalam peranan, anda merujuk kedua-duanya dengan nama fail sahaja tanpa laluan, kerana Ansible mencari direktori peranan itu sendiri terlebih dahulu.
  • meta/main.yml mengisytiharkan dependensi peranan dan metadata yang dibaca oleh Ansible Galaxy.

Susun atur ini bukan sekadar pilihan gaya. Ansible mencari dalam laluan tepat ini, jadi templat yang anda letakkan dalam roles/common/template/ (kata tunggal) tidak akan ditemui sama sekali.

Bina peranan umum dengan ansible-galaxy init

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

Perintah tersebut menulis keseluruhan rangka kerja di bawah roles/common, termasuk direktori yang tidak akan anda gunakan dan stub main.yml yang hanya mengandungi ---. Padamkan fail yang dibiarkan kosong. vars/main.yml yang kosong tidak menjejaskan Ansible, namun ia menyukarkan pengenalpastian fail yang sebenarnya penting dalam peranan tersebut.

Sekarang, isikan fail yang melaksanakan tugasan. Mulakan dengan defaults, kerana ia merupakan antara muka awam bagi peranan tersebut.

# 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 petikan untuk "no" dan "yes". Ansible menghuraikan YAML menggunakan PyYAML, yang membaca no kosong sebagai boolean false, menyebabkan baris konfigurasi yang dirender menjadi PermitRootLogin False dan sshd akan menolaknya. Tanda petikan mengekalkan nilai tersebut sebagai rentetan (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 dipanggil ssh, manakala pada sistem keluarga RHEL, ia adalah sshd. Handler yang menamakan unit yang salah hanya akan gagal apabila sesuatu benar-benar mengubah templat, itulah sebabnya masalah ini biasanya timbul selepas beberapa minggu.

Baris validate adalah perkara paling berguna dalam tugasan tersebut. Ansible merender templat ke fail sementara, menggantikan laluan fail tersebut untuk %s, dan menjalankan perintah tersebut. Destinasi hanya akan diganti jika perintah tersebut keluar dengan kod 0. Masukkan arahan yang tidak sah ke dalam templat dan jalankan semula: tugasan akan gagal dengan failed to validate, /etc/ssh/sshd_config.d/99-hardening.conf yang sebenar tidak disentuh, dan anda masih mempunyai pelayan yang boleh diakses. Perlu diingat bahawa semakan ini menguji lebih daripada sekadar sintaks anda. Jika sshd -t tidak dapat membaca kunci hos, ia akan keluar dengan sshd: no hostkeys available -- exiting. dan Ansible melaporkan failed to validate yang sama, jadi baca msg modul tersebut sebelum menyalahkan templat.

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 tersebut harus berakhir dengan failed=0 dalam ringkasan. Hantarkan parameter di tapak panggilan menggunakan bentuk yang dikembangkan, iaitu cara satu role melayani dua kumpulan hos:

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

Terdapat satu peraturan susunan yang mengejutkan hampir semua orang. Satu play boleh mengandungi pre_tasks, roles, tasks dan post_tasks, dan Ansible menjalankannya mengikut susunan tersebut tanpa mengira susunan yang anda tulis dalam fail. Letakkan tasks: di atas roles: dan role tersebut tetap akan dijalankan terlebih dahulu. Jadi, jika sesuatu perkara mesti berlaku sebelum satu role, ia perlu diletakkan dalam pre_tasks:, bukan di bahagian 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 senarai task dan bukannya menggunakan kunci 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 statik. Ansible membaca role tersebut pada masa penghuraian (parse time) dan task-tasknya menjadi sebahagian daripada play, jadi ansible-playbook --list-tasks site.yml akan menyenaraikannya dan tag pada import tersebut akan terpakai pada setiap task di dalamnya. include_role bersifat dinamik. Tiada apa-apa yang dibaca sehingga task tersebut dijalankan, yang membolehkan anda menentukan nama role daripada pemboleh ubah atau gelung (loop). Kesannya ialah task-task tersebut tidak kelihatan kepada --list-tasks dan --start-at-task.

Satu perangkap wujud di sini. when: pada task include_role dinilai sebelum defaults/main.yml bagi role yang disertakan berada dalam skop. Tulis when: common_packages | length > 0 pada include tersebut dan pelaksanaan akan terhenti dengan 'common_packages' is undefined, walaupun pemboleh ubah tersebut ditakrifkan dalam role yang anda sertakan itu sendiri. Penyelesaiannya adalah dengan mengalihkan togol tersebut keluar daripada role: letakkannya dalam group_vars/all.yml, di mana ia berada dalam skop di mana-mana sahaja, dan biarkan nilai lalai (defaults) role tersebut untuk nilai yang digunakan oleh role itu sendiri.

Pemboleh ubah mana yang menang: defaults, group_vars, vars, extra vars

Ansible mendokumentasikan lebih daripada dua puluh tahap keutamaan pemboleh ubah. Empat daripadanya menyelesaikan hampir setiap perdebatan sebenar, dan berikut adalah susunannya daripada yang paling lemah kepada yang paling kuat.

  • roles/<name>/defaults/main.yml berada di kedudukan bawah. Hampir apa sahaja yang anda tetapkan di tempat lain akan mengatasinya, itulah sebabnya ia merupakan tempat yang tepat untuk tetapan boleh laras (tunable knobs) sesuatu peranan.
  • group_vars/ dan host_vars/ berada di tengah. Di sinilah jawapan khusus tapak anda diletakkan, dan ia mengatasi nilai lalai peranan dengan kemas.
  • roles/<name>/vars/main.yml berada di atas host_vars. Nilai yang anda letakkan di sini tidak boleh diatasi daripada inventori. Simpan ia untuk perkara yang diperlukan oleh peranan tersebut agar kekal konsisten secara dalaman, seperti nama pakej yang perlu sepadan dengan nama servis.
  • Parameter peranan yang dihantar di lokasi panggilan mengatasi vars/main.yml, dan -e pada baris perintah mengatasi segala-galanya, termasuk parameter peranan.

Anda boleh melihat proses penyelesaian ini dalam masa kira-kira seminit. Berikan satu nilai lalai dan satu pemboleh ubah peranan kepada satu peranan kecil, kemudian tetapkan nama yang sama dalam 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

Jalankan kali pertama akan mencetak tunable=from-hostvars internal=from-rolevars. Inventori mengatasi nilai lalai peranan tetapi kalah kepada pemboleh ubah peranan. Jalankan kali kedua mencetak internal=from-cli, kerana extra vars berada di kedudukan paling atas dan tiada apa-apa di bawahnya yang boleh mengubahnya. Itulah juga sebabnya -e sesuai untuk jalankan sekali sahaja (one-off) tetapi salah jika digunakan dalam skrip yang anda simpan: ia secara senyap mengatasi setiap keputusan yang telah dipertimbangkan dalam repositori anda.

Peraturan kerja: jika anda mahu sesuatu nilai boleh ditetapkan, letakkannya dalam defaults/. Meletakkannya dalam vars/ memberitahu setiap pengguna peranan tersebut pada masa hadapan bahawa inventori mungkin tidak boleh mengubahnya. Kadangkala itu memang tujuan anda, namun biasanya ia berlaku secara tidak sengaja.

Buktikan peranan tersebut adalah idempoten: jalankan sebanyak dua kali

Jalankan Ansible yang boleh dipercayai akan menghasilkan keputusan yang sama pada kali kedua dan melaporkan bahawa tiada perubahan dilakukan. Jalankan playbook sebanyak dua kali dan baca rumusan (recap) yang diberikan.

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

Rumusan kedua sepatutnya kelihatan seperti ini:

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

changed=0 bermaksud setiap modul telah memeriksa keadaan semasa dan mendapati kerja tersebut telah pun selesai. changed=2 pada larian kedua bermaksud dua tugasan tidak dapat membezakan keadaan, jadi ia akan terus menulis semula fail dan memulakan semula servis selama-lamanya. Punca biasa ialah command atau shell, kerana Ansible tidak mempunyai cara untuk mengetahui apa yang dilakukan oleh sesuatu arahan arbitrari.

# 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 sebanyak dua kali, kemudian kira baris yang mengandungi wc -l /tmp/grow.txt /tmp/guarded.txt. /tmp/grow.txt mengandungi dua baris dan /tmp/guarded.txt mengandungi satu baris. Pada larian kedua, tugasan yang dikawal (guarded task) tidak dilaksanakan langsung, dan hasilnya membawa mesej skipped, since /tmp/guarded.txt exists, kerana creates memberikan modul satu produk yang boleh dilihat untuk diperiksa terlebih dahulu. Apabila sesuatu arahan tidak meninggalkan produk sedemikian, daftarkan outputnya dan buat keputusan sendiri dengan changed_when.

ansible-playbook --check --diff site.yml meramalkan perubahan tanpa melaksanakannya, dan --diff mencetak baris tepat yang akan ditulis semula oleh templat. Baca output tersebut dengan mengambil kira satu amaran: tugasan shell dan command dilangkau dalam mod semakan (check mode), jadi pelan yang kelihatan bersih masih boleh menyembunyikan kerja yang akan dilakukan.

Mengapa Ansible menyatakan peranan tidak ditemui

Ansible mencari direktori roles/ bersebelahan dengan fail playbook, kemudian di dalam roles_path. Carian ini mengikut lokasi playbook, bukan shell anda.

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

Mesej tersebut bermaksud site.yml dan roles/ telah terpisah, dan ia mencetak laluan yang telah dicuba sebagai bantuan. Pastikan kedua-duanya berada dalam direktori yang sama. Menjalankan arahan dari direktori induk adalah dibolehkan, kerana laluan playbook adalah perkara yang diambil kira:

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

Terdapat versi masalah yang sama tetapi lebih senyap. Ansible mengabaikan ansible.cfg dalam direktori semasa apabila direktori tersebut boleh ditulis oleh sesiapa sahaja (world writable), kerana mana-mana pengguna pada pelayan tersebut boleh meletakkan konfigurasi di sana dan mengubah tindakan yang dijalankan oleh anda.

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

Tetapan roles_path dan inventory anda kemudiannya hilang tanpa sebarang amaran, dan carian peranan gagal atas sebab yang tiada kaitan dengan peranan. ansible --version mencetak config file yang sebenarnya dimuatkan, dan ansible-config dump --only-changed mencetak setiap tetapan yang berbeza daripada lalai terbina dalam. Semak kedua-duanya setiap kali sesuatu pelaksanaan berkelakuan seolah-olah konfigurasi anda tidak wujud.

Perkongsian peranan: requirements.yml dan versi yang ditetapkan

Peranan yang ditulis oleh orang lain perlu dipasang, bukan disalin. Isytiharkannya sekali sahaja:

# 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

Sentiasa tetapkan version. Tanpanya, anda akan mendapat apa sahaja kandungan cawangan lalai pada hari anda menjalankan arahan tersebut, menyebabkan penggunaan yang berjaya bulan lepas gagal walaupun tiada perubahan dalam repositori anda sendiri. Halakan roles_path ke direktori muat turun, dan pastikan direktori tersebut tidak dimasukkan ke dalam git:

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

Peranan dalam roles/ bersebelahan dengan playbook masih ditemui, kerana laluan tersebut sentiasa dicari sebagai tambahan kepada roles_path. Oleh itu, peranan anda sendiri kekal dikomit dan disemak, manakala peranan pihak ketiga adalah muat turun yang boleh dihasilkan semula dan ditetapkan pada tag tertentu.

Apabila peranan bukan lagi jawapan yang tepat

Sesuatu peranan (role) ialah unit guna semula dalam satu pelaksanaan Ansible. Ia tidak mencipta pelayan atau rekod DNS pada pembekal anda, dan cubaan untuk memaksanya melakukan perkara tersebut akan menyebabkan playbook menjadi sesuatu yang sukar diselenggara oleh sesiapa pun. pembahagian kerja antara Ansible dan Terraform wajar dibaca sebelum anda bermula. Sesuatu peranan juga tidak menggantikan reka bentuk inventori: sebaik sahaja anda menguruskan lebih daripada beberapa buah mesin, cara anda mengumpul dan mencapai pelayan tersebut adalah lebih penting daripada cara tugasan difailkan.

Pengukuhan (hardening) yang dipasang oleh peranan common ini juga memerlukan keputusan tersendiri. Konfigurasi di atas hanya menetapkan dua arahan dan tidak lebih daripada itu, jadi bacalah tetapan SSH yang sebenarnya berbaloi untuk diubah dan cara untuk membolehkan Ubuntu melaksanakan kemas kini keselamatan secara automatik sebelum anda memutuskan perkara yang perlu dimasukkan ke dalam peranan untuk setiap hos yang anda miliki.

FAQ

Bilakah saya perlu menukar playbook Ansible kepada role?

Apabila blok tugas yang sama perlu dijalankan dalam play kedua, atau terhadap kumpulan hos kedua. Menyalin tugas antara playbook adalah petanda, kerana mulai saat itu setiap pembetulan perlu dilakukan dua kali dan suatu hari nanti ia hanya akan dilakukan sekali. Playbook tunggal yang mempunyai kira-kira 100 baris dan hanya menyasarkan satu kumpulan tidak mendapat sebarang manfaat daripada role, malah direktori tambahan menjadikannya lebih sukar untuk dibaca.

Adakah role dijalankan sebelum tugas dalam play yang sama?

Ya. Ansible menjalankan pre_tasks, kemudian semua yang disenaraikan di bawah roles:, kemudian tasks:, kemudian post_tasks:, dan ia mengabaikan susunan kekunci tersebut dalam fail anda. Menulis tasks: di atas roles: tidak menyebabkan tugas tersebut dijalankan terlebih dahulu. Jika sesuatu perlu berlaku sebelum role, letakkannya dalam pre_tasks:.

Mengapa nilai group_vars saya tidak mengatasi role?

Semak sama ada pemboleh ubah tersebut ditetapkan dalam vars/main.yml milik role dan bukannya defaults/main.yml. vars/ berada di atas group_vars dan host_vars dalam susunan keutamaan Ansible, jadi inventori tidak boleh mengatasinya. Alihkan pemboleh ubah tersebut ke defaults/main.yml, yang berada hampir di bahagian bawah susunan dan merupakan tempat yang betul untuk sebarang perkara yang perlu diubah oleh pemanggil. Untuk mengesahkan bahawa keutamaan adalah puncanya dan bukannya kesilapan taip, jalankan sekali dengan -e name=value, yang mengatasi setiap sumber lain.

Mengapa Ansible menyatakan role tidak ditemui?

Carian bermula bersebelahan dengan fail playbook, jadi site.yml dan roles/ mesti berada dalam direktori yang sama. Ralat tersebut mencetak laluan yang telah dicuba, seperti dalam the role 'common' was not found in /home/deploy/lonely/roles:/home/deploy/lonely. Menjalankan playbook dari direktori induk adalah dibolehkan, kerana carian mengikut laluan playbook dan bukannya direktori kerja shell anda. Jika anda bergantung pada roles_path daripada ansible.cfg, sahkan bahawa fail tersebut dimuatkan dengan ansible --version, memandangkan direktori kerja yang boleh ditulis oleh sesiapa sahaja (world writable) akan menyebabkan Ansible mengabaikannya.

Adakah saya perlu menggunakan ansible-galaxy init untuk mencipta role?

Tidak. Role hanyalah direktori dengan nama yang dijangkakan, jadi mkdir -p roles/common/tasks berserta tasks/main.yml sudah pun menjadi role yang berfungsi. ansible-galaxy init --init-path roles common menjimatkan masa menaip dan memberikan anda rangka penuh, termasuk meta/main.yml dan stub README. Padamkan direktori yang anda biarkan kosong, kerana vars/main.yml yang kosong akan menyembunyikan fail mana dalam role yang sebenarnya melakukan sesuatu.

#ansible#roles#playbook#structure#automation