Ansible Playbook ve Role Arasındaki Farklar
Ansible playbook ile role yapısı arasındaki farkları ve hangisinin ne zaman kullanılacağını öğrenin. Kod tekrarını önlemek için 100 satır kuralı ve dizin yapısı detayları.
Ansible playbook ve role farkı
Ansible playbook, ansible-playbook ile çalıştırdığınız dosyadır. Bir grup sunucuyu yapılması gereken işlerle eşleştirir. Ansible role ise görevleri, şablonları, işleyicileri ve varsayılan değişkenleri barındıran, sabit bir dizin yapısına sahip bir yapıdır ve bir playbook tarafından ismiyle çağrılır. Her ikisinin içindeki görev sözdizimi aynıdır, dolayısıyla bu neyi ifade edebileceğinizle ilgili bir soru değildir. Bu, yeniden kullanılabilirlik ile ilgili bir sorudur.
Düz bir playbook ile başlayın. Bir site.yml içinde yer alan bir tasks: listesi, ilk otomasyonunuz için doğru biçimdir ve çoğu kişinin beklediğinden daha uzun süre yeterli olur. Aynı görev bloğunun ikinci bir sunucu grubu için çalıştırılması gerektiğinde veya dosya yaklaşık 100 satırı aşıp artık kaydırarak bir görevi bulamaz hale geldiğinizde role yapısına geçin.
Henüz bir tane yazmadıysanız, tek bir VPS üzerinde ilk playbook ile başlayın ve dosyanız büyümeye başladığında tekrar buraya dönün.
Düz bir playbook ne zaman doğru tercihtir
Düz bir playbook; iş tek seferlikse, tek bir sunucu üzerinde gerçekleşiyorsa veya başka kimse tarafından okunmayacaksa doğru tercihtir. Tek bir uygulama sunucusunun hazırlanması veya bir bakım penceresi öncesinde bir makinenin yamalanması gibi durumlar, bir dizin ağacını hak etmez. Bir rol, yedi dizin ve bir dolaylılık katmanı ekler. Eğer tek çağırıcı yanındaki playbook ise, bu dolaylılık hiçbir fayda sağlamaz; aksine neyin çalıştığını görmek istediğiniz her seferinde fazladan bir adım atmanıza neden olur.
Düz bir playbook'un doğru tercih olmaktan çıktığı an bellidir ve bu anı tespit etmek kolaydır. Bir görev bloğunu ikinci bir playbook'a kopyaladığınızda, bu kopyalama bir sinyaldir. O andan itibaren her düzeltmenin iki kez yapılması gerekir ve bir gün mutlaka sadece bir kez yapılacaktır.
Bir rol dizini aslında ne içerir
roles/common/
defaults/main.yml
vars/main.yml
tasks/main.yml
handlers/main.yml
templates/99-hardening.conf.j2
files/
meta/main.ymltasks/main.ymlgiriş noktasıdır. Bir rol çağrıldığında Ansible bu dosyayı çalıştırır; diğer tüm dizinler isteğe bağlıdır.defaults/main.yml, çağrı yapanın geçersiz kılması beklenen değişkenleri tutar. Ansible içindeki en düşük öncelikli kaynak olduğundan, neredeyse her şey bunun önüne geçer.vars/main.yml, çağrı yapanın geçersiz kılması beklenmeyen değişkenleri tutar. Öncelik sırasına göre envanterin üzerinde yer alır; bu oldukça güçlü bir ifadedir. Nadiren kullanın.handlers/main.yml,notifytarafından tetiklenen görevleri tutar. Bir handler, kaç görev onu bildirse bildirsin, oyunun (play) sonunda yalnızca bir kez çalışır.files/,copymodülü tarafından olduğu gibi kopyalanan dosyaları tutar;templates/isetemplatemodülü tarafından işlenen Jinja2 şablonlarını tutar. Bir rol içerisinde her ikisine de yol belirtmeden, sadece dosya adıyla referans verirsiniz; çünkü Ansible öncelikle rolün kendi dizinlerinde arama yapar.meta/main.yml, rol bağımlılıklarını ve Ansible Galaxy tarafından okunan meta verileri tanımlar.
Bu yapı bir stil tercihi değildir. Ansible tam olarak bu yollara bakar; dolayısıyla roles/common/template/ (tekil) içine koyduğunuz bir şablon asla bulunamaz.
ansible-galaxy init ile ortak rolü oluşturun
mkdir -p ~/infra/roles
cd ~/infra
ansible-galaxy init --init-path roles commonBu komut, roles/common altında, kullanmayacağınız dizinler ve yalnızca --- içeren main.yml taslakları dahil olmak üzere tüm iskeleti oluşturur. Boş bıraktıklarınızı silin. Boş bir vars/main.yml dosyası Ansible için zararsızdır ancak rolde hangi dosyaların gerçekten önemli olduğunu gizler.
Şimdi işi yapan dosyaları doldurun. Önce varsayılanlar (defaults), çünkü bunlar rolün genel arayüzüdür.
# roles/common/defaults/main.yml
---
common_packages:
- ufw
- fail2ban
- unattended-upgrades
common_admin_group: admins
common_permit_root_login: "no"
common_password_authentication: "no""no" ve "yes" değerlerini tırnak içine alın. Ansible, YAML dosyalarını PyYAML ile ayrıştırır; bu kütüphane çıplak bir no ifadesini boolean false olarak okur, bu durumda oluşturulan yapılandırma satırı PermitRootLogin False haline gelir ve sshd bunu reddeder. Tırnak işaretleri değerin string olarak kalmasını sağlar.
# 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 }}Debian ve Ubuntu sistemlerinde systemd birimi ssh olarak adlandırılırken, RHEL ailesi sistemlerde bu isim sshd şeklindedir. Yanlış ismi kullanan bir handler, yalnızca şablonda bir değişiklik yapıldığında hata verir; bu nedenle sorun genellikle haftalar sonra ortaya çıkar.
validate satırı, bu görevdeki en yararlı kısımdır. Ansible şablonu geçici bir dosyaya işler, %s yerine bu dosyanın yolunu koyar ve komutu çalıştırır. Hedef dosya, yalnızca komut 0 çıkış koduyla biterse değiştirilir. Şablona anlamsız bir direktif ekleyip tekrar çalıştırın: görev failed to validate hatasıyla başarısız olur, gerçek /etc/ssh/sshd_config.d/99-hardening.conf dosyasına dokunulmaz ve sunucuya giriş yapmaya devam edebilirsiniz. Bu kontrolün sadece sözdiziminden fazlasını test ettiğini unutmayın. Eğer sshd -t ana bilgisayar anahtarlarını okuyamazsa sshd: no hostkeys available -- exiting. ile çıkar ve Ansible aynı failed to validate hatasını raporlar; bu nedenle şablonu suçlamadan önce modülün msg belgelerini okuyun.
Bir playbook bir rolü nasıl çağırır
# 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, özet kısmında failed=0 ile bitmelidir. Parametreleri çağrı noktasında genişletilmiş biçimle iletin; bir rolün iki farklı ana bilgisayar grubuna bu şekilde hizmet vermesi sağlanır:
roles:
- role: common
common_admin_group: ops
common_permit_root_login: prohibit-passwordNeredeyse herkesi şaşırtan bir sıralama kuralı vardır. Bir play pre_tasks, roles, tasks ve post_tasks içerebilir; Ansible bunları dosyada hangi sırada yazmış olursanız olun bu sırayla çalıştırır. tasks: kısmını roles: kısmının üzerine koysanız bile roller yine de ilk sırada çalışır. Bu nedenle, bir şeyin bir rolden önce gerçekleşmesi gerekiyorsa, bu tasks: dosyasının en üstünde değil, pre_tasks: içinde yer almalıdır.
- 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: postBir rolü roles: anahtarı yerine bir görev listesinin içinden çağırmak için import_role veya include_role kullanın.
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 statiktir. Ansible rolü ayrıştırma (parse) zamanında okur ve görevleri play'in bir parçası haline gelir; bu nedenle ansible-playbook --list-tasks site.yml bunları listeler ve import işlemine eklenen bir etiket, içerideki her göreve uygulanır. include_role dinamiktir. Görev çalışana kadar hiçbir şey okunmaz; bu da rol adını bir değişken veya döngü ile belirlemenize olanak tanır. Bunun bedeli, söz konusu görevlerin --list-tasks ve --start-at-task tarafından görülememesidir.
Burada bir tuzak mevcuttur. Bir include_role görevi üzerindeki when:, dahil edilen rolün defaults/main.yml kapsamı dahil edilmeden önce değerlendirilir. Include işlemi üzerine when: common_packages | length > 0 yazarsanız, söz konusu değişken dahil ettiğiniz rolün içinde tanımlanmış olsa bile çalışma 'common_packages' is undefined hatasıyla durur. Çözüm, bu anahtarı rolün dışına taşımaktır: değişkeni, her yerde kapsam dahilinde olan group_vars/all.yml içine yerleştirin ve rolün kendi varsayılanlarını, rolün bizzat tükettiği değerler için bırakın.
Hangi değişken önceliklidir: defaults, group_vars, vars, extra vars
Ansible, yirmiden fazla değişken öncelik seviyesi tanımlar. Bunlardan dördü, pratikteki hemen hemen tüm durumları belirler; en zayıftan en güçlüye doğru sıralaması şöyledir:
roles/<name>/defaults/main.ymlen alt seviyelerde yer alır. Başka herhangi bir yerde tanımladığınız hemen hemen her şey bunun önüne geçer; bu nedenle bir rolün ayarlanabilir parametreleri için en doğru yer burasıdır.group_vars/vehost_vars/orta seviyededir. Sitenize özgü değerler burada yer almalı ve bunlar rol varsayılanlarını temiz bir şekilde geçersiz kılmalıdır.roles/<name>/vars/main.yml,host_varsüzerinde yer alır. Burada tanımladığınız bir değer, envanter üzerinden geçersiz kılınamaz. Burayı, bir servis ismiyle eşleşmesi gereken paket ismi gibi, rolün kendi içinde tutarlı kalması gereken öğeler için ayırın.- Çağrı noktasında iletilen bir rol parametresi
vars/main.ymldeğerini yener; komut satırındaki-eise rol parametreleri dahil her şeyin önüne geçer.
Bu çözümleme sürecini yaklaşık bir dakikada gözlemleyebilirsiniz. Küçük bir role bir varsayılan ve bir rol değişkeni atayın, ardından aynı isimleri host_vars içinde tanımlayın.
# 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İlk çalıştırma tunable=from-hostvars internal=from-rolevars değerini yazdırır. Envanter, rol varsayılanını yener ancak rol değişkenine karşı kaybeder. İkinci çalıştırma internal=from-cli değerini yazdırır; çünkü extra vars en üst seviyededir ve altındaki hiçbir şey buna engel olamaz. -e kullanımının tek seferlik çalıştırmalar için uygun, ancak kalıcı bir betik için yanlış olmasının nedeni budur: depodaki tüm planlı kararları sessizce geçersiz kılar.
Çalışma kuralı şudur: Bir değerin değiştirilebilir olmasını istiyorsanız, onu defaults/ içine yerleştirin. Değeri vars/ içine koymak, rolün gelecekteki tüm kullanıcılarına envanterin bunu değiştiremeyeceğini bildirir. Bu bazen amaçlanan bir durum olsa da, genellikle bir hatadır.
Rolün idempotent olduğunu kanıtlayın: iki kez çalıştırın
Güvenilir bir Ansible çalışması, ikinci kez çalıştırıldığında aynı sonucu üretmeli ve hiçbir değişiklik yapmadığını raporlamalıdır. Playbook'u iki kez çalıştırın ve özet kısmını inceleyin.
ansible-playbook -i inventory.ini site.yml
ansible-playbook -i inventory.ini site.ymlİkinci özet şu şekilde görünmelidir:
PLAY RECAP *********************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0changed=0, her modülün mevcut durumu incelediği ve işin zaten yapılmış olduğunu tespit ettiği anlamına gelir. İkinci çalıştırmada changed=2 görülmesi, iki görevin mevcut durum ile hedef durum arasındaki farkı ayırt edemediği anlamına gelir; bu durumda dosyalar sürekli yeniden yazılır ve servisler sürekli yeniden başlatılır. Bunun yaygın nedeni command veya shell kullanımıdır, çünkü Ansible rastgele bir komutun ne yaptığını bilemez.
# 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.txtPlaybook'u iki kez çalıştırın ve ardından wc -l /tmp/grow.txt /tmp/guarded.txt içeren satırları sayın. /tmp/grow.txt iki satır, /tmp/guarded.txt ise bir satır içerir. İkinci çalıştırmada korumalı görev hiç çalışmaz ve sonucu skipped, since /tmp/guarded.txt exists mesajını taşır; çünkü creates, modüle ilk olarak bakması gereken görünür bir ürün sunar. Bir komut böyle bir ürün bırakmadığında, çıktısını kaydedin ve changed_when ile kararı kendiniz verin.
ansible-playbook --check --diff site.yml, değişiklikleri yapmadan önce tahmin eder ve --diff, bir şablonun yeniden yazacağı tam satırları yazdırır. Çıktıyı okurken şu uyarıyı dikkate alın: shell ve command görevleri check mode içerisinde atlanır, bu nedenle temiz görünen bir plan yine de arka planda işlem yapabilir.
Ansible neden rolün bulunamadığını söylüyor
Ansible, roles/ dizinini playbook dosyasının hemen yanında, ardından roles_path içinde arar. Arama işlemi shell dizininize göre değil, playbook dosyasının konumuna göre yapılır.
ERROR! the role 'common' was not found in /home/deploy/lonely/roles:/home/deploy/lonelyBu mesaj, site.yml ve roles/ dosyalarının birbirinden koptuğu anlamına gelir; Ansible denediği yolları çıktı olarak vererek yardımcı olur. İki dosyayı aynı dizinde tutun. Bir üst dizinden çalıştırmak sorun yaratmaz, çünkü esas olan playbook yoludur:
ansible-playbook -i infra/inventory.ini infra/site.ymlAynı sorunun daha sessiz bir versiyonu daha vardır. Ansible, mevcut dizin herkes tarafından yazılabilir (world writable) olduğunda, o dizindeki ansible.cfg dosyasını görmezden gelir. Bunun nedeni, sunucudaki herhangi bir kullanıcının oraya bir yapılandırma dosyası bırakıp çalıştırdığınız komutun işleyişini değiştirebilme riskidir.
[WARNING]: Ansible is being run in a world writable directory (/tmp/infra), ignoring it as an ansible.cfg source.Bu durumda roles_path ve inventory ayarlarınız sessizce devre dışı kalır ve rol arama işlemi, rollerle hiçbir ilgisi olmayan bir nedenden dolayı başarısız olur. ansible --version komutu fiilen yüklenen config file dosyasını, ansible-config dump --only-changed komutu ise varsayılan ayarlardan farklı olan tüm ayarları listeler. Bir çalıştırma, yapılandırmanız yokmuş gibi davranıyorsa her iki komutu da kontrol edin.
Rolleri paylaşma: requirements.yml ve sabitlenmiş sürüm
Başkasının yazdığı bir rol kopyalanmaz, kurulur. Bunu bir kez tanımlayın:
# 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_rolesHer zaman version ayarını yapın. Bu ayar olmadan, komutu çalıştırdığınız gün varsayılan dalda ne varsa onu alırsınız; bu da geçen ay çalışan bir dağıtımın, kendi deponuzda hiçbir değişiklik yapmasanız bile bozulmasına neden olur. roles_path değerini indirme dizinine yönlendirin ve bu dizini git dışında tutun:
# ansible.cfg
[defaults]
inventory = inventory.ini
roles_path = ./galaxy_rolesPlaybook'un yanındaki roles/ dizininde bulunan roller yine de bulunur, çünkü bu yol roles_path dizinine ek olarak her zaman aranır. Böylece kendi rolleriniz commit edilip gözden geçirilebilir durumda kalırken, üçüncü taraf roller bir etikete sabitlenmiş, yeniden üretilebilir indirmeler olarak yönetilir.
Rollerin yetersiz kaldığı durumlar
Rol, bir Ansible çalışması içindeki yeniden kullanılabilirlik birimidir. Sağlayıcınız üzerinde sunucu veya DNS kaydı oluşturmaz; bunu yapmaya çalışmak, playbook'ların kimsenin bakımını yapmak istemediği bir yapıya dönüşmesine neden olur. Başlamadan önce Ansible ve Terraform arasındaki iş bölümü konusunu okumak faydalıdır. Bir rol, envanter tasarımının yerini de tutmaz: birkaç makineyi geçtikten sonra, sunucuları nasıl gruplandırdığınız ve onlara nasıl eriştiğiniz, görevlerin nasıl dosyalandığından daha önemli hale gelir.
Bu common rolünün uyguladığı sıkılaştırma işlemleri de kendi içinde özel kararlar gerektirir. Yukarıdaki ek yapılandırma sadece iki direktif belirler ve fazlasını yapmaz; bu nedenle, sahip olduğunuz her sunucu için role nelerin dahil edilmesi gerektiğine karar vermeden önce hangi SSH ayarlarının değiştirilmeye değer olduğu ve Ubuntu'nun güvenlik güncellemelerini nasıl otomatik uygulayacağı konularını inceleyin.
FAQ
Bir Ansible playbook'unu ne zaman role yapısına dönüştürmeliyim?
Aynı görev bloğunun ikinci bir play içerisinde veya ikinci bir sunucu grubuna karşı çalıştırılması gerektiğinde. Görevleri playbook'lar arasında kopyalamak bir uyarı işaretidir; çünkü o andan itibaren her düzeltmenin iki kez uygulanması gerekir ve bir gün mutlaka sadece birine uygulanacaktır. Yaklaşık 100 satırın altında olan ve sadece tek bir grubu hedefleyen bir playbook, role yapısından bir kazanç sağlamaz; aksine fazladan dizinler okunabilirliği zorlaştırır.
Role'ler aynı play içindeki görevlerden önce mi çalışır?
Evet. Ansible önce pre_tasks, ardından roles: altında listelenen her şeyi, sonra tasks: ve en son post_tasks: kısmını çalıştırır; bu anahtarların dosyanızdaki sıralamasını dikkate almaz. tasks: kısmını roles: üzerinde yazmak, o görevlerin önce çalışmasını sağlamaz. Bir işlemin role'den önce gerçekleşmesi gerekiyorsa, bunu pre_tasks: içerisine yerleştirin.
Neden group_vars değerim role içindeki değeri geçersiz kılmıyor?
Değişkenin role'ün defaults/main.yml dosyası yerine vars/main.yml içerisinde tanımlanıp tanımlanmadığını kontrol edin. vars/, Ansible'ın öncelik sıralamasında group_vars ve host_vars üzerinde yer alır; bu nedenle envanter dosyası bunu geçersiz kılamaz. Değişkeni, öncelik sıralamasının en alt kısımlarında yer alan ve çağıran tarafın değiştirebilmesi gereken her şey için doğru yer olan defaults/main.yml içerisine taşıyın. Sorunun bir yazım hatası değil de öncelik sıralaması olduğunu doğrulamak için, diğer tüm kaynaklardan daha üstün olan -e name=value ile bir kez çalıştırın.
Ansible neden role'ün bulunamadığını söylüyor?
Arama işlemi playbook dosyasının yanında başlar; bu nedenle site.yml ve roles/ aynı dizinde bulunmalıdır. Hata mesajı, the role 'common' was not found in /home/deploy/lonely/roles:/home/deploy/lonely örneğinde olduğu gibi denediği yolları yazdırır. Playbook'u bir üst dizinden çalıştırmak sorun yaratmaz, çünkü arama işlemi shell'in çalışma dizinini değil, playbook'un yolunu takip eder. Eğer ansible.cfg içerisindeki roles_path dosyasına bağımlıysanız, dosyanın ansible --version ile yüklendiğini doğrulayın; çünkü herkesin yazabileceği (world writable) bir çalışma dizini, Ansible'ın bu dosyayı görmezden gelmesine neden olur.
Bir role oluşturmak için ansible-galaxy init kullanmam gerekiyor mu?
Hayır. Bir role sadece beklenen isimlere sahip dizinlerden ibarettir; dolayısıyla mkdir -p roles/common/tasks ve bir tasks/main.yml dosyası zaten çalışan bir role oluşturur. ansible-galaxy init --init-path roles common, yazım yükünü azaltır ve meta/main.yml ile bir README taslağı da dahil olmak üzere tam bir iskelet sunar. Boş bıraktığınız dizinleri silin; çünkü boş bir vars/main.yml, role içerisindeki hangi dosyaların gerçekten bir işlev gördüğünü gizler.