Ansible mı Terraform mu: Hangisine İhtiyacınız Var?
Terraform VPS'yi oluşturur, Ansible yapılandırır. Gerçek görev ayrımını, provisioner sorunlarını, devir komutlarını ve yalnızca Ansible'ın yeterli olduğu durumları öğrenin.
Bir cümlede Ansible ve Terraform
Ansible ve Terraform, aynı işi yapan iki araç arasında seçim yapmak anlamına gelmez. Terraform hangi altyapının mevcut olduğunu bildirir: sunucular, diskler, ağlar ve DNS kayıtları. Ansible ise zaten mevcut olan bir makinenin içinde hangi durumun geçerli olması gerektiğini bildirir: paketler, kullanıcılar, yapılandırma dosyaları ve çalışan hizmetler. Terraform VPS oluşturur. Ansible bu VPS'yi web sunucusuna dönüştürür.
Her ikisi de bildirimseldir ve her ikisine de kod olarak altyapı (IaC) denir. Asıl fark, neyi hatırladıklarıdır. Terraform, kodunuzdaki her kaynağı bir API aracılığıyla oluşturduğu gerçek nesneyle eşleyen bir durum dosyası yazar. Böylece beş satırın silinmesi gerektiğinde bir sunucunun yok edilmesi gerektiğini belirleyebilir. Ansible çalıştırmalar arasında hiçbir şeyi hatırlamaz. SSH üzerinden bağlanır, makineyi inceler ve yalnızca playbook ile zaten eşleşmeyen durumları değiştirir.
Bu tek fark, iki görevin tek bir araçta birleştirilmesinin neden sorunlara yol açtığı da dahil olmak üzere bu kılavuzun geri kalanını açıklar.
Terraform gerçekte ne yapar
Terraform, bir provider eklentisi üzerinden bir API ile iletişim kurar. Provider kayıt defteri sayfası, yazabileceğiniz kaynak türlerini tanımlar. Bu nedenle bir ana bilgisayardaki sunucu ile başka bir ana bilgisayardaki sunucu, farklı bağımsız değişkenlere sahip farklı kaynak adlarıdır.
resource "cloud_server" "web" {
name = "web1"
image = "ubuntu-24.04"
type = "small"
}
output "web_ip" {
value = cloud_server.web.ipv4_address
}cloud_server yerine provider belgelerinde tanımlanan kaynak türünü yazın. output bloğu bu kılavuz için önemlidir, çünkü adresin Terraform'dan nasıl çıktığını gösterir.
terraform init
terraform fmt -check
terraform validate
terraform plan -out=tfplan
terraform apply tfplanterraform init provider'ı indirir ve bir kilit dosyası yazar. terraform plan kodunuz ile durum dosyası arasındaki farkı yazdırır ve Plan: 1 to add, 0 to change, 0 to destroy. gibi bir satırla sona erer. Bu satır her seferinde okunmalıdır. Bazı bağımsız değişkenler yerinde değiştirilemez. Plan, özniteliğin yanında # forces replacement göstererek bunu belirtir ve ardından 1 to add, 0 to change, 1 to destroy gelir. Bu plan uygulanırsa sunucu silinir ve yeni, boş bir sunucu oluşturulur. Güvenli olduğunu düşündükleri verileri kullanıcıların kaybetmesinin nedeni budur.
Planın bir dosyaya kaydedilmesi ve doğrudan terraform apply çalıştırmak yerine dosyanın uygulanması, incelenen planın çalıştırılmasını sağlar. İki komut arasında başka biri altyapıyı değiştirmiş olabilir.
terraform.tfstate bellektir. Bu dosya kaybedilirse Terraform artık bu sunucuların size ait olduğunu bilmez. Bu nedenle sonraki apply yinelenen sunucular oluşturmaya çalışır. Komutları birden fazla kişi çalıştırmaya başladığı anda dosya uzak bir backend'de tutulmalıdır. Çünkü iki kişinin aynı anda apply çalıştırması aşağıdaki sonucu üretir:
Error: Error acquiring the state lockOpenTofu, aynı komutlara ve aynı dosya biçimine sahip bir Terraform fork'udur. Temmuz 2026 itibarıyla bu kılavuzdaki her şey, terraform yerine tofu yazıldığında çalışır.
Ansible'in gerçekte yaptığı
Ansible için aracı ve API gerekmez. Bir SSH bağlantısı açar, hedefe küçük bir Python modülü kopyalar, modülü çalıştırır ve siler. SSH ve sudo parolasıyla erişilebilen her şeyi Ansible yapılandırabilir.
- name: Base web server
hosts: web
become: true
tasks:
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
- name: Ensure nginx is running at boot
ansible.builtin.service:
name: nginx
state: started
enabled: trueansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.yml --check --diff
ansible-playbook -i inventory.ini site.ymlping modülü, playbook'ta hata ayıklamaya başlamadan önce SSH, Python ve sudo'yu doğrular. Sağlıklı sonuç web1 | SUCCESS => {"ping": "pong"} olur. --check --diff çalıştırması, Ansible'da plan kavramına en yakın işlemdir: herhangi bir değişiklik yapmadan nelerin değişeceğini bildirir. Ancak önceki görevlere bağlı görevler check mode'da yanlış sonuç bildirebilir. Bunun nedeni, önceki değişikliğin gerçekte uygulanmamış olmasıdır.
Her çalıştırma ok=6 changed=2 unreachable=0 failed=0 gibi bir özetle sona erer. Aynı playbook'u iki kez çalıştırın. İkinci çalıştırma changed=0 bildirmelidir. Her çalıştırmada changed bildiren bir görev idempotent değildir. Bu görev genellikle gerçek bir modül olması gerekirken command veya shell olan bir görevdir. Bu konuya yeni başlanıyorsa tek bir VPS üzerinde ilk Ansible playbook'u ile başlanıp buradan ilerlenmelidir.
İki aracın kesiştiği ve çakıştığı noktalar
Terraform, yeni bir sunucuda remote-exec provisioner ile komut çalıştırabilir. HashiCorp'un kendi belgelerinde provisioner'ların son çare olarak kullanılması gerektiği belirtilir. Bunun iyi nedenleri vardır.
Bir provisioner yalnızca kaynak oluşturulduğunda çalışır. Betik düzenlendiğinde mevcut sunucuda hiçbir işlem gerçekleşmez; çünkü Terraform açısından kaynak hâlâ kodla eşleşmektedir. Provisioner adımları hiçbir zaman terraform plan içinde görünmez. Bu nedenle yapılan incelemede bu adımlara ilişkin herhangi bir kayıt bulunmaz. Betik başarısız olursa Terraform kaynağı tainted olarak işaretler. Sonraki apply işlemi, büyük olasılıkla sorunsuz olan bir sunucuyu silip yeniden oluşturur.
Hata ayrıca uygun olmayan bir zamanda ortaya çıkar. Sağlayıcı, API sunucunun oluşturulduğunu bildirir bildirmez sunucuyu oluşturulmuş kabul eder. O sırada işletim sistemi hâlâ başlatılıyor olabilir ve sshd henüz dinleme yapmıyor olabilir.
Error: remote-exec provisioner error
timeout - last error: dial tcp 203.0.113.10:22: connect: connection refusedAnsible için bunun tersi yönde bir eğilim vardır. Cloud modülleri sunucu oluşturabilir ve birkaç makine için bu yaklaşım işe yarar. Ancak bağımlılık grafiğinden ve durum dosyasından vazgeçilmiş olur. Ansible bir kaynağı sorunsuz şekilde oluşturabilir. Ancak görevi playbook'tan silerseniz kaynak çalışmaya ve ücretlendirmeye devam eder; çünkü kaynağın size ait olduğunu kaydeden herhangi bir mekanizma yoktur.
Buradan çıkan kural şudur: API aracılığıyla oluşturulan ve silinen nesnelerin yönetimi Terraform'a bırakılmalı, başlatılmış bir işletim sisteminin içindeki her şeyin yönetimi ise Ansible'a bırakılmalıdır.
Devir teslimi, uygulamalı
Devir teslimi bir sınırdır, entegrasyon değildir. Terraform işlemi tamamlar, bir adres yayımlar ve durur. Ansible bu adres üzerinden başlar.
terraform apply -auto-approve
terraform output -raw web_ip
printf '[web]\n%s ansible_user=root\n' "$(terraform output -raw web_ip)" > inventory.ini
ansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.ymlterraform output -raw, kabuk değişken yerine koyma işlemi içinde kullanılması gereken biçimde, tırnak işareti ve JSON sarmalayıcısı olmadan tek bir değer yazdırır. Birden fazla sunucu için terraform output -json kullanılmalı ve envanter buradan oluşturulmalıdır; çünkü -raw yalnızca tek bir dizeyi, sayıyı veya boole değerini işler.
İki araç arasındaki ping adımı korunmalıdır. Bu adım, "Terraform bana yanlış adresi verdi" sorununu "playbook'umda hata var" sorunundan ayırır. Playbook yeni makineye ilk erişen bileşense, bu iki sorun aynı görünür.
Terraform durumunu Ansible envanteri olarak okuma
Bir envanter dosyası hiç yazmak istemiyorsanız cloud.terraform koleksiyonu durumu doğrudan okur.
ansible-galaxy collection install cloud.terraformterraform.yml dosyasını playbook dosyanızın yanına yazın:
plugin: cloud.terraform.terraform_provider
project_path: /home/deploy/infraansible-inventory -i terraform.yml --graph
ansible-playbook -i terraform.yml site.ymlBuna güvenmeden önce bilinmesi gereken iki nokta vardır. Eklenti, project_path üzerinde terraform show çalıştırır. Bu nedenle ilgili dizinin önceden başlatılmış olması gerekir; aksi halde eklenti başarısız olur. Ayrıca sunucu kaynaklarından otomatik olarak host oluşturmaz. Terraform kodunda Ansible provider kullanılarak tanımlanan ansible_host ve ansible_group kaynaklarını okur. Bunları eklemediğiniz sürece ansible-inventory --graph içinde hiçbir şey görünmez.
Oluşturulan düz bir envanter dosyasının hata ayıklaması daha kolaydır ve herhangi bir provider ile çalışır. Eklenti, envanter birkaç makineyi aştığında ve elle düzenleme yazım hatalarına yol açmaya başladığında yararlı olur. Bu, birden çok Linux sunucusunu tek bir kontrol makinesinden yönetmenin alışkanlık olmaktan çıkıp gerçek bir iş akışına dönüştüğü noktadır.
Terraform'a gerçekten ihtiyacınız var mı?
Bunu okuyan kişilerin çoğunun henüz Terraform'a ihtiyacı yoktur. Terraform, altyapı oluşturma ve kaldırma işlemleri tekrarlanan görevler olduğunda maliyetini karşılar. Bir kontrol paneli üzerinden tek bir VPS sipariş ettiyseniz ve bunu iki yıl boyunca kullanmayı planlıyorsanız Terraform, yalnızca bir kez gerçekleşecek bir işlemi tanımlar ve kaybetmemeniz gereken bir durum dosyası ekler.
Ortamları sık sık yeniden oluşturuyorsanız, staging ortamının production ortamıyla tam olarak eşleşmesi gerekiyorsa, birden fazla kişi altyapıyı değiştiriyor ve herhangi bir şey silinmeden önce incelenebilir bir plan istiyorsanız veya yönettiğiniz kaynaklar sunucuların ötesine geçerek bir provider API'sinde bulunan DNS kayıtlarını, load balancer'ları ve firewall kurallarını kapsıyorsa Terraform kullanın.
Sunucular uzun süre çalışıyor ve sayıları azsa ve günlük soru "bu sunucu doğru yapılandırılmış mı" yerine "bu sunucu var mı" ise yalnızca Ansible kullanmaya devam edin. Yeni bir sunucuyu güvenli hale getiren tek bir playbook, yeni bir VPS'te ilk on dakikada yapılan işlemlerin aynısını kapsar. Ayrıca sonraki sunucuda da aynı şekilde çalıştırılabilir.
Öğrenme sırası da buradan çıkar. Ansible, sahip olduğunuz ilk sunucuda karşılığını verir. Terraform ise yeniden oluşturduğunuz üçüncü ortamda karşılığını verir.
Devir sırasında ne bozulur
Sunucu hazır değil. Terraform başarılı olur, ancak Ansible hemen başarısız olur.
fatal: [web1]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 203.0.113.10 port 22: Connection refused", "unreachable": true}API, sshd dinlemeye başlamadan önce bir adres döndürdü. Sabit bir bekleme süresi eklemek yerine portu bekleyin. Ansible'da bunun için ansible.builtin.wait_for_connection bulunur; bunu play'in ilk görevi olarak çalıştırın.
Ana makine anahtarı değişti. Sunucuyu sildiniz ve yeniden oluşturdunuz. Yeni sunucu aynı adreste yeni bir anahtarla yanıt veriyor.
Host key verification failed.Eski girdiyi ssh-keygen -R 203.0.113.10 ile kaldırın. Terraform yeniden oluşturma işlemlerini gerçekleştirdiğinde bu durum sürekli ortaya çıkar. Bu nedenle veri barındıran makinelerde yeniden oluşturma işlemlerinin seyrek yapılması gerekir.
Sudo başarısız oluyor. fatal: [web1]: FAILED! => {"msg": "Missing sudo password"}, become: true için bu ana makinede parola gerektiği anlamına gelir. Dağıtım kullanıcısı için parolasız sudo yapılandırın veya --ask-become-pass değerini geçirin.
Terraform, değiştirmediğiniz bir şeyi yok etmek istiyor. Plan, yazmadığınız değişiklikleri gösteriyor. Bu, genellikle bir kişinin provider'ın web panelinde bir ayarı değiştirmesi nedeniyle gerçek altyapının koddan farklılaştığı anlamına gelir. Bu farkı tek başına görmek için terraform plan -refresh-only çalıştırın. Ardından kodun mu yoksa canlı kaynağın mı yanlış olduğuna karar verin. Satır satır açıklayamadığınız yıkıcı bir planı hiçbir zaman uygulamayın.
Ansible her çalıştırmada changed bildiriyor. creates veya when koşulu bulunmayan bir shell görevi koşulsuz olarak çalışır. Bu yalnızca görsel bir sorun değildir. Çünkü artık changed=0 değerini, sunucunun istenen durumda olduğuna ilişkin gösterge olarak kullanamazsınız.
FAQ
Terraform, Ansible'ın yerini alabilir mi?
Sunucu içi yapılandırma için hayır. Terraform, remote-exec provisioner'ı ile betikleri çalıştırabilir. Ancak bunlar yalnızca kaynak oluşturulurken çalışır, terraform plan içinde görünmez ve başarısız olduklarında kaynağı tainted durumuna getirir. Bu durum, sonraki apply işleminde kaynağın silinip yeniden oluşturulmasını planlar. Terraform'da nginx'in zaten kurulu olup olmadığını denetleyen ve kuruluysa işlem yapmayan bir module eşdeğeri yoktur. Makineyi oluşturmak için Terraform kullanılmalı, ardından yönetim devredilmelidir.
Ansible, Terraform'ın yerini alabilir mi?
Az sayıda uzun süre çalışan sunucu için evet. Ansible'da sunucu oluşturan cloud module'ları bulunur. 2 VPS instance sipariş edilip bunlar korunacaksa bu yeterlidir. Ancak state file ve dependency graph kaybedilir. Playbook'tan bir task kaldırıldığında kaynak çalışmaya ve ücretlendirmeye devam eder. Bunun nedeni, Ansible'ın kaynağı kendisinin oluşturduğunu hiçbir zaman kaydetmemesidir. Terraform ise bir destroy işlemi planlardı.
Önce hangisini öğrenmeliyim?
Günümüzde sunucular yönetiliyorsa Ansible öğrenilmelidir. İlk makinede fayda sağlar, SSH dışında bir gereksinimi yoktur ve edinilen beceri elle sipariş edilen bir sunucuda da kullanılabilir. Terraform daha sonra, ortamlar tekrar tekrar yeniden oluşturulduğunda veya sunucuların yanı sıra DNS records ve firewall rules gibi provider resources da yönetildiğinde fayda sağlar.
Terraform'dan yeni sunucu IP adresini Ansible'a nasıl aktarırım?
Terraform kodunda bir output tanımlanmalı, ardından bu değer apply işleminden sonra okunmalıdır. terraform output -raw web_ip, shell substitution için yalın değeri yazdırır. terraform output -json ise birden fazla host olduğunda tüm output değerlerini aynı anda sağlar. Bu değerler bir inventory file'a yazılmalı veya cloud.terraform collection'ı kurulup ansible-inventory -i terraform.yml --graph project directory'ye yönlendirilmelidir.
Terraform tamamlandıktan hemen sonra playbook neden başarısız oluyor?
Provider, API sunucunun oluşturulduğunu bildirir bildirmez sunucuyu oluşturulmuş olarak raporlar. Ancak işletim sistemi hâlâ başlatılıyor olabilir. Bu nedenle ilk saniyelerde SSH bağlantısı reddedilir. Hata, UNREACHABLE! ile birlikte Connection refused şeklindedir. Uyku süresi tahmin etmek yerine ansible.builtin.wait_for_connection play içindeki ilk task yapılmalıdır. Başlatma süresi image ve plan'a göre değişir.