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
| Kavram | Açıklama |
|---|---|
| Root Module | terraform apply çalıştırdığın ana dizin |
| Child Module | Başka bir modül tarafından çağrılan modül |
| Module Source | Modülün nerede olduğu (local, registry, github) |
Neden Modül Kullanılır?
| Sorun | Modül Çözümü |
|---|---|
| Aynı kodu kopyala-yapıştır | Tek modül, birden fazla kullanım |
| 10 sunucu için 10 kez aynı kod | count veya for_each ile modül |
| Değişiklik yapılacak | Sadece 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: “Benimimageadında, tipi metin (string) olan bir değişkene ihtiyacım var, bunu bir kenara not et” demektir. (Tanımlama)var.imageifadesi: “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: “Gitenv_varsiç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
}
}
}
EOFdynamic 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:
- Bağımlılık (Dependency):
image = docker_image.app.image_iddiyerek Terraform’a şunu söylüyoruz: “Önce imajı çek, sonra container’ı oluştur.” - Dinamik Blok (
dynamic "env"): Eğer kullanıcı hiçenv_varsvermezse bu blok boş geçilir; eğer 10 tane verirse otomatik olarak 10 taneenvsatı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
}
EOFBurası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:
terraform init: Docker provider’ını (eklentisini) indirir.terraform plan: Variables kısmındaki boşlukları sana sorar (veya bir.tfvarsdosyasından okur) ve ne yapacağını listeler.terraform apply:- Docker imajını çeker.
- İmajın ID’sini alır, container ayarlarında kullanır.
- Container’ı başlatır.
- En son
outputs.tfiçindeki tüm bilgileri ekrana yazdırır.
- Docker imajını çeker.
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-demoklasörü oluşturuldumodules/docker-container/alt klasörü oluşturulduvariables.tf– 5 input tanımlandımain.tf– docker_image ve docker_container resource’ları yazıldıoutputs.tf– 5 output tanımlandıdynamicbloğu anlaşıldı (environment variables için)
Bu Adımda Öğrendiklerimiz
| Konu | Açıklama |
|---|---|
| Child Module | Başka modüller tarafından çağrılan modül |
| Module dosyaları | main.tf, variables.tf, outputs.tf |
| Module inputs | variable blokları ile tanımlanır |
| Module outputs | output blokları ile dışarıya verilir |
| dynamic bloğu | Flexible 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’ü
modulebloğ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-democat <<'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"
}
}
EOFDİ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:
cloudbloğ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:
source: “Git./modules/docker-containerklasöründeki kodları oku.”- Girdiler (Inputs): O klasördeki
variables.tfiçinde tanımlananname,image,internal_portgibi 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_varsiç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.tfdosyası 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
}
}
EOFBu 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_idBurada Terraform’a şunu diyorsun:
- Git
nginx_serverisimli modüle (hanimain.tfiçinde tanımladığın o blok). - O modülün kendi içindeki
outputs.tfdosyasına bak. - Orada adı
container_idolan çı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) terminaldeterraform applybittiğ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)
variables.tf: “Bana ne lazım?” (Girdiler)main.tf(Modül): “İşi nasıl yaparım?” (İşleyiş)outputs.tf(Modül): “Hangi bilgileri dışarı sızdırabilirim?” (Potansiyel Çıktılar)main.tf(Root): “Kalıbı kullanarak kaç tane ve hangi ayarlarla üreteceğim?” (Üretim Hattı)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:
- https://app.terraform.io git
- devops-learning-fikriulas organization’ına gir
- “New workspace” tıkla
- “No VCS connection” seç
- Name:
terraform-modules-demo - “Create workspace” tıkla
- Settings → General → Execution Mode → “Local” seç
- “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üklenditerraform apply– 6 kaynak oluşturuldu (3 image + 3 container)docker ps– 3 container çalışıyor
Bu Adımda Öğrendiklerimiz
| Konu | Açıklama |
|---|---|
| module bloğu | Child module’ü çağırmak için kullanılır |
| source | Modülün yeri (local: ./modules/xxx) |
| Module inputs | Module’a değer gönderme |
| Module outputs | module.xxx.output_adı ile erişim |
| Tek modül, çok kullanım | Aynı 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
| Özellik | Açıklama |
|---|---|
| Adres | https://registry.terraform.io |
| İçerik | Hazır modüller, provider’lar |
| Ücretsiz | Evet, çoğu modül açık kaynak |
| Güvenlik | Verified modüller (✓ işareti) |
Neden Registry Modülleri Kullanmalıyız?
| Avantaj | Açıklama |
|---|---|
| Zaman tasarrufu | Sıfırdan yazmana gerek yok |
| Best practices | Topluluk tarafından test edilmiş |
| Güncellemeler | Modül geliştiricileri bakım yapıyor |
| Dokümantasyon | Detaylı 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ül | Açıklama |
|---|---|
terraform-aws-modules/vpc/aws | AWS VPC oluşturma |
terraform-aws-modules/ec2-instance/aws | AWS EC2 instance |
terraform-aws-modules/rds/aws | AWS RDS database |
azuread | Azure 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üncellenir | version ile kilitle |
| Breaking changes | Eski versiyonu kullanmaya devam et |
| Ekip uyumu | Herkes aynı versiyonu kullanır |
Versiyon Sözdizimi:
| Expression | Anlamı |
|---|---|
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 inither seferinde aynı provider/modülü çeker
Local vs Registry Modülleri
| Özellik | Local Module | Registry Module |
|---|---|---|
| Source | ./modules/xxx | namespace/name/provider |
| Bakım | Kendin yaparsın | Topluluk/modül sahibi |
| Güncelleme | Manuel | terraform init -upgrade |
| Dokümantasyon | Kendin yazarsın | Registry’de hazır |
| Test | Kendin test edersin | Binlerce 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
