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
stringDatakullanı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-secretsoperator’ı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 yamlile 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: trueile 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ı:
stringDatayerinedatakullanırken değerin base64 encoded olduğundan emin olun - Permission denied: Volume mount’ta
defaultModeve 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.