26 Temmuz 2026 Pazar

Ansible değişkenlerini doğru yerde tutmak

Ansible Değişkenlerini Doğru Yerde Tutmak

Ansible · Değişkenler

Ansible değişkenlerini doğru yerde tutmak

Ansible'da 22 farklı değişken katmanı var. Asıl soru “hangisi kazanır?” değil — belgelerde yazıyor. Asıl soru şu: bu değişkeni nereye koymalıyım?

Ansible öğrenirken herkesin karşılaştığı bir an vardır: bir değişkeni değiştirirsiniz ama hiçbir şey olmaz. Başka bir yerde tanımlı aynı isimli değişken sizinkini eziyordur. Bu noktada çoğu kişi öncelik tablosunu ezberlemeye çalışır. Oysa tabloyu ezberlemek semptomu tedavi eder; hastalığı değil.

Hastalık şu: değişkeni ne olduğuna değil, o an aklınıza gelen yere koymak. Bu yazıda gerçek bir projeden — RHEL 9 ve RHEL 10 için osbuild-composer ile imaj üreten bir rolden — örneklerle, bir değişkenin yerini nasıl belirleyeceğinizi anlatacağım. Kendi yaptığım hatayı da göstereceğim.

Önce kısa hatırlatma: öncelik sırası

Ansible değişkenleri en düşükten en yükseğe doğru şu sırayla uygular. Sondakiler öncekileri ezer:

#KatmanNe zaman kullanılır
2Rol defaults/Rolün makul varsayılanı
3Envanter dosyasındaki grup değişkenleriNadiren; dağınık olur
4Envanter group_vars/allTüm makinelere ortak makine bilgisi
5Playbook group_vars/allPlaybook'a özgü, tüm gruplara
6Envanter group_vars/*Ortama/gruba özgü makine bilgisi
7Playbook group_vars/*Playbook'un gruba özgü zorlaması
9Envanter host_vars/*Tek makineye özgü
11Facts ve set_factÇalışma anında keşfedilen
12Play vars:Playbook'un kendi bağlamı
15Rol vars/Rolün ezilmemesi gereken sabitleri
18include_varsKoşullu yükleme (ör. sürüme göre)
22-e extra varsTek çalıştırmaya özgü; her şeyi ezer

Dikkat edilecek iki nokta var. Birincisi: group_vars/ ve host_vars/ iki ayrı yerde bulunabilir — envanterin yanında ve playbook'un yanında. İkincisi: playbook'un yanındaki, envanterinkini ezer.

Bunu kanıtlayalım

İddiaya güvenmeyin, ölçün. Üç katmanda aynı değişkeni tanımlayıp tek tek kaldıralım:

$ tree
├── inventory/
│   ├── hosts.yml
│   └── group_vars/all.yml        → nereden: "2-INVENTORY group_vars/all"
├── group_vars/all.yml            → nereden: "3-PLAYBOOK group_vars/all"
├── roles/demo/defaults/main.yml  → nereden: "1-ROLE defaults"
└── pb.yml
$ ansible-playbook -i inventory/hosts.yml pb.yml
kazanan -> 3-PLAYBOOK group_vars/all

$ rm group_vars/all.yml && ansible-playbook ...
kazanan -> 2-INVENTORY group_vars/all

$ rm inventory/group_vars/all.yml && ansible-playbook ...
kazanan -> 1-ROLE defaults
Playbook group_vars > envanter group_vars > rol defaults. Beklendiği gibi.

Pratik ipucu. Bir değerin nereden geldiğini merak ettiğinizde tahmin etmeyin: ansible-inventory --graph --vars ve ansible-inventory --host <makine> size envanterden gelenlerin tamamını gösterir. Rol katmanları için de ansible-playbook --list-tasks ve bir debug görevi en hızlı yoldur.

Asıl soru: bu değişken neyi tarif ediyor?

Karar kuralı bu kadar basit. Değişkenin adına değil, anlamına bakın:

Değişken neyi tarif ediyor?YeriNeden
Makineyi / ortamı
“RHEL 9 sunucusu şudur”
Envanter group_vars/ Envanterle birlikte seyahat eder. Aynı envanteri kullanan her playbook aynı bilgiyi alır.
Rolün makul varsayılanını
“aksi söylenmedikçe böyle davran”
Rol defaults/ En düşük öncelik = her katmandan ezilebilir. Rol kendi kendine yeter hale gelir.
Playbook'un kendi bağlamını
“çıktıları buraya yaz”
Play vars: Hedef makineyle ilgisi yoktur; envantere konursa yanlış yere yayılır.
Bu çalıştırmaya özgü seçimi
“şimdi RHEL 10 üret”
-e Her şeyi ezer, kalıcı değildir. Tam da geçici seçim için.
Ezilmemesi gerekeni
iç sabitler, eşleme tabloları
Rol vars/ Yüksek önceliklidir; kullanıcının kazara ezmesini istemediğiniz şeyler.

Gerçek bir örnek: osbuild imaj üretim rolü

Bu rol, parametre alarak RHEL 9 veya RHEL 10 için ISO / qcow2 / vmdk imajları üretiyor:

ansible-playbook build-image.yml \
  -e rhel_version=10 -e image_template=devel \
  -e '{"image_types":["qcow2","vmdk"]}'

rhel_version doğrudan hedef grubu seçiyor: RHEL 9 imajı RHEL 9 sunucusunda üretilmeli, çünkü osbuild-composer kendi host sürümünü varsayılan distro olarak alır.

Envanterde ne var

# inventory/group_vars/osbuild_rhel10.yml
ansible_user: remzi
ansible_become: true
osbuild_expected_major: "10"
osbuild_extra_sources:
  - id: crb
    url: https://cdn.redhat.com/.../codeready-builder/os
    required_for_templates: ["devel"]

Bunların hepsi makineyi tarif ediyor. “RHEL 10 sunucusunda CRB deposu gerekir” bilgisi, hangi playbook'u çalıştırdığınızdan bağımsızdır. Yarın bir cleanup.yml yazarsanız o da aynı envanteri kullanıp aynı bilgiyi alır.

Rol varsayılanlarında ne var

# roles/osbuild_image/defaults/main.yml
run_boot_test: true
enable_kdump: true
kdump_crashkernel: "2G-64G:256M,64G-:512M"
min_free_gib: {minimal: 12, devel: 30}

lvm_volumes:
  - {name: rootlv,     mount: /,          size_gib: 30, label: root}
  - {name: varcrashlv, mount: /var/crash, size_gib: 2,  label: var_crash}
  # ... 11 mantıksal birim

Disk şeması burada olmalı, çünkü rolün ne ürettiğini tarif ediyor. Üstelik tek kaynak olması sayesinde hem customizations.disk (qcow2/vmdk) hem de kickstart logvol satırları (ISO) aynı listeden üretiliyor — ikisinin birbirinden ayrışması yapısal olarak imkânsız hale geliyor.

Playbook'ta ne var

# build-image.yml
  vars:
    image_fetch_dir: "{{ playbook_dir }}/imajlar"
    artifact_dir: "{{ playbook_dir }}/artifacts"

Bunlar kontrol makinesinin yolları. Hedef sunucuyla hiçbir ilgileri yok. Ayrıca playbook_dir ancak playbook bağlamında anlamlıdır — envantere koyarsanız, aynı envanter başka bir dizinden kullanıldığında yanlış çözülür.

Benim yaptığım hata

İlk versiyonda politika değişkenlerinin hepsini envanterin group_vars/all.yml dosyasına koymuştum:

# inventory/group_vars/all.yml   ← YANLIŞ
run_boot_test: true
enable_kdump: true
min_free_gib: {minimal: 12, devel: 30}
compose_timeout_min: 90
image_fetch_dir: "{{ playbook_dir }}/imajlar"

Çalışıyordu. Ama iki gerçek sakıncası vardı:

  1. Ezme zinciri yanlış yerden başlıyordu. enable_kdump rolün varsayılanı olmalıydı. Envantere konunca rol defaults'ı sessizce eziliyor ve rol tek başına anlamsız hale geliyor — birisi rolü başka bir projede kullanmak istese, hangi değişkenleri tanımlaması gerektiğini bilemez.
  2. İkinci bir ortam eklendiğinde kopyalama zorunluluğu. inventory/prod/ ve inventory/test/ açtığınızda, ortamla hiç ilgisi olmayan politika değişkenlerini de iki kez yazmanız gerekir. Kopya olan yerde er geç ikisi birbirinden ayrışır.

Düzeltilmiş hali:

DeğişkenÖnceSonra
ansible_user, osbuild_extra_sourcesenvanter group_varsaynı — doğruydu
enable_kdump, run_boot_test, min_free_gibenvanter group_vars/allrol defaults
image_fetch_dir, artifact_direnvanter group_vars/allplay vars:
rhel_version, image_types-eaynı — doğruydu

Envanterin group_vars/all.yml dosyası artık boş — ama silmedim. İleride gerçekten tüm makinelere ortak bir makine ayarı gerektiğinde (proxy, zaman sunucusu, ortak depo) yeri orasıdır. Dosyanın içine bunu bir yorum olarak yazdım; boş bir dosyadan çok, niyeti belgelenmiş bir dosya bırakmak daha iyidir.

Sürüme göre değişenler: include_vars

Bu projede öğrendiğim en değerli şeylerden biri, sürüm farklarını veri olarak tutmaktı. RHEL 10'da kdump, kexec-tools paketinden ayrılıp kdump-utils'e taşınmış. Bunu göreve gömmek yerine:

- name: "Sürüme özel değişkenleri yükle"
  ansible.builtin.include_vars:
    file: "rhel{{ rhel_version }}.yml"

Böylece vars/rhel9.yml ve vars/rhel10.yml sürüm farklarını taşır, görevler sürümden bağımsız kalır. include_vars'ın önceliği yüksektir (18. sıra) — bu tam olarak istediğimiz şey: sürüme özgü veri, genel varsayılanı ezmeli.

Daha da iyisi: bu projede kdump paketini sabit yazmak yerine çalışma anında sorduk — dnf repoquery --file /usr/lib/systemd/system/kdump.service. Sonuç: RHEL 9'da kexec-tools, RHEL 10'da kdump-utils bulundu, hiçbir yere sabit yazılmadan. Değişkene hiç gerek kalmadı. En iyi değişken, yazmak zorunda kalmadığınız değişkendir.

Kaçınılması gereken üç tuzak

1. set_fact tipi sessizce değiştirir

Bir JSON dosyasını okuyup metin araması yapmak istedim:

- set_fact:
    manifest_text: "{{ manifest_raw.content | b64decode }}"

- assert:
    that: "'org.osbuild.lvm2.create' in manifest_text"   # her zaman False!

İçerik JSON'a benzediği için Ansible onu otomatik olarak dict'e çevirdi. Dolayısıyla in operatörü metin değil anahtar araması yaptı. Aradığım şey dosyada vardı ama testi hiç geçmiyordu. Çözüm: metin araması yerine from_json ile ayrıştırıp yapısal denetim yapmak.

2. delegate_to bağlantı değişkenlerini değiştirir

- command: "{{ 'cp ...' if ansible_connection == 'local' else 'scp ...' }}"
  delegate_to: localhost      # ← ansible_connection artık 'local'!

Delegasyon yapılan görevde ansible_connection, delegasyon hedefinin bağlantı türüdür. Asıl hedefin bilgisini kullanacaksanız, onu delegasyondan önce bir set_fact ile saklayın. Bu hatayı fark etmem, aynı kodun bir sunucuda çalışıp diğerinde patlamasını görmemi gerektirdi.

3. -e geri alınamaz

Extra vars her şeyi ezer ve playbook içinden değiştirilemez. Bu yüzden -e'yi yalnızca o çalıştırmaya özgü seçimler için kullanın (rhel_version, image_types). Kalıcı politikayı -e ile taşımaya başlarsanız, her çalıştırmada uzun bir komut satırı ezberlemek zorunda kalırsınız — ve bir gün birini yazmayı unutursunuz.

Özet: karar ağacı

SoruCevap
Makineyi/ortamı mı tarif ediyor?Envanter group_vars/ · host_vars/
Rolün makul varsayılanı mı?Rol defaults/
Sürüme/platforma göre mi değişiyor?Rol vars/ + include_vars
Playbook'un kendi bağlamı mı?Play vars:
Bu çalıştırmaya mı özgü?-e
Kullanıcı ezerse bozulur mu?Rol vars/ (defaults değil)

Bu kuralı uyguladığınızda öncelik tablosunu ezberlemenize gerek kalmaz — çünkü çakışma çıkmaz. Tablo, ancak aynı değişkeni birden fazla katmanda tanımladığınızda gerekir; ve bunu yapıyorsanız muhtemelen değişkeni yanlış yere koymuşsunuzdur.

Bu yazıdaki örnekler, RHEL 9 ve RHEL 10 için osbuild-composer ile ISO / qcow2 / vmdk imajları üreten gerçek bir Ansible rolünden alınmıştır. Öncelik sırasının tamamı için: Ansible — Using Variables.

Ansible değişkenlerini doğru yerde tutmak

Ansible Değişkenlerini Doğru Yerde Tutmak Ansible · Değişkenler Ansible değişkenlerini doğru yerde tutmak Ansible'd...