Ansible Inventory Dosyası Yazımı: Statik ve Dinamik Yapılar

Ansible ile iş yapmaya başladığınızda ilk karşılaştığınız kavram genellikle inventory dosyasıdır. “Hangi sunuculara bağlanacağım? Bunları nasıl gruplayacağım? Her ortam için ayrı dosya mı tutmalıyım?” gibi sorular kafanızı karıştırabilir. Bu yazıda hem statik hem de dinamik inventory yapılarını gerçek dünya senaryolarıyla ele alacağız. Sonunda elinizde gerçekten kullanılabilir, bakımı kolay bir inventory yapısı olacak.

Statik Inventory Nedir ve Ne Zaman Kullanılır?

Statik inventory, sunucu listesini elle yazdığınız, dosya sisteminde saklanan bir yapıdır. Küçük ve değişmeyen altyapılarda son derece pratiktir. On sunucunuz varsa ve bu sunucular pek değişmiyorsa dinamik bir çözüme ihtiyacınız yok demektir. Ansible varsayılan olarak /etc/ansible/hosts dosyasını inventory olarak kabul eder, ama bunu -i parametresiyle istediğiniz dosyaya yönlendirebilirsiniz.

Statik inventory iki formatta yazılabilir: INI ve YAML. Ben genellikle büyük projelerde YAML, küçük projelerde INI tercih ederim. Neden mi? INI daha hızlı yazılır, YAML daha okunabilir ve karmaşık değişken yapılarını daha iyi destekler.

INI Formatında Basit Bir Inventory

En temel haliyle bir INI inventory dosyası şuna benzer:

# /etc/ansible/hosts veya proje_dizini/inventory/hosts

[webservers]
web01.example.com
web02.example.com
web03.example.com

[dbservers]
db01.example.com ansible_port=5522
db02.example.com ansible_port=5522

[loadbalancers]
lb01.example.com

[monitoring]
grafana.example.com
prometheus.example.com

Bu yapıda her köşeli parantez bir grup tanımlar. Gruplar olmadan da çalışır ama gruplar hayatınızı inanılmaz kolaylaştırır. Bir playbook yazarken hosts: webservers diyerek o gruptaki tüm sunuculara ulaşırsınız.

YAML Formatında Inventory

Aynı yapıyı YAML formatında yazdığımızda daha düzenli bir görünüm elde ederiz:

# inventory/hosts.yml

all:
  children:
    webservers:
      hosts:
        web01.example.com:
          ansible_port: 22
          ansible_user: deploy
        web02.example.com:
          ansible_port: 22
          ansible_user: deploy
        web03.example.com:
          ansible_port: 22
          ansible_user: deploy
    dbservers:
      hosts:
        db01.example.com:
          ansible_port: 5522
          ansible_user: postgres
        db02.example.com:
          ansible_port: 5522
          ansible_user: postgres
    loadbalancers:
      hosts:
        lb01.example.com:

YAML formatında her host’a özel değişkenleri doğrudan tanımlayabiliyorsunuz. Bu çok güçlü bir özellik.

Grup Değişkenleri ve Host Değişkenleri

Inventory dosyasına her şeyi doldurmak yerine, group_vars ve host_vars dizin yapısını kullanmak çok daha temiz bir yaklaşım sunar. Bu yapı, değişkenleri ayrı dosyalarda tutmanızı sağlar.

# Dizin yapısı:
# inventory/
# ├── hosts
# ├── group_vars/
# │   ├── all.yml
# │   ├── webservers.yml
# │   └── dbservers.yml
# └── host_vars/
#     ├── web01.example.com.yml
#     └── db01.example.com.yml

group_vars/all.yml dosyası tüm hostlara uygulanır. Buraya genel ayarları koyabilirsiniz:

# inventory/group_vars/all.yml

ansible_user: ansible
ansible_ssh_private_key_file: ~/.ssh/ansible_key
ntp_server: ntp.example.com
timezone: Europe/Istanbul
log_retention_days: 30

Webserver grubuna özel değişkenler için:

# inventory/group_vars/webservers.yml

http_port: 80
https_port: 443
max_connections: 1000
nginx_worker_processes: auto
app_deploy_dir: /var/www/html

Belirli bir host için özel ayar gerektiğinde host_vars kullanırsınız:

# inventory/host_vars/web01.example.com.yml

# Bu sunucu production'da daha fazla yük alıyor
nginx_worker_processes: 8
max_connections: 2000
backup_enabled: true

Çoklu Ortam için Inventory Yapısı

Gerçek dünyada genellikle development, staging ve production ortamlarına sahip olursunuz. Bu ortamları ayrı inventory dosyalarında tutmak en sağlıklı yaklaşımdır.

# Önce dizin yapısını oluşturalım
mkdir -p inventories/{production,staging,development}/{group_vars,host_vars}
touch inventories/production/hosts
touch inventories/staging/hosts
touch inventories/development/hosts

Production inventory örneği:

# inventories/production/hosts

[webservers]
web01.prod.example.com
web02.prod.example.com
web03.prod.example.com

[dbservers]
db01.prod.example.com
db02.prod.example.com

[webservers:vars]
env=production
deployment_branch=main

[dbservers:vars]
env=production
db_replica_count=2

[all:vars]
ansible_user=ansible
ansible_ssh_private_key_file=~/.ssh/prod_key

Staging için farklı bir inventory:

# inventories/staging/hosts

[webservers]
web01.staging.example.com

[dbservers]
db01.staging.example.com

[webservers:vars]
env=staging
deployment_branch=develop

[dbservers:vars]
env=staging
db_replica_count=0

[all:vars]
ansible_user=ansible
ansible_ssh_private_key_file=~/.ssh/staging_key

Artık playbook’larınızı ortama göre çalıştırabilirsiniz:

# Production'da çalıştır
ansible-playbook -i inventories/production/hosts deploy.yml

# Staging'de çalıştır
ansible-playbook -i inventories/staging/hosts deploy.yml

# Development'da sadece webserver'lara çalıştır
ansible-playbook -i inventories/development/hosts -l webservers deploy.yml

İç İçe Gruplar: children Kullanımı

Büyük altyapılarda grupları hiyerarşik olarak organize etmek çok değerlidir. Örneğin tüm web ve DB sunucularını kapsayan bir “datacenter” grubu oluşturabilirsiniz:

# inventory/hosts

[webservers]
web01.example.com
web02.example.com

[dbservers]
db01.example.com

[appservers]
app01.example.com
app02.example.com

# Datacenter Istanbul'daki tüm sunucular
[datacenter_ist:children]
webservers
dbservers
appservers

# Tüm uygulama katmanı sunucuları
[app_tier:children]
webservers
appservers

[datacenter_ist:vars]
datacenter=istanbul
ntp_server=ntp.ist.example.com

Bu yapıyla ansible datacenter_ist -m ping diyerek Istanbul datacenter’ındaki tüm sunuculara ping atabilirsiniz.

Dinamik Inventory: Neden İhtiyaç Duyarız?

Cloud ortamlarında sunucular gelir gider. AWS’de otomatik ölçekleme yapıyorsanız, bir sabah 5 sunucuyla uyanıp öğleden sonra 50 sunucuyla çalışıyor olabilirsiniz. Bu sunucuların IP adreslerini elle takip etmek hem imkansız hem de anlamsızdır. İşte burada dinamik inventory devreye girer.

Dinamik inventory, bir script veya plugin aracılığıyla sunucu listesini harici bir kaynaktan (AWS, Azure, GCP, VMware, Terraform state, vb.) çekerek Ansible’a iletir.

AWS EC2 Dynamic Inventory

AWS kullanıyorsanız amazon.aws collection’ı içindeki aws_ec2 plugin’i hayat kurtarır. Önce gerekli kurulumları yapalım:

# AWS collection kurulumu
ansible-galaxy collection install amazon.aws

# Gerekli Python kütüphanesi
pip3 install boto3 botocore

# AWS kimlik bilgilerini ayarla (ya credentials dosyası ya environment variable)
export AWS_ACCESS_KEY_ID="your_access_key"
export AWS_SECRET_ACCESS_KEY="your_secret_key"
export AWS_DEFAULT_REGION="eu-west-1"

Şimdi AWS EC2 için bir dynamic inventory YAML dosyası oluşturalım:

# inventory/aws_ec2.yml
# Dosya adının _ec2.yml ile bitmesi zorunlu!

plugin: amazon.aws.aws_ec2
regions:
  - eu-west-1
  - eu-central-1

filters:
  instance-state-name: running
  tag:Environment:
    - production

keyed_groups:
  - key: tags.Role
    prefix: role
  - key: tags.Environment
    prefix: env
  - key: placement.region
    prefix: region
  - key: instance_type
    prefix: type

hostnames:
  - private-ip-address
  - dns-name

compose:
  ansible_host: private_ip_address
  ansible_user: "'ec2-user'"

Bu yapıyla env_production grubuna, role_webserver grubuna veya region_eu_west_1 grubuna göre sunuculara ulaşabilirsiniz. Tagler üzerinden otomatik gruplama harika bir özellik.

Inventory’yi test etmek için:

# Dynamic inventory listesini gör
ansible-inventory -i inventory/aws_ec2.yml --list

# Grafik görünüm
ansible-inventory -i inventory/aws_ec2.yml --graph

# Sadece belirli bir gruba ping at
ansible -i inventory/aws_ec2.yml role_webserver -m ping

Özel Dinamik Inventory Script Yazmak

Bazen hazır plugin’ler işinizi görmez. Kendi CMDB sisteminiz, özel bir kayıt sistemi veya Terraform state dosyasından inventory üretmeniz gerekebilir. Bu durumda Python ile basit bir script yazabilirsiniz.

Şöyle bir senaryo düşünelim: Bir PostgreSQL veritabanında sunucu bilgileri tutuluyor ve buradan dinamik olarak inventory üretmek istiyorsunuz:

#!/usr/bin/env python3
# scripts/dynamic_inventory.py

import json
import sys
import psycopg2
import argparse

def get_inventory():
    # Veritabanından sunucu bilgilerini çek
    conn = psycopg2.connect(
        host="cmdb.example.com",
        database="infrastructure",
        user="ansible_reader",
        password="secret"
    )
    cursor = conn.cursor()
    
    cursor.execute("""
        SELECT hostname, ip_address, role, environment, datacenter
        FROM servers
        WHERE status = 'active'
    """)
    
    servers = cursor.fetchall()
    
    inventory = {
        "_meta": {
            "hostvars": {}
        }
    }
    
    for hostname, ip, role, environment, datacenter in servers:
        # Gruplara ekle
        for group in [role, f"env_{environment}", f"dc_{datacenter}"]:
            if group not in inventory:
                inventory[group] = {"hosts": [], "vars": {}}
            inventory[group]["hosts"].append(hostname)
        
        # Host değişkenlerini ekle
        inventory["_meta"]["hostvars"][hostname] = {
            "ansible_host": ip,
            "server_role": role,
            "environment": environment,
            "datacenter": datacenter
        }
    
    conn.close()
    return inventory

def get_host(hostname):
    # Belirli bir host için değişkenleri döndür
    inventory = get_inventory()
    return inventory["_meta"]["hostvars"].get(hostname, {})

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--list", action="store_true")
    parser.add_argument("--host", type=str)
    args = parser.parse_args()
    
    if args.list:
        print(json.dumps(get_inventory(), indent=2))
    elif args.host:
        print(json.dumps(get_host(args.host), indent=2))
    else:
        print(json.dumps({}))

Script’i çalıştırılabilir yapın ve test edin:

chmod +x scripts/dynamic_inventory.py

# Test et
./scripts/dynamic_inventory.py --list
./scripts/dynamic_inventory.py --host web01.example.com

# Ansible ile kullan
ansible-playbook -i scripts/dynamic_inventory.py deploy.yml

Statik ve Dinamik Inventory’yi Bir Arada Kullanmak

Gerçek projelerde çoğu zaman her ikisini de kullanmak istersiniz. Ansible buna izin veriyor: Bir dizini inventory olarak belirttiğinizde, o dizindeki tüm geçerli inventory dosyaları ve scriptler birleştirilir.

# Karma inventory dizin yapısı
mkdir -p inventory/

# Statik dosya - network ekipmanları gibi değişmeyen şeyler
cat > inventory/static_hosts.ini << 'EOF'
[network_devices]
switch01.example.com ansible_connection=network_cli
switch02.example.com ansible_connection=network_cli
firewall01.example.com ansible_connection=network_cli

[monitoring]
grafana.example.com
prometheus.example.com
alertmanager.example.com
EOF

# Bu dizinde aws_ec2.yml de bulunuyor
# Ansible her ikisini otomatik birleştirir

# Tüm inventory'yi tek seferde kullan
ansible-playbook -i inventory/ deploy.yml

# Hangi hostların mevcut olduğunu kontrol et
ansible-inventory -i inventory/ --graph

Bu yaklaşımda network cihazlarınız hep statik kalırken, cloud sunucularınız dinamik olarak güncellenir. İkisi bir arada sorunsuz çalışır.

ansible.cfg ile Inventory Ayarları

Her seferinde -i parametresi vermek yerine, proje dizinindeki ansible.cfg dosyasında inventory konumunu belirtebilirsiniz:

# ansible.cfg

[defaults]
inventory = ./inventory
roles_path = ./roles
collections_path = ./collections
remote_user = ansible
private_key_file = ~/.ssh/ansible_key
host_key_checking = False
retry_files_enabled = False
forks = 20
timeout = 30

[inventory]
enable_plugins = host_list, script, auto, yaml, ini, toml, aws_ec2

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
pipelining = True

enable_plugins satırı önemli: Kullanmak istediğiniz inventory plugin’lerini buraya eklemeniz gerekir.

Inventory’yi Doğrulama ve Hata Ayıklama

Yazdığınız inventory’nin doğru çalışıp çalışmadığını kontrol etmek için birkaç pratik yöntem:

# Tüm hostları listele
ansible-inventory -i inventory/ --list

# Grafik görünüm - grupları ve alt grupları göster
ansible-inventory -i inventory/ --graph

# Belirli bir hostin tüm değişkenlerini gör
ansible-inventory -i inventory/ --host web01.example.com

# Belirli bir gruba ping at
ansible -i inventory/ webservers -m ping

# Fact toplama ile bağlantı ve değişkenleri doğrula
ansible -i inventory/ webservers -m setup | grep -i ansible_distribution

# Hangi sunuculara erişilebileceğini kontrol et (gerçekten bağlanmadan)
ansible -i inventory/ all --list-hosts

# Dry run ile playbook öncesi kontrol
ansible-playbook -i inventory/ --list-hosts -l webservers deploy.yml

Gerçek Dünya Senaryosu: E-ticaret Platformu Inventory Yapısı

Bir e-ticaret platformu yönettiğinizi düşünün. Frontend, API, veritabanı, cache, CDN ve monitoring katmanlarından oluşan bir yapı var. Hem on-premise hem de AWS’de sunucularınız mevcut:

# inventories/production/hosts

# On-premise sunucular
[frontend_onprem]
fe01.dc.example.com
fe02.dc.example.com

[api_onprem]  
api01.dc.example.com
api02.dc.example.com
api03.dc.example.com

[database_onprem]
db-master.dc.example.com
db-replica01.dc.example.com
db-replica02.dc.example.com

[cache_onprem]
redis01.dc.example.com
redis02.dc.example.com
redis03.dc.example.com

[monitoring_onprem]
grafana.dc.example.com
prometheus.dc.example.com

# Grup hiyerarşisi
[onpremise:children]
frontend_onprem
api_onprem
database_onprem
cache_onprem
monitoring_onprem

[webservers:children]
frontend_onprem

[appservers:children]
api_onprem

[databases:children]
database_onprem

# Tüm ortam
[production:children]
onpremise

[production:vars]
env=production
slack_channel=#prod-alerts
deploy_timeout=300
health_check_retries=5

AWS EC2 kısmı ayrı bir dynamic inventory dosyasında:

# inventories/production/aws_ec2.yml

plugin: amazon.aws.aws_ec2
regions:
  - eu-west-1
filters:
  instance-state-name: running
  tag:Environment: production
  tag:Project: ecommerce

keyed_groups:
  - key: tags.Tier
    prefix: tier
  - key: tags.Role  
    prefix: role

compose:
  ansible_host: private_ip_address
  ansible_user: "'ec2-user'"
  env: "'production'"

Tüm bu yapıyla tek bir komutla tüm production ortamını yönetebilirsiniz.

Inventory Cache: Dinamik Inventory Performansı

Dinamik inventory her çalıştırmada API çağrısı yapar. Büyük AWS hesaplarında bu yavaş olabilir. Cache kullanarak bunu hızlandırabilirsiniz:

# ansible.cfg'e ekleyin

[inventory]
cache = yes
cache_plugin = jsonfile
cache_connection = /tmp/ansible_inventory_cache
cache_timeout = 300

Ya da plugin konfigürasyonunda:

# inventory/aws_ec2.yml

plugin: amazon.aws.aws_ec2
regions:
  - eu-west-1
filters:
  instance-state-name: running

cache: true
cache_plugin: jsonfile
cache_connection: /tmp/aws_inventory_cache
cache_timeout: 300

Cache’i manuel olarak temizlemek için:

# Cache'i temizle ve yeniden oluştur
ansible-inventory -i inventory/ --list --flush-cache

Sonuç

Inventory yönetimi Ansible’ın kalbi sayılabilir. Doğru kurgulanmış bir inventory yapısı; bakımı kolaylaştırır, hataları azaltır ve ekip içi iletişimi güçlendirir. Şu ilkeleri aklınızda tutun:

  • Küçük, değişmeyen altyapı için statik INI veya YAML yeterlidir
  • Cloud ortamları ve dinamik ölçekleme için mutlaka dynamic inventory kullanın
  • group_vars ve host_vars dizin yapısını tercih edin, değişkenleri inventory dosyasına doldurmayın
  • Ortam başına ayrı inventory tutun, production ve staging’i asla aynı dosyaya koymayın
  • Statik ve dinamik inventory’yi bir dizinde birleştirerek karma yapılar oluşturabilirsiniz
  • Cache kullanımı büyük dynamic inventory senaryolarında performansı ciddi artırır
  • ansible-inventory --graph ve --list komutlarını sık kullanın, yapınızı düzenli doğrulayın

Bu yazıda anlattığım yapıları kendi altyapınıza uyarlamak için küçük başlayın. Önce basit bir statik inventory yazın, çalıştırın, alışın. Sonra group_vars ekleyin. Cloud kullanıyorsanız dynamic inventory’ye geçin. Adım adım ilerlemek, her şeyi sıfırdan karmaşık yazmaktan çok daha sağlıklı sonuçlar verir.

Yorum yapın