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.
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.ymltasks/main.ymlialah titik masuk. Ansible menjalankan fail ini apabila peranan dipanggil, dan setiap direktori lain adalah pilihan.defaults/main.ymlmenyimpan 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.ymlmenyimpan 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.ymlmenyimpan tugasan yang dicetuskan olehnotify. Pengendali (handler) dijalankan pada penghujung play, sekali sahaja, tidak kira berapa banyak tugasan yang memberitahunya.files/menyimpan fail yang disalin secara verbatim oleh modulcopy, dantemplates/menyimpan templat Jinja2 yang dirender oleh modultemplate. Di dalam peranan, anda merujuk kedua-duanya dengan nama fail sahaja tanpa laluan, kerana Ansible mencari direktori peranan itu sendiri terlebih dahulu.meta/main.ymlmengisytiharkan 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 commonPerintah 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=localansible-playbook -i inventory.ini site.ymlPlay 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-passwordTerdapat 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: postUntuk 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.ymlberada 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/danhost_vars/berada di tengah. Di sinilah jawapan khusus tapak anda diletakkan, dan ia mengatasi nilai lalai peranan dengan kemas.roles/<name>/vars/main.ymlberada di atashost_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-epada 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-cliJalankan 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.ymlRumusan kedua sepatutnya kelihatan seperti ini:
PLAY RECAP *********************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0changed=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.txtJalankan 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/lonelyMesej 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.ymlTerdapat 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.0ansible-galaxy install -r requirements.yml -p galaxy_rolesSentiasa 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_rolesPeranan 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.