SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor · Dikemas kini 2026-08-29

Ansible Playbook vs Role: Bila Perlu Gunakan Setiap Satu

Ketahui perbezaan antara Ansible playbook dan role untuk automasi anda. Kami jelaskan bila anda perlu beralih daripada fail YAML rata kepada struktur direktori role.

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 tasks, templates, handlers dan default variables, dan playbook akan memanggilnya mengikut nama. Sintaks task di dalam kedua-duanya adalah sama, jadi ini bukan persoalan tentang apa yang boleh anda lakukan. Ini adalah persoalan tentang penggunaan semula.

Mulakan dengan playbook yang rata. Satu site.yml yang mengandungi senarai tasks: adalah bentuk yang tepat untuk automasi pertama anda, dan ia kekal sesuai untuk tempoh yang lebih lama daripada jangkaan kebanyakan orang. Tukar kepada role apabila blok tugasan yang sama perlu dijalankan untuk kumpulan hos kedua, atau apabila fail tersebut melebihi kira-kira 100 baris dan anda tidak lagi dapat mencari tugasan 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 sahaja, atau apabila tiada orang lain yang akan membacanya. Melakukan provisioning pada satu pelayan aplikasi, atau menampal (patch) satu mesin sebelum tempoh penyelenggaraan: kedua-duanya tidak memerlukan struktur direktori yang kompleks. Penggunaan role menambah tujuh direktori dan satu lapisan indirection. Jika pemanggil tunggal hanyalah playbook yang berada di sebelahnya, indirection 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 dikenal pasti. 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 kuat. Gunakannya dengan jarang.
  • handlers/main.yml menyimpan tugasan yang dicetuskan oleh notify. Pengendali (handler) dijalankan pada akhir 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 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, tetapi ia menyembunyikan fail mana dalam peranan tersebut yang sebenarnya penting.

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, jadi 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. 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 peranan

# 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 peranan 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. Sesuatu play boleh mengandungi pre_tasks, roles, tasks dan post_tasks, dan Ansible akan menjalankannya mengikut susunan tersebut tanpa mengira susunan yang anda tulis dalam fail. Letakkan tasks: di atas roles: dan peranan tersebut tetap akan dijalankan terlebih dahulu. Jadi, jika sesuatu perkara mesti berlaku sebelum peranan, 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 peranan dari dalam senarai tugas 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 adalah statik. Ansible membaca peranan tersebut pada masa penghuraian (parse time) dan tugas-tugasnya menjadi sebahagian daripada play, jadi ansible-playbook --list-tasks site.yml akan menyenaraikannya dan tag pada import akan terpakai pada setiap tugas di dalamnya. include_role adalah dinamik. Tiada apa-apa yang dibaca sehingga tugas tersebut dijalankan, itulah yang membolehkan anda menentukan nama peranan daripada pemboleh ubah atau gelung. Kekurangannya ialah tugas-tugas tersebut tidak kelihatan kepada --list-tasks dan --start-at-task.

Satu perangkap wujud di sini. when: pada tugas include_role dinilai sebelum defaults/main.yml peranan yang disertakan berada dalam skop. Tulis when: common_packages | length > 0 pada include dan pelaksanaan akan terhenti dengan 'common_packages' is undefined, walaupun pemboleh ubah tersebut ditakrifkan dalam peranan yang anda sertakan itu sendiri. Penyelesaiannya adalah dengan mengalihkan togol tersebut keluar daripada peranan: letakkannya dalam group_vars/all.yml, di mana ia berada dalam skop di mana-mana sahaja, dan biarkan nilai lalai peranan untuk nilai yang digunakan oleh peranan 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 hampir di bahagian bawah. Hampir apa sahaja yang anda tetapkan di tempat lain akan mengatasinya, itulah sebabnya ia merupakan tempat yang sesuai untuk parameter boleh laras bagi sesuatu peranan (role).
  • 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 bagi mengekalkan konsistensi dalaman, seperti nama pakej yang perlu sepadan dengan nama servis.
  • Parameter peranan yang dihantar di tapak 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 satu minit. Berikan satu nilai lalai dan satu pemboleh ubah peranan kepada 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 pertama mencetak tunable=from-hostvars internal=from-rolevars. Inventori mengatasi nilai lalai peranan tetapi kalah kepada pemboleh ubah peranan. Jalankan 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 tetapi salah jika digunakan dalam skrip yang anda simpan: ia secara senyap mengatasi setiap keputusan yang dipertimbangkan dalam repositori anda.

Peraturan kerja: jika anda mahu sesuatu nilai boleh ditetapkan, letakkannya dalam defaults/. Meletakkannya dalam vars/ memberitahu setiap pengguna peranan pada masa hadapan bahawa inventori mungkin tidak boleh mengubahnya. Kadangkala itu adalah maksud anda, tetapi biasanya ia adalah satu kesilapan.

Buktikan peranan tersebut adalah idempoten: jalankan dua kali

Jalankan Ansible yang boleh dipercayai akan menghasilkan keputusan yang sama pada kali kedua dan melaporkan bahawa tiada perubahan dilakukan. Jalankan playbook 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 arahan sewenang-wenangnya.

# 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 itu 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. 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 produk yang boleh dilihat untuk dicari 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 satu peringatan: tugasan shell dan command dilangkau dalam mod semakan (check mode), jadi pelan yang kelihatan bersih masih boleh menyembunyikan kerja yang akan dilakukan.

Satu lagi lajur dalam rumusan tersebut memerlukan perhatian yang sama: hos yang tidak dapat dihubungkan oleh Ansible dikira di bawah unreachable dan bukannya failed, dan tiada satu pun tugasannya dijalankan. Oleh itu, tentukan lebih awal sama ada satu hos yang tidak boleh dicapai harus menghentikan keseluruhan larian sebelum anda menghalakan peranan ini kepada lebih daripada beberapa buah mesin.

Mengapa Ansible menyatakan peranan tidak ditemui

Ansible mencari direktori roles/ di sebelah 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/ tidak lagi selari, 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 sistem 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 tiada secara senyap, 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 apabila sesuatu proses berjalan seolah-olah konfigurasi anda tidak wujud.

Perkongsian peranan: requirements.yml dan versi yang dipinkan

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 perintah tersebut, menyebabkan penggunaan (deployment) 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 dipinkan pada tag tertentu.

Apabila peranan bukan lagi penyelesaiannya

Peranan ialah unit guna semula dalam satu pelaksanaan Ansible. Ia tidak mencipta pelayan atau rekod DNS di penyedia anda, dan cubaan untuk menjadikannya melakukan perkara tersebut akan menyebabkan playbook menjadi sesuatu yang tidak mahu diselenggara oleh sesiapa. pembahagian kerja antara Ansible dan Terraform wajar dibaca sebelum anda bermula. 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 tambahan di atas hanya menetapkan dua arahan dan tidak lebih daripada itu, jadi baca tetapan SSH yang sebenarnya berbaloi untuk diubah dan cara 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 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 kunci tersebut muncul 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 ditetapkan dalam vars/main.yml role tersebut 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 daripada direktori induk adalah dibolehkan, kerana carian mengikut laluan playbook dan bukannya direktori kerja shell anda. Jika anda bergantung pada roles_path daripada ansible.cfg, pastikan fail tersebut dimuatkan dengan ansible --version, kerana direktori kerja yang boleh ditulis oleh semua orang (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 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 menyembunyikan fail mana dalam role yang sebenarnya melakukan sesuatu.

#ansible#roles#playbook#structure#automation