Terraform Modülleri (Modules) Rehberi: Daha Temiz ve Tekrar Kullanılabilir Kod

Hedef

  • Local Module oluşturmak
  • Module inputs/outputs kullanmak
  • Public modules (Terraform Registry) kullanmak
  • Module versioning anlamak

1. Adım: Modül Nedir ve İlk Local Modülümüzü Oluşturmak

Modül = Tekrar kullanılabilir Terraform kod paketi

KavramAçıklama
Root Moduleterraform apply çalıştırdığın ana dizin
Child ModuleBaşka bir modül tarafından çağrılan modül
Module SourceModülün nerede olduğu (local, registry, github)

Neden Modül Kullanılır?

SorunModül Çözümü
Aynı kodu kopyala-yapıştırTek modül, birden fazla kullanım
10 sunucu için 10 kez aynı kodcount veya for_each ile modül
Değişiklik yapılacakSadece modülü güncelle
Ekip paylaşımıModülleri paylaş, herkes kullansın

1.2 Proje Yapısını Oluştur

Şöyle bir yapı oluşturacağız:

terraform-modules-demo/
├── main.tf              # Root module (ana kod)
├── variables.tf         # Root module değişkenleri
├── outputs.tf           # Root module çıktıları
└── modules/
    └── docker-container/
        ├── main.tf      # Child module (docker container oluşturur)
        ├── variables.tf # Child module girdileri
        └── outputs.tf   # Child module çıktıları

1.3 Proje Dizinini Oluştur

# Proje dizini oluştur
mkdir -p ~/terraform-modules-demo/modules/docker-container

# Dizine gir
cd ~/terraform-modules-demo

1.4 Child Module Oluştur – variables.tf

Bu modül, Docker container oluşturacak. Önce girdileri (inputs) tanımlayalım:

cat <<'EOF' > modules/docker-container/variables.tf
# Container adı
variable "name" {
  description = "Docker container adı"
  type        = string
}

# Kullanılacak image
variable "image" {
  description = "Docker image adı (örn: nginx:latest)"
  type        = string
}

# İç port
variable "internal_port" {
  description = "Container iç port numarası"
  type        = number
  default     = 80
}

# Dış port
variable "external_port" {
  description = "Container dış port numarası"
  type        = number
}

# Environment variables (opsiyonel)
variable "env_vars" {
  description = "Environment değişkenleri"
  type        = map(string)
  default     = {}
}
EOF

Burayı biraz açıklayalım:

variables.tf dosyasındaki bu bloklar, aslında bir “boş kutu” veya bir “form alanı” gibidir. var.image dediğinde Terraform, o kutunun içine ne yazılmışsa onu alır.

Peki o kutunun içine değer nasıl girer? İşte burada 3 ana senaryo var:

1. Senaryo: Sen terraform apply deyince sana sorar

Eğer değişkenin içinde bir default (varsayılan) değer yoksa ve sen önceden bir değer belirtmediysen, terminalde karşına şu çıkar: var.image: Enter a value: Sen oraya klavyeden nginx:latest yazıp enter’a basarsan, var.image o an için nginx:latest olur.

2. Senaryo: Bir .tfvars dosyası kullanırsın (En yaygın yöntem)

Proje klasöründe terraform.tfvars isimli bir dosya oluşturup içine şunu yazarsın: image = "mysql:8.0" Bu durumda var.image dediğin her yer otomatik olarak "mysql:8.0" değerini alır.

3. Senaryo: Komut satırından verirsin

Komutu çalıştırırken direkt belirtebilirsin: terraform apply -var="image=alpine:latest" Bu durumda var.image değeri "alpine:latest" olur.

Özetle Mantık Şudur:

  • variable "image" { ... } bloğu: “Benim image adında, tipi metin (string) olan bir değişkene ihtiyacım var, bunu bir kenara not et” demektir. (Tanımlama)
  • var.image ifadesi: “Git o not ettiğin değişkenin içine kullanıcı ne yazdıysa (nginx mi, mysql mi, ubuntu mu) onu buraya getir ve kullan” demektir. (Çağırma)

Birde buradaki dynamic yapı hakkında konuşalım. Öncelikle yapıyı parçalara ayırmak gerekiyor:

1. Değişken Tipi: map(string)

variables.tf içindeki bu tanım, Terraform’a şunu söyler: “Bana bir liste ver ama bu listede her elemanın bir anahtarı (key) ve bir değeri (value) olsun.”

Örneğin, bir veritabanı container’ı çalıştıracaksan kullanıcı şu değerleri girebilir:

env_vars = {
  USER     = "ulas"
  PASSWORD = "secret_password_123"
  DB_NAME  = "orders"
}

default = {}: Eğer kullanıcı hiçbir şey yazmazsa, bu “boş bir sözlük” olsun demektir. Yani hata verme, sadece boş geç.

2. Dinamik Blok: dynamic "env"

Normalde Docker container’larında env ayarı şöyle yapılır:

env = ["USER=ulas", "PASS=123"] # Statik yöntem (Zahmetli)

Ama buradaki dynamic blok şunu yapar: “Kullanıcı kaç tane değişken girdiyse, ben o kadar env satırı oluşturacağım.”

Kodun İçindeki Terimler Ne Anlama Geliyor?

  • for_each = var.env_vars: “Git env_vars içine bak. Kaç tane anahtar-değer çifti varsa, bu bloğu o kadar kez döndür (loop).”
  • content { ... }: Döngünün her bir adımında container içine ne yazılacağını belirler.
  • env.key: O anki sıradaki değişkenin adını (örneğin: USER) alır.
  • env.value: O anki sıradaki değişkenin değerini (örneğin: ulas) alır.

Özetle Mantık Şudur:

Sen Terraform’a diyorsun ki: “Ben sana bir liste (map) vereceğim, sen o listenin içini gez ve her bulduğun satır için bir env ayarı oluştur.”

1.5 Child Module Oluştur – main.tf

Şimdi modülün ana kodunu yazalım:

cat <<'EOF' > modules/docker-container/main.tf

terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 3.0"
    }
  }
}

# Docker image'ı çek
resource "docker_image" "app" {
  name         = var.image
  keep_locally = false
}

# Docker container oluştur
resource "docker_container" "app" {
  name  = var.name
  image = docker_image.app.image_id

  ports {
    internal = var.internal_port
    external = var.external_port
  }

  # Environment variables (varsa)
  dynamic "env" {
    for_each = var.env_vars
    content {
      name  = env.key
      value = env.value
    }
  }
}
EOF

dynamic bloğu: Environment variables için flexible bir yapı. Eğer env_vars boşsa, hiç env bloğu oluşturmaz. Doluysa her birini ekler.

Burası hakkında da kısaca bahsedelim:

docker_image Kaynağı:

Önce Docker Hub’dan hangi resmi indireceğini söylüyoruz. keep_locally = false ile, terraform destroy yaptığında imajın yerel bilgisayarından da silinmesini sağlar (tercihe bağlı bir temizlik bu).

docker_container Kaynağı:

Burada iki kritik nokta var:

  1. Bağımlılık (Dependency): image = docker_image.app.image_id diyerek Terraform’a şunu söylüyoruz: “Önce imajı çek, sonra container’ı oluştur.”
  2. Dinamik Blok (dynamic "env"): Eğer kullanıcı hiç env_vars vermezse bu blok boş geçilir; eğer 10 tane verirse otomatik olarak 10 tane env satırı oluşturulur.

1.6 Child Module Oluştur – outputs.tf

Modülden dışarıya bilgi verecek çıktıları tanımlayalım:

cat <<'EOF' > modules/docker-container/outputs.tf
# Container ID
output "container_id" {
  description = "Oluşturulan container'ın ID'si"
  value       = docker_container.app.id
}

# Container name
output "container_name" {
  description = "Oluşturulan container'ın adı"
  value       = docker_container.app.name
}

# Container IP
output "container_ip" {
  description = "Container'ın IP adresi"
  value       = try(docker_container.app.network_data[0].ip_address, "N/A")
}

# Access URL
output "access_url" {
  description = "Container'a erişim URL'si"
  value       = "http://localhost:${var.external_port}"
}

# Image ID
output "image_id" {
  description = "Kullanılan image ID'si"
  value       = docker_image.app.id
}
EOF

Burasıda işlem bittikten sonra Terraform’un sana rapor vermesini sağladığın yerdir.

  • Operasyonel Kolaylık: Container ayağa kalktığında ID’sini veya IP adresini Docker CLI üzerinden aramak yerine, ekranda direkt görüyorsun.
  • Kullanıcı Dostu: Kullanıcının hangi portu verdiğini hatırlamasına gerek kalmadan direkt tıklanabilir bir link sunmuş oluyoruz.

Bu Yapının Çalışma Mantığı (Workflow)

Bu dosyalar aynı dizindeyken şu komutları çalıştırdığında olaylar şöyle gelişir:

  1. terraform init: Docker provider’ını (eklentisini) indirir.
  2. terraform plan: Variables kısmındaki boşlukları sana sorar (veya bir .tfvars dosyasından okur) ve ne yapacağını listeler.
  3. terraform apply:
    • Docker imajını çeker.
    • İmajın ID’sini alır, container ayarlarında kullanır.
    • Container’ı başlatır.
    • En son outputs.tf içindeki tüm bilgileri ekrana yazdırır.

1.7 Child Module Yapısını Kontrol Et

# Modül dosyalarını kontrol et
ls -la modules/docker-container/

Beklenen Çıktı:

main.tf
outputs.tf
variables.tf
# Modül dosyalarını görüntüle
cat modules/docker-container/variables.tf
cat modules/docker-container/main.tf
cat modules/docker-container/outputs.tf

Şu Anki Durum

┌─────────────────────────────────────────────────────────────┐
│  CHILD MODULE: modules/docker-container/                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  INPUTS (variables.tf):                                     │
│  ├── name          → Container adı                          │
│  ├── image         → Docker image                           │
│  ├── internal_port → İç port (default: 80)                  │
│  ├── external_port → Dış port                               │
│  └── env_vars      → Environment variables (opsiyonel)      │
│                                                             │
│  RESOURCES (main.tf):                                       │
│  ├── docker_image.app    → Image çek                        │
│  └── docker_container.app → Container oluştur               │
│                                                             │
│  OUTPUTS (outputs.tf):                                      │
│  ├── container_id   → ID                                    │
│  ├── container_name → Ad                                    │
│  ├── container_ip   → IP adres                              │
│  ├── access_url     → URL                                   │
│  └── image_id       → Image ID                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘
                              ↑
                              │ Çağrılacak
                              │
┌─────────────────────────────────────────────────────────────┐
│  ROOT MODULE: ~/terraform-modules-demo/                     │
│                                                             │
│  (Henüz oluşturulmadı - 2. Adım'da yapacağız)              │
│                                                             │
└─────────────────────────────────────────────────────────────┘

1. Adım Tamamlandığında Ne Elde Etmeli?

  • ~/terraform-modules-demo klasörü oluşturuldu
  • modules/docker-container/ alt klasörü oluşturuldu
  • variables.tf – 5 input tanımlandı
  • main.tf – docker_image ve docker_container resource’ları yazıldı
  • outputs.tf – 5 output tanımlandı
  • dynamic bloğu anlaşıldı (environment variables için)

Bu Adımda Öğrendiklerimiz

KonuAçıklama
Child ModuleBaşka modüller tarafından çağrılan modül
Module dosyalarımain.tf, variables.tf, outputs.tf
Module inputsvariable blokları ile tanımlanır
Module outputsoutput blokları ile dışarıya verilir
dynamic bloğuFlexible resource oluşturma (örn: env vars)

2. Adım: Root Module ve Module Çağırma

Bu adımda:

  • Root module dosyalarını oluşturacağız
  • Child module’ü module bloğu ile çağıracağız
  • Module’a input göndereceğiz
  • Module’dan output alacağız

2.1 Root Module – main.tf Oluştur

Şimdi child module’ü çağıran ana kodu yazalım:

cd ~/terraform-modules-demo
cat <<'EOF' > main.tf
# Terraform Cloud Backend
terraform {
  cloud {
    organization = "devops-learning-fikriulas"
    workspaces {
      name = "terraform-modules-demo"
    }
  }

  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 3.0"
    }
  }
}

# Docker Provider
provider "docker" {}

# -----------------------------------------------------------
# MODULE ÇAĞIRMA - NGINX CONTAINER
# -----------------------------------------------------------

module "nginx_server" {
  source = "./modules/docker-container"

  # Module inputs
  name          = "nginx-web-server"
  image         = "nginx:alpine"
  internal_port = 80
  external_port = 8080
}

# -----------------------------------------------------------
# MODULE ÇAĞIRMA - APACHE CONTAINER (İkinci container!)
# -----------------------------------------------------------

module "apache_server" {
  source = "./modules/docker-container"

  # Module inputs
  name          = "apache-web-server"
  image         = "httpd:alpine"
  internal_port = 80
  external_port = 8081
}

# -----------------------------------------------------------
# MODULE ÇAĞIRMA - REDIS CONTAINER (Environment variables ile!)
# -----------------------------------------------------------

module "redis_server" {
  source = "./modules/docker-container"

  # Module inputs
  name          = "redis-cache"
  image         = "redis:alpine"
  internal_port = 6379
  external_port = 6379

  # Environment variables (opsiyonel)
  env_vars = {
    REDIS_MAXMEMORY     = "128mb"
    REDIS_MAXMEMORY_POLICY = "allkeys-lru"
  }
}
EOF

DİKKAT: organization = “devops-learning-fikriulas” kısmını kendi organization adınla değiştir!
Terraform’un neden bu kadar güçlü olduğunu gördüğümüz nokta burası. Daha önce yazdığımız o dosyalar birer “kalıp” (blueprint) idi; bu main.tf ise o kalıpları kullanarak seri üretime geçtiğimiz “fabrika bandı” gibi düşünebiliriz.

Şimdi bu kısmı adım adım inceleyelim:

1. Altyapı Ayarları: terraform { ... } ve provider

Kodun en başındaki bu kısım, projenin “nerede” ve “nasıl” çalışacağını belirler.

  • Terraform Cloud Backend: cloud bloğu ile “Ben bu projenin durumunu (state file) kendi bilgisayarımda değil, Terraform Cloud üzerinde saklamak istiyorum” diyorsun. Bu sayede ekiple çalışırken veya bilgisayarın bozulduğunda altyapın güvende kalır.
  • Required Providers: Docker ile konuşabilmek için gerekli olan eklentiyi (kreuzwerker/docker) ve hangi versiyonunu kullanacağımızı belirtiyoruz.
  • Provider “docker” {}: Docker motoruna bağlanmak için gerekli yetkiyi verir. İçini boş bırakmak, genellikle yerel Docker socket’ine (/var/run/docker.sock) bağlanacağı anlamına gelir.

2. Modül Çağırma: module "..." { ... }

Daha önce yazılan o karmaşık main.tf, variables.tf ve outputs.tf dosyalarını birer fonksiyon gibi çağırıyoruz.

Ortak Mantık:

Her module bloğu şunları yapıyor:

  1. source: “Git ./modules/docker-container klasöründeki kodları oku.”
  2. Girdiler (Inputs): O klasördeki variables.tf içinde tanımlanan name, image, internal_port gibi boşlukları doldur.

Örneklerin Analizi:

  • nginx_server: Basit bir Nginx kuruyor.
  • apache_server: Aynı kod bloğunu kullanarak ikinci bir sunucu ayağa kaldırıyor. Sadece imaj adını ve dış portu (8081) değiştirdik. İşte modülerliğin gücü bu oluyor zaten, kod tekrarından kaçınmış oluyoruz.
  • redis_server: Burada bir adım öteye gidip, dynamic "env" bloğunu test ediyoruz. env_vars içine yazılan Redis ayarları, modülün içindeki döngü sayesinde otomatik olarak container içine enjekte ediliyor.

3. Modüller Arası İzolasyon

Burada dikkat edilmesi gereken çok kritik bir nokta var: module "nginx_server" ve module "apache_server" birbirinden tamamen bağımsız dünyalardır. Birinde yaptığın hata veya değişiklik diğerini etkilemez. Terraform bunları ayrı ayrı yönetir ama tek bir terraform apply komutuyla hepsini birden oluşturur.

Bu yapı neden çok profesyonel?
  • DRY (Don’t Repeat Yourself): Aynı Docker kaynağını üç kere alt alta yazmadık. Bir kere yazdık, üç kere çağırdık.
  • Standardizasyon: Yarın bir gün tüm container’lara “Log kaydı tutma” özelliği eklemek istersek, sadece ./modules/docker-container/main.tf dosyası değiştirilir; 3 container birden güncellenmiş olur.

2.2 Root Module – outputs.tf Oluştur

Child module’den gelen output’ları root module’de de tanımlayalım:

cat <<'EOF' > outputs.tf
# NGINX Outputs
output "nginx_container_id" {
  description = "Nginx container ID"
  value       = module.nginx_server.container_id
}

output "nginx_access_url" {
  description = "Nginx erişim URL'si"
  value       = module.nginx_server.access_url
}

# APACHE Outputs
output "apache_container_id" {
  description = "Apache container ID"
  value       = module.apache_server.container_id
}

output "apache_access_url" {
  description = "Apache erişim URL'si"
  value       = module.apache_server.access_url
}

# REDIS Outputs
output "redis_container_id" {
  description = "Redis container ID"
  value       = module.redis_server.container_id
}

output "redis_container_ip" {
  description = "Redis container IP"
  value       = module.redis_server.container_ip
}

# ÖZET
output "all_containers" {
  description = "Tüm container'ların özeti"
  value = {
    nginx  = module.nginx_server.container_name
    apache = module.apache_server.container_name
    redis  = module.redis_server.container_name
  }
}
EOF

Bu dosya, Terraform modülleriyle çalışırken en çok karıştırılan ama en kritik olan “Değer Döndürme” (Return Values) mantığını çözdüğümüz yer.

Modülleri birer kapalı kutu (kara kutu) gibi düşünün. Modülün içindeki outputs.tf o kutunun içinden dışarıya bir pencere açar. Ana dizindeki (root) outputs.tf ise o pencereden bakıp bilgiyi alır ve senin terminal ekranına yansıtır.

1. Modülden Veri Çekme Mantığı: module.<İSİM>.<ÇIKTI>

Bir modülün içindeki veriye ulaşmak için şu formülü kullanılır:

module + modülün_adı + modül_içindeki_output_adı

Örnek:

value = module.nginx_server.container_id

Burada Terraform’a şunu diyorsun:

  1. Git nginx_server isimli modüle (hani main.tf içinde tanımladığın o blok).
  2. O modülün kendi içindeki outputs.tf dosyasına bak.
  3. Orada adı container_id olan çıktıyı bul ve değerini buraya getir.

2. Neden İki Tane Output Dosyası Var?

Bu konu genelde kafa karıştırır, o yüzden netleştirelim:

  • Modül İçindeki Output (modules/docker-container/outputs.tf): Bu, modülün dış dünyaya “Benim içimde şu bilgiler var, istersen kullanabilirsin” demesidir.
  • Ana Dizindeki Output (./outputs.tf): Bu ise kullanıcının (senin) terminalde terraform apply bittiğinde ne göreceğini belirler. Modülden gelen veriyi ithal eder ve ekrana basar.

3. Blokların Analizi

Nginx ve Apache Blokları

Burada access_url kısmına dikkat et. Modülün içinde şöyle bir şey yazmıştık: value = "http://localhost:${var.external_port}" Sen ana dizinde module.nginx_server.access_url dediğinde, Terraform o modüle gider, Nginx için verdiğin 8080 portunu içine yerleştirir ve sana tertemiz bir http://localhost:8080 sonucu döndürür.

Redis Bloğu

Burada container_ip bilgisini çekiyoruz. Hatırlarsanız modül içinde bu docker_container.app.ip_address idi. Redis modülün ayağa kalktığında Docker’ın ona atadığı gerçek IP adresini ana ekranda görebileceksin.

Özet Blok (all_containers)

Tüm modüllerden gelen verileri tek bir Map (Sözlük) yapısında topluyoruz.

  • Avantajı: Terminal çıktısında karmaşa yerine derli toplu bir özet görünür.
  • Görünümü: all_containers = { "nginx" = "nginx-web-server" "apache" = "apache-web-server" "redis" = "redis-cache" }

Genel Özet (Büyük Resim)

  1. variables.tf: “Bana ne lazım?” (Girdiler)
  2. main.tf (Modül): “İşi nasıl yaparım?” (İşleyiş)
  3. outputs.tf (Modül): “Hangi bilgileri dışarı sızdırabilirim?” (Potansiyel Çıktılar)
  4. main.tf (Root): “Kalıbı kullanarak kaç tane ve hangi ayarlarla üreteceğim?” (Üretim Hattı)
  5. outputs.tf (Root): “Ekranımda ne görmek istiyorum?” (Raporlama)

2.3 Terraform Cloud’da Yeni Workspace Oluştur

Yeni bir workspace oluşturmamız gerekiyor.

Başka bir cihazdan browser ile:

  1. https://app.terraform.io git
  2. devops-learning-fikriulas organization’ına gir
  3. “New workspace” tıkla
  4. “No VCS connection” seç
  5. Name: terraform-modules-demo
  6. “Create workspace” tıkla
  7. Settings → General → Execution Mode → “Local” seç
  8. “Save settings” tıkla

2.4 Terraform Init Çalıştır

cd ~/terraform-modules-demo
terraform init

Beklenen Çıktı:

Initializing Terraform Cloud...
Initializing modules...
- nginx_server in modules/docker-container
- apache_server in modules/docker-container
- redis_server in modules/docker-container

Initializing provider plugins...
- Finding kreuzwerker/docker versions matching "~> 3.0"...
- Installing kreuzwerker/docker v3.0.x...

Terraform has been successfully initialized!

Önemli: Terraform modülleri otomatik olarak algıladı!

2.5 Terraform Plan Çalıştır

terraform plan

Beklenen Çıktı (özet):

Terraform used the selected providers to generate the following execution plan.

module.nginx_server.docker_image.app will be created
module.nginx_server.docker_container.app will be created
module.apache_server.docker_image.app will be created
module.apache_server.docker_container.app will be created
module.redis_server.docker_image.app will be created
module.redis_server.docker_container.app will be created

Plan: 6 to add, 0 to change, 0 to destroy.

2.6 Terraform Apply Çalıştır

terraform apply

Beklenen Çıktı:

module.nginx_server.docker_image.app: Creating...
module.apache_server.docker_image.app: Creating...
module.redis_server.docker_image.app: Creating...
...
Apply complete! Resources: 6 added, 0 changed, 0 destroyed.

Outputs:

all_containers = {
  apache = "apache-web-server"
  nginx  = "nginx-web-server"
  redis  = "redis-cache"
}
apache_access_url = "http://localhost:8081"
apache_container_id = "..."
nginx_access_url = "http://localhost:8080"
nginx_container_id = "..."
redis_container_id = "..."
redis_container_ip = "..."

2.7 Container’ları Doğrula

# Tüm container'ları listele
docker ps

Beklenen Çıktı:

CONTAINER ID   IMAGE              PORTS                     NAMES
abc123...      nginx:alpine       0.0.0.0:8080->80/tcp      nginx-web-server
def456...      httpd:alpine       0.0.0.0:8081->80/tcp      apache-web-server
ghi789...      redis:alpine       0.0.0.0:6379->6379/tcp    redis-cache
# Nginx test
curl localhost:8080

# Apache test
curl localhost:8081

# Redis test
docker exec redis-cache redis-cli ping

Beklenen:

  • Nginx: “Welcome to nginx!” sayfası
  • Apache: “It works!” sayfası
  • Redis: PONG çıktısı

Module Çağırma Sözdizimi

module "MODULE_ADI" {
  source = "./modules/MODULE_KLASORU"

  # Module inputs (variable'lar)
  input_adı_1 = değer_1
  input_adı_2 = değer_2
  ...
}

# Module output'larına erişim
module.MODULE_ADI.output_adı

Örnek:

module "nginx_server" {
  source        = "./modules/docker-container"
  name          = "nginx-web-server"
  image         = "nginx:alpine"
  internal_port = 80
  external_port = 8080
}

# Output erişimi
output "url" {
  value = module.nginx_server.access_url
}

2. Adım Tamamlandığında Ne Elde Etmeli?

  • main.tf – 3 farklı module çağrısı (nginx, apache, redis)
  • outputs.tf – Module output’ları root’ta yayınlandı
  • Terraform Cloud’da yeni workspace oluşturuldu
  • terraform init – Modüller yüklendi
  • terraform apply – 6 kaynak oluşturuldu (3 image + 3 container)
  • docker ps – 3 container çalışıyor

Bu Adımda Öğrendiklerimiz

KonuAçıklama
module bloğuChild module’ü çağırmak için kullanılır
sourceModülün yeri (local: ./modules/xxx)
Module inputsModule’a değer gönderme
Module outputsmodule.xxx.output_adı ile erişim
Tek modül, çok kullanımAynı modülü farklı parametrelerle çağırma

3. Adım: Public Modules (Terraform Registry)

Şimdi Terraform Registry’den hazır modüllerü inceleyelim.

3.1 Terraform Registry Nedir?

Terraform Registry = Modül marketi

ÖzellikAçıklama
Adreshttps://registry.terraform.io
İçerikHazır modüller, provider’lar
ÜcretsizEvet, çoğu modül açık kaynak
GüvenlikVerified modüller (✓ işareti)

Neden Registry Modülleri Kullanmalıyız?

AvantajAçıklama
Zaman tasarrufuSıfırdan yazmana gerek yok
Best practicesTopluluk tarafından test edilmiş
GüncellemelerModül geliştiricileri bakım yapıyor
DokümantasyonDetaylı kullanım kılavuzları

3.2 Registry’de Modül Arama

Browser’da aç :

https://registry.terraform.io/browse/modules

Örnek popüler modüller:

ModülAçıklama
terraform-aws-modules/vpc/awsAWS VPC oluşturma
terraform-aws-modules/ec2-instance/awsAWS EC2 instance
terraform-aws-modules/rds/awsAWS RDS database
azureadAzure Active Directory

Not: AWS/Azure modülleri için hesap gerekiyor. Biz öğrenme amaçlı inceleyelim.

3.3 Modül Source Tipleri

# 1. Local module
source = "./modules/docker-container"

# 2. Terraform Registry (public)
source = "terraform-aws-modules/vpc/aws"

# 3. Terraform Registry (versiyonlu)
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"

# 4. GitHub
source = "github.com/terraform-aws-modules/terraform-aws-vpc"

# 5. Private registry
source = "app.terraform.io/corporation/vpc/aws"

3.4 Registry Modülü Nasıl Kullanılır? (Örnek)

AWS VPC Modülü Örneği (teorik):

module "vpc" {
  source = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"  # Versiyon kilitleme!

  # Required inputs
  name = "my-vpc"
  cidr = "10.0.0.0/16"
  azs  = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]

  # Optional inputs
  public_subnets  = ["10.0.1.0/24", "10.0.2.0/24"]
  private_subnets = ["10.0.3.0/24", "10.0.4.0/24"]

  tags = {
    Environment = "dev"
    Terraform   = "true"
  }
}

# Modül output'larını kullanma
output "vpc_id" {
  value = module.vpc.vpc_id
}

output "public_subnet_ids" {
  value = module.vpc.public_subnets
}

Bu tek modül ne yapar?

  • VPC oluşturur
  • Public subnet’ler (3 AZ’de)
  • Private subnet’ler
  • NAT Gateway
  • Route tables
  • Internet Gateway
  • VPN Gateway (opsiyonel)

Manuel yazsaydık işin içinden çıkamazdık büyük ihtimal.

3.5 Module Versioning (Versiyon Kilitleme)

Neden Önemli?

SorunÇözüm
Modül güncellenirversion ile kilitle
Breaking changesEski versiyonu kullanmaya devam et
Ekip uyumuHerkes aynı versiyonu kullanır

Versiyon Sözdizimi:

ExpressionAnlamı
version = "1.0.0"Tam olarak bu versiyon
version = "~> 1.0"1.x.x (1.0, 1.1, 1.2… ama 2.0 değil)
version = ">= 1.0.0"1.0.0 veya üstü
version = ">= 1.0.0, < 2.0.0"1.x.x aralığı

Önerilen: ~> kullan (semantic versioning)

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"  # 5.x.x serisi
  
  # ...
}

3.6 .terraform.lock.hcl Dosyası

Her terraform init sonrası bu dosya güncellenir:

# This file is maintained automatically by "terraform init".
provider "registry.terraform.io/hashicorp/aws" {
  version = "5.0.0"
  hashes = [
    "h1:abc123...",
  }
}

Bu dosya Git’e eklenmeli! Neden?

  • Ekipteki herkes aynı versiyonu kullanır
  • terraform init her seferinde aynı provider/modülü çeker

Local vs Registry Modülleri

ÖzellikLocal ModuleRegistry Module
Source./modules/xxxnamespace/name/provider
BakımKendin yaparsınTopluluk/modül sahibi
GüncellemeManuelterraform init -upgrade
DokümantasyonKendin yazarsınRegistry’de hazır
TestKendin test edersinBinlerce kullanıcı test etti

Final özet olarak şunu diyebiliriz:

┌─────────────────────────────────────────────────────────────┐
│  TERRAFORM MODULES - TAM ÖZET                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. LOCAL MODULES                                           │
│  ├── Kendi modülünü oluşturma                               │
│  ├── Dosya yapısı: main.tf, variables.tf, outputs.tf        │
│  ├── source = "./modules/xxx"                               │
│  └── Tek modül, birden fazla kullanım                       │
│                                                             │
│  2. PUBLIC MODULES (Terraform Registry)                     │
│  ├── source = "namespace/name/provider"                     │
│  ├── version = "~> x.x" (versiyon kilitleme)                │
│  └── Örnek: terraform-aws-modules/vpc/aws                   │
│                                                             │
│  3. MODULE INPUTS/OUTPUTS                                   │
│  ├── Input: variable blokları                               │
│  ├── Output: output blokları                                │
│  └── Erişim: module.xxx.output_adi                          │
│                                                             │
│  4. MODULE VERSIONING                                       │
│  ├── ~> x.x  → x.x.x serisi                                 │
│  ├── = "x.x.x" → Tam versiyon                               │
│  └── .terraform.lock.hcl → Otomatik kilit dosyası           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Son durumda proje yapısı şöyle olmalı:

terraform-modules-demo/
├── main.tf              # Root module (3 module çağrısı)
├── outputs.tf           # Root module outputs
├── .terraform/          # İndirilen provider'lar
├── .terraform.lock.hcl  # Versiyon kilidi
└── modules/
    └── docker-container/
        ├── main.tf      # Child module kodu
        ├── variables.tf # Child module inputs
        └── outputs.tf   # Child module outputs

Leave a Reply

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir