Docker & DevOps

    Ansible Rol Yapısı ve Yeniden Kullanım

    Ansible rollerinin dizin düzeni, değişken öncelikleri ve tekrar kullanılabilir yapı kurma yöntemi.

    12 dk okuma Güncellendi: 25 Ağustos 2026

    Ansible ile ilk playbook'unuzu yazdıktan bir süre sonra hep aynı yere gelirsiniz: site.yml dosyası altı yüz satırı geçmiştir, aynı Nginx görevlerini ikinci bir projeye kopyalamışsınızdır ve iki kopya çoktan birbirinden ayrılmaya başlamıştır. Ansible rol yapısı tam olarak bu noktada devreye girer; görevleri, şablonları, dosyaları ve değişkenleri belirli bir dizin düzeninde paketleyip tek bir isimle çağrılabilir hâle getirir.

    Bu rehberde rolün ne olduğunu, standart dizin yapısındaki her klasörün ne işe yaradığını, sıfırdan bir rol yazmayı, defaults ile vars arasındaki kritik farkı ve değişken önceliklerini, rolleri playbook'ta çağırmanın üç ayrı yolunu, rol bağımlılıklarını ve Galaxy üzerinden paylaşımı ele alacağım. Sonunda da yeniden kullanılabilir bir rolü, yalnızca dizinlere bölünmüş bir playbook'tan ayıran tasarım kararlarına ve sık düşülen tuzaklara değineceğim.

    Rol Nedir ve Ne Zaman Role Geçmelisiniz#

    Bir rol, tek bir sorumluluğu yerine getiren Ansible bileşenidir: "bu sunucuya Nginx kur ve yapılandır", "bu sunucuyu güvenlik temeline oturt", "PostgreSQL kur". Teknik olarak rol, önceden belirlenmiş isimlere sahip alt dizinlerden oluşan bir klasördür. Ansible bu isimleri tanır ve içindeki main.yml dosyalarını kendiliğinden yükler; siz hiçbir yerde "şu dosyayı da oku" demezsiniz.

    Role geçme zamanının geldiğini üç işaretten anlarsınız. Birincisi, aynı görev bloğunu ikinci bir playbook'a kopyalamış olmanız. İkincisi, playbook dosyanızın tek ekrana sığmaması ve içinde arama yapmaya başlamanız. Üçüncüsü, aynı yapılandırmayı iki farklı ortamda (test ve üretim) küçük farklarla kullanmanız gerekmesi. Bu üçünden biri olduysa rol yazmanın maliyeti, yazmamanın maliyetinin altına inmiş demektir.

    Rolün getirdiği asıl kazanç dizin düzeni değil, arayüz kavramıdır. İyi yazılmış bir rolün defaults/main.yml dosyası, o rolün ayarlanabilir tüm parametrelerini listeler; rolü kullanan kişi rolün içine hiç bakmadan sadece bu değişkenleri ezerek davranışı değiştirir. Bu, tıpkı bir fonksiyonun imzası gibidir.

    YapıNe zaman uygunSınırı
    Tek playbookTek sunucu, tek seferlik işBüyüdükçe okunmaz olur
    include_tasks ile bölünmüş playbookOrta boy, tek projeŞablon ve değişkenler dağınık kalır
    RolTekrar kullanılan her yapılandırmaÖğrenme eşiği biraz yüksek
    CollectionRol + modül + eklenti paketi dağıtımıKüçük projeler için fazla ağır

    Standart Rol Dizin Yapısı#

    Ansible bir rolü çağırdığında sabit bir dizin listesine bakar. Hepsini oluşturmak zorunda değilsiniz; olmayan dizin sessizce atlanır. ansible-galaxy komutu iskeleti sizin için üretir:

    mkdir -p roles && cd roles
    ansible-galaxy init nginx
    # - Role nginx was created successfully
    
    tree nginx
    # nginx/
    # ├── defaults/main.yml
    # ├── files/
    # ├── handlers/main.yml
    # ├── meta/main.yml
    # ├── tasks/main.yml
    # ├── templates/
    # ├── tests/
    # └── vars/main.yml
    

    Her dizinin görevi net biçimde ayrılmıştır ve bu ayrımı bilmek rolün doğru yazılması için şarttır:

    DizinİçeriğiOtomatik yüklenir mi
    tasks/Rolün ana görev listesi (main.yml)Evet
    handlers/notify ile tetiklenen görevlerEvet
    defaults/En düşük öncelikli değişkenlerEvet
    vars/Yüksek öncelikli, ezilmemesi gereken değişkenlerEvet
    files/copy modülünün olduğu gibi kopyaladığı dosyalarYol otomatik çözülür
    templates/Jinja2 şablonları (.j2)Yol otomatik çözülür
    meta/Rol bağımlılıkları ve üst veriEvet
    library/Role özel Python modülleriEvet

    files/ ve templates/ dizinlerinin "yol otomatik çözülür" özelliği pratikte çok işe yarar: rol içindeki bir görevde src: site.conf.j2 yazdığınızda Ansible dosyayı önce roles/nginx/templates/ altında arar. Tam yol yazmanıza gerek kalmaz ve rolü başka bir projeye taşıdığınızda yollar bozulmaz.

    Sıfırdan Bir Rol Yazmak#

    Nginx rolünü gerçek içerikle dolduralım. Önce ayarlanabilir arayüzü, yani defaults/main.yml dosyasını yazın. Buraya rolün tüm parametreleri, makul varsayılan değerleriyle girer:

    # roles/nginx/defaults/main.yml
    ---
    nginx_paket_adi: nginx
    nginx_worker_processes: auto
    nginx_worker_connections: 1024
    nginx_client_max_body_size: 16m
    nginx_gzip: true
    
    # Rolü kullanan kişi bu listeyi ezerek kendi sitelerini tanımlar
    nginx_siteler:
      - ad: firmaniz.com
        kok: /var/www/firmaniz.com
        port: 80
    

    Ardından görevler. Görevleri okunabilir tutmak için alt dosyalara bölmek, rol büyüdükçe hayat kurtarır:

    # roles/nginx/tasks/main.yml
    ---
    - name: Dağıtıma özel değişkenleri yükle
      ansible.builtin.include_vars: "{{ ansible_facts['os_family'] }}.yml"
      failed_when: false
    
    - name: Nginx kurulumu
      ansible.builtin.include_tasks: kurulum.yml
      tags: [nginx, kurulum]
    
    - name: Site yapılandırmaları
      ansible.builtin.include_tasks: siteler.yml
      tags: [nginx, yapilandirma]
    
    # roles/nginx/tasks/kurulum.yml
    ---
    - name: Paketi kur
      ansible.builtin.package:
        name: "{{ nginx_paket_adi }}"
        state: present
    
    - name: Ana yapılandırmayı üret
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: "0644"
        validate: "nginx -t -c %s"
      notify: nginx yeniden yükle
    
    - name: Servisi etkinleştir
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true
    

    Buradaki validate parametresine dikkat edin: dosyayı hedefe yazmadan önce geçici bir kopyada nginx -t çalıştırır, söz dizimi hatalıysa dosyayı hiç yerleştirmez. Bu tek satır, bozuk bir şablonun sunucuyu yeniden başlatılamaz hâle getirmesini önler ve rol yazarken edinilecek en değerli alışkanlıklardan biridir.

    Siteleri döngüyle üretelim:

    # roles/nginx/tasks/siteler.yml
    ---
    - name: Site yapılandırma dosyalarını oluştur
      ansible.builtin.template:
        src: site.conf.j2
        dest: "/etc/nginx/conf.d/{{ item.ad }}.conf"
        mode: "0644"
      loop: "{{ nginx_siteler }}"
      loop_control:
        label: "{{ item.ad }}"
      notify: nginx yeniden yükle
    

    Ve handler:

    # roles/nginx/handlers/main.yml
    ---
    - name: nginx yeniden yükle
      ansible.builtin.service:
        name: nginx
        state: reloaded
    

    loop_control.label küçük ama önemli bir ayrıntıdır: onsuz çıktıda tüm sözlük yapısı satır satır basılır ve ekran okunmaz hâle gelir. Playbook temelleri konusunda tazelenmek isterseniz Ansible kurulumu ve ilk playbook yazısı görev, modül ve handler kavramlarını sıfırdan ele alıyor. Nginx yerine Apache tercih ediyorsanız Apache ve Nginx karşılaştırması yazısı seçimde yardımcı olur.

    defaults mı vars mı: Değişken Öncelikleri#

    Yeni başlayanların en çok karıştırdığı konu budur ve doğru anlaşılmadığında "değişkenim neden ezilmiyor" sorusuyla saatler kaybedilir. Ansible'da onlarca değişken kaynağı vardır ve hepsinin kesin bir öncelik sırası bulunur. Rol yazarken bilmeniz gereken kısmı şudur:

    KaynakÖncelikKullanım amacı
    roles/x/defaults/main.ymlEn düşükRolün ezilebilir varsayılanları
    group_vars/allDüşükTüm ortam için ortak ayar
    group_vars/<grup>OrtaGruba özel ayar
    host_vars/<host>Orta-yüksekTek sunucuya özel ayar
    Play içindeki vars:YüksekBu çalıştırmaya özel
    roles/x/vars/main.ymlYüksekRolün ezilmemesi gereken sabitleri
    -e komut satırı değişkeniEn yüksekAcil müdahale, tek seferlik

    Kural basit: rolün ayarlanabilir her parametresi defaults/ içinde olmalıdır. vars/ dizinini yalnızca kullanıcının değiştirmemesi gereken, dağıtıma bağlı sabitler için kullanın; örneğin paket adının Debian'da nginx, RedHat ailesinde de nginx ama servis dosyası yolunun farklı olması gibi.

    # roles/nginx/vars/Debian.yml
    nginx_conf_dizini: /etc/nginx/conf.d
    nginx_kullanici: www-data
    
    # roles/nginx/vars/RedHat.yml
    nginx_conf_dizini: /etc/nginx/conf.d
    nginx_kullanici: nginx
    

    Bir değişkenin gerçekte hangi değeri aldığını merak ettiğinizde tahmin yürütmeyin, sorun:

    # Tek bir hostta değişkenin son hâlini gör
    ansible web1 -m ansible.builtin.debug -a "var=nginx_worker_processes"
    
    # Bir hostun aldığı tüm değişkenleri listele
    ansible-inventory --host web1 -y
    

    Değişken adlarını rol adıyla öneklemek de yazılı olmayan ama çok değerli bir kuraldır: nginx_port yerine sadece port yazarsanız, aynı playbook'ta çalışan başka bir rolün port değişkeniyle çakışır ve teşhisi zor bir hata üretirsiniz. Ansible'da değişkenler global bir alan paylaşır; önek koymak tek korumanızdır.

    Rolleri Playbook'ta Çağırmanın Üç Yolu#

    Rolü kullanmanın üç ayrı yöntemi vardır ve aralarındaki fark yalnızca söz dizimi değildir.

    ---
    - name: Web sunucularını hazırla
      hosts: web
      become: true
    
      # 1) Klasik yöntem: statik, play başında çözülür
      roles:
        - common
        - role: nginx
          nginx_siteler:
            - { ad: firmaniz.com, kok: /var/www/firmaniz.com, port: 80 }
    
      tasks:
        # 2) import_role: statik, derleme anında çözülür
        - name: Sertifika rolünü içe aktar
          ansible.builtin.import_role:
            name: certbot
    
        # 3) include_role: dinamik, çalışma anında çözülür
        - name: Yalnızca veritabanı sunucularında
          ansible.builtin.include_role:
            name: postgresql
          when: "'db' in group_names"
    

    Aradaki fark şöyle özetlenir: roles: ve import_role statiktir, playbook derlenirken çözülür; bu yüzden --list-tasks çıktısında görünürler ve etiketler (tags) beklendiği gibi çalışır. include_role ise dinamiktir, sıra oraya geldiğinde çözülür; when koşulu, döngü ve çalışma anında hesaplanan değişkenlerle kullanılabilir ama çalıştırmadan önce görev listesinde görünmez.

    Pratik tavsiye: ne yapacağınızı önceden biliyorsanız statik kullanın, çünkü hata ayıklamak ve etiketlemek kolaydır. Karar çalışma anında veriye bağlıysa dinamik kullanın. roles: bloğundaki roller, tasks: bloğundan önce çalışır; bu sıralama sık atlanır ve "rol görevden sonra çalışsın istiyordum" şaşkınlığına yol açar. Öyle bir ihtiyacınız varsa post_tasks kullanın ya da rolü import_role ile görev sırasına yerleştirin.

    Bağımlılıklar, Galaxy ve Collection#

    Bir rol başka bir role ihtiyaç duyabilir. Örneğin wordpress rolü çalışmadan önce php ve mysql rollerinin çalışmış olması gerekir. Bunu meta/main.yml dosyasında ilan edersiniz:

    # roles/wordpress/meta/main.yml
    ---
    galaxy_info:
      role_name: wordpress
      author: firmaniz
      description: WordPress kurulumu ve yapılandırması
      license: MIT
      min_ansible_version: "2.14"
      platforms:
        - name: Debian
          versions: [bookworm]
        - name: EL
          versions: ["9"]
    
    dependencies:
      - role: php
        php_surum: "8.3"
      - role: mysql
    

    ⚠️ Bağımlılıklar varsayılan olarak yalnızca bir kez çalışır; aynı rol birden fazla yerden bağımlılık olarak çağrılsa bile tekrar edilmez. Bu davranışı değiştirmek isterseniz allow_duplicates: true kullanabilirsiniz, ama genellikle istemezsiniz. Ayrıca bağımlılık zinciri derinleştikçe rollerin hangi sırada çalıştığını izlemek zorlaşır; iki seviyeden fazla bağımlılıktan kaçının ve sıralamayı playbook'ta açıkça yazmayı tercih edin.

    Dışarıdan hazır rol veya koleksiyon kullanmak için requirements.yml dosyası yazıp topluca kurabilirsiniz:

    # requirements.yml
    ---
    collections:
      - name: community.general
        version: ">=8.0.0"
      - name: ansible.posix
    
    roles:
      - name: firmaniz.temel_guvenlik
        src: https://github.com/ornek/ansible-temel-guvenlik
        scm: git
        version: v1.4.0
    
    # Hepsini kur
    ansible-galaxy install -r requirements.yml
    
    # Kurulu rolleri listele
    ansible-galaxy role list
    
    # Yalnızca koleksiyonları belirli bir dizine kur
    ansible-galaxy collection install -r requirements.yml -p ./collections
    

    Kendi rollerinizi Git deposunda tutup sürüm etiketiyle sabitlemek en sağlıklı yöntemdir; version: v1.4.0 yazdığınızda rolün ana dalındaki bir değişiklik altınızdan zemini çekmez. Rolleri ayrı depolarda tutup ana projeye bağlamanın bir başka yolu da alt modüldür; artıları ve tuzakları için Git submodule kullanımı yazısına bakabilirsiniz. Gizli değerleri rol değişkenlerine düz metin koymayın; onlar Ansible Vault ile şifrelenir.

    Sık Yapılan Hatalar ve Tasarım Tuzakları#

    Her şeyi tek role tıkmak. "Sunucu kurulum" adında, içinde güvenlik duvarı, PHP, MySQL, Nginx ve yedekleme olan bir rol, aslında role bölünmemiş bir playbook'tur. Bir rolün adı tek bir isim olabiliyorsa (nginx, redis, firewall) sınırı doğru çizmişsiniz demektir; adında "ve" geçiyorsa bölün.

    defaults yerine vars kullanmak. Rolün parametrelerini vars/main.yml içine koyarsanız, kullanan kişi group_vars ile bunları ezemez, çünkü vars daha yüksek önceliklidir. Sonuç: rol yeniden kullanılamaz hâle gelir. Ezilebilir olması gereken her şey defaults içinde durur.

    Değişken adlarını öneksiz bırakmak. port, kullanici, dizin gibi genel adlar başka rollerle çakışır. Rol adıyla öneklemek (nginx_port) çakışmayı yapısal olarak önler.

    Rolü belirli bir ortama gömmek. İçinde 185.12.34.56 gibi sabit IP, /home/deploy gibi sabit yol ya da tek bir alan adı geçen rol, ikinci projede çalışmaz. Ortama bağlı her değer bir değişken olmalı, gerçek değeri group_vars ya da host_vars içinde durmalıdır.

    Handler adlarını değiştirmek. notify ile handler adı birebir eşleşir. Handler adını düzenleyip notify satırını güncellemeyi unutmak sessiz bir hatadır: Ansible eşleşmeyen bir notify için uyarı verir ama play başarılı sayılır, servis ise hiç yeniden yüklenmez.

    BelirtiOlası nedenKontrol
    Değişken ezilmiyorvars/ içinde tanımlıansible-inventory --host
    Şablon bulunamıyorDosya templates/ dışındaRol dizin ağacını kontrol edin
    Handler çalışmıyorAd eşleşmiyor veya değişiklik yoknotify adını birebir karşılaştırın
    Rol iki kez çalışıyorallow_duplicates açıkmeta/main.yml bakın
    Etiket işe yaramıyorinclude_role dinamik çözülüyorimport_role ile deneyin

    Son bir tavsiye: her rol için kısa bir README.md yazın ve içinde yalnızca iki şey bulunsun — rolün ne yaptığı ve defaults içindeki değişkenlerin tablosu. Altı ay sonra o rolü kullanacak kişi (muhtemelen siz olacaksınız) rolün kodunu okumak zorunda kalmaz.

    Sıkça Sorulan Sorular#

    Ansible rolü ile playbook arasındaki fark nedir#

    Playbook, hangi sunucularda hangi işlerin yapılacağını tanımlayan üst düzey dosyadır; hedefleri, sırayı ve genel akışı belirler. Rol ise tek bir sorumluluğu yerine getiren, görev, şablon, dosya ve varsayılan değişkenlerini birlikte taşıyan yeniden kullanılabilir bir pakettir. Yani playbook "ne zaman ve nerede", rol "nasıl" sorusunu cevaplar. Bir playbook birden çok rolü çağırır, roller ise playbook'tan bağımsız olarak başka projelerde de kullanılabilir.

    defaults ve vars dizinleri arasındaki fark nedir#

    İkisi de değişken tanımlar ama öncelikleri taban tabana zıttır. defaults/main.yml Ansible'daki en düşük öncelikli kaynaktır, dolayısıyla oraya yazdığınız her değer group_vars, host_vars veya playbook tarafından kolayca ezilir; rolün ayarlanabilir arayüzü burasıdır. vars/main.yml ise çok yüksek önceliklidir ve neredeyse hiçbir şeyle ezilemez; yalnızca dağıtıma bağlı sabitler gibi değiştirilmemesi gereken değerler için kullanılmalıdır.

    Rolleri nasıl sürümlerim ve paylaşırım#

    En pratik yöntem her rolü kendi Git deposunda tutmak ve anlamlı sürüm etiketleri atmaktır. Ardından projelerinizde bir requirements.yml dosyası oluşturup rolü depo adresi ve version alanıyla sabitlersiniz; ansible-galaxy install -r requirements.yml komutu hepsini kurar. Sürümü sabitlemek kritiktir, çünkü ana dalda yapılan bir değişikliğin bir gün habersizce üretim playbook'unuza girmesini önler.

    include_role ile import_role arasında hangisini seçmeliyim#

    Rolün çalışıp çalışmayacağı önceden belliyse import_role (ya da roles: bloğu) kullanın; statik olduğu için --list-tasks çıktısında görünür, etiketler beklendiği gibi çalışır ve hata mesajları daha anlaşılır olur. Karar çalışma anında hesaplanan bir değişkene, bir döngüye ya da hedefe göre değişen bir koşula bağlıysa include_role gerekir. Şüphedeyseniz statik olanı tercih edin.

    Bir rolü test etmeden üretime almamak için ne yapabilirim#

    En basit yol, rolü önce tek bir test sunucusunda --limit ile çalıştırmak ve --check --diff ile ne değişeceğini görmektir. Bir adım ötesi, rolü her değişiklikte temiz bir konteyner ya da sanal makinede baştan uygulayıp ikinci çalıştırmada changed sayısının sıfır olduğunu doğrulamaktır; bu, hem çalıştığını hem de idempotan olduğunu kanıtlar. Ekipçe çalışıyorsanız bu iki adımı sürekli entegrasyon hattınıza taşıyın.

    Aynı rolü aynı playbook'ta iki kez çalıştırabilir miyim#

    Evet, roles: bloğunda ya da include_role ile farklı değişken setleriyle iki kez çağırabilirsiniz; örneğin iki ayrı siteyi ayrı ayrı kurmak için. Ancak bağımlılık olarak çağrılan roller varsayılan olarak yalnızca bir kez çalışır; bunu değiştirmek için rolün meta/main.yml dosyasında allow_duplicates: true yazmanız gerekir. Çoğu durumda daha temiz çözüm, rolün içinde bir döngü kullanıp tek çağrıyla birden fazla örneği yapılandırmaktır.

    Rol içinde hangi görevleri etiketlemeliyim#

    Rolün doğal aşamalarını etiketleyin: kurulum, yapılandırma, servis yönetimi gibi. Böylece --tags yapilandirma diyerek yalnızca dosyaları güncelleyebilir, paket kurulumunu atlayabilirsiniz. Etiketleri tek tek göreve değil, include_tasks bloklarına vermek daha sürdürülebilirdir. Unutmayın ki include_role ile dinamik çağrılan rollerde etiket davranışı beklediğinizden farklı olabilir; etiketlere güveniyorsanız statik çağrı kullanın.

    Kapanış#

    Rol yapısı, Ansible'da bir eşiği geçmenin adıdır: kopyala-yapıştır otomasyondan, tekrar kullanılabilir yapılandırma bileşenlerine geçersiniz. Aklınızda kalması gereken dört alışkanlık şunlar: her rol tek bir sorumluluk üstlensin ve adı tek kelimeyle söylenebilsin; ayarlanabilir her şey defaults içinde dursun, vars yalnızca sabitler için kullanılsın; değişken adlarını rol adıyla önekleyin; ve rolleri Git'te sürüm etiketiyle sabitleyerek projelerinize bağlayın. Bunlar yerine oturduğunda yeni bir sunucu kurmak, birkaç satır YAML yazmaktan ibaret hâle gelir.

    Rollerinizi uygulayacağınız altyapı için tam root erişimli VDS, sanal sunucu ve bulut sunucu paketlerimize göz atabilirsiniz; hem test hem üretim ortamını aynı rollerle kurmak isteyenler için uygun bir zemin sunarlar. Kurulum ve sürekli bakımı bizim üstlenmemizi tercih ederseniz sunucu yönetimi hizmetimiz bu işi devralır, yapılandırma değişikliklerinden önce güvenli bir nokta için yedekleme çözümlerimizi kullanabilirsiniz.

    AnsibleOtomasyonDevOps

    Uygulamaya geçmeye hazır mısınız?

    NVMe SSD, ücretsiz SSL ve %99.9 uptime garantisiyle Clou.TR hosting ve sunucu çözümleriyle projenizi hayata geçirin.