Kubernetes’te ConfigMap ve Secret ile Uygulama Yapılandırma Yönetimi

Kubernetes üzerinde uygulama geliştirip deploy etmeye başladığınızda, er ya da geç şu soruyla yüzleşirsiniz: “Uygulamamın konfigürasyon dosyalarını ve hassas bilgilerini nereye koyacağım?” Docker imajının içine gömmek en kötü seçenek, environment variable’ları direkt pod tanımına yazmak ise orta seviye kötü. Kubernetes bu problemi iki güçlü kaynak tipiyle çözüyor: ConfigMap ve Secret. Bu yazıda bu iki kaynağı derinlemesine inceleyeceğiz, gerçek dünya senaryolarıyla nasıl kullanıldıklarını göreceğiz ve yanlış yapılan yaygın hataları ele alacağız.

ConfigMap Nedir ve Ne Zaman Kullanılır

ConfigMap, Kubernetes’te hassas olmayan konfigürasyon verilerini anahtar-değer çiftleri olarak saklamanızı sağlayan bir API nesnesidir. Bir uygulamanın hangi veritabanı host’una bağlanacağı, hangi log seviyesinde çalışacağı, hangi feature flag’lerin aktif olduğu gibi bilgiler ConfigMap’e mükemmel adaylardır.

ConfigMap’in temel mantığı şudur: Uygulama kodunuzu konfigürasyondan ayırırsınız. Aynı Docker imajını farklı ortamlara (dev, staging, prod) farklı ConfigMap’lerle deploy edebilirsiniz. İmajı yeniden build etmenize gerek kalmaz.

İlk ConfigMap’inizi Oluşturmak

En basit yöntemle başlayalım. Komut satırından direkt literal değerlerle ConfigMap oluşturabilirsiniz:

kubectl create configmap app-config 
  --from-literal=APP_ENV=production 
  --from-literal=LOG_LEVEL=info 
  --from-literal=MAX_CONNECTIONS=100 
  --from-literal=CACHE_TTL=3600

Oluşturduğunuz ConfigMap’i incelemek için:

kubectl get configmap app-config -o yaml

Bu komutu çalıştırdığınızda şöyle bir çıktı görürsünüz:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: default
data:
  APP_ENV: production
  CACHE_TTL: "3600"
  LOG_LEVEL: info
  MAX_CONNECTIONS: "100"

Peki ya bir konfigürasyon dosyasını olduğu gibi ConfigMap’e yüklemek istiyorsanız? Bunu da --from-file ile yapabilirsiniz. Diyelim ki bir nginx konfigürasyon dosyanız var:

# nginx.conf dosyasini configmap'e yukle
kubectl create configmap nginx-config --from-file=nginx.conf=./nginx.conf

# Tum bir dizini yuklemek icin
kubectl create configmap app-configs --from-file=./config-dir/

YAML ile ConfigMap Tanımlamak

Gerçek dünya projelerinde ConfigMap’leri YAML dosyası olarak tutmak ve Git’e commit etmek en iyi pratiktir. İşte kapsamlı bir örnek:

apiVersion: v1
kind: ConfigMap
metadata:
  name: webapp-config
  namespace: production
  labels:
    app: webapp
    version: "2.1"
data:
  # Basit anahtar-deger cifltleri
  APP_ENV: "production"
  LOG_LEVEL: "warn"
  API_TIMEOUT: "30"
  
  # Cok satirli konfigürasyon dosyasi
  app.properties: |
    server.port=8080
    spring.datasource.url=jdbc:postgresql://postgres-service:5432/appdb
    spring.cache.type=redis
    spring.redis.host=redis-service
    spring.redis.port=6379
    
  # Nginx konfigürasyonu
  nginx.conf: |
    upstream backend {
        server webapp-service:8080;
    }
    server {
        listen 80;
        location / {
            proxy_pass http://backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }

Bu YAML’ı apply etmek için:

kubectl apply -f webapp-config.yaml
kubectl describe configmap webapp-config -n production

ConfigMap’i Pod’lara Bağlamak

ConfigMap oluşturdunuz, güzel. Ama asıl mesele bunu pod’larınızın kullanmasını sağlamak. Bunun üç farklı yolu var.

Environment Variable Olarak Kullanmak

En yaygın yöntem. Pod tanımınızda ConfigMap’ten değerleri environment variable olarak çekebilirsiniz:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: myapp:2.1
        # Tek tek secmek istediginizde
        env:
        - name: APP_ENVIRONMENT
          valueFrom:
            configMapKeyRef:
              name: webapp-config
              key: APP_ENV
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: webapp-config
              key: LOG_LEVEL
        # Ya da tum configmap'i direkt yuklemek icin
        envFrom:
        - configMapRef:
            name: webapp-config

envFrom kullandığınızda ConfigMap’teki tüm anahtar-değer çiftleri direkt olarak container’ın environment variable’larına eklenir. Pratik ama dikkatli olun, isim çakışmalarına yol açabilir.

Volume Olarak Mount Etmek

Konfigürasyon dosyalarını volume olarak mount etmek, özellikle uygulama konfigürasyon dosyaları için çok daha temiz bir yöntemdir:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx:1.25
    volumeMounts:
    - name: nginx-config-vol
      mountPath: /etc/nginx/conf.d
    - name: app-properties-vol
      mountPath: /app/config
      readOnly: true
  volumes:
  - name: nginx-config-vol
    configMap:
      name: webapp-config
      items:
      - key: nginx.conf
        path: default.conf
  - name: app-properties-vol
    configMap:
      name: webapp-config
      items:
      - key: app.properties
        path: application.properties

Bu yöntemin güzel bir özelliği var: ConfigMap güncellendiğinde, mount edilen dosyalar otomatik olarak güncellenir (birkaç dakika içinde). Environment variable olarak kullanıldığında bu olmaz, pod’u yeniden başlatmanız gerekir.

Secret: Hassas Bilgiler İçin Güvenli Depo

ConfigMap’i anladınız. Şimdi gelelim hassas bilgilere. Veritabanı şifreleri, API anahtarları, TLS sertifikaları, token’lar bunları ConfigMap’e koymamalısınız. Bunlar için Secret var.

Secret, yapısal olarak ConfigMap’e benzer ama birkaç önemli farkı var. Veriler base64 encoded olarak saklanır (bu şifreleme değil, encoding’dir, unutmayın), etcd’de ayrı bir erişim kontrolüne tabi tutulabilir ve audit loglarında özel muamele görür.

Secret Oluşturma Yöntemleri

Komut satırından generic Secret oluşturmak:

# Literal degerlerden
kubectl create secret generic db-credentials 
  --from-literal=DB_USER=appuser 
  --from-literal=DB_PASSWORD='S3cur3P@ssw0rd!' 
  --from-literal=DB_NAME=production_db

# Dosyadan (ornegin API anahtari bir dosyada sakliysa)
kubectl create secret generic api-keys 
  --from-file=google-api-key=./google-api-key.txt 
  --from-file=stripe-secret=./stripe-secret.txt

TLS Secret oluşturmak için özel bir tip var:

# Sertifika ve anahtar dosyalarindan TLS secret
kubectl create secret tls webapp-tls 
  --cert=./tls.crt 
  --key=./tls.key

YAML ile Secret Tanımlamak

Secret’ları YAML olarak tanımladığınızda değerlerin base64 encoded olması gerekir:

# Once base64 encode edin
echo -n 'appuser' | base64
# Cikti: YXBwdXNlcg==

echo -n 'S3cur3P@ssw0rd!' | base64
# Cikti: UzNjdXIzUEBzc3cwcmQh
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
  namespace: production
type: Opaque
data:
  DB_USER: YXBwdXNlcg==
  DB_PASSWORD: UzNjdXIzUEBzc3cwcmQh
  DB_NAME: cHJvZHVjdGlvbl9kYg==

Alternatif olarak stringData kullanabilirsiniz, bu durumda base64 encode etmenize gerek yok, Kubernetes bunu otomatik yapar:

apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
  namespace: production
type: Opaque
stringData:
  DB_USER: appuser
  DB_PASSWORD: "S3cur3P@ssw0rd!"
  DB_NAME: production_db
  connection-string: "postgresql://appuser:S3cur3P@ssw0rd!@postgres:5432/production_db"

Dikkat: stringData içeren Secret’ları Git’e commit etmeyin. Bu dosyalar açık metin içerir.

Secret’ı Pod’lara Bağlamak

Secret’ları da ConfigMap gibi environment variable veya volume olarak kullanabilirsiniz:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: myapp:2.1
        env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: connection-string
        - name: STRIPE_SECRET_KEY
          valueFrom:
            secretKeyRef:
              name: api-keys
              key: stripe-secret
        volumeMounts:
        - name: tls-certs
          mountPath: /app/certs
          readOnly: true
        - name: db-creds-vol
          mountPath: /app/secrets
          readOnly: true
      volumes:
      - name: tls-certs
        secret:
          secretName: webapp-tls
      - name: db-creds-vol
        secret:
          secretName: db-credentials
          defaultMode: 0400

defaultMode: 0400 ile volume olarak mount edilen Secret dosyalarına sadece root kullanıcısının okuyabilmesini sağlıyorsunuz. Bu güvenlik açısından önemli.

Gerçek Dünya Senaryosu: Multi-Tier Uygulama

Bir e-ticaret uygulaması deploy ettiğinizi düşünün. Frontend (React), Backend API (Node.js) ve PostgreSQL veritabanından oluşuyor. Konfigürasyonları nasıl organize edeceğinize bakalım:

# Namespace olustur
kubectl create namespace ecommerce-prod

# Veritabani credential'lari
kubectl create secret generic postgres-secret 
  --from-literal=POSTGRES_USER=ecom_user 
  --from-literal=POSTGRES_PASSWORD='Xk9#mP2$vL8' 
  --from-literal=POSTGRES_DB=ecommerce 
  -n ecommerce-prod

# JWT ve API anahtarlari
kubectl create secret generic app-secrets 
  --from-literal=JWT_SECRET='your-256-bit-secret-key-here' 
  --from-literal=REDIS_PASSWORD='redis-pass-123' 
  --from-literal=SENDGRID_API_KEY='SG.xxxxx' 
  -n ecommerce-prod

# Genel uygulama konfigürasyonu
kubectl create configmap app-config 
  --from-literal=NODE_ENV=production 
  --from-literal=PORT=3000 
  --from-literal=REDIS_HOST=redis-service 
  --from-literal=REDIS_PORT=6379 
  --from-literal=POSTGRES_HOST=postgres-service 
  --from-literal=POSTGRES_PORT=5432 
  --from-literal=MAX_POOL_SIZE=20 
  --from-literal=LOG_LEVEL=error 
  -n ecommerce-prod

Şimdi bu konfigürasyonları kullanan backend deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-api
  namespace: ecommerce-prod
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backend-api
  template:
    metadata:
      labels:
        app: backend-api
    spec:
      containers:
      - name: api
        image: ecom-backend:3.2.1
        ports:
        - containerPort: 3000
        envFrom:
        - configMapRef:
            name: app-config
        env:
        - name: POSTGRES_USER
          valueFrom:
            secretKeyRef:
              name: postgres-secret
              key: POSTGRES_USER
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: postgres-secret
              key: POSTGRES_PASSWORD
        - name: POSTGRES_DB
          valueFrom:
            secretKeyRef:
              name: postgres-secret
              key: POSTGRES_DB
        - name: JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: JWT_SECRET
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"

ConfigMap ve Secret Güncelleme Stratejileri

Konfigürasyon değiştirmeniz gerektiğinde ne yapacaksınız? Birkaç senaryo var.

Rolling Update ile Güvenli Güncelleme

ConfigMap’i güncellediğinizde pod’lar otomatik olarak yeniden başlamaz (environment variable kullanıyorsanız). Bunu tetiklemek için bir trick kullanabilirsiniz:

# ConfigMap'i guncelle
kubectl patch configmap app-config 
  --patch '{"data":{"LOG_LEVEL":"debug"}}' 
  -n ecommerce-prod

# Deployment'i yeniden baslatarak yeni konfigürasyonu almasini sagla
kubectl rollout restart deployment/backend-api -n ecommerce-prod

# Rollout durumunu izle
kubectl rollout status deployment/backend-api -n ecommerce-prod

Daha iyi bir yaklaşım: ConfigMap versiyonunu Deployment annotation’larına eklemek. Bu sayede ConfigMap değiştiğinde Deployment otomatik olarak rollout yapar:

spec:
  template:
    metadata:
      annotations:
        configmap-version: "v3"  # Bu degeri her degisiklikte artirin

Secret Rotation

Üretimde şifre rotasyonu hayati önem taşır. Sıfır kesinti ile nasıl yapılır:

# Yeni secret olustur (eski silinmeden once)
kubectl create secret generic db-credentials-v2 
  --from-literal=DB_PASSWORD='NewSecurePass#2024' 
  -n ecommerce-prod

# Deployment'i yeni secret'i kullanacak sekilde guncelle
kubectl set env deployment/backend-api 
  --from=secret/db-credentials-v2 
  -n ecommerce-prod

# Rollout tamamlandiktan sonra eski secret'i sil
kubectl delete secret db-credentials-v1 -n ecommerce-prod

Güvenlik En İyi Pratikleri

Secret yönetimi güvenlik açısından kritik noktalar içeriyor. Birkaç önemli kural:

RBAC ile Erişim Kısıtlama

# Sadece belirli service account'in secret okuyabilmesi icin
kubectl create role secret-reader 
  --verb=get,list 
  --resource=secrets 
  --resource-name=db-credentials 
  -n ecommerce-prod

kubectl create rolebinding webapp-secret-reader 
  --role=secret-reader 
  --serviceaccount=ecommerce-prod:webapp-sa 
  -n ecommerce-prod

Önemli güvenlik kuralları:

  • Etcd şifreleme: Kubernetes cluster’ınızda etcd-at-rest encryption aktif olmalı, aksi halde Secret’lar etcd’de base64 olarak düz okunabilir şekilde durur
  • Secret’ları Git’e commit etmeyin: YAML dosyalarınızda stringData kullanıyorsanız kesinlikle .gitignore‘a ekleyin
  • External Secret yönetimi: Üretim ortamlarında HashiCorp Vault, AWS Secrets Manager veya Azure Key Vault entegrasyonu için external-secrets operator’ını değerlendirin
  • Minimum yetki prensibi: Her pod sadece ihtiyacı olan Secret’lara erişebilmeli
  • Image pull secret’larını unutmayın: Private registry kullanıyorsanız ayrı bir secret tipi gerekli
# Private registry icin image pull secret
kubectl create secret docker-registry regcred 
  --docker-server=registry.yourcompany.com 
  --docker-username=robot-account 
  --docker-password='token-here' 
  -n ecommerce-prod

ConfigMap ile Secret Arasındaki Temel Farklar

Bu iki kaynağı doğru kullanmak için farkları net anlamak gerekiyor:

  • ConfigMap: Hassas olmayan veriler, konfigürasyon dosyaları, ortam parametreleri
  • Secret: Şifreler, API token’ları, TLS sertifikaları, SSH anahtarları, OAuth token’ları
  • ConfigMap boyutu: Maksimum 1MB veri saklayabilir
  • Secret boyutu: Maksimum 1MB (base64 encoded değil, decoded değer)
  • ConfigMap görünürlüğü: kubectl get configmap -o yaml ile içerik direkt okunabilir
  • Secret görünürlüğü: Base64 encoded görünür ama erişim RBAC ile kısıtlanabilir
  • Immutable flag: Her ikisi de immutable: true ile değişmez yapılabilir, bu performans ve güvenlik için önerilir
# Immutable secret ornegi
apiVersion: v1
kind: Secret
metadata:
  name: static-api-keys
immutable: true
data:
  API_KEY: base64encodedvalue==

Immutable olarak işaretlenmiş kaynak güncellenemez, silinip yeniden oluşturulması gerekir. Sık değişmeyen konfigürasyonlar için bunu kullanmak kubelet’in sürekli bu kaynakları watch etmesini engeller, cluster performansını olumlu etkiler.

Sorun Giderme

Konfigürasyon sorunları en yaygın Kubernetes sorunları arasında yer alır. Birkaç kullanışlı komut:

# Pod'un environment variable'larini kontrol et
kubectl exec -it pod-name -n namespace -- env | grep APP

# ConfigMap mount durumunu kontrol et
kubectl exec -it pod-name -n namespace -- cat /app/config/app.properties

# Secret degerini decode ederek kontrol et
kubectl get secret db-credentials -o jsonpath='{.data.DB_PASSWORD}' -n ecommerce-prod | base64 --decode

# ConfigMap event'larini izle
kubectl describe configmap app-config -n ecommerce-prod

# Pod'un konfigürasyonu neden alamamadigini anlamak icin
kubectl describe pod problematic-pod -n ecommerce-prod | grep -A 10 Events

Sık karşılaşılan hatalar ve çözümleri:

  • “secret not found”: Secret ve Pod aynı namespace’te olmalı
  • “key not found”: ConfigMap’teki anahtar adı ile referans verilen ad eşleşmeli, büyük-küçük harf duyarlı
  • Base64 decode hatası: stringData yerine data kullanırken değerin base64 encoded olduğundan emin olun
  • Permission denied: Volume mount’ta defaultMode ve container’ın çalıştığı kullanıcı uyumlu olmalı

Sonuç

ConfigMap ve Secret, Kubernetes’in “12-factor app” prensiplerini hayata geçirmenizi sağlayan temel yapı taşları. Konfigürasyonunuzu kod tabanından ayırdığınızda aynı imajı farklı ortamlarda çalıştırabilir, acil durumlarda uygulamayı yeniden build etmeden konfigürasyonu güncelleyebilir ve ekip üyelerine kod erişimi vermeden konfigürasyon yönetimi yapabilirsiniz.

Özetlemek gerekirse: Hassas olmayan her şey ConfigMap’e, hassas bilgiler Secret’a. Üretim ortamında mutlaka etcd şifrelemeyi etkinleştirin ve external secret yönetimi çözümlerini değerlendirin. Secret’larınızı Git repository’sine commit etmeyin, bunun için Sealed Secrets veya External Secrets Operator gibi araçlara bakın. Volume mount, environment variable’a göre genellikle daha esnektir çünkü pod yeniden başlatılmadan güncellenebilir.

Bu temelleri oturttuğunuzda Helm chart’ları ile konfigürasyon yönetimi, GitOps yaklaşımıyla Secret yönetimi ve HashiCorp Vault entegrasyonu gibi ileri konulara geçiş çok daha kolay olacak.

Yorum yapın