Ansible playbook’larınızı yazdıkça kaçınılmaz olarak şu soruyla karşılaşırsınız: “Bu görev her sunucuda çalışmalı mı, yoksa sadece belirli koşullarda mı?” İşte tam bu noktada when direktifi devreye giriyor. Basit görünen bu tek kelime, Ansible’ı gerçek anlamda güçlü kılan özelliklerden biri. Bugün when ile akış kontrolünü, gerçek dünya senaryolarıyla birlikte derinlemesine inceleyeceğiz.
when Direktifi Nedir ve Neden Kullanırız?
Ansible varsayılan olarak bir play’deki tüm görevleri, tanımlı tüm host’larda sırasıyla çalıştırır. Ama gerçek dünya bu kadar düz değil. Elinde hem Ubuntu hem CentOS sunucuları var, hem production hem de staging ortamları mevcut, bazı sunucularda belirli servisler kurulu iken diğerlerinde yok. Bu kaotik gerçekliği yönetmek için koşullu mantığa ihtiyaç duyarsınız.
when direktifi, bir görevin çalışıp çalışmayacağını belirleyen bir Jinja2 ifadesidir. Koşul doğru (true) olduğunda görev çalışır, yanlış (false) olduğunda görev atlanır ve Ansible bunu playbook çıktısında skipping olarak gösterir.
Temel sözdizimi son derece basit:
- name: Sadece Debian sistemlerde Apache kur
apt:
name: apache2
state: present
when: ansible_facts['os_family'] == "Debian"
Burada dikkat edilmesi gereken önemli bir nokta var: when ifadesinde değişkenleri çift süslü parantez {{ }} içine almıyoruz. Çünkü when zaten bir Jinja2 ifadesi olarak değerlendiriliyor, ekstra paranteze gerek yok.
Ansible Facts ile Koşullu Kontrol
En yaygın kullanım senaryosu, sistem bilgilerini (facts) kullanarak platform bazlı koşullar oluşturmak. setup modülünden gelen bu bilgiler altın değerinde.
İşletim Sistemi Bazlı Koşullar
---
- name: Web sunucu kurulumu
hosts: webservers
become: yes
tasks:
- name: Apache kurulumu - Debian/Ubuntu
apt:
name: apache2
state: present
update_cache: yes
when: ansible_facts['os_family'] == "Debian"
- name: Apache kurulumu - RedHat/CentOS
yum:
name: httpd
state: present
when: ansible_facts['os_family'] == "RedHat"
- name: Apache servisini başlat - Debian
service:
name: apache2
state: started
enabled: yes
when: ansible_facts['os_family'] == "Debian"
- name: Apache servisini başlat - RedHat
service:
name: httpd
state: started
enabled: yes
when: ansible_facts['os_family'] == "RedHat"
Bu klasik bir senaryo ama biraz daha akıllıca yapabilirsiniz. Değişkenlerle birleştirerek kodu sadeleştirelim:
---
- name: Web sunucu kurulumu - gelişmiş versiyon
hosts: webservers
become: yes
vars:
apache_package:
Debian: apache2
RedHat: httpd
apache_service:
Debian: apache2
RedHat: httpd
tasks:
- name: Apache paketini kur
package:
name: "{{ apache_package[ansible_facts['os_family']] }}"
state: present
when: ansible_facts['os_family'] in ['Debian', 'RedHat']
- name: Apache servisini başlat
service:
name: "{{ apache_service[ansible_facts['os_family']] }}"
state: started
enabled: yes
when: ansible_facts['os_family'] in ['Debian', 'RedHat']
Kernel Versiyonu ve Donanım Koşulları
Bazen belirli bir kernel versiyonu üzerinde çalışmanız gerekebilir. Örneğin belirli bir güvenlik yaması sadece 5.x kernel’lerde geçerli:
- name: Yeni kernel özelliğini etkinleştir
sysctl:
name: net.core.default_qdisc
value: fq
state: present
when: ansible_kernel.split('.')[0] | int >= 5
- name: Yüksek RAM için büyük sayfa boyutu ayarla
sysctl:
name: vm.nr_hugepages
value: "{{ (ansible_memtotal_mb / 2048) | int }}"
state: present
when: ansible_memtotal_mb > 16384
Değişken Tabanlı Koşullar
Facts dışında, kendi tanımladığınız değişkenleri de koşul olarak kullanabilirsiniz. Ortam yönetimi için bu çok işe yarıyor.
---
- name: Ortam bazlı uygulama dağıtımı
hosts: all
vars:
environment: production
debug_mode: false
backup_enabled: true
tasks:
- name: Debug loglarını etkinleştir
lineinfile:
path: /etc/myapp/config.ini
line: "debug = true"
state: present
when: debug_mode | bool
- name: Yedekleme scriptini çalıştır
script: /scripts/backup.sh
when:
- backup_enabled | bool
- environment == "production"
- name: Performans ayarlarını uygula
template:
src: prod_performance.conf.j2
dest: /etc/myapp/performance.conf
when: environment in ['production', 'staging']
- name: Test verisi yükle
command: /usr/local/bin/load_test_data.sh
when: environment == "development"
Inventory Değişkenleriyle Koşullar
Gerçek dünyada host’larınızı gruplarken inventory’de değişkenler tanımlarsınız. Bu değişkenlerle güçlü koşullar kurabilirsiniz:
# inventory/hosts.yml
all:
children:
webservers:
hosts:
web01:
ansible_host: 192.168.1.10
role: primary
ssl_enabled: true
web02:
ansible_host: 192.168.1.11
role: secondary
ssl_enabled: false
dbservers:
hosts:
db01:
ansible_host: 192.168.1.20
db_role: master
db02:
ansible_host: 192.168.1.21
db_role: slave
---
- name: Web sunucu SSL yapılandırması
hosts: webservers
become: yes
tasks:
- name: SSL sertifikası kopyala
copy:
src: ssl/cert.pem
dest: /etc/ssl/certs/myapp.pem
when: ssl_enabled | default(false) | bool
- name: SSL konfigürasyonunu etkinleştir
template:
src: apache-ssl.conf.j2
dest: /etc/apache2/sites-enabled/ssl.conf
when:
- ssl_enabled | default(false) | bool
- ansible_facts['os_family'] == "Debian"
- name: Primary sunucu özel ayarları
template:
src: primary-config.j2
dest: /etc/myapp/primary.conf
when: role is defined and role == "primary"
Çoklu Koşullar: AND ve OR Mantığı
Birden fazla koşulu birleştirmeniz gerektiğinde iki yöntem var.
AND Koşulları (liste formatı)
Liste formatında yazdığınızda tüm koşullar AND ile birleştirilir. Hepsi doğru olmak zorunda:
- name: Production veritabanı yedeklemesi
command: /usr/local/bin/db_backup.sh
when:
- environment == "production"
- ansible_facts['os_family'] == "RedHat"
- db_role == "master"
- backup_hour == ansible_date_time.hour
OR Koşulları
OR mantığı için or anahtar kelimesini kullanırsınız:
- name: Firewall servisini yeniden başlat
service:
name: firewalld
state: restarted
when: ansible_facts['distribution'] == "CentOS" or ansible_facts['distribution'] == "Rocky"
# Daha temiz yazımı:
- name: Firewall servisini yeniden başlat
service:
name: firewalld
state: restarted
when: ansible_facts['distribution'] in ["CentOS", "Rocky", "AlmaLinux"]
Karma Koşullar
AND ve OR’u birleştirdiğinizde parantez kullanımı kritik önem kazanıyor:
- name: Özel monitoring kurulumu
include_tasks: monitoring_setup.yml
when: >
(environment == "production" and ansible_memtotal_mb > 8192)
or
(environment == "staging" and monitoring_forced | default(false) | bool)
> karakteri YAML’da çok satırlı string için kullanılıyor. Bu sayede uzun koşulları okunabilir şekilde yazabiliyorsunuz.
register ve when Kombinasyonu
En güçlü kullanım senaryolarından biri, bir görevin çıktısını register ile saklayıp sonraki görevde when ile kontrol etmek. Bu pattern gerçekten çok işe yarıyor.
---
- name: Servis durumuna göre işlem yap
hosts: webservers
become: yes
tasks:
- name: Apache servis durumunu kontrol et
command: systemctl is-active apache2
register: apache_status
ignore_errors: yes
changed_when: false
- name: Apache çalışmıyorsa yeniden başlat
service:
name: apache2
state: started
when: apache_status.rc != 0
- name: Apache loglarını temizle (sadece servis çalışıyorsa)
file:
path: /var/log/apache2/access.log
state: absent
when: apache_status.rc == 0
- name: Disk kullanımını kontrol et
command: df -h /var/log
register: disk_usage
changed_when: false
- name: Log rotasyonu zorla (disk %80 doluysa)
command: logrotate -f /etc/logrotate.conf
when: "'8' in disk_usage.stdout or '9' in disk_usage.stdout"
Daha gerçekçi bir senaryo: Bir paket kurulu mu değil mi kontrol edip buna göre işlem yapmak:
---
- name: Node.js uygulama dağıtımı
hosts: appservers
become: yes
tasks:
- name: Node.js kurulu mu kontrol et
command: node --version
register: node_check
ignore_errors: yes
changed_when: false
- name: Node.js kurulu değilse kur
apt:
name: nodejs
state: present
update_cache: yes
when:
- node_check.rc != 0
- ansible_facts['os_family'] == "Debian"
- name: Mevcut Node versiyonunu göster
debug:
msg: "Node.js versiyonu: {{ node_check.stdout }}"
when: node_check.rc == 0
- name: PM2 global olarak kurulu mu kontrol et
command: pm2 --version
register: pm2_check
ignore_errors: yes
changed_when: false
- name: PM2 kur
npm:
name: pm2
global: yes
state: present
when: pm2_check.rc != 0
- name: Uygulamayı PM2 ile başlat
command: pm2 start /opt/myapp/app.js --name myapp
when:
- pm2_check.rc == 0 or pm2_check.rc != 0
- node_check.rc == 0
Döngülerle Koşullu Görevler
loop ile when birlikte kullanıldığında dikkatli olmak lazım. when koşulu her döngü iterasyonu için ayrı ayrı değerlendirilir:
---
- name: Seçici paket kurulumu
hosts: all
become: yes
vars:
packages:
- name: nginx
install_on: webservers
required: true
- name: postgresql
install_on: dbservers
required: true
- name: redis
install_on: webservers
required: false
- name: htop
install_on: all
required: true
tasks:
- name: Paketleri koşullu olarak kur
package:
name: "{{ item.name }}"
state: present
loop: "{{ packages }}"
when:
- item.required | bool
- item.install_on == "all" or item.install_on == group_names[0]
Handler’larla Koşullu Tetikleme
Handler’lar zaten değişiklik durumunda çalışır ama ekstra koşullar ekleyebilirsiniz:
---
- name: Nginx yapılandırma yönetimi
hosts: webservers
become: yes
handlers:
- name: nginx yeniden başlat
service:
name: nginx
state: restarted
when: nginx_restart_enabled | default(true) | bool
- name: nginx konfigürasyonu doğrula
command: nginx -t
register: nginx_test
changed_when: false
tasks:
- name: Nginx ana konfigürasyonunu dağıt
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
validate: nginx -t -c %s
notify:
- nginx konfigürasyonu doğrula
- nginx yeniden başlat
when: ansible_facts['os_family'] in ['Debian', 'RedHat']
- name: Sanal host konfigürasyonunu dağıt
template:
src: vhost.conf.j2
dest: "/etc/nginx/sites-enabled/{{ domain_name }}.conf"
notify: nginx yeniden başlat
when:
- domain_name is defined
- ssl_certificate_exists | default(false) | bool
Gerçek Dünya Senaryosu: Çok Katmanlı Deployment Playbook
Şimdiye kadar öğrendiklerimizi birleştiren kapsamlı bir örnek:
---
- name: Uygulama deployment - tam senaryo
hosts: all
become: yes
vars:
app_version: "2.1.0"
deploy_environment: "{{ env | default('staging') }}"
rollback_enabled: false
health_check_url: "http://localhost:8080/health"
pre_tasks:
- name: Deployment öncesi disk alanı kontrolü
command: df -BG /opt
register: disk_info
changed_when: false
- name: Yetersiz disk alanı uyarısı
fail:
msg: "Yetersiz disk alanı! En az 5GB gerekli."
when: disk_info.stdout | regex_search('([0-9]+)G', '\1') | first | int < 5
tasks:
- name: Mevcut uygulama versiyonunu kontrol et
command: cat /opt/myapp/VERSION
register: current_version
ignore_errors: yes
changed_when: false
- name: Versiyon zaten güncel, atla
debug:
msg: "Uygulama zaten {{ app_version }} versiyonunda çalışıyor."
when:
- current_version.rc == 0
- current_version.stdout == app_version
- name: Eski versiyonu yedekle
archive:
path: /opt/myapp
dest: "/opt/backups/myapp-{{ current_version.stdout }}-{{ ansible_date_time.date }}.tar.gz"
when:
- current_version.rc == 0
- current_version.stdout != app_version
- deploy_environment == "production"
- name: Yeni versiyonu indir
get_url:
url: "https://releases.example.com/myapp-{{ app_version }}.tar.gz"
dest: /tmp/myapp-new.tar.gz
when:
- current_version.rc != 0 or current_version.stdout != app_version
- name: Uygulamayı dağıt
unarchive:
src: /tmp/myapp-new.tar.gz
dest: /opt/myapp
remote_src: yes
when:
- current_version.rc != 0 or current_version.stdout != app_version
- name: Production konfigürasyonu uygula
template:
src: "config-{{ deploy_environment }}.j2"
dest: /opt/myapp/config/app.conf
when: deploy_environment in ['production', 'staging']
- name: Uygulamayı yeniden başlat
service:
name: myapp
state: restarted
when:
- current_version.rc != 0 or current_version.stdout != app_version
- name: Health check yap
uri:
url: "{{ health_check_url }}"
method: GET
status_code: 200
register: health_check
retries: 5
delay: 10
until: health_check.status == 200
when: deploy_environment == "production"
- name: Deployment başarısız, rollback yap
command: /opt/scripts/rollback.sh
when:
- health_check is defined
- health_check.status != 200
- rollback_enabled | bool
Sık Yapılan Hatalar ve İpuçları
Birkaç kritik noktayı paylaşmak istiyorum:
- Tanımsız değişken hatası: Bir değişkenin tanımlı olup olmadığından emin değilseniz
is definedkontrolü yapın.when: myvar is defined and myvar == "value"şeklinde.
- Boolean karmaşası: YAML’da
true,True,yes,onhepsi boolean true ama Ansible’a string olarak geçebilir. Güvenli taraf için| boolfiltresini kullanın.
- Facts toplamayı kapatmayın:
gather_facts: nokullanıyorsanızansible_factserişemezsiniz. Bunu bilmedenwhen: ansible_facts['os_family'] == "Debian"yazarsanız hata alırsınız.
- Koşul her zaman string değil:
when: "{{ myvar }}"yerinewhen: myvaryazın. String interpolasyon bazen beklenmedik davranışlara yol açar.
- register sonuçlarını debug edin: Beklenmeyen davranış görüyorsanız
debug: var=kayit_degiskeniile sonucu inceleyin.
# Değişken tanımlı mı kontrolü
- name: İsteğe bağlı konfigürasyon
template:
src: optional.conf.j2
dest: /etc/myapp/optional.conf
when:
- optional_feature is defined
- optional_feature | bool
# Versiyon karşılaştırması
- name: Yeni özelliği etkinleştir (versiyon 2.0+)
command: myapp --enable-new-feature
when: app_version is version('2.0', '>=')
Sonuç
when direktifi, Ansible playbook’larınızı gerçek dünya karmaşıklığına uyum sağlayabilen esnek araçlara dönüştürüyor. Başlangıçta basit platform kontrollerinden başlayıp, register ile dinamik koşullara, çoklu mantık operatörlerine ve kapsamlı deployment senaryolarına kadar uzanan geniş bir kullanım alanı var.
En önemli tavsiyem: Koşullarınızı mümkün olduğunca basit tutun. Karmaşık when ifadeleri genellikle playbook yapınızın yeniden düzenlenmesi gerektiğinin işareti. Roles ve include_tasks kullanarak koşullu mantığı daha yönetilebilir parçalara bölmek uzun vadede çok daha iyi sonuç veriyor.
when ile akış kontrolüne hakim olduğunuzda, Ansible’ın gerçek gücünü keşfetmiş oluyorsunuz. Artık tek bir playbook ile onlarca farklı ortamı, işletim sistemini ve yapılandırma senaryosunu yönetebilir hale geliyorsunuz. Bu da sysadmin olarak en değerli varlığınızı koruyorsunuz: zamanınızı.