Terraform Nedir: Altyapı Kod Olarak Temelleri
Sunucu kurarken elle yapılandırma dosyalarını düzenlemek, komutları tek tek çalıştırmak ve sonra “bu sunucuyu nasıl kurmuştum acaba?” diye düşünmek… Bunu yaşamayan sysadmin yoktur. İşte tam bu noktada Terraform devreye giriyor. Altyapını kod olarak tanımla, versiyon kontrol sistemine at, ekibinle paylaş ve her ortamda tutarlı şekilde deploy et. Bu yazıda Terraform’un ne olduğunu, nasıl çalıştığını ve gerçek dünya senaryolarında nasıl kullanacağını detaylıca ele alacağız.
Terraform Nedir ve Neden Kullanmalıyız?
Terraform, HashiCorp tarafından geliştirilen açık kaynaklı bir Infrastructure as Code (IaC) aracıdır. AWS, Azure, Google Cloud, DigitalOcean gibi bulut sağlayıcılarından tutun da on-premise VMware altyapısına kadar her şeyi kod olarak tanımlamanı sağlar.
Klasik yaklaşımda ne yapıyorduk? AWS konsoluna giriyorduk, EC2 instance açıyorduk, security group ayarlıyorduk, RDS veritabanı oluşturuyorduk. Her şey manuel, her şey tekrarlanabilir değil. Bir ortamı staging’de kuruyordun, production’da birebir aynısını yapmak için saatler harcıyordun. Üstelik neyi ne zaman değiştirdiğini takip etmek neredeyse imkansızdı.
Terraform ile bu süreç tamamen değişiyor. Altyapını HCL (HashiCorp Configuration Language) adı verilen, okunması son derece kolay bir dilde tanımlıyorsun. Bu dosyalar Git’e gidiyor, pull request açıyorsun, ekip arkadaşın inceliyor ve merge edince altyapı otomatik olarak oluşturuluyor.
Terraform’un öne çıkan özellikleri:
- Deklaratif yapı: “Nasıl yapılacağını” değil, “ne istediğini” söylüyorsun
- Idempotent: Aynı konfigürasyonu defalarca çalıştırsan da sonuç değişmiyor
- State yönetimi: Mevcut altyapı durumuyla konfigürasyonu karşılaştırıyor
- Provider ekosistemi: 1000’den fazla provider desteği
- Plan ve Apply: Değişiklik yapmadan önce ne olacağını görebiliyorsun
Temel Kavramlar
Terraform’u anlamak için birkaç temel kavramı kafaya oturtmak gerekiyor.
Provider
Provider, Terraform’un hangi platformla konuşacağını belirliyor. AWS, Azure, GCP gibi büyük bulut sağlayıcılarının yanı sıra Kubernetes, Datadog, GitHub gibi servislerin de Terraform provider’ları var.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "eu-west-1"
}
Resource
Resource, oluşturmak istediğin altyapı bileşenidir. Bir EC2 instance, bir S3 bucket, bir DNS kaydı… Bunların hepsi birer resource.
resource "aws_instance" "web_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = {
Name = "web-sunucu"
Environment = "production"
ManagedBy = "terraform"
}
}
Variable
Değerleri sabit kodlamak yerine değişken kullanmak, konfigürasyonunu yeniden kullanılabilir yapıyor. Bir modülü hem staging hem production için kullanabiliyorsun.
variable "instance_type" {
description = "EC2 instance tipi"
type = string
default = "t3.micro"
}
variable "environment" {
description = "Ortam adi (staging, production)"
type = string
}
variable "allowed_cidrs" {
description = "Izin verilen IP bloklari"
type = list(string)
default = ["10.0.0.0/8"]
}
Output
Output’lar, Terraform’un oluşturduğu kaynakların değerlerini dışa aktarmana yarıyor. Mesela oluşturulan EC2’nin IP adresini output olarak alabilirsin.
output "instance_public_ip" {
description = "EC2 instance public IP adresi"
value = aws_instance.web_server.public_ip
}
output "instance_id" {
description = "EC2 instance ID"
value = aws_instance.web_server.id
sensitive = false
}
State Dosyası
Terraform, oluşturduğu altyapının anlık görüntüsünü terraform.tfstate dosyasında saklıyor. Bu dosya son derece kritik çünkü Terraform bir sonraki çalışmasında mevcut durumu bu dosyayla karşılaştırıyor ve neyin değişmesi gerektiğini hesaplıyor.
Önemli uyarı: State dosyasını asla Git’e push etme! İçinde hassas veriler olabilir ve birden fazla kişi aynı anda çalışırsa state corruption yaşarsın. Bunun çözümü remote backend kullanmak, onu da ilerleyen bölümlerde göreceğiz.
Terraform Kurulumu
Linux üzerinde Terraform kurulumu oldukça basit:
# Ubuntu/Debian icin
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.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 terraform
# Kurulumu dogrula
terraform version
Temel Workflow
Terraform’un çalışma döngüsü dört adımdan oluşuyor:
terraform init: Provider plugin’lerini indirir, backend’i yapılandırır. Bir proje üzerinde ilk kez çalışırken veya provider eklediğinde çalıştırırsın.
terraform plan: Mevcut state ile konfigürasyonu karşılaştırır, yapılacak değişiklikleri gösterir. Hiçbir değişiklik yapmaz, sadece gösterir.
terraform apply: Plan’ı onaylayıp altyapı değişikliklerini uygular.
terraform destroy: Terraform ile oluşturulmuş tüm kaynakları siler. Dikkatli kullan!
# Projeyi baslat
terraform init
# Degisiklikleri incele
terraform plan
# Degisiklikleri uygula
terraform apply
# Otomatik onayla uygula (CI/CD icin)
terraform apply -auto-approve
# Belirli bir degiskeni gecici olarak override et
terraform plan -var="environment=staging"
# Var dosyasiyla calis
terraform plan -var-file="staging.tfvars"
Gerçek Dünya Senaryosu: Web Uygulaması Altyapısı
Şimdi gerçek bir senaryo üzerinden gidelim. Bir web uygulaması için temel AWS altyapısı kuracağız: VPC, subnet, security group ve EC2 instance.
Önce proje yapısını oluşturalım:
terraform-web-altyapi/
├── main.tf # Ana kaynak tanimlari
├── variables.tf # Degisken tanimlari
├── outputs.tf # Output tanimlari
├── providers.tf # Provider konfigurasyonu
├── terraform.tfvars # Degisken degerleri (gitignore'a ekle)
└── backend.tf # Remote state konfigurasyonu
Şimdi main.tf dosyamızı yazalım:
# VPC olustur
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-vpc"
Environment = var.environment
}
}
# Public subnet
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidr
availability_zone = "${var.aws_region}a"
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-public-subnet"
}
}
# Internet Gateway
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-igw"
}
}
# Route table
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "${var.project_name}-public-rt"
}
}
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
# Security Group
resource "aws_security_group" "web" {
name = "${var.project_name}-web-sg"
description = "Web sunucu security group"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTP"
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTPS"
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = var.ssh_allowed_cidrs
description = "SSH - Sadece guvenli IP'lerden"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-web-sg"
}
}
# EC2 Instance
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.instance_type
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.web.id]
key_name = var.key_pair_name
root_block_device {
volume_size = 20
volume_type = "gp3"
encrypted = true
}
user_data = <<-EOF
#!/bin/bash
apt-get update
apt-get install -y nginx
systemctl enable nginx
systemctl start nginx
EOF
tags = {
Name = "${var.project_name}-web"
Environment = var.environment
}
}
Remote State ile Ekip Çalışması
Tek kişilik projede local state işe yarar ama ekip olarak çalışınca sorunlar başlar. İki kişi aynı anda terraform apply çalıştırırsa state bozulabilir. Çözüm: Remote backend.
AWS S3 ve DynamoDB kullanarak remote state yapılandırması:
# backend.tf
terraform {
backend "s3" {
bucket = "sirket-terraform-state"
key = "web-altyapi/terraform.tfstate"
region = "eu-west-1"
encrypt = true
dynamodb_table = "terraform-state-lock"
}
}
DynamoDB tablosunu ve S3 bucket’ı elle oluşturman gerekiyor (ya da ayrı bir bootstrap Terraform projesiyle). DynamoDB tablosu state locking için kullanılıyor, yani iki kişi aynı anda apply çalıştıramıyor.
# DynamoDB tablosunu olustur
aws dynamodb create-table
--table-name terraform-state-lock
--attribute-definitions AttributeName=LockID,AttributeType=S
--key-schema AttributeName=LockID,KeyType=HASH
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5
--region eu-west-1
# S3 bucket olustur ve versiyonlamayı etkinlestir
aws s3api create-bucket
--bucket sirket-terraform-state
--region eu-west-1
--create-bucket-configuration LocationConstraint=eu-west-1
aws s3api put-bucket-versioning
--bucket sirket-terraform-state
--versioning-configuration Status=Enabled
Modüler Yapı
Terraform projeler büyüdükçe her şeyi tek dosyaya yazmak yerine modüller kullanmak şart. Modüller, yeniden kullanılabilir altyapı bileşenleri oluşturmana yarıyor.
modules/
├── vpc/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── ec2/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
└── rds/
├── main.tf
├── variables.tf
└── outputs.tf
Modülü kullanmak için:
# Ana main.tf'de modul cagrisi
module "vpc" {
source = "./modules/vpc"
project_name = var.project_name
environment = var.environment
vpc_cidr = "10.0.0.0/16"
public_subnet_cidr = "10.0.1.0/24"
private_subnet_cidr = "10.0.2.0/24"
}
module "web_server" {
source = "./modules/ec2"
project_name = var.project_name
environment = var.environment
vpc_id = module.vpc.vpc_id
subnet_id = module.vpc.public_subnet_id
instance_type = var.instance_type
ami_id = var.ami_id
}
Modüller ayrıca Terraform Registry’den de alınabiliyor. HashiCorp’un resmi modülleri oldukça kaliteli:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.0.0"
name = "production-vpc"
cidr = "10.0.0.0/16"
azs = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
single_nat_gateway = false
}
Ortam Yönetimi: Workspace vs Dizin Yapısı
Staging ve production ortamlarını yönetmenin iki popüler yolu var.
Workspace yaklaşımı:
# Staging workspace olustur
terraform workspace new staging
terraform workspace select staging
terraform apply -var-file="staging.tfvars"
# Production'a gec
terraform workspace select production
terraform apply -var-file="production.tfvars"
# Mevcut workspace'leri listele
terraform workspace list
Dizin yapısı yaklaşımı (genellikle daha tercih edilen):
environments/
├── staging/
│ ├── main.tf
│ ├── terraform.tfvars
│ └── backend.tf
└── production/
├── main.tf
├── terraform.tfvars
└── backend.tf
Büyük ekiplerde dizin yapısı yaklaşımı daha güvenli çünkü staging ve production state’leri tamamen birbirinden izole.
CI/CD Entegrasyonu
Terraform’u GitLab CI’ya entegre etmek için basit bir örnek:
# .gitlab-ci.yml
stages:
- validate
- plan
- apply
variables:
TF_ROOT: ${CI_PROJECT_DIR}/environments/production
TF_VERSION: "1.6.0"
image:
name: hashicorp/terraform:${TF_VERSION}
entrypoint: [""]
before_script:
- cd ${TF_ROOT}
- terraform init
validate:
stage: validate
script:
- terraform validate
- terraform fmt -check
plan:
stage: plan
script:
- terraform plan -out=tfplan
artifacts:
paths:
- ${TF_ROOT}/tfplan
expire_in: 1 week
apply:
stage: apply
script:
- terraform apply -auto-approve tfplan
when: manual
only:
- main
Sık Yapılan Hatalar ve İpuçları
Terraform fmt kullanmayı unutma. Kod stilini otomatik düzenliyor, PR’larda gereksiz tartışmalardan kurtarıyor:
# Tum dosyalari formatla
terraform fmt -recursive
# Sadece kontrol et, degistirme
terraform fmt -check -recursive
terraform validate ile konfigürasyonu kontrol et. Syntax hatalarını apply’dan önce yakalar:
terraform validate
Hassas verileri outputs’ta dikkatli kullan. Veritabanı şifresi gibi değerleri output’a koyuyorsan sensitive = true ekle:
output "db_password" {
value = random_password.db.result
sensitive = true
}
Kaynakları taşırken dikkatli ol. Bir resource’u rename etmek Terraform’un onu silip yeniden oluşturmasına neden olabilir. terraform state mv komutunu kullan:
# Kaynagi yeni isimle taşi, silme/olusturma olmasin
terraform state mv aws_instance.web aws_instance.web_server
Sonuç
Terraform, modern altyapı yönetiminin vazgeçilmez bir parçası haline geldi. Manuel kurulum ve yapılandırma süreçlerinden kod tabanlı, versiyon kontrollü ve tekrarlanabilir bir altyapı yönetimine geçmek başlangıçta biraz zahmetli görünebilir. Ancak bir kez alıştığında “bunu daha önce neden kullanmadım?” diyeceksin.
Özetle öğrendiklerimize bakalım:
- Terraform’un temel kavramları: provider, resource, variable, output ve state
- Temel workflow: init, plan, apply, destroy
- Remote backend ile ekip çalışmasının nasıl güvenli hale getirileceği
- Modüler yapıyla büyük projelerin nasıl organize edileceği
- CI/CD pipeline entegrasyonu
- Sık yapılan hatalar ve kaçınma yolları
Başlangıç için küçük bir projeyle dene. Mesela mevcut sunucularından birini Terraform ile yeniden kur, state dosyasını S3’e taşı ve basit bir CI pipeline kur. Terraform’un gücünü gerçekten anlamak için üretim ortamında kullanman gerekiyor. Tabii dikkatli ve plan aşamasını atlamamak şartıyla.
Sonraki yazılarda Terraform ile Kubernetes yönetimi, advanced state manipülasyonu ve Terragrunt ile çoklu hesap yönetimini ele alacağız. Sorularınız olursa yorumlarda buluşalım.
