Üç 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.
| Kavram | Ne demek | Nerede yaşar |
|---|---|---|
| Kontrol düğümü | Ansible'ın kurulu olduğu makine | Sizin dizüstünüz veya bir yönetim sunucusu |
| Yönetilen düğüm | Yapılandırılan sunucu | Uzak sunucular |
| Envanter | Hedef sunucuların listesi ve grupları | inventory.ini / inventory.yml |
| Modül | Tek 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 |
| Playbook | Görevlerin sıralı listesi | site.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:
| Bayrak | Ne yapar |
|---|---|
-v, -vvv | Ayrıntı düzeyini artırır, -vvv SSH komutlarını da gösterir |
--check --diff | Değ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 web1 | Yalnı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.