Docker & DevOps

    Terraform Nedir: Altyapıyı Kod ile Yönetmek

    Terraform ile altyapıyı kod olarak tanımlamanın mantığı, HCL temelleri ve temel iş akışı.

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

    Bir altyapıyı panelden tıklayarak kurmak ilk seferde hızlıdır. Sorun ikinci seferde başlar: aynı ortamı test için bir kez daha kurmanız gerektiğinde hangi ayarları seçtiğinizi hatırlamazsınız, altı ay sonra bir kaynağın neden orada olduğunu kimse bilemez ve "kim ne zaman değiştirdi" sorusunun cevabı hiçbir yerde yazmaz. Terraform, altyapının tamamını okunabilir metin dosyalarına yazmanızı ve o dosyaları gerçeğe dönüştürmeyi üstlenmenizi sağlar; böylece sunucularınız da uygulama kodunuz gibi versiyon kontrolünde, gözden geçirilebilir ve tekrarlanabilir hâle gelir.

    Bu rehberde Terraform'a sıfırdan başlıyoruz. Altyapıyı kod ile yönetmenin (IaC) ne anlama geldiğini, Terraform kurulumunu, HCL dilinin temel yapı taşlarını (provider, resource, variable, output, data), init, plan, apply, destroy döngüsünü ve bildirimsel yaklaşımın nasıl bir bağımlılık grafiğine dönüştüğünü göreceksiniz. Sonda Terraform ile Ansible'ın nerede ayrıldığını netleştirip yeni başlayanların en pahalı hatalarını sıralayacağım.

    Altyapıyı Kod ile Yönetmek Ne Demek#

    Altyapıyı kod olarak yönetmek (Infrastructure as Code), sunucu, ağ, disk, güvenlik duvarı kuralı, DNS kaydı gibi her bileşeni bir yapılandırma dosyasında tanımlamak demektir. Bu dosyalar deponuzda durur, pull request ile gözden geçirilir, sürüm etiketi alır ve gerektiğinde geri alınabilir. Panelde yapılan bir tıklama hiçbir iz bırakmazken, kodda yapılan bir değişiklik kim tarafından, ne zaman ve hangi gerekçeyle yapıldığını taşır.

    Terraform'un bu alandaki ayırt edici özelliği bildirimsel olmasıdır. Ona "şu adımları uygula" demezsiniz; "sonuç şöyle olsun" dersiniz. Terraform mevcut durumu okur, hedef durumla karşılaştırır ve aradaki farkı kapatmak için gereken işlemleri kendisi planlar. Bir kaynağı dosyadan silerseniz Terraform onu yok eder; bir parametreyi değiştirirseniz kaynağı günceller ya da gerekiyorsa yeniden yaratır.

    YaklaşımNasıl ifade edilirÖrnek araçZayıf yanı
    Elle (panel/SSH)Tıklama, komutTekrarlanamaz, izi yok
    Buyurgan betik"Şunu yap, sonra şunu"Kabuk betiğiİkinci çalıştırmada bozulur
    Bildirimsel IaC"Sonuç şu olsun"TerraformDurum dosyası yönetimi gerekir
    Yapılandırma yönetimi"Sunucunun içi şöyle olsun"AnsibleKaynak yaratmaz

    Terraform'un çalışabilmesi için üçüncü bir bileşene daha ihtiyacı vardır: durum dosyası (state). Terraform, yönettiği kaynakların gerçek kimliklerini bu dosyada tutar; onsuz hangi kaynağın kendisine ait olduğunu bilemez. Durum yönetimi kendi başına bir konu olduğu için ayrıntısını Terraform state yönetimi yazısına bıraktım, ama burada da temel davranışına değineceğim.

    Terraform Kurulumu#

    Terraform tek bir çalıştırılabilir dosyadan ibarettir; kurulumu bir ikili dosyayı PATH üzerindeki bir dizine koymaktan ibarettir. Resmî depoyu ekleyerek paket yöneticisiyle kurmak, güncellemeleri kolaylaştırır:

    # Debian / Ubuntu — HashiCorp deposu
    sudo apt update && sudo apt install -y gnupg software-properties-common curl
    curl -fsSL https://apt.releases.hashicorp.com/gpg \
      | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp.gpg
    echo "deb [signed-by=/usr/share/keyrings/hashicorp.gpg] \
    https://apt.releases.hashicorp.com $(lsb_release -cs) main" \
      | sudo tee /etc/apt/sources.list.d/hashicorp.list
    sudo apt update && sudo apt install -y terraform
    
    # AlmaLinux / Rocky
    sudo dnf install -y dnf-plugins-core
    sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
    sudo dnf install -y terraform
    

    Kurulumu doğrulayın ve kabuk tamamlamayı açın; komut adlarını ve kaynak isimlerini elle yazmak yerine sekme ile tamamlamak hata oranını gözle görülür azaltır:

    terraform -version
    # Terraform v1.x.x on linux_amd64
    
    terraform -install-autocomplete
    

    Proje dizininizde birkaç dosyayı en baştan oluşturmak iyi bir alışkanlıktır. Terraform dizindeki tüm .tf dosyalarını birlikte okur, dolayısıyla ayrım tamamen okunabilirlik içindir:

    altyapi/
    ├── main.tf         # kaynaklar
    ├── variables.tf    # girdi değişkenleri
    ├── outputs.tf      # çıktılar
    ├── versions.tf     # terraform ve provider sürüm kısıtları
    ├── terraform.tfvars # gerçek değerler (sırlar buraya YAZILMAZ)
    └── .gitignore
    

    .gitignore dosyasına şunları mutlaka ekleyin; durum dosyası ve indirilen sağlayıcı ikilikleri asla depoya girmemelidir:

    .terraform/
    *.tfstate
    *.tfstate.*
    crash.log
    *.tfvars.secret
    

    HCL Temelleri: Blok Blok Yapı Taşları#

    Terraform, HCL adlı kendi yapılandırma dilini kullanır. Dil birkaç blok tipinden oluşur ve hepsini bir sayfada öğrenebilirsiniz. Önce sürüm kısıtlarını sabitleyelim; bu, altı ay sonra farklı bir makinede aynı sonucu almanın en temel şartıdır:

    # versions.tf
    terraform {
      required_version = ">= 1.5.0"
    
      required_providers {
        docker = {
          source  = "kreuzwerker/docker"
          version = "~> 3.0"
        }
        random = {
          source  = "hashicorp/random"
          version = "~> 3.5"
        }
      }
    }
    

    provider bloğu, Terraform'un hangi platformla konuşacağını ve nasıl kimlik doğrulayacağını söyler. Sağlayıcı, bulut API'si de olabilir, yerel Docker soketi de, bir DNS servisi de:

    # main.tf
    provider "docker" {
      host = "unix:///var/run/docker.sock"
    }
    

    variable bloğu girdileri tanımlar. Tip belirtmek ve doğrulama eklemek, hatalı değerin apply anında değil plan anında yakalanmasını sağlar:

    # variables.tf
    variable "ortam" {
      description = "Ortam adı: test veya uretim"
      type        = string
      default     = "test"
    
      validation {
        condition     = contains(["test", "uretim"], var.ortam)
        error_message = "ortam yalnızca test veya uretim olabilir."
      }
    }
    
    variable "web_port" {
      description = "Dışarıya açılacak HTTP portu"
      type        = number
      default     = 8080
    }
    

    resource bloğu asıl işi yapar: yaratılacak nesneyi tanımlar. İki etiket alır — kaynak tipi ve bu yapılandırma içindeki yerel adı:

    resource "random_password" "db" {
      length  = 24
      special = true
    }
    
    resource "docker_image" "nginx" {
      name         = "nginx:stable-alpine"
      keep_locally = false
    }
    
    resource "docker_container" "web" {
      name  = "web-${var.ortam}"
      image = docker_image.nginx.image_id
    
      ports {
        internal = 80
        external = var.web_port
      }
    
      restart = "unless-stopped"
    }
    

    data bloğu ise var olan bir şeyi okur, yaratmaz. Terraform'un yönetmediği bir kaynağa referans vermek için kullanılır. output bloğu da apply sonunda ekrana basılacak ve başka modüllerin okuyabileceği değerleri tanımlar:

    # outputs.tf
    output "web_adresi" {
      description = "Servisin erişilebileceği adres"
      value       = "http://185.12.34.56:${var.web_port}"
    }
    
    output "db_parolasi" {
      value     = random_password.db.result
      sensitive = true   # ekrana basılmaz
    }
    

    ⚠️ sensitive = true çıktının ekrana basılmasını engeller ama durum dosyasında değeri düz metin olarak saklamayı engellemez. Terraform state, içindeki her şeyi okunabilir tutar; bu yüzden durum dosyasını şifreli ve erişimi kısıtlı bir yerde tutmak zorunludur.

    Yaşam Döngüsü: init, plan, apply, destroy#

    Terraform ile çalışmak dört komuta indirgenir ve bu sıra neredeyse hiç değişmez.

    1. terraform init — Sağlayıcıları indirir, .terraform/ dizinini kurar, arka uç (backend) yapılandırmasını başlatır ve bir kilit dosyası (.terraform.lock.hcl) üretir. Bu kilit dosyası depoya commit edilmelidir; sağlayıcı sürümlerinin herkeste aynı olmasını sağlar.
    2. terraform plan — Mevcut durumu gerçek dünyayla karşılaştırır ve ne yapılacağını gösterir. Hiçbir değişiklik uygulamaz.
    3. terraform apply — Planı uygular. Onay ister; onay olmadan hiçbir şey yapmaz.
    4. terraform destroy — Yapılandırmadaki tüm kaynakları yok eder.
    cd altyapi
    
    # 1) Başlat
    terraform init
    # Initializing provider plugins...
    # Terraform has been successfully initialized!
    
    # Biçimlendir ve doğrula (alışkanlık hâline getirin)
    terraform fmt -recursive
    terraform validate
    
    # 2) Ne olacağını gör ve planı dosyaya kaydet
    terraform plan -out=degisiklik.tfplan
    # Plan: 3 to add, 0 to change, 0 to destroy.
    
    # 3) Tam olarak o planı uygula
    terraform apply degisiklik.tfplan
    
    # Durumdaki kaynakları listele
    terraform state list
    # docker_container.web
    # docker_image.nginx
    # random_password.db
    
    # 4) Her şeyi kaldır
    terraform destroy
    

    Planı dosyaya kaydedip onu uygulamak (-out ile) küçük ama önemli bir disiplindir: plan ile apply arasında geçen sürede altyapı değişmiş olabilir, kaydedilmiş plan uygulandığında sürpriz yaşamazsınız. Sürekli entegrasyon hatlarında bu neredeyse zorunludur.

    Plan çıktısındaki sembolleri okumayı öğrenin; hepsi ilk karakterden anlaşılır:

    SembolAnlamıDikkat
    +Yeni kaynak yaratılacakNormal
    ~Kaynak yerinde güncellenecekGenellikle güvenli
    -Kaynak yok edilecekVeri kaybı riski
    -/+Yok edilip yeniden yaratılacakEn riskli; kesinti demek
    <=Yalnızca veri okunacakDeğişiklik yok

    -/+ işaretini gördüğünüzde durun ve nedenini okuyun. Terraform hangi parametrenin yeniden yaratmayı tetiklediğini # forces replacement notuyla yazar. Bir veritabanı sunucusunun yeniden yaratılması, üzerindeki verinin gitmesi anlamına gelebilir.

    Bildirimsel Yaklaşım ve Bağımlılık Grafiği#

    Terraform, yapılandırmanızı okuduğunda bir bağımlılık grafiği kurar. docker_container.web kaynağı docker_image.nginx.image_id değerine referans verdiği için Terraform, imajın konteynerden önce yaratılması gerektiğini kendiliğinden anlar. Siz sıralamayı hiçbir yerde yazmazsınız; referanslar sıralamayı belirler.

    Bu, dosyalarınızdaki blok sırasının anlamsız olduğu anlamına gelir — kaynakları istediğiniz sırayla yazabilirsiniz. Bağımlılığı Terraform çıkaramadığı durumlarda (örneğin bir kaynak diğerine hiç referans vermiyor ama yine de sonra çalışması gerekiyorsa) açıkça belirtirsiniz:

    resource "docker_container" "uygulama" {
      name  = "uygulama-${var.ortam}"
      image = docker_image.nginx.image_id
    
      # Açık bağımlılık: veritabanı konteyneri hazır olmadan başlama
      depends_on = [docker_container.veritabani]
    }
    

    Grafiği görselleştirmek, karmaşık bir yapılandırmayı anlamanın en hızlı yoludur:

    # Grafiği DOT biçiminde bas
    terraform graph > grafik.dot
    
    # Belirli bir kaynağı neden değiştirdiğini anlamak için
    terraform plan -target=docker_container.web
    

    ⚠️ -target bayrağı bir hata ayıklama aracıdır, günlük iş akışı değil. Onunla yapılan bir apply, yapılandırmanın geri kalanını yok sayar ve durumu yapılandırmadan sapmış bırakabilir. Acil bir durumda kullanın, ardından hedefsiz tam bir plan çalıştırıp her şeyin tutarlı olduğunu doğrulayın.

    Bildirimsel modelin bir başka sonucu, yapılandırma dışı değişikliklerin geri alınmasıdır. Birisi panelden bir ayarı elle değiştirirse, bir sonraki plan bunu fark eder ve dosyadaki hâline döndürmeyi önerir. Bu davranış özellik olarak tasarlanmıştır: tek doğru kaynak koddur. Elle yapılan değişikliklerin kalıcı olmasını istiyorsanız onları koda taşımanız gerekir.

    Terraform mu Ansible mı: İkisi de#

    Bu iki araç sık sık karşılaştırılır ama aslında farklı katmanlarda çalışırlar. Terraform kaynak yaratır: sunucuyu açar, diski ekler, ağı kurar, DNS kaydını yazar. Ansible sunucunun içini yapılandırır: paket kurar, dosya yerleştirir, servis yönetir. Bir sunucuyu Terraform ile açıp Ansible ile kurmak, sektörde en yaygın kullanılan düzendir.

    SoruTerraformAnsible
    ModelBildirimselAğırlıklı bildirimsel, buyurgan da olabilir
    Durum takibiEvet (state dosyası)Hayır (her çalıştırmada gerçeği okur)
    Kaynak yaratmaGüçlü yanıSınırlı
    İşletim sistemi yapılandırmaSınırlıGüçlü yanı
    Kaynak silmeKod'dan silince yok ederAçıkça yazmanız gerekir
    Ajan gerekir miHayırHayır (SSH yeter)

    Pratikte akış şöyle kurulur: Terraform sunucuları açar ve IP adreslerini output olarak verir, bu çıktı Ansible envanterine beslenir, Ansible da sunucuların üzerini kurar. Ansible tarafındaki temel kavramlar için Ansible kurulumu ve ilk playbook, tekrar kullanılabilir yapı için Ansible rol yapısı yazılarına bakabilirsiniz. Konteyner tarafını yeni öğreniyorsanız Docker nedir yazısı bu rehberdeki Docker örneklerini oturtmanıza yardımcı olur.

    Sık Yapılan Hatalar ve Tuzaklar#

    Durum dosyasını Git'e koymak. terraform.tfstate içinde çıktılar, kaynak kimlikleri ve çoğu zaman parolalar düz metin olarak durur. Depoya girdiğinde bunların hepsi ifşa olur. Ayrıca iki kişi aynı anda çalıştığında dosya çakışır ve durum bozulur. Durum uzak bir arka uçta, kilitleme desteğiyle tutulmalıdır.

    Sürümleri sabitlememek. required_providers içinde sürüm kısıtı yazmazsanız, üç ay sonra init çalıştıran biri yeni bir ana sürüm indirir ve yapılandırmanız çalışmaz hâle gelir. Hem required_version hem sağlayıcı sürümlerini kısıtlayın ve .terraform.lock.hcl dosyasını commit edin.

    Sırları .tfvars içine düz yazmak. Değişken değer dosyaları sıradan metindir. Parolaları ortam değişkeniyle (TF_VAR_db_parolasi) geçirin ya da bir sır kasasından okuyun; .tfvars dosyasını depoya koyacaksanız içinde sır bulunmadığından emin olun.

    plan çıktısını okumadan apply demek. Terraform onay istediğinde çıktının sonundaki özet satırına bakın: 3 to add, 1 to change, 2 to destroy. Beklemediğiniz bir destroy varsa "yes" yazmayın. Otomatik hatlarda -auto-approve kullanacaksanız, öncesinde planı bir insanın onayladığından emin olun.

    Elle değişiklik yapıp koda yansıtmamak. Panelden yapılan bir düzeltme bir sonraki apply ile geri alınır. Acil müdahalede elle bir şey değiştirdiyseniz, aynı gün içinde kodu güncelleyin; yoksa "kod ne diyor, gerçek ne" ayrımı büyür ve bir gün beklenmedik bir silme işlemine dönüşür.

    Her şeyi tek bir dizine yığmak. Test ve üretim aynı durum dosyasını paylaşırsa, test için yaptığınız bir değişiklik üretimi etkileyebilir. Ortamları ayrı dizinlerde ve ayrı durum dosyalarında tutun; ortak parçaları modül hâline getirip her ortamdan çağırın.

    Sıkça Sorulan Sorular#

    Terraform ücretsiz mi#

    Terraform'un komut satırı aracı açık kaynaklıdır ve ücretsiz kullanılır; bu rehberdeki her şey ücretsiz sürümle yapılabilir. HashiCorp'un barındırdığı ekip çalışması, uzak durum saklama, çalıştırma geçmişi ve politika denetimi sunan bulut hizmeti ise ücretli katmanlar içerir, ancak küçük ekipler için ücretsiz bir seviyesi de vardır. Durum dosyanızı kendi nesne depolamanızda tutarak hiçbir ücret ödemeden ekipçe çalışabilirsiniz.

    Terraform ile Ansible arasında hangisini öğrenmeliyim#

    Sorunuz "sunucuyu nasıl açarım" ise Terraform, "sunucunun içini nasıl kurarım" ise Ansible öğrenmelisiniz. Çoğu ekip ikisini birlikte kullanır: Terraform altyapıyı sağlar, Ansible üzerine yapılandırmayı kurar. Elinizde zaten açılmış sunucular varsa ve tek ihtiyacınız onları tutarlı biçimde kurmaksa Ansible ile başlamak daha hızlı fayda verir; sık sık ortam açıp kapatıyorsanız Terraform öncelik kazanır.

    terraform plan gerçekten hiçbir şey değiştirmez mi#

    Evet, plan yalnızca okuma yapar ve hiçbir kaynağı yaratmaz, değiştirmez veya silmez. Ancak mevcut durumu öğrenmek için sağlayıcı API'sine okuma istekleri gönderir ve durum dosyasını gerçek dünyayla eşitler (refresh). Bu eşitleme, durum dosyasında bir güncelleme oluşturabilir. Gerçek kaynaklarınıza dokunmadığı için güvenle çalıştırabilirsiniz; şüphedeyseniz -refresh=false ile bu eşitlemeyi de kapatabilirsiniz.

    Var olan altyapımı Terraform'a nasıl taşırım#

    Elle kurulmuş kaynakları terraform import komutuyla ya da yapılandırmanızda import bloğu tanımlayarak duruma alabilirsiniz. Yöntem şudur: önce kaynağın Terraform karşılığını koda yazarsınız, sonra gerçek kimliğiyle içe aktarırsınız, ardından plan çalıştırıp çıktının boş olmasını beklersiniz. Çıktı boş değilse kodunuz gerçekle uyuşmuyor demektir ve farkları kapatana kadar apply çalıştırmamalısınız.

    Terraform durum dosyasını kaybedersem ne olur#

    Terraform hangi kaynakların kendisine ait olduğunu unutur; bir sonraki apply var olan kaynakları görmez ve hepsini yeniden yaratmaya çalışır. Bu, üretim ortamında ciddi bir kaza demektir. Bu yüzden durum uzak bir arka uçta, sürümlemeli ve yedeklenen bir konumda tutulmalıdır. Kaybedilmiş bir durumu kurtarmanın tek yolu, tüm kaynakları tek tek içe aktarmaktır ve bu, büyük bir altyapıda günler alabilir.

    Aynı kodu birden fazla ortam için nasıl kullanırım#

    En yaygın iki yöntem vardır. Birincisi, ortak kaynakları bir modüle koyup her ortam için ayrı bir dizin açmak ve o dizinden modülü farklı değişkenlerle çağırmaktır; her ortamın kendi durum dosyası olur, en net ayrım budur. İkincisi çalışma alanı (workspace) kullanmaktır; aynı kod farklı durum dosyalarıyla çalışır ama yanlışlıkla yanlış alanda apply yapma riski taşır. Üretim söz konusuysa ayrı dizin yaklaşımı daha güvenlidir.

    Terraform sunucumun içine paket kurabilir mi#

    Doğrudan bunun için tasarlanmamıştır. provisioner blokları ile uzak komut çalıştırabilirsiniz ama HashiCorp bunu son çare olarak önerir; çünkü sağlayıcılar hata durumunda temiz bir geri dönüş sunamaz ve kaynak "kirlenmiş" olarak işaretlenir. Doğru yaklaşım, Terraform'un sunucuyu açması ve ardından Ansible gibi bir yapılandırma aracının devreye girmesi ya da önceden hazırlanmış bir makine imajı kullanılmasıdır.

    Kapanış#

    Terraform'a başlarken aklınızda kalması gereken dört şey var: yapılandırma bildirimseldir, yani "ne yapılacağını" değil "sonucun ne olacağını" yazarsınız; plan çıktısını, özellikle -/+ işaretlerini okumadan asla apply demeyin; sürümleri hem required_version hem sağlayıcı düzeyinde sabitleyip kilit dosyasını commit edin; ve durum dosyasını Git'e koymayıp uzak, kilitlenebilir bir arka uçta tutun. Bu dördü, Terraform ile yaşanan kazaların büyük çoğunluğunu daha başlamadan önler.

    Terraform ile yöneteceğiniz altyapı için bulut sunucu, VDS ve sanal sunucu paketlerimize göz atabilirsiniz; test ve üretim ortamlarını ayrı ayrı ayağa kaldırmak için uygun bir zemin sunarlar. Kurulum, izleme ve bakım yükünü bize devretmek isterseniz sunucu yönetimi hizmetimiz devrededir; alan adı ve DNS kayıtlarınızı aynı yerden yönetmek için alan adı sayfamıza, diğer DevOps rehberleri için wiki bölümüne bakabilirsiniz.

    TerraformIaCDevOps

    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.