Docker & DevOps

    Ansible Kurulumu ve İlk Playbook

    Ansible kontrol düğümü kurulumu, envanter yapısı ve Nginx kuran ilk playbook örneği.

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

    Üç sunucunuz varken her birine tek tek SSH ile bağlanıp aynı komutları yazmak katlanılabilir. Sekiz sunucuya çıktığınızda bu iş hem sıkıcı hem de hataya açık hâle gelir: birinde paket kurulur, diğerinde unutulur, üçüncüsünde yapılandırma dosyası eski kalır. Ansible tam olarak bu dağılmayı önlemek için var; sunucularınızın nasıl olması gerektiğini bir metin dosyasına yazarsınız, o da gerçek durumu o hedefe getirir.

    Bu rehberde Ansible kurulumunu sıfırdan yapacağız. Önce ajansız (agentless) mimarinin ne anlama geldiğini, sonra kontrol düğümüne kurulumu, SSH anahtarlarının doğru ayarlanmasını, envanter dosyasının yapısını ve ad-hoc komutlarla ilk temasınızı göreceksiniz. Ardından Nginx kuran, yapılandırma dosyası yerleştiren ve servisi yöneten gerçek bir playbook yazacağız; en sonda da idempotans kavramını ve yeni başlayanların en çok takıldığı hataları ele alacağım.

    Ansible Nasıl Çalışır: Ajansız Mimari#

    Ansible'ın en belirgin özelliği hedef sunuculara hiçbir şey kurmamasıdır. Sunucularınıza bir ajan yüklemez, arka planda bir servis çalıştırmaz, açık bir port beklemez. İhtiyacı olan tek şey, zaten sahip olduğunuz SSH erişimi ve hedefte kurulu bir Python yorumlayıcısıdır. Komutu verdiğiniz makineye kontrol düğümü (control node), yönetilen sunuculara ise yönetilen düğüm (managed node) denir.

    Çalışma biçimi şöyle: Ansible çalıştırdığınız her görev için küçük bir Python modülü üretir, SSH üzerinden hedefe kopyalar, orada çalıştırır, sonucu JSON olarak geri alır ve geçici dosyayı siler. Kalıcı hiçbir iz bırakmaz. Bu tasarımın pratikteki üç sonucu var: yeni bir sunucuyu yönetmeye başlamak için ek kurulum gerekmez, güvenlik yüzeyi büyümez, ve Ansible sürümünü yükseltmek için yalnızca kontrol düğümüne dokunursunuz.

    KavramNe demekNerede yaşar
    Kontrol düğümüAnsible'ın kurulu olduğu makineSizin dizüstünüz veya bir yönetim sunucusu
    Yönetilen düğümYapılandırılan sunucuUzak sunucular
    EnvanterHedef sunucuların listesi ve gruplarıinventory.ini / inventory.yml
    ModülTek bir işi yapan birim (paket kur, dosya kopyala)Ansible'ın içinde
    Görev (task)Bir modül çağrısıPlaybook içinde
    PlaybookGörevlerin sıralı listesisite.yml gibi YAML dosyası

    Bu tabloyu aklınızda tutun; Ansible belgelerinde ve hata mesajlarında bu kelimeler sürekli geçer. Windows hedefleri için SSH yerine WinRM kullanılır, ama Linux dünyasında çalışıyorsanız yolunuz hep SSH'tan geçer.

    Kontrol Düğümüne Ansible Kurulumu#

    Kontrol düğümü Linux ya da macOS olmalıdır; Windows'ta doğrudan çalışmaz, WSL üzerinden kullanılır. Dağıtımınıza göre kurulum:

    # Debian / Ubuntu
    sudo apt update
    sudo apt install -y ansible
    
    # AlmaLinux / Rocky Linux (EPEL deposu gerekir)
    sudo dnf install -y epel-release
    sudo dnf install -y ansible-core
    
    # Dağıtımdan bağımsız, en güncel sürüm için pip
    python3 -m pip install --user ansible
    

    Paket yöneticisiyle gelen sürüm genellikle biraz geridedir ama kararlıdır; en yeni modüllere ihtiyacınız yoksa onunla başlayın. AlmaLinux ve Rocky tarafında dnf kullanımına yabancıysanız dnf paket yönetimi yazısı depo ekleme ve sürüm sabitleme konularını ayrıntılı ele alıyor.

    Kurulumu doğrulayın:

    ansible --version
    # ansible [core 2.x.x]
    #   config file = /etc/ansible/ansible.cfg
    #   python version = 3.x.x
    
    # Hedeflerde çalışacak Python yorumlayıcısını görmek için
    ansible localhost -m ansible.builtin.setup -a "filter=ansible_python_version"
    

    Bir sonraki adım, proje dizininizde bir ansible.cfg oluşturmaktır. Ansible yapılandırmayı önce bulunduğunuz dizinde arar, bu yüzden proje bazlı ayar yapmak en temiz yöntemdir:

    ; ansible.cfg — proje kökünde
    [defaults]
    inventory = ./inventory.ini
    host_key_checking = True
    remote_user = deploy
    ; Çıktıyı okunabilir yapar
    stdout_callback = yaml
    ; Her görevde sunucu bilgisi toplamak yavaşlatır; gerekmiyorsa kapatın
    gathering = smart
    fact_caching = jsonfile
    fact_caching_connection = ./.ansible_facts
    
    [privilege_escalation]
    become = True
    become_method = sudo
    become_ask_pass = False
    

    ⚠️ host_key_checking = False yazan örnekler internette çok yaygındır; bu ayar ilk bağlantıdaki parmak izi doğrulamasını kapatır ve ortadaki adam saldırısına kapı aralar. Doğru çözüm, hedeflerin anahtarlarını bir kez ssh-keyscan ile toplayıp known_hosts dosyasına yazmaktır.

    SSH Anahtarı ve Envanter Dosyası#

    Ansible parola sorarak da çalışabilir ama otomasyonun anlamı budur: parolasız, anahtar tabanlı erişim kurun. Kontrol düğümünde bir anahtar çifti oluşturup açık anahtarı hedeflere dağıtın:

    # Ed25519 modern ve kısa bir anahtar tipidir
    ssh-keygen -t ed25519 -C "ansible-kontrol" -f ~/.ssh/ansible_ed25519
    
    # Açık anahtarı hedefe kopyala (bir kez parola sorar)
    ssh-copy-id -i ~/.ssh/ansible_ed25519.pub [email protected]
    
    # Doğrula: parola sormadan girmeli
    ssh -i ~/.ssh/ansible_ed25519 [email protected] 'hostname'
    

    Şimdi envanteri yazalım. En basit biçimi INI'dir ve küçük ortamlarda fazlasıyla yeterlidir. Sunucuları gruplara ayırmak, ileride "sadece web sunucularına şunu yap" demenizi sağlar:

    ; inventory.ini
    [web]
    web1 ansible_host=185.12.34.56
    web2 ansible_host=185.12.34.57
    
    [db]
    db1 ansible_host=185.12.34.60
    
    ; Grupları birleştiren üst grup
    [uretim:children]
    web
    db
    
    ; Tüm hedefler için ortak bağlantı ayarları
    [all:vars]
    ansible_user=deploy
    ansible_ssh_private_key_file=~/.ssh/ansible_ed25519
    ansible_python_interpreter=/usr/bin/python3
    

    Aynı envanteri YAML olarak da yazabilirsiniz; büyük ortamlarda YAML biçimi iç içe yapıları daha okunur kılar:

    # inventory.yml
    all:
      children:
        uretim:
          children:
            web:
              hosts:
                web1: { ansible_host: 185.12.34.56 }
                web2: { ansible_host: 185.12.34.57 }
            db:
              hosts:
                db1: { ansible_host: 185.12.34.60 }
      vars:
        ansible_user: deploy
    

    Envanteri doğrulamak için ansible-inventory komutunu kullanın; hangi hostun hangi gruba düştüğünü ve hangi değişkeni aldığını net gösterir:

    ansible-inventory --list -y
    ansible-inventory --graph
    # @all:
    #   |--@uretim:
    #   |  |--@web:
    #   |  |  |--web1
    #   |  |  |--web2
    

    Ad-Hoc Komutlarla İlk Temas#

    Playbook yazmadan önce bağlantının çalıştığını doğrulayın. Ad-hoc komutlar, tek bir modülü doğrudan komut satırından çalıştırmanızı sağlar ve keşif için idealdir:

    # Bağlantı testi
    ansible all -m ansible.builtin.ping
    # web1 | SUCCESS => { "ping": "pong" }
    
    # Tüm web sunucularında disk durumu
    ansible web -m ansible.builtin.command -a "df -h /"
    
    # Yalnızca üretim grubunda paket önbelleğini güncelle (sudo ile)
    ansible uretim -m ansible.builtin.apt -a "update_cache=yes" --become
    
    # Bir sunucunun işletim sistemi bilgisi
    ansible web1 -m ansible.builtin.setup -a "filter=ansible_distribution*"
    

    -m modül adını, -a modüle geçirilen argümanları belirtir. --become ise komutu sudo ile çalıştırır. Bu üç bayrağı öğrendiğinizde Ansible'ın yarısını öğrenmiş olursunuz, çünkü playbook'lar da tam olarak aynı modülleri aynı argümanlarla çağırır; tek fark, tek seferlik komut yerine dosyaya yazılmış sıralı bir liste olmalarıdır.

    Ad-hoc komutları kalıcı işler için kullanmayın. Bir kez çalıştırılan ve kaydı tutulmayan komut, üç ay sonra "bu sunucuya ne yapmıştık" sorusuna cevap vermez. Keşif ve acil müdahale için ad-hoc, tekrarlanabilir her şey için playbook.

    İlk Playbook: Nginx Kurulumu#

    Şimdi gerçek işe geçelim. Aşağıdaki playbook, web grubundaki sunuculara Nginx kurar, yapılandırma dosyasını şablondan üretir, güvenlik duvarında portu açar ve servisi etkinleştirir. Dosyayı web.yml olarak kaydedin:

    ---
    - name: Web sunucularını hazırla
      hosts: web
      become: true
      vars:
        site_adi: firmaniz.com
        web_kok: /var/www/firmaniz.com
        nginx_worker: auto
    
      tasks:
        - name: Gerekli paketleri kur
          ansible.builtin.package:
            name:
              - nginx
              - curl
            state: present
    
        - name: Yayın dizinini oluştur
          ansible.builtin.file:
            path: "{{ web_kok }}"
            state: directory
            owner: www-data
            group: www-data
            mode: "0755"
    
        - name: Basit bir açılış sayfası koy
          ansible.builtin.copy:
            dest: "{{ web_kok }}/index.html"
            content: "{{ site_adi }} yayında.\n"
            mode: "0644"
    
        - name: Nginx site yapılandırmasını şablondan üret
          ansible.builtin.template:
            src: templates/site.conf.j2
            dest: "/etc/nginx/sites-available/{{ site_adi }}.conf"
            mode: "0644"
          notify: nginx yeniden yükle
    
        - name: Siteyi etkinleştir
          ansible.builtin.file:
            src: "/etc/nginx/sites-available/{{ site_adi }}.conf"
            dest: "/etc/nginx/sites-enabled/{{ site_adi }}.conf"
            state: link
          notify: nginx yeniden yükle
    
        - name: Nginx servisini başlat ve açılışa ekle
          ansible.builtin.service:
            name: nginx
            state: started
            enabled: true
    
      handlers:
        - name: nginx yeniden yükle
          ansible.builtin.service:
            name: nginx
            state: reloaded
    

    Şablon dosyası templates/site.conf.j2 şöyle olabilir; süslü parantezler Jinja2 değişkenleridir ve playbook'taki vars bloğundan gelir:

    server {
        listen 80;
        server_name {{ site_adi }} www.{{ site_adi }};
        root {{ web_kok }};
        index index.html;
    
        access_log /var/log/nginx/{{ site_adi }}.access.log;
        error_log  /var/log/nginx/{{ site_adi }}.error.log;
    
        location / {
            try_files $uri $uri/ =404;
        }
    }
    

    Çalıştırmadan önce mutlaka kuru çalıştırma (dry run) yapın; --check gerçekten hiçbir şey değiştirmeden neyin değişeceğini gösterir, --diff ise dosya farklarını basar:

    # Söz dizimi kontrolü
    ansible-playbook web.yml --syntax-check
    
    # Neyin değişeceğini gör, değiştirme
    ansible-playbook web.yml --check --diff
    
    # Gerçekten uygula
    ansible-playbook web.yml
    
    # Tek bir sunucuda dene
    ansible-playbook web.yml --limit web1
    

    Çıktının sonundaki özet satırı en önemli kısımdır: ok sayısı zaten doğru olan görevleri, changed değiştirilenleri, failed başarısız olanları gösterir. Nginx ile Apache arasında hangisini seçeceğinizden emin değilseniz Apache ve Nginx karşılaştırması yazısı karar vermenizi kolaylaştırır.

    İdempotans: Aynı Playbook'u İki Kez Çalıştırmak#

    Ansible'ın en değerli özelliği idempotans'tır: aynı playbook'u ikinci kez çalıştırdığınızda hiçbir şey değişmemelidir. Yukarıdaki playbook'u tekrar çalıştırırsanız tüm görevler ok döner, changed sayısı sıfır olur. Bu, playbook'unuzun bir durum tanımı olduğu anlamına gelir: "şu paket kurulu olsun, şu dosya şu içerikte olsun, şu servis çalışıyor olsun". Bir komut listesi değil, bir hedef tarifi.

    İdempotansı bozan en yaygın şey command ve shell modülleridir. Bu modüller ne yaptığınızı bilemez, her çalıştırmada changed döner. Kaçınılmaz olduklarında creates, removes veya changed_when ile durumu kendiniz tarif edin:

    - name: Yalnızca dosya yoksa arşivi aç
      ansible.builtin.command: tar -xzf /tmp/uygulama.tar.gz -C /opt/uygulama
      args:
        creates: /opt/uygulama/VERSION   # bu dosya varsa görev atlanır
    
    - name: Sürüm bilgisi oku (asla değişiklik sayılmasın)
      ansible.builtin.command: /opt/uygulama/bin/surum
      register: surum_ciktisi
      changed_when: false
    

    Handler mekanizması da idempotansın parçasıdır. notify ile tetiklenen bir handler, ilgili görev gerçekten değiştiyse çalışır; dosya zaten doğru içerikteyse Nginx boş yere yeniden yüklenmez. Handler'lar play sonunda ve yalnızca bir kez çalışır, kaç görev tetiklerse tetiklesin.

    Değişkenleri playbook içine gömmek yerine ayrı dosyalara taşımak da erken öğrenilmesi gereken bir alışkanlıktır. group_vars/web.yml ve host_vars/web1.yml dosyaları otomatik olarak okunur:

    # group_vars/web.yml
    site_adi: firmaniz.com
    web_kok: /var/www/firmaniz.com
    php_surum: "8.3"
    

    Playbook'unuz büyümeye başladığında görevleri role bölmek gerekir; bu yapıyı Ansible rol yapısı yazısında ayrıntılı anlatıyorum. Parola, API anahtarı gibi değerleri düz metin değişken dosyasında tutmayın; onlar için Ansible Vault kullanılır.

    Sık Yapılan Hatalar ve Hata Ayıklama#

    YAML girintisi. Ansible'da alınan hataların büyük kısmı Ansible'ın değil YAML'ın hatasıdır. Sekme karakteri kullanmayın, yalnızca boşluk kullanın; iki boşluklu tutarlı girinti standarttır. ansible-playbook --syntax-check bunları çalıştırmadan yakalar.

    become unutmak. Paket kurulumu, servis yönetimi ve /etc altına yazma yetkisi ister. become: true yazmadığınızda Permission denied alırsınız. Tersi de sorunludur: her şeyi root ile çalıştırmak, kullanıcı dizinine yazan görevlerde sahiplik karışıklığı yaratır.

    Python yorumlayıcısı bulunamıyor. Minimal kurulmuş bazı sunucularda Python yoktur ya da farklı yoldadır. ansible_python_interpreter=/usr/bin/python3 ayarını envantere ekleyin; Python hiç yoksa raw modülüyle önce onu kurun.

    Değişkenin tırnaksız başlaması. YAML'da bir değer {{ ile başlıyorsa mutlaka tırnak içine alınmalıdır: path: "{{ web_kok }}". Tırnaksız yazarsanız YAML ayrıştırıcısı süslü parantezi sözlük başlangıcı sanar ve anlaşılmaz bir hata verir.

    Yavaş çalışma. Her çalıştırmada tüm sunuculardan bilgi toplamak (fact gathering) zaman alır. İhtiyacınız yoksa play düzeyinde gather_facts: false yazın; ihtiyacınız varsa ansible.cfg içinde fact önbelleğini açın. Ayrıca forks değerini yükseltmek paralelliği artırır.

    Hata ayıklarken bu üç bayrak işinizi görür:

    BayrakNe yapar
    -v, -vvvAyrıntı düzeyini artırır, -vvv SSH komutlarını da gösterir
    --check --diffDeğiştirmeden neyin değişeceğini ve dosya farkını gösterir
    --start-at-task "ad"Playbook'u belirli bir görevden başlatır
    --limit web1Yalnızca tek hedefte çalıştırır
    --list-tasksÇalıştırmadan görev listesini basar

    Bir de debug modülünü sevin; bir değişkenin gerçekte ne içerdiğini görmek çoğu sorunun cevabıdır:

    - name: Değişkeni yazdır
      ansible.builtin.debug:
        var: ansible_facts['distribution']
    

    Sıkça Sorulan Sorular#

    Ansible hedef sunuculara bir şey kurmak gerekir mi#

    Hayır, Ansible ajansızdır. Hedefte ihtiyaç duyduğu tek şey SSH erişimi ve bir Python yorumlayıcısıdır; modern Linux dağıtımlarının neredeyse tamamında Python zaten kuruludur. Kalıcı bir servis çalıştırmaz, açık port beklemez, iş bittiğinde geçici dosyalarını temizler. Python'un hiç bulunmadığı çok minimal bir imajda ise raw modülüyle önce Python kurulur, sonrası normal akışla devam eder.

    Ansible ile Terraform arasındaki fark nedir#

    Terraform altyapıyı oluşturur: sunucu, ağ, disk, DNS kaydı gibi kaynakları sağlayıcı API'leri üzerinden yaratır ve bunların durumunu takip eder. Ansible ise var olan sunucunun içini yapılandırır: paket kurar, dosya yerleştirir, servis yönetir. İkisi rakip değil, ardışıktır; yaygın düzen Terraform ile makineleri açmak, ardından Ansible ile üzerlerini kurmaktır. Terraform tarafına giriş için ilgili rehbere bakabilirsiniz.

    Playbook'u çalıştırmadan önce ne olacağını nasıl görürüm#

    ansible-playbook web.yml --check --diff komutu kuru çalıştırma yapar: hiçbir değişiklik uygulamadan hangi görevlerin changed döneceğini ve dosya içeriklerinin nasıl değişeceğini gösterir. Tüm modüller kuru çalıştırmayı tam desteklemez; özellikle command ve shell görevleri atlanır, bu yüzden çıktıyı mutlak gerçek değil güçlü bir ön izleme olarak görün. Riskli değişikliklerde ayrıca --limit ile tek sunucuda deneyin.

    Ansible ücretsiz mi#

    Ansible'ın çekirdeği açık kaynaktır ve ücretsiz kullanılır; komut satırından çalıştırdığınız her şey buna dahildir. Kurumsal ölçekte web arayüzü, rol tabanlı yetkilendirme, iş planlama ve denetim kaydı isteyen ekipler için ticari bir yönetim platformu da mevcuttur, ancak küçük ve orta ölçekli bir altyapıyı yönetmek için buna ihtiyacınız yoktur. Açık kaynak sürüm tek başına üretim ortamları için fazlasıyla yeterlidir.

    Envanteri elle yazmak yerine otomatik oluşturabilir miyim#

    Evet. Ansible dinamik envanter eklentilerini destekler; bulut sağlayıcınızın API'sinden, bir veritabanından ya da kendi yazdığınız bir betikten sunucu listesini anlık olarak çekebilir. Küçük ve sabit bir sunucu parkınız varsa statik INI ya da YAML dosyası daha basit ve öngörülebilirdir. Sunucular sık açılıp kapanıyorsa dinamik envantere geçmek, listeyi güncel tutma yükünden sizi kurtarır.

    Aynı playbook farklı dağıtımlarda çalışır mı#

    Kısmen. package gibi soyut modüller dağıtımın paket yöneticisini kendisi seçer, dolayısıyla paket adı aynıysa sorun çıkmaz. Ancak paket adları ve dosya yolları dağıtımlar arasında değişir; Debian'da apache2 olan servis AlmaLinux'ta httpd adını taşır. Bu farkları when: ansible_facts['os_family'] == 'RedHat' gibi koşullarla ya da dağıtıma göre değişken dosyası yükleyerek yönetirsiniz.

    Ansible çalıştırırken parola sormasını nasıl engellerim#

    İki ayrı parola söz konusudur: SSH parolası ve sudo parolası. SSH tarafını anahtar tabanlı kimlik doğrulamayla çözersiniz. sudo tarafında ise hedef sunucularda ilgili kullanıcı için parolasız yükseltme tanımlayabilir ya da --ask-become-pass ile her çalıştırmada bir kez sorulmasını sağlayabilirsiniz. Otomatik çalışan işlerde parolasız sudo gerekir; bu yetkiyi mümkün olduğunca dar komut kümesiyle sınırlandırın.

    Kapanış#

    Ansible'a başlarken akılda tutulması gereken dört şey var: kontrol düğümü haricinde hiçbir yere kurulum yapmazsınız; envanteri gruplarla düzenlemek ileride her şeyi kolaylaştırır; playbook bir komut listesi değil bir durum tanımıdır, dolayısıyla ikinci çalıştırmada changed sayısının sıfır olması hedefinizdir; ve her değişikliği önce --check --diff ile görmeden uygulamayın. Bu dördü oturduğunda sunucu kurmak, tekrarlanabilir ve gözden geçirilebilir bir işe dönüşür.

    Ansible ile yönetmek için kendi sunucularınıza tam root erişimi gerekir; VDS, sanal sunucu ve bulut sunucu paketlerimiz bu tür otomasyon kurulumları için uygundur. Yapılandırmayı ve bakımı bizim üstlenmemizi isterseniz sunucu yönetimi hizmetimiz devreye girer, mevcut kurulumunuzu yeni bir altyapıya aktarırken site taşıma sayfamıza göz atabilirsiniz. Diğer otomasyon rehberleri wiki bölümünde.

    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.