Istanbul Local Zone için Terragrunt proje yapısını, VPC subnet stratejisini ve EKS cluster kurulumunu gerçek kod örnekleriyle anlatıyorum.
Local Zone Opt-in
Istanbul Local Zone varsayılan olarak kapalı gelir, hesabınızda aktif etmeniz gerekir. İki yöntem var:
Seçenek 1 — Terragrunt (önerilen):
Opt-in işlemini altyapı koduna dahil etmek için Terraform'ın aws_ec2_availability_zone_group resource'unu kullanabilirsiniz. Böylece bu adım da versiyon kontrolünde olur ve VPC'den önce otomatik uygulanır.
# modules/local-zone-optin/main.tf
resource "aws_ec2_availability_zone_group" "istanbul" {
group_name = "eu-central-1-ist-1a"
opt_in_status = "opted-in"
}
# env/hepapi/eu-central-1/prod/local-zone-optin/terragrunt.hcl
terraform {
source = "../../../../modules/local-zone-optin"
}
include "root" {
path = find_in_parent_folders()
}
VPC modülünün dependency bloğuna bu modülü ekleyerek sıralamayı garantileyin. Aktifleştirme birkaç dakika sürebilir; Terraform bunu otomatik bekler.
Seçenek 2 - AWS CLI / Console:
AWS Console:
EC2 → Sol menü → "Zone Groups" → eu-central-1-ist-1a → Actions → Enable
AWS CLI:
aws ec2 modify-availability-zone-group \
--group-name eu-central-1-ist-1a \
--opt-in-status opted-in \
--region eu-central-1 \
--profile hepapi-sso
Aktifleştirme birkaç dakika alabilir. Kontrol edin:
aws ec2 describe-availability-zones \
--all-availability-zones \
--region eu-central-1 \
--query 'AvailabilityZones[?ZoneName==`eu-central-1-ist-1a`]' \
--profile hepapi-sso
Araçlar
# macOS
brew install terraform terragrunt awscli kubectl helm
# Versiyon kontrol
terraform --version # >= 1.5.0
terragrunt --version # >= 0.55.0
aws --version # >= 2.0.0
S3 Backend ve DynamoDB
Terragrunt remote state için S3 bucket ve DynamoDB tablosu gerekir. İki yöntem var:
Seçenek 1 - Terragrunt (önerilen):
terragrunt.hcl dosyasındaki remote_state bloğuna bucket ve dynamodb_table tanımlandığında Terragrunt, ilk terragrunt apply sırasında bu kaynakları otomatik olarak oluşturur - ayrıca bir şey yapmanıza gerek yoktur.
# terragrunt.hcl (root)
remote_state {
backend = "s3"
config = {
encrypt = true
bucket = "hepapi-local-zone-terragrunt-states"
key = "${path_relative_to_include()}/terraform.tfstate"
region = "eu-central-1"
dynamodb_table = "hepapi-local-zone-terragrunt-lock-tables"
profile = local.profile
}
generate = {
path = "backend.tf"
if_exists = "overwrite_terragrunt"
}
}
Bucket versioning ve server-side encryption da otomatik aktif edilir.
Seçenek 2 - AWS CLI (manuel):
# S3 bucket
aws s3api create-bucket \
--bucket hepapi-local-zone-terragrunt-states \
--region eu-central-1 \
--create-bucket-configuration LocationConstraint=eu-central-1 \
--profile hepapi-sso
# Versioning aktif et
aws s3api put-bucket-versioning \
--bucket hepapi-local-zone-terragrunt-states \
--versioning-configuration Status=Enabled \
--profile hepapi-sso
# DynamoDB lock table
aws dynamodb create-table \
--table-name hepapi-local-zone-terragrunt-lock-tables \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region eu-central-1 \
--profile hepapi-sso
SSO ile Kimlik Doğrulama
AWS SSO kullanıyorsanız kritik bir sorunla karşılaşırsınız: Terraform S3 backend sso_session formatını desteklemez. Her terminal oturumunda şunu çalıştırın:
aws sso login --profile hepapi-sso
eval "$(aws configure export-credentials --profile hepapi-sso --format env)"
Bu komut AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY ve AWS_SESSION_TOKEN ortam değişkenlerini set eder. Terraform bu değişkenleri otomatik okur.
.
├── terragrunt.hcl # Root config (provider, backend)
├── account.hcl # Account ID
└── env/
└── hepapi/
└── eu-central-1/
└── prod/
├── env.hcl # Tüm versiyon ve config değerleri
├── vpc/
│ └── terragrunt.hcl
└── eks/
├── eks/
│ └── terragrunt.hcl
├── node-group/
│ └── terragrunt.hcl
├── karpenter/
│ ├── karpenter-module/
│ ├── karpenter-controller-helm/
│ └── karpenter-configs/
├── ebs-csi/
├── alb-controller/
│ ├── aws-alb-controller-role/
│ └── aws-alb-controller/
└── common-security-group/
Kök terragrunt.hcl dosyası tüm alt modüller için provider ve backend ayarlarını otomatik üretir. Her modülde tekrar tekrar yazmaya gerek kalmaz.
# terragrunt.hcl (root)
locals {
account_vars = read_terragrunt_config(find_in_parent_folders("account.hcl"))
env_vars = read_terragrunt_config(find_in_parent_folders("env.hcl"))
region = local.env_vars.locals.region
profile = "hepapi-sso"
}
generate "provider" {
path = "providers.tf"
if_exists = "overwrite"
contents = <<EOF
provider "aws" {
region = "${local.region}"
profile = "${local.profile}"
}
EOF
}
generate "provider_version" {
path = "provider_version_override.tf"
if_exists = "overwrite_terragrunt"
contents = <<EOF
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.83.0"
}
helm = {
source = "hashicorp/helm"
version = "~> 3.1.1"
}
kubectl = {
source = "gavinbunney/kubectl"
version = ">= 1.19.0"
}
}
}
EOF
}
remote_state {
backend = "s3"
config = {
encrypt = true
bucket = "hepapi-local-zone-terragrunt-states"
key = "${path_relative_to_include()}/terraform.tfstate"
region = "eu-central-1"
dynamodb_table = "hepapi-local-zone-terragrunt-lock-tables"
profile = local.profile
}
generate = {
path = "backend.tf"
if_exists = "overwrite_terragrunt"
}
}
Versiyon Uyumluluğu - En Kritik Konu
Bu kurulumda en çok zaman harcadığımız konu provider ve module versiyon uyumluluğuydu. Kısaca:
Tüm versiyon değerleri tek yerden yönetilir:
# env/hepapi/eu-central-1/prod/env.hcl
locals {
module_versions = {
vpc = "5.19.0" # v6 AWS Provider v6 gerektirir, uyumsuz
eks = "20.33.1" # AWS Provider ~> 5.83.0 gerektirir
node-group = "20.33.1"
}
helm_versions = {
karpenter-chart = "1.1.2"
ebs-csi-chart = "2.40.3"
efs-csi-chart = "3.1.5"
aws-load-balancer-controller = "1.11.0"
}
}
Subnet İndeksleme Stratejisi
Local Zone kurulumunun temel tasarım kararı budur. VPC module subnet'leri azs listesinin sırasına göre oluşturur ve bu sıra garanti edilmiştir.
azs = ["eu-central-1a", "eu-central-1b", "eu-central-1-ist-1a"]
private_subnets[0] → 10.20.0.0/19 → eu-central-1a → EKS control plane
private_subnets[1] → 10.20.64.0/19 → eu-central-1b → EKS control plane
private_subnets[2] → 10.20.32.0/19 → eu-central-1-ist-1a → Istanbul workers
Bu indeks stratejisi sayesinde:
# env.hcl — VPC konfigürasyonu
vpc = {
vpc_name = "hepapi-local-zone"
cidr = "10.20.0.0/16"
azs = ["eu-central-1a", "eu-central-1b", "eu-central-1-ist-1a"]
private_subnets = ["10.20.0.0/19", "10.20.64.0/19", "10.20.32.0/19"]
public_subnets = ["10.20.100.0/24", "10.20.102.0/24", "10.20.101.0/24"]
enable_nat_gateway = true
single_nat_gateway = true # Istanbul trafiği Frankfurt NAT üzerinden
one_nat_gateway_per_az = false
}
# env/hepapi/eu-central-1/prod/vpc/terragrunt.hcl
terraform {
source = "tfr:///terraform-aws-modules/vpc/aws//?version=${local.env_vars.locals.module_versions.vpc}"
}
inputs = {
name = "${local.env_vars.locals.vpc.vpc_name}-vpc"
cidr = local.env_vars.locals.vpc.cidr
azs = local.env_vars.locals.vpc.azs
private_subnets = local.env_vars.locals.vpc.private_subnets
public_subnets = local.env_vars.locals.vpc.public_subnets
# Local Zone'da database subnet group desteklenmiyor
create_database_subnet_group = false
create_database_subnet_route_table = false
enable_nat_gateway = local.env_vars.locals.vpc.enable_nat_gateway
single_nat_gateway = local.env_vars.locals.vpc.single_nat_gateway
one_nat_gateway_per_az = local.env_vars.locals.vpc.one_nat_gateway_per_az
enable_dns_hostnames = true
enable_dns_support = true
# ALB discovery için public subnet tag'i
public_subnet_tags = {
"kubernetes.io/role/elb" = 1
"kubernetes.io/cluster/${local.env_vars.locals.eks.cluster_name}" = "shared"
}
# Karpenter subnet discovery için private subnet tag'leri
private_subnet_tags = {
"kubernetes.io/role/internal-elb" = 1
"kubernetes.io/cluster/${local.env_vars.locals.eks.cluster_name}" = "shared"
"karpenter.sh/discovery/${local.env_vars.locals.eks.cluster_name}" = "${local.env_vars.locals.eks.cluster_name}"
}
}
NAT Gateway Notu: single_nat_gateway = true ile NAT Gateway yalnızca eu-central-1a'da oluşturulur. Istanbul pod'larının internet trafiği Frankfurt NAT üzerinden geçer. Bu cross-zone data transfer ücreti doğurur (~$0.02/GB). Yüksek egress trafiğiniz varsa bunu maliyet planlamasına ekleyin.
Sıradaki bölümler için takipte kalın: