Ansible check mode ve --diff ile dry run kullanımı
Ansible check mode ve --diff parametrelerinin çalışma mantığını öğrenin. Hangi modüllerin desteklendiğini ve dry run sırasında neden yanlış sonuç alınabileceğini inceleyin.
Ansible check mode ne işe yarar
Ansible check mode bir "dry run" (deneme çalıştırması) işlemidir: ansible-playbook --check, play içerisindeki her bir sunucuya bağlanır, her modüle mevcut durumun talep edilen durumla eşleşip eşleşmediğini sorar ve herhangi bir değişiklik yapmadan nelerin değişeceğini raporlar. --diff eklendiğinde, üzerinde işlem yapılacak dosyaların önceki ve sonraki içeriklerini de görüntüler. Bu iki özellik birlikte, her gerçek çalıştırmadan önce sorulması gereken şu soruyu yanıtlar: bu sunucularda nelerin değişmesi bekleniyor?
Check mode, playbook'unuzun bir simülasyonu değildir. Sunucunun herhangi bir yerde bir modeli tutulmaz. Her modülden sadece yazmak yerine bakması istenir. Salt okunur yanıt verebilen bir modül changed raporlar ve bir sonraki adıma geçer. Yanıt veremeyen bir modül ise hiçbir şey yapmaz ve hiçbir şey raporlamaz. Ansible dokümantasyonu bunu tek bir cümlede özetler: "Check mode desteği olmayan modüller hiçbir şey raporlamaz ve hiçbir şey yapmaz." Dry run işleminin yanlış sonuç verebileceği durumlar bu boşluktan kaynaklanır; bu rehberin büyük bir kısmı da bu boşlukla ilgilidir.
Deneme çalıştırmasını gerçekleştirin: --check ve --diff
ansible-playbook -i inventory.ini site.yml --check --diff --limit web1-C ve -D, iki bayrağın kısa biçimleridir. --limit kullanımı bilinçlidir. Tek bir ana bilgisayarın diff çıktısı okunabilir bir şeydir. Yirmi ana bilgisayarın diff çıktısı ise hızlıca geçip gideceğiniz bir metin yığınıdır.
Dört sonuç kelimesi tüm raporu özetler.
ok: [web1], görevin çalıştığını ve herhangi bir değişiklik bildirmediğini ifade eder.aptveyatemplategibi durumu yöneten bir modül için bu, modülün kontrol ettiği ve durumun zaten eşleştiği anlamına gelir.debugveyaset_factiçin ise sadece görevin çalıştığı anlamına gelir.changed: [web1], modülün bir şey yazmış olacağını öngördüğü anlamına gelir.--diffile birlikte, üzerindeki satırlar nelerin değişeceğini gösterir. Çoğu modül doğru tahmin yürütür, ancak kısmi check mode desteğine sahip birkaç modül kendi belgelerinde aksi yönde uyarı verir:get_url, check mode içinde yalnızca bir HEAD isteği gönderir ve yanlış bir changed durumu bildirebileceğini belirtir.skipping: [web1], görevin çalışmadığı anlamına gelir. Yawhendeğerlendirilmiş ve false sonucu çıkmıştır ya da modül check mode içinde çalışamıyordur.fatal: [web1], görevin kontrol sırasında başarısız olduğu anlamına gelir. Playbook'un bozuk olduğunu varsaymadan önce hata mesajını okuyun.
--diff, dosya modülleri için birleşik bir diff çıktısı yazdırır; kaldırılan satırlar -, eklenen satırlar ise + ile işaretlenir. Bu çıktı, satırları --- before ve +++ after ile başlayan ve hedef yolu belirten bir başlık altında sunulur. Dosya yazmayan modüller kendi öncesi ve sonrası durumlarını yazdırır; bu nedenle ansible.builtin.user, dosya içeriği yerine değiştireceği öznitelikleri gösterir.
Bayrağı unutmamak için diff özelliğini ansible.cfg içinde kalıcı olarak açın:
[diff]
always = true
context = 5Check mode öncesinde iki daha düşük maliyetli kontrol uygulanabilir. ansible-playbook site.yml --syntax-check, herhangi bir ana bilgisayarla bağlantı kurmadan YAML dosyasını ve play yapısını ayrıştırır. ansible-playbook site.yml --list-tasks, çalışacak görevleri listeler; etiketlendiğini düşündüğünüz bir rolün neden çalışmadığını bu şekilde tespit edebilirsiniz. Her iki komut da bağlantı kurmadığı için anında sonuç verir.
Check mode ise bağlantı kurar. Belirtilen düzendeki her ana bilgisayara SSH üzerinden bağlanır ve fact toplar; bu nedenle kapalı olan bir ana bilgisayar deneme çalıştırmasının başarısız olmasına neden olur. Bu başlı başına yararlı bir sinyaldir ve aynı zamanda erişilemeyen ana bilgisayarlar için playbook'un ne yapacağına karar vermenin bir deneme çalıştırmasını CI sürecine dahil etmeden önce neden önemli olduğunun nedenidir.
Neden check modu yeni bir sunucuda başarısız olur
Bu playbook doğrudur. Bunu, üzerinde henüz nginx bulunmayan bir sunucuya karşı --check ile çalıştırdığınızda, işlemlerin çoğu başarısız olur.
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
- name: Write the site config
ansible.builtin.template:
src: site.conf.j2
dest: /etc/nginx/conf.d/site.conf
- name: Start and enable nginx
ansible.builtin.service:
name: nginx
state: started
enabled: trueapt görevi changed rapor eder ve bu doğrudur: paket mevcut değildir, dolayısıyla gerçek bir çalıştırma onu kuracaktır. Check modu onu kurmamıştır. Ardından template görevi başarısız olur, çünkü bu ana bilgisayarda /etc/nginx/conf.d/ mevcut değildir ve onu hiçbir şey oluşturmamıştır. service görevi de başarısız olur, çünkü sorgulayabileceği bir nginx birimi yoktur. Hataların hiçbiri playbook içindeki bir hata değildir. Kuru çalıştırma (dry run), ihtiyaç duyduğu durumdan yoksun kalmıştır; dokümantasyonun, check modunun girdisi önceki bir görevin değişikliğine bağlı olan bir görev için yararlı çıktı üretemeyeceği konusunda uyardığı durum budur.
Kuralın dürüst versiyonu şudur: check modu, playbook'un daha önce yakınsadığı (converged) bir ana bilgisayara karşı en doğru sonucu verir, yeni bir ana bilgisayarda ise gürültülüdür. Her görevin ok rapor ettiği bir --check çalıştırması, yakınsamış bir ana bilgisayar hakkında gerçek bir beyandır, çünkü hiçbir şeyin değişmeyeceği anlamına gelir; ancak her ana bilgisayar için geçerli olan iki istisna vardır: atlanan bir command görevi bir cevap değil boş bir alandır ve get_url gibi kısmi check modu desteğine sahip bir modül, changed durumunu hala yanlış rapor edebilir. Yepyeni bir ana bilgisayarda --check size çoğunlukla ana bilgisayarın yeni olduğunu söyler. İlk Ansible playbook'unuzu bir VPS üzerinde yazarken, ilk kuru çalıştırmanın kırmızı bir duvar gibi görünmesini bekleyin ve playbook'u ikincisine göre değerlendirin.
Komut ve kabuk görevlerinin kontrol modunda atlanmasının nedenleri
ansible.builtin.command ve ansible.builtin.shell, çalıştırdığınız komutun ne işe yaradığını bilmez. Rastgele bir ikili dosyayı salt okunur bir şekilde çalıştırmanın yolu yoktur; bu nedenle kontrol modunda modül, komutu çalıştırmayı reddeder. Görev sonucu skipped: true değerini ve Command would have run if not in check mode mesajını taşır; çıktılarınızda ise skipping: [web1] ifadesi görünür.
Modül belgeleri, kontrol modu desteğini "kısmi" olarak tanımlar ve önerilen geçici çözüm creates ve removes kullanımıdır. Göreve bir creates yolu tanımladığınızda, kontrol modu en azından dosya testini değerlendirebilir:
- name: Extract the release bundle
ansible.builtin.command: /usr/bin/tar xf /tmp/app.tar.gz -C /opt/app
args:
creates: /opt/app/bin/appEğer /opt/app/bin/app zaten mevcutsa, kontrol modu gerçek bir yanıt olan Would not run command since '/opt/app/bin/app' exists sonucunu bildirir. Yol eksikse, yine gerçek bir yanıt olan Command would have run if not in check mode sonucunu alırsınız. creates olmadan, ilgili görev kuru çalışma (dry run) sırasında boş bir alan olarak kalır.
Bunun zincirleme etkisi, boş bir alandan daha kötüdür. Atlanan bir command görevi yine de bir sonuç kaydeder ve bu sonuç stdout anahtarını içerir; ancak hiçbir şey çalışmadığı için bu değer boş bir dizgidir. Bir sonraki görevin koşulu bu boş dizgiye göre değerlendirilir ve sessizce yanlış dala sapar: bir sürüm kontrolü sürüm bulamaz ve 'active' in svc.stdout gibi bir test yanlış (false) sonuç verir. Playbook'unuz gerçek bir çalıştırmada farklı, kuru çalıştırmada farklı bir yanıt verir ve ortada işaret edebileceğiniz bir hata olmadığı için bu, tüm özelliğin en kafa karıştırıcı başarısızlığıdır. 'dict object' has no attribute 'stdout' hatası, farklı bir atlama türüne aittir: when değeri yanlış (false) olduğu için atlanan bir görev, hiçbir stdout anahtarı içermeyen bir sonuç kaydeder.
check_mode: false ve ait olduğu tek yer
Bir görev üzerindeki check_mode: false, "bunu --check altında bile olsa gerçekten çalıştır" anlamına gelir. Bu, atlanan komut sorununa yönelik çözümdür ve yalnızca okuma yapan görevlerde güvenlidir.
- name: Read the installed app version
ansible.builtin.command: /usr/local/bin/app --version
register: app_version
check_mode: false
changed_when: falseBu görev her iki modda da dürüsttür. Bir sürüm bilgisini okur ve asla yazma yapmaz; changed_when: false, yapmadığı bir değişikliği raporlamasını engeller ve check_mode: false, app_version.stdout değişkeninin kuru çalışma (dry run) sırasında boş bir dizge yerine gerçek sürümü tutmasını sağlar; böylece buna dayalı koşullar doğru şekilde değerlendirilir.
Anahtar kelimeyi başka bir yere yapıştırmadan önce kelime anlamını dikkate alın. check_mode: false içeren bir görev, ansible-playbook --check sırasında sunucularınıza yazma işlemi yapar. Bunu bir apt veya template görevi üzerine koyarsanız kuru çalışma daha düzenli görünür ancak kuru çalışmanız artık bir kuru çalışma olmaktan çıkar. Bir yazma görevi güvenli hale getirilemediğinde, onu şu şekilde korumaya alın:
- name: Apply the database migration
ansible.builtin.command: /usr/local/bin/app migrate --apply
when: not ansible_check_modeansible_check_mode, Ansible'ın bir kontrol çalışması sırasında true değerine ayarladığı sihirli bir değişkendir. Bunun tersi olan anahtar kelime de mevcuttur. check_mode: true, bir görevi gerçek bir çalışma sırasında bile her zaman kontrol moduna sabitler; bu da onu bir sapma (drift) probuna dönüştürür: sonucu kaydedin; changed raporu, sunucunun artık görevin talep ettiği durumla eşleşmediği anlamına gelir.
Bir görevin her çalıştırılmada neden değiştiği raporu
Playbook'u araya hiçbir işlem girmeden arka arkaya iki kez çalıştırın. İkinci çalıştırmada her görev ok rapor etmelidir. Hâlâ changed rapor eden herhangi bir görev size iki şeyden birini anlatıyordur: modül yönettiği durumu göremiyordur ya da sağladığınız girdi kararlı değildir. Her ikisi de düzeltilebilir ve hiçbiri susturulması gereken bir gürültü değildir.
creates,removesveyachanged_wheniçermeyencommandveshell, her seferindechangedrapor eder; çünkü modül bir şeyin gerçekleşip gerçekleşmediğini bilme yoluna sahip değildir.createsekleyin veya çıktıdaki bir dizgeye karşıchanged_whenayarlayın.state: touchiçerenansible.builtin.file, tasarım gereği her çalıştırmadachangedrapor eder; çünkü bir dosyaya dokunmak zaman damgalarını günceller. Eğer tek istediğiniz sahibi veya modu ayarlamaksastate: filekullanın.- Oluşturulan çıktısı değişen bir
template, dosyayı her çalıştırmada yeniden yazar.ansible_date_time'den gelen bir zaman damgası,now()çağrısı veya her seferinde yeni üretilen bir parola, farklı baytlar üretir; bu yüzden modül doğru bir şekilde değişiklik rapor eder. Değişen değeri şablondan çıkarın. password: "{{ pw | password_hash('sha512') }}"içerenansible.builtin.userher çalıştırmada değişir; çünküpassword_hashher çağrıldığında rastgele bir salt seçer, bu yüzden sonuçta ortaya çıkan hash değeri/etc/shadowiçinde zaten bulunanla asla eşleşmez. Kararlı bir veriden türetilen açık bir salt değeri geçirin.- Bir paket modülündeki
state: latest, bir yükseltme mevcut olduğundachangedrapor eder. Bu dürüst bir davranıştır.state: latest'nın sonucunu tahmin edemeyeceğiniz bir playbook vermesinin nedeni de budur.state: presentkullanın ve yükseltmeyi bilinçli olarak yapın. createsiçermeyen bir URL'ye yönlendirilenansible.builtin.unarchive, her çalıştırmada arşivi yeniden indirir ve karşılaştırma yapamadığı arşiv türleri için dosyaları yeniden açar. Ona bircreatesyolu verin.
--diff, bunları birbirinden ayırmanın en hızlı yoludur. Eğer bir görev changed diyorsa ve fark (diff) farklı baytlar gösteriyorsa, girdiniz kararsızdır. Eğer changed diyorsa ve fark hiçbir şey göstermiyorsa, modül neyi değiştirdiğini ifade edemiyordur; bu genellikle bir command görevi veya zaman damgası gibi sadece meta veri yazan bir işlem anlamına gelir.
Gürültülü bir görevi susturmak için changed_when: false kullanmayın. Bu, raporu bastırır; dolayısıyla notify asla tetiklenmez ve servisi yeniden başlatan işleyici (handler) asla çalışmaz. Bunun yerine görevi düzeltin.
Etki alanını daraltma: --limit, --tags ve --step
Check mode, nelerin değişeceğini size bildirir. Bu bayraklar, kaç makinenin aynı anda işlem göreceğine karar verir.
--limit, çalışmayı envanterin bir alt kümesiyle sınırlar. hosts: ile aynı desenleri kabul eder, bu nedenle hem --limit web1 hem de --limit 'webservers:!web3' çalışır. Deseni tırnak içine alın. Etkileşimli bir bash oturumunda tırnak içine alınmamış bir !, ünlem işareti üzerinde geçmiş genişletmesini tetikler ve kabuğunuz, komutu Ansible görmeden önce yeniden yazar.
Desene güvenmeden önce doğrulayın. ansible-playbook site.yml --limit 'webservers:!web3' --list-hosts, eşleşen sunucuları yazdırır ve hiçbirine bağlanmadan çıkar. Hiçbir şeyle eşleşmeyen bir desen güvenlidir çünkü Ansible tüm envantere geri dönmez. Sunucu deseniyle eşleşemediğine dair bir uyarı yazdırır ve ardından envanterin, sunucu deseninin ve --limit değerinin hedeflenecek hiçbir sunucu bırakmadığını belirten bir hatayla çıkar. Envanter dosyasının bu grupları nasıl tanımladığını bilmek, bir desenin en başta öngörülebilir olmasını sağlayan şeydir.
--tags deploy yalnızca etiketli görevleri çalıştırır, --skip-tags packages ise diğer her şeyi çalıştırır. --list-tags, mevcut olanları listeler. Etiketler, bir oyunun tamamını çalıştırmak istemeyeceğiniz bir noktaya geldiğinde işe yarar; bu durum aynı zamanda uzun bir playbook'u rollere bölmenin nedenlerinden biridir.
--start-at-task "Write the site config", başarısız olan bir çalışmayı adlandırılmış bir görevden itibaren devam ettirir. Kurtarma için kullanın ve maliyetini anlayın: o görevden önceki her şey atlanır; buna fact'leri ayarlayan veya sonraki görevlerin okuduğu değişkenleri kaydeden görevler de dahildir.
--step, her görevden önce onay ister ve evet, hayır veya devam et yanıtınızı bekler. Yavaştır ancak yıkıcı bir işlemi ilk kez çalıştırırken doğru araçtır; çünkü yirmi görevden sonra değil, iki görev arasında durabilirsiniz.
Değişikliği serial ile kademeli olarak dağıtın
Varsayılan olarak Ansible, bir play içindeki her görevi, bir sonraki göreve geçmeden önce tüm sunucular üzerinde çalıştırır. Bu yöntem hızlıdır ancak hatalı bir görev, tüm filoyu aynı anda etkiler. Hatayı fark edip Ctrl-C tuşlarına basana kadar değişiklik çoktan her yere yayılmış olur.
serial, play'i gruplara (batch) böler. Tüm play önce ilk grup üzerinde, ardından bir sonraki grup üzerinde çalıştırılır.
- name: Roll out the web tier
hosts: webservers
serial: [1, 5, "30%"]
max_fail_percentage: 0
tasks:
- name: Deploy the release
ansible.builtin.include_role:
name: webappİlk grup tek bir sunucudan oluşur. Eğer bu sunucu sorunsuz çalışırsa, ikinci grup beş sunucudan oluşur ve sonraki her grup, play kapsamındaki sunucuların yüzde 30'unu içerir. max_fail_percentage: 0, bir gruptaki herhangi bir sunucu başarısız olduğunda play'in geri kalanını durdurur; çünkü hata eşiğinin aşılması gerekir ve herhangi bir hata, sıfır eşiğini aşmış sayılır. Tek makine garantisi bu ayardan değil, ilk gruptan gelir: her yerde hata verecek bir sürüm, ilk gruptaki tek sunucuda başarısız olur ve süreç orada durur. Beş sunuculuk grupta ortaya çıkan bir hata ise o gruptaki diğer sunuculara zaten ulaşmış olur; çünkü Ansible, bir sonraki göreve geçmeden önce her görevi tüm grup üzerinde çalıştırır. any_errors_fatal: true, ilk sunucu hatasında play'i herkes için sonlandıran daha sert bir yöntemdir.
Önce tek bir sunucu üzerinde çalıştırmak paranoya değildir; bunun belirli bir nedeni vardır. Envanter grupları zamanla sapma gösterir. Diğerlerinden altı ay sonra eklenen bir sunucu, farklı bir dağıtım sürümü çalıştırıyor olabilir, elle kurulmuş bir servis barındırıyor olabilir veya disk yapılandırması farklı olabilir. Playbook grup için doğru olsa bile o sunucu için yanlış olabilir ve konfigüre edilmiş bir sunucu üzerinde yapılan "dry run" bunu göstermeyecektir. Linux sunucu filosunu yönetmek, büyük ölçüde değişikliğin kendisi fark etmeden önce uyumsuz sunucuyu bulma pratiğidir.
Çalıştırma sırası
ansible-playbook site.yml --syntax-check, herhangi bir ağ bağlantısı olmadan YAML ve yapı hatalarını yakalar.ansible-playbook site.yml --limit web1 --list-hosts, deseninizin düşündüğünüz şeyle eşleştiğini kanıtlar.ansible-playbook site.yml --limit web1 --check --diffbir deneme çalıştırmasıdır. Farkları (diff) inceleyin.ansible-playbook site.yml --limit web1 --diff, yapılandırmayı ilgili tek bir sunucuya uygular.- 4. adımı tekrar çalıştırın. Her şey
okrapor etmelidir. Hâlâchangedolan herhangi bir görev, tüm sunucu filosuna dokunmadan önce düzeltilmelidir. ansible-playbook site.yml --check --diffartık tüm envanter genelinde anlamlı bir yanıt döndürür; çünkü yapılandırması tamamlanan sunucular sessizdir ve geriye kalanlar gerçek farkı (delta) oluşturur.
- adım hakkında bir uyarı:
--diff, dosya içeriklerini terminalinize ve CI iş günlüğünüze yazdırır; bu nedenle bir veritabanı parolasını işleyen şablon, o parolayı günlüğe açık metin olarak yansıtır. Çıktıyı gizlemek için ilgili görevdediff: falseayarını yapın veya sonucun tamamını gizlemek içinno_log: truekullanın. Değerin kendisini ise depoda tutmak yerine şifrelenmiş bir Ansible Vault dosyası içinde saklayın.
FAQ
ansible-playbook --check sunucuda herhangi bir değişiklik yapar mı?
Hayır, kontrolünüzde olan bir istisna dışında. Check modunda her modülden yazmak yerine rapor vermesi istenir; bunu yapamayan modüller ise hiçbir şey rapor etmez ve hiçbir işlem yapmaz. İstisna, ilgili görevin --check çalışması sırasında dahi gerçek anlamda yürütülmesini zorunlu kılan check_mode: false görev anahtar kelimesidir. Bir dry run işlemine güvenmeden önce playbook ve rollerinizde check_mode: false araması yapın ve her eşleşmenin yalnızca durumu okuyan bir görev olduğunu doğrulayın.
--check ve --diff arasındaki fark nedir?
--check, herhangi bir işlemin gerçek anlamda çalışıp çalışmayacağına karar verir. --diff, ne kadar ayrıntı göreceğinize karar verir. --check tek başına size bir dosyanın değişeceğini söyler. --diff tek başına değişikliği uygular ve değişen satırları gösterir. Okunabilir bir dry run için bunları birlikte kullanın ve ansible.cfg içindeki [diff] altında always = true ayarını yaparak gerçek çalıştırmalar için de --diff seçeneğini açık bırakın.
Ansible görevim neden her çalıştırmada changed rapor ediyor?
Çünkü modül yönettiği durumu göremiyor veya ona verdiğiniz değer her seferinde farklılık gösteriyor. command ve shell, creates veya changed_when eklemediğiniz sürece her zaman changed rapor eder. file, state: touch ile tasarımı gereği değişir. Zaman damgası veya yeni oluşturulmuş bir parola içeren bir şablon, her çalıştırmada farklı baytlar üretir; bu nedenle dosya gerçekten yeniden yazılır. Playbook'u arka arkaya iki kez çalıştırın: ikinci geçişte hala changed olan görev, düzeltilmesi gereken görevdir.
command ve shell görevlerim neden dry run sırasında atlanıyor?
Çünkü rastgele bir komutu salt okunur şekilde çalıştırmanın bir yolu yoktur. Check modunda command modülü, Command would have run if not in check mode mesajıyla skipped: true değerini ayarlar. Check modunun dosya testini değerlendirebilmesi için creates veya removes ekleyin. Yalnızca durumu okuyan bir görev için check_mode: false değerini changed_when: false ile birlikte ayarlayın; böylece kayıtlı sonuç dry run sırasında gerçek çıktıyı tutar ve buna dayalı koşullar çalışmaya devam eder.
Check modu neden yeni bir sunucuda başarısız oluyor ancak mevcut olanda başarılı oluyor?
Çünkü check modu, sonraki görevlerin bağlı olduğu durumu oluşturmaz. Nginx olmayan bir ana bilgisayara karşı yapılan dry run, kurulumu changed olarak rapor eder, ardından /etc/nginx/conf.d/ içine yazan görevde başarısız olur; çünkü bu dizin hiçbir zaman oluşturulmamıştır. Bu beklenen bir davranıştır. Check modu, playbook'un halihazırda yakınsadığı ana bilgisayarlar için bir sapma dedektörüdür. İlk çalıştırmayı doğrulayamaz. Yeni bir ana bilgisayarda, playbook'u bir makineye uygulayın ve ikinci çalıştırmayı okuyun.