Ansible Role Yapısı: Yeniden Kullanılabilir Görev Grupları

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.yml giriş noktasıdır.
  • handlers/: Servis yeniden başlatma gibi tetikleyici görevler.
  • templates/: Jinja2 şablon dosyaları (.j2 uzantı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. when koş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 when alı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: -e flag 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.

Yorum yapın