Yeni bir uygulama sunucusu kurman gerekiyor ve mevcut olanın nasıl kurulduğunu bilen tek kaynak, sekiz ay önce birinin panele yazdığı birkaç not. Paketler elle kuruldu, güvenlik duvarı kuralları elle eklendi, bir yerlerde unutulmuş bir sysctl ayarı var. İkinci sunucuyu kurduğunda ilkiyle aynı olmadığını fark edersin ve o fark, sonraki üç ayın en zor hata ayıklama seansını doğurur. Infrastructure as Code, yani altyapıyı kod olarak yönetmek, tam olarak bu belirsizliği ortadan kaldırır: sunucunun nasıl olması gerektiği bir depoda, sürüm kontrolü altında ve gözden geçirilebilir biçimde durur.
Bu rehberde infrastructure as code kavramının ne olduğunu, bildirimsel ve emirsel yaklaşımlar arasındaki farkı, kaynak sağlama (Terraform) ile yapılandırma yönetimi (Ansible) arasındaki iş bölümünü, durum dosyasının neden bu kadar kritik olduğunu, yapılandırma kayması (configuration drift) sorununu, gizli bilgilerin nasıl yönetileceğini ve sahada en çok can yakan hataları anlatacağım.
Infrastructure as Code Nedir#
IaC, altyapının istenen durumunu makine tarafından okunabilir dosyalarda tanımlamak ve bu dosyaları çalıştırarak altyapıyı o duruma getirmektir. Kurulan şey bir sanal sunucu, bir güvenlik duvarı kuralı, bir DNS kaydı ya da bir yük dengeleyici olabilir. Kritik olan, o kaynağın nasıl oluştuğunun bir insanın hafızasında değil bir dosyada yazması ve bu dosyanın sürüm kontrolünde tutulmasıdır.
Bunun kazandırdıkları, elle kurulumla karşılaştırıldığında net biçimde görünür:
| Konu | Elle kurulum | IaC ile |
|---|---|---|
| Tekrar edilebilirlik | Her seferinde biraz farklı | Aynı girdi, aynı çıktı |
| Gözden geçirme | Yok, kimse görmez | Kod incelemesinden geçer |
| Geçmiş | Yok ya da dağınık notlar | Git geçmişi, kim ne zaman değiştirdi |
| Felaket kurtarma | Saatler, belki günler | Dosyayı çalıştır, dakikalar |
| Belgeleme | Ayrı yazılır, bayatlar | Kodun kendisi belgedir |
| Test ortamı kurma | Zahmetli, atlanır | Aynı kodla, farklı değişkenlerle |
Son satır özellikle önemlidir. Test ortamını üretimle aynı kodla kurabildiğinde "test ortamında çalışıyordu" cümlesi anlamını yitirir; çünkü ikisi arasındaki fark artık tanımlanmış ve görünür bir değişken listesidir. Bu, DevOps pratiklerinin en somut kazanımlarından biridir.
Bildirimsel ve Emirsel Yaklaşım#
IaC araçları temelde iki felsefeden birine dayanır. Emirsel (imperative) yaklaşımda adımları sırayla yazarsın: paketi kur, dosyayı kopyala, servisi başlat. Kabuk betiği bunun en saf örneğidir. Bildirimsel (declarative) yaklaşımda ise sonucu tarif edersin: "bu sunucuda Nginx kurulu ve çalışır durumda olsun." Araç mevcut durumu okur, istenen durumla karşılaştırır ve yalnızca farkı uygular.
Aradaki fark tekrar çalıştırdığında ortaya çıkar. Emirsel bir betiği ikinci kez çalıştırmak genellikle sorun üretir: kullanıcı zaten var, satır dosyaya ikinci kez eklenir, servis zaten çalışıyor diye hata döner. Bildirimsel araç ise ikinci çalıştırmada hiçbir şey yapmaz, çünkü istenen durum zaten sağlanmıştır. Bu özelliğe idempotentlik denir ve IaC'nin temel taşıdır.
# Emirsel ve idempotent OLMAYAN: her çalıştırmada satır tekrar eklenir
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
# Emirsel ama idempotent hale getirilmiş: yalnızca yoksa ekle
grep -q '^net.ipv4.ip_forward=1' /etc/sysctl.conf || \
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
sysctl -p
Bildirimsel araçlarda bu kontrolü sen yazmazsın, araç zaten yapar. Bu yüzden kabuk betikleriyle başlayan ekiplerin bir süre sonra Ansible ya da Terraform'a geçmesi tesadüf değildir: betiklerin yarısı, aslında idempotentliği elle kurmaya çalışan if bloklarından oluşmaya başlar.
Terraform ile Kaynak Sağlama#
Terraform, altyapı kaynaklarını (sanal sunucu, ağ, disk, DNS kaydı, güvenlik duvarı kuralı) oluşturmak ve yaşam döngüsünü yönetmek için kullanılan bildirimsel bir araçtır. Bir kaynağı tarif edersin, Terraform mevcut durumla farkı hesaplar ve sana uygulamadan önce ne yapacağını gösterir. Bu "önce göster, sonra uygula" akışı, üretim ortamında en çok değer verdiğin özelliktir.
# Değişkenler ortamlar arası farkı tek yerde toplar
variable "ortam" {
type = string
default = "uretim"
}
resource "local_file" "nginx_site" {
filename = "/etc/nginx/conf.d/${var.ortam}.conf"
content = templatefile("${path.module}/site.conf.tftpl", {
sunucu_adi = "firmaniz.com"
upstream = "127.0.0.1:8080"
})
file_permission = "0644"
}
output "yapilandirma_yolu" {
value = local_file.nginx_site.filename
}
Günlük kullandığın komutlar kısadır ve sıraları önemlidir:
# 1) Sağlayıcıları indir, çalışma dizinini hazırla
terraform init
# 2) Biçim ve söz dizimi kontrolü (CI'de bunlar da çalışmalı)
terraform fmt -check
terraform validate
# 3) Ne değişecek? Uygulamadan ÖNCE mutlaka oku
terraform plan -out=plan.tfplan
# 4) Yalnızca incelediğin planı uygula
terraform apply plan.tfplan
Dördüncü adımda planı dosyaya yazıp onu uygulamak, göründüğünden önemli bir alışkanlıktır. terraform apply komutunu doğrudan çalıştırırsan, planı gördüğün an ile uyguladığın an arasında altyapıda bir şey değişmişse farklı bir sonuç alırsın. Kaydedilmiş plan bu pencereyi kapatır.
Terraform çıktısındaki en kritik işaret, kaynağın yeniden oluşturulacağını söyleyen satırdır. Plan çıktısında -/+ resource ... (forces replacement) ifadesini gördüğünde dur ve düşün: bu, mevcut kaynağın silinip yenisinin yaratılacağı anlamına gelir. Bir veritabanı sunucusunda bu, verinin gitmesi demektir.
Ansible ile Yapılandırma Yönetimi#
Terraform kaynağı yaratır; Ansible ise var olan sunucunun içini yapılandırır: paket kurar, dosya yazar, servis başlatır, kullanıcı açar. İkisi rakip değil, ardışık iki adımdır. Ansible'ın en pratik yanı, hedef makinede ajan gerektirmemesi ve yalnızca SSH ile çalışmasıdır.
- name: Uygulama sunucusu temel yapılandırması
hosts: uygulama
become: true
vars:
uygulama_kullanici: appuser
nginx_upstream: "127.0.0.1:8080"
tasks:
- name: Gerekli paketler kurulu olsun
ansible.builtin.package:
name:
- nginx
- ufw
- fail2ban
state: present
- name: Uygulama kullanıcısı oluşturulsun
ansible.builtin.user:
name: "{{ uygulama_kullanici }}"
shell: /usr/sbin/nologin
system: true
- name: Nginx site yapılandırması yazılsın
ansible.builtin.template:
src: templates/site.conf.j2
dest: /etc/nginx/conf.d/firmaniz.conf
owner: root
group: root
mode: "0644"
validate: "nginx -t -c /etc/nginx/nginx.conf"
notify: nginx yeniden yukle
- name: Yalnızca 22, 80 ve 443 açık olsun
community.general.ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop: [22, 80, 443]
handlers:
- name: nginx yeniden yukle
ansible.builtin.service:
name: nginx
state: reloaded
Buradaki validate parametresi hayat kurtarır: şablondan üretilen yapılandırma hatalıysa Ansible dosyayı hedefe hiç yazmaz, dolayısıyla Nginx bozuk yapılandırmayla yeniden yüklenmez. notify ve handlers ikilisi de idempotentliğin güzel bir örneğidir; handler yalnızca dosya gerçekten değiştiyse tetiklenir, her çalıştırmada gereksiz yere servis yeniden yüklenmez.
Değişiklikleri uygulamadan önce ne olacağını görmek için kuru çalıştırma yap:
# Neyin değişeceğini göster, hiçbir şey uygulama
ansible-playbook -i envanter.ini site.yml --check --diff
# Yalnızca belirli bir sunucu grubunda çalıştır
ansible-playbook -i envanter.ini site.yml --limit uygulama --diff
Durum Dosyası ve Yapılandırma Kayması#
Terraform, hangi kaynağı yönettiğini bir durum dosyasında (terraform.tfstate) tutar. Bu dosya kaynak kimliklerini ve son bilinen özelliklerini içerir; kaybolduğunda Terraform yönettiği altyapıyı tanımaz hale gelir ve mevcut kaynakları yeniden oluşturmaya kalkar. Bu yüzden durum dosyasıyla ilgili üç kural pazarlığa kapalıdır.
- Durum dosyasını asla depoya koyma. İçinde gizli bilgi bulunabilir ve
.gitignoreiçine*.tfstate*eklemek ilk adımdır. - Uzak ve kilitlenebilir bir yerde sakla. İki kişi aynı anda uygulama yaparsa durum bozulur; kilit mekanizması bunu engeller.
- Yedekle. Durum dosyasını kaybetmek, altyapının kendisini kaybetmekten daha zor bir sorundur çünkü kaynaklar ayakta ama sahipsizdir.
Yapılandırma kayması, altyapının gerçek durumunun koddaki tanımdan uzaklaşmasıdır ve neredeyse her zaman aynı şekilde başlar: acil bir sorun çıkar, biri sunucuya bağlanıp elle bir ayar değiştirir, sorun çözülür ve kimse kodu güncellemez. Bir sonraki terraform apply ya da Ansible çalıştırması o elle yapılan düzeltmeyi geri alır ve sorun geri gelir; ya da daha kötüsü, kimse çalıştırmadığı için kod ile gerçek aylarca birbirinden uzaklaşır.
Kaymayı tespit etmek için düzenli kontrol çalıştır:
# Terraform: gerçek altyapı koda uyuyor mu? Fark varsa çıkış kodu 2 döner
terraform plan -detailed-exitcode -lock=false
# Ansible: hangi görevler değişiklik yapacaktı?
ansible-playbook -i envanter.ini site.yml --check --diff | grep -E '^changed:'
Bunu haftalık bir zamanlanmış görev haline getirmek, kaymayı aylar sonra değil günler içinde yakalamanı sağlar. Zamanlanmış görev kurulumunu Linux cron görevleri yazısında ayrıntılı anlattım. Kültürel karşılığı ise net bir kuraldır: üretim sunucusuna elle yapılan her değişiklik ya kod olarak geri yazılır ya da geçici olduğu bilinerek ve kaydedilerek yapılır.
Gizli Bilgileri Yönetmek#
IaC'nin en sık yapılan ve en pahalı hatası, parolaları ve API anahtarlarını doğrudan koda yazmaktır. Depoya bir kez giren gizli bilgi, sonradan silinse bile Git geçmişinde kalır; deponun kopyası olan herkeste kalır. Bu yüzden çözüm silmek değil, hiç girmemesini sağlamaktır.
Uygulanabilir seçenekler, karmaşıklık sırasına göre şöyledir:
| Yöntem | Uygun olduğu durum | Dikkat |
|---|---|---|
| Ortam değişkeni | Küçük kurulum, CI çalıştırıcısı | Süreç listesinde görünebilir |
| Şifrelenmiş dosya (Ansible Vault) | Ekip içi, orta ölçek | Vault parolasının yönetimi ayrı sorun |
| CI gizli değişkenleri | Otomatik dağıtım hatları | Log'a yazdırmamaya dikkat |
| Gizli bilgi yöneticisi | Çok ekipli, denetim gereken ortam | Kurulum ve bakım maliyeti |
Ansible Vault, küçük ve orta ölçekli ekipler için pratik bir dengedir:
# Gizli değişkenleri şifreli bir dosyada tut
ansible-vault create group_vars/uretim/vault.yml
# Sonradan düzenle
ansible-vault edit group_vars/uretim/vault.yml
# Playbook'u vault parolasını dosyadan okuyarak çalıştır
ansible-playbook -i envanter.ini site.yml --vault-password-file ~/.vault_pass
Ne olursa olsun depoya bir gizli bilginin sızmadığını düzenli kontrol et. Bu konuda en sık görülen kaza, ortam dosyalarının ve .git dizininin web kökünde erişilebilir kalmasıdır; sonuçlarını ve korunma yollarını .git klasörü ve .env dosyası ifşası yazısında somut örneklerle anlattım.
Sık Yapılan Hatalar#
Her şeyi tek dosyaya yığmak. Beş yüz satırlık tek bir Terraform dosyası ya da tek bir dev playbook, kimsenin dokunmak istemediği bir yapıya dönüşür. Ortama ve sorumluluğa göre modüllere ya da rollere böl; küçük parçalar hem gözden geçirilebilir hem yeniden kullanılabilir olur.
Plan çıktısını okumadan uygulamak. Terraform'un en değerli özelliği, uygulamadan önce ne yapacağını göstermesidir. "Zaten hep aynı" diye geçmek, bir gün silinen bir veritabanı diski olarak geri döner. Özellikle forces replacement satırlarını her seferinde ara.
Kaynak sağlama ile yapılandırmayı karıştırmak. Terraform ile paket kurmaya, Ansible ile sanal makine yaratmaya çalışmak ikisini de zorlaştırır. Terraform kaynağı yaratsın, çıktısını Ansible'a envanter olarak versin; her araç güçlü olduğu işi yapsın.
Test ortamını farklı kurmak. Aynı kodu farklı değişkenlerle çalıştırmıyorsan IaC'nin en büyük faydasını kaçırıyorsun demektir. Ortam farkı bir dosyada listelenebilir olmalı; "canlıda ayrıca şu da var" cümlesi tehlike işaretidir.
Sürümleri sabitlememek. Sağlayıcı ve modül sürümlerini serbest bırakırsan, koduna hiç dokunmadığın halde bir gün plan çıktısı değişir. Sağlayıcı sürümlerini sabitle ve kilit dosyasını depoya ekle; yükseltmeyi bilinçli ve ayrı bir değişiklik olarak yap.
Sıkça Sorulan Sorular#
Infrastructure as Code için hangi araçla başlamalıyım#
Tek bir sunucun varsa ve onu düzgün kurmak istiyorsan Ansible ile başla; SSH dışında bir şey gerektirmez ve öğrenme eğrisi yumuşaktır. Birden fazla sunucu, ağ ve disk gibi kaynakları yaratıp yönetmen gerekiyorsa Terraform'u da ekle. Kabuk betiğiyle başlamak da yanlış değildir ama betiklerin idempotent olması için yazdığın kontroller bir noktada bu araçların yaptığı işi taklit etmeye başlar.
Terraform ve Ansible aynı işi mi yapıyor#
Hayır, birbirini tamamlarlar. Terraform kaynak sağlar: sunucuyu, ağı, diski, DNS kaydını yaratır ve yaşam döngüsünü izler. Ansible ise var olan sunucunun içini yapılandırır: paket kurar, dosya yazar, servis yönetir. Tipik akış, Terraform'un yarattığı sunucuların listesini Ansible'a envanter olarak verip yapılandırmayı ona bırakmaktır.
Durum dosyasını kaybedersem ne olur#
Terraform yönettiği kaynakları tanımaz hale gelir ve bir sonraki uygulamada onları yeniden yaratmaya çalışır; yani mevcut altyapının kopyası oluşur ya da isim çakışmaları yüzünden hata alırsın. Kaynakları terraform import ile tek tek geri tanıtmak mümkündür ama zahmetlidir. Bu yüzden durum dosyasını uzak, kilitlenebilir ve yedeklenen bir yerde tutmak isteğe bağlı değildir.
Tek sunucu için IaC kurmak abartı mı#
Değil, çünkü asıl fayda sunucu sayısında değil tekrar edilebilirlikte. Tek sunucun çöktüğünde onu birkaç dakikada yeniden kurabilmek, aylarca biriken elle yapılmış ayarları yeniden hatırlamaya çalışmaktan çok daha değerlidir. Küçük ölçekte tek bir Ansible playbook'u genellikle yeterlidir; karmaşık araç kurmana gerek yok.
Elle yaptığım değişiklikler ne olacak#
Bir sonraki çalıştırmada geri alınırlar; bu bir hata değil, aracın tasarım amacıdır. Bu yüzden kural şudur: acil bir müdahale için elle değişiklik yaptıysan, sorunu çözdükten sonra aynı değişikliği koda geri yaz. Yazmazsan ya bir sonraki çalıştırmada sorun geri gelir ya da kimse çalıştırmadığı için kod ile gerçek birbirinden kopar.
IaC kodunu nasıl test ederim#
En basit ve en etkili katman söz dizimi ve biçim kontrolüdür: terraform validate, terraform fmt -check ve ansible-lint. Bir üst katman kuru çalıştırmadır (terraform plan, ansible-playbook --check); değişikliği uygulamadan etkisini gösterir. En kapsamlısı ise kodu geçici bir test ortamında gerçekten uygulayıp sonucu doğrulamak ve ortamı silmektir; bunu CI hattında otomatikleştirmek en sağlam güvencedir.
Kapanış#
Infrastructure as Code'un değeri araçlarda değil, birkaç alışkanlıkta toplanır: altyapının istenen durumunu depoda tut, uygulamadan önce planı oku, elle yaptığın her değişikliği koda geri yaz ve gizli bilgileri asla koda gömme. Buna düzenli kayma kontrolü ve sabitlenmiş sağlayıcı sürümlerini eklediğinde, sunucu kurmak hatırlamaya çalışılan bir ritüel olmaktan çıkıp gözden geçirilebilir bir değişikliğe dönüşür.
Bu otomasyonu deneyebileceğin, root erişimiyle sıfırdan kurup silebileceğin ortamlar için VDS, sanal sunucu ve bulut sunucu paketlerimize göz atabilirsin. Altyapı kurulumu, güvenlik sıkılaştırması ve bakımını kendin üstlenmek yerine devretmeyi tercih edersen sunucu yönetimi hizmetimiz bu işi baştan sona yürütür; kurduğun her şeyin geri dönüş noktası için de yedekleme çözümlerimiz var.