Ansible ile bir süredir çalışıyorsanız, büyük ihtimalle şu noktaya geldiniz: playbook’larınız büyüdü, görevler tekrar etmeye başladı ve bir konfigürasyonu üç farklı yerde güncellemeniz gerekiyor. İşte bu noktada role yapısı devreye giriyor. Ansible rollerini anlamak, otomasyon işinizi gerçek anlamda ölçeklendirmenin kapısını açıyor.
Role Nedir ve Neden Gereklidir?
Ansible role’ü, birbiriyle ilişkili görevleri, değişkenleri, şablonları ve handler’ları tek bir yeniden kullanılabilir birim altında toplayan bir yapıdır. Düz bir playbook yazdığınızda her şey tek dosyada birikir. Zamanla bu dosya yüzlerce satıra ulaşır, neyin ne yaptığını anlamak zorlaşır ve başka projelerde aynı mantığı tekrar yazmak zorunda kalırsınız.
Bunu somutlaştıralım: Diyelim ki hem web sunucularınızı hem de veritabanı sunucularınızı Ansible ile yönetiyorsunuz. Her iki sunucu tipinde de ortak güvenlik ayarları yapmanız gerekiyor: SSH hardening, firewall kuralları, NTP konfigürasyonu. Bu mantığı iki ayrı playbook’a kopyalamak yerine security adında bir rol oluşturup her iki playbook’ta da kullanabilirsiniz. Birinde değişiklik yaptığınızda diğeri otomatik olarak güncellenir.
Role Dizin Yapısı
Ansible role’leri belirli bir dizin standardını takip eder. Bu standart sayesinde Ansible, rolü otomatik olarak tanır ve doğru dosyaları yükler.
roles/
webserver/
tasks/
main.yml
handlers/
main.yml
templates/
nginx.conf.j2
files/
index.html
vars/
main.yml
defaults/
main.yml
meta/
main.yml
README.md
Bu yapıyı elle oluşturabileceğiniz gibi ansible-galaxy komutuyla da otomatik oluşturabilirsiniz:
# Tek rol oluşturma
ansible-galaxy init roles/webserver
# Birden fazla rol aynı anda
ansible-galaxy init roles/webserver roles/database roles/security
# Mevcut dizinde oluşturma
cd roles && ansible-galaxy init monitoring
Her dizinin belirli bir sorumluluğu var:
- tasks/: Rolün ana görevleri burada.
main.ymlgiriş noktasıdır. - handlers/: Servis yeniden başlatma gibi tetikleyici görevler.
- templates/: Jinja2 şablon dosyaları (
.j2uzantılı). - files/: Statik dosyalar, script’ler, sertifikalar.
- vars/: Yüksek öncelikli değişkenler, genellikle değiştirilmesi istenmeyen.
- defaults/: Düşük öncelikli varsayılan değişkenler, kolayca override edilebilir.
- meta/: Rol bağımlılıkları ve metadata.
İlk Rolünüzü Yazmak: Nginx Webserver
Gerçek dünyadan bir örnek üzerinden gidelim. Production ortamında Nginx kuran, yapılandıran ve yöneten bir rol oluşturalım.
roles/webserver/tasks/main.yml:
---
- name: Gerekli paketleri kur
ansible.builtin.package:
name:
- nginx
- curl
- logrotate
state: present
tags:
- install
- packages
- name: Nginx yapılandırmasını dağıt
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
backup: yes
notify: reload nginx
tags:
- configure
- name: Site yapılandırmasını oluştur
ansible.builtin.template:
src: vhost.conf.j2
dest: "/etc/nginx/conf.d/{{ item.domain }}.conf"
owner: root
group: root
mode: '0644'
loop: "{{ nginx_vhosts }}"
notify: reload nginx
tags:
- configure
- vhosts
- name: Nginx servisini başlat ve enable et
ansible.builtin.service:
name: nginx
state: started
enabled: yes
tags:
- service
- name: Web root dizinlerini oluştur
ansible.builtin.file:
path: "{{ item.root }}"
state: directory
owner: "{{ nginx_user }}"
group: "{{ nginx_group }}"
mode: '0755'
loop: "{{ nginx_vhosts }}"
when: item.root is defined
roles/webserver/handlers/main.yml:
---
- name: reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
- name: restart nginx
ansible.builtin.service:
name: nginx
state: restarted
- name: test nginx config
ansible.builtin.command: nginx -t
changed_when: false
roles/webserver/defaults/main.yml:
---
nginx_user: www-data
nginx_group: www-data
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65
nginx_client_max_body_size: "10m"
nginx_server_tokens: "off"
nginx_vhosts:
- domain: example.com
root: /var/www/example.com
index: index.html
Görevleri Dosyalara Bölmek
Büyük rollerde tasks/main.yml dosyası şişer. Bu dosyayı mantıksal parçalara bölebilirsiniz:
tasks/
main.yml
install.yml
configure.yml
ssl.yml
security.yml
roles/webserver/tasks/main.yml:
---
- name: Paket kurulum görevlerini içe aktar
ansible.builtin.import_tasks: install.yml
- name: Yapılandırma görevlerini içe aktar
ansible.builtin.import_tasks: configure.yml
- name: SSL yapılandırması
ansible.builtin.import_tasks: ssl.yml
when: nginx_ssl_enabled | bool
- name: Güvenlik hardening
ansible.builtin.include_tasks: security.yml
when: nginx_security_hardening | bool
import_tasks ve include_tasks arasındaki fark önemlidir:
- import_tasks: Statik içe aktarma, parse zamanında çalışır.
whenkoşulları tüm görevlere uygulanır. - include_tasks: Dinamik içe aktarma, çalışma zamanında değerlendirilir. Döngü içinde kullanılabilir, her görev bağımsız
whenalır.
Role Bağımlılıkları ve meta/main.yml
Gerçek dünyada roller birbirine bağımlı olabilir. Örneğin application rolünüz çalışmadan önce webserver ve database rollerinin kurulu olması gerekebilir.
roles/application/meta/main.yml:
---
galaxy_info:
author: "Ahmet Yilmaz"
description: "Python Flask uygulama rolü"
company: "Sirket AS"
license: "MIT"
min_ansible_version: "2.10"
platforms:
- name: Ubuntu
versions:
- focal
- jammy
- name: EL
versions:
- "8"
- "9"
dependencies:
- role: webserver
vars:
nginx_server_tokens: "off"
- role: database
vars:
db_name: "app_production"
db_user: "appuser"
- role: security
Bu yapıyla application rolünü çağırdığınızda Ansible otomatik olarak önce bağımlı rolleri çalıştırır.
Rolleri Playbook’ta Kullanmak
Rol oluşturdunuz, şimdi onu playbook’a ekleyin. Birkaç farklı yöntem var:
---
# Klasik yöntem: roles anahtarı
- name: Web sunucularını yapılandır
hosts: webservers
become: yes
roles:
- common
- webserver
- monitoring
# Değişken override ile
- name: Production web sunucuları
hosts: prod_webservers
become: yes
roles:
- role: webserver
vars:
nginx_worker_processes: 4
nginx_worker_connections: 4096
nginx_vhosts:
- domain: production.sirket.com
root: /var/www/production
- role: monitoring
vars:
monitoring_interval: 30
# Görev içinde rol çağırma (ansible 2.4+)
- name: Dinamik rol kullanımı
hosts: all
become: yes
tasks:
- name: Ortak güvenlik rolünü uygula
ansible.builtin.include_role:
name: security
vars:
security_ssh_port: 2222
- name: Ortama göre rol seç
ansible.builtin.include_role:
name: "{{ app_type }}_server"
when: app_type is defined
Gerçek Dünya Senaryosu: Çok Katmanlı Uygulama
Diyelim ki bir e-ticaret platformu kuruyorsunuz. Load balancer, web sunucuları, uygulama sunucuları ve veritabanı katmanları var. Role yapısı bunu nasıl organize eder?
roles/
common/ # Tüm sunucularda ortak ayarlar
security/ # SSH hardening, firewall, fail2ban
monitoring/ # Node exporter, telegraf, log agent
nginx_lb/ # Load balancer spesifik Nginx ayarları
nginx_web/ # Web katmanı Nginx ayarları
app_python/ # Python uygulama sunucusu
postgresql/ # Veritabanı sunucusu
redis/ # Cache katmanı
site.yml – Ana playbook:
---
- name: Tüm sunuculara ortak ayarları uygula
hosts: all
become: yes
roles:
- common
- security
- monitoring
- name: Load balancer yapılandır
hosts: loadbalancers
become: yes
roles:
- role: nginx_lb
vars:
lb_algorithm: least_conn
lb_backends: "{{ groups['webservers'] }}"
- name: Web katmanını yapılandır
hosts: webservers
become: yes
roles:
- nginx_web
- app_python
- name: Veritabanı katmanını yapılandır
hosts: databases
become: yes
roles:
- postgresql
- role: monitoring
vars:
monitoring_extra_plugins:
- postgres_exporter
- name: Cache katmanını yapılandır
hosts: cache
become: yes
roles:
- redis
Bu yapı sayesinde 50 sunucuyu tek komutla yapılandırabilirsiniz:
ansible-playbook -i inventory/production site.yml
Sadece belirli bir rolü çalıştırmak istediğinizde tag kullanırsınız:
# Sadece güvenlik rolünü çalıştır
ansible-playbook -i inventory/production site.yml --tags security
# Belirli host grubunu atla
ansible-playbook -i inventory/production site.yml --limit webservers
# Dry run (değişiklik yapmadan kontrol)
ansible-playbook -i inventory/production site.yml --check --diff
Değişken Öncelik Hiyerarşisi
Role’lerde değişken yönetimi başlangıçta kafa karıştırabilir. Ansible’da değişken öncelikleri şu şekilde işler (düşükten yükseğe):
- role defaults:
roles/x/defaults/main.yml– En kolay override edilir - inventory vars: Envanter dosyasındaki değişkenler
- playbook vars:
vars:bölümündeki değişkenler - role vars:
roles/x/vars/main.yml– Zor override edilir - extra vars:
-eflag ile geçilen değişkenler – Her zaman kazanır
Pratik kural: Kullanıcının değiştirmesini istediğiniz değerleri defaults/main.yml‘a, değiştirilmemesi gereken kritik değerleri vars/main.yml‘a koyun.
Ansible Galaxy ile Topluluk Rolleri
Her şeyi sıfırdan yazmak zorunda değilsiniz. Ansible Galaxy’de binlerce hazır rol var. Ama production’da dikkatli kullanın.
# Rol ara
ansible-galaxy search nginx --author geerlingguy
# Rol yükle
ansible-galaxy install geerlingguy.nginx
# Belirli sürümü yükle
ansible-galaxy install geerlingguy.nginx,3.1.0
# requirements.yml ile toplu yükleme
ansible-galaxy install -r requirements.yml
requirements.yml – Proje bağımlılıklarını burada tutun:
---
roles:
- name: geerlingguy.nginx
version: "3.1.0"
- name: geerlingguy.postgresql
version: "3.3.2"
- src: https://github.com/sirket/custom-role.git
name: custom_security
version: main
collections:
- name: community.postgresql
version: "2.3.0"
- name: ansible.posix
version: "1.5.1"
Galaxy rollerini doğrudan production’a almak yerine önce test edin, sürümü kilitleyin ve fork alıp kendi repo’nuzda tutmayı düşünün. Bağımlılık güncellemelerini siz kontrol etmek istersiniz.
Role Test Etmek: Molecule
Yazdığınız rolle production’a gitmeden önce test etmek için Molecule kullanabilirsiniz. Molecule, role’ü Docker veya sanal makine üzerinde çalıştırır ve testlerinizi koşturur.
# Molecule kurulumu
pip install molecule molecule-docker
# Var olan role'e molecule ekle
cd roles/webserver
molecule init scenario
# Test koş
molecule test
# Sadece converge (role uygula, sil)
molecule converge
# Container içine gir ve debug et
molecule login
roles/webserver/molecule/default/converge.yml:
---
- name: Converge
hosts: all
become: yes
vars:
nginx_worker_processes: 2
nginx_vhosts:
- domain: test.local
root: /var/www/test
roles:
- role: webserver
post_tasks:
- name: Nginx process çalışıyor mu?
ansible.builtin.command: pgrep nginx
changed_when: false
register: nginx_process
- name: Port 80 dinleniyor mu?
ansible.builtin.wait_for:
port: 80
timeout: 10
Yaygın Hatalar ve Çözümleri
Circular dependency sorunu: A rolü B’ye, B rolü A’ya bağımlıysa Ansible çöker. Role tasarımında her rolün tek bir sorumluluğu olduğundan emin olun ve bağımlılık grafiğini çizin.
Değişken isim çakışması: Farklı rollerde aynı isimde değişken kullanmayın. Role adını prefix olarak ekleyin: nginx_port, postgresql_port gibi.
Handler’ların tetiklenmemesi: Handler’lar play sonunda çalışır. Bir task başarısız olursa handler çalışmaz. Kritik handler’ları meta: flush_handlers ile erken tetikleyebilirsiniz:
- name: Nginx yapılandırmasını güncelle
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: reload nginx
- name: Handler'ları hemen çalıştır
meta: flush_handlers
- name: Sitenin ayakta olduğunu kontrol et
uri:
url: http://localhost
status_code: 200
Template cache sorunu: Jinja2 template’lerinde değişken tanımsızsa playbook hata verir. Güvenli erişim için default filter kullanın:
worker_processes {{ nginx_worker_processes | default('auto') }};
Sonuç
Ansible role yapısı, otomasyon kodunuzu yazılım projesi gibi yönetmenizi sağlar. Düzgün tasarlanmış roller:
- Tekrar kullanılabilir: Bir kez yazıp onlarca projede kullanırsınız.
- Test edilebilir: Molecule ile her değişikliği izole ortamda doğrularsınız.
- Bakımı kolay: Değişiklik tek yerden yapılır, her yere yayılır.
- Ekip dostu: Standart yapı sayesinde yeni ekip üyeleri hızla adapte olur.
Başlangıç için bütün altyapıyı aynı anda role’e çevirmeye çalışmayın. En çok tekrar eden görevlerinizi belirleyin, onları role haline getirin ve kademeli olarak genişletin. İlk common rolünüzü oluşturduktan sonra bu yaklaşımın değerini hemen göreceksiniz. Birkaç ay sonra bakıyorsunuz, playbook’larınız okunaklı, test edilmiş ve production’a güvenle push edebileceğiniz bir hale gelmiş oluyor.