Kubernetes Production Checklist#
"
kubectl apply -fçalıştı diye production'da çalışıyor demek değil."Bu checklist, prod'a çıkacak her workload için resource → security → reliability → observability → ops beş ekseninde doğrulanması gerekenleri gruplar. Tek satırlık tablo değil — her madde niye ve nasıl açıklamalı.
📊 Hızlı bakış (50 madde)#
| # | Eksen | Madde sayısı |
|---|---|---|
| A | Workload Tasarımı | 12 |
| B | Resource & Performance | 7 |
| C | Security | 11 |
| D | Reliability & HA | 8 |
| E | Observability | 6 |
| F | Operations & GitOps | 6 |
A. Workload Tasarımı#
A1 — Deployment vs StatefulSet doğru seçildi#
- ✅ Stateless workload →
Deployment - ✅ Stable identity (Postgres, Kafka, ZK) →
StatefulSet+ headlessService - ✅ Tek-instance task →
JobveyaCronJob - ✅ Node-level daemon →
DaemonSet
A2 — Image tag'i immutable#
image: <REGISTRY>/<APP>:v1.2.3 # ✅ semantic versioned
image: <REGISTRY>/<APP>@sha256:abc... # ✅✅ SHA pinned (en güvenli)
image: <REGISTRY>/<APP>:latest # ❌ rollback imkansız
A3 — imagePullPolicy doğru#
IfNotPresent(default) — node'da varsa çekmeAlways— sadece dev için (production'da gereksiz registry yükü)- SHA-pinned image kullanırsanız
Alwayszaten gerekmiyor
A4 — Replica sayısı en az 2#
- HA için minimum 2; ideali 3 (zone başına 1)
- Tek replica = single point of failure
- Singleton workload bile leader-election + 2 replica + PDB
A5 — revisionHistoryLimit makul#
A6 — Rolling update stratejisi tanımlı#
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25% # geçici fazla pod
maxUnavailable: 0 # ❗ zero-downtime için MUTLAKA 0
A7 — terminationGracePeriodSeconds yeterli#
- Default 30 saniye
- Uzun-süren request'leri bekletme süresi + buffer
preStophook + graceful shutdown akışı toplam < bu değer
A8 — preStop hook ile graceful shutdown#
Bu olmadan: pod kill → mevcut bağlantılar 503 döndürür. A9 — Container command/args net#
- Image'ın default
CMD'si üzerine yazıyorsanız neden net olsun argsüzerinden çalışma profili değiştirilebiliyor olsun
A10 — Multi-container pattern uygun mu?#
- Sidecar (istio-proxy, log forwarder): OK
- Init container (DB migration, secret fetch): OK
- 3+ container'lı pod: muhtemelen ayrı workload'lara bölünmeli
A11 — Headless service var mı? (StatefulSet için)#
apiVersion: v1
kind: Service
metadata:
name: <NAME>
spec:
clusterIP: None # ← headless
selector: ...
A12 — Pod DNS subdomain kontrolü (StatefulSet için)#
B. Resource & Performance#
B1 — resources.requests her container'da var#
Yoksa: scheduler bilemediği için BestEffort QoS, evict edilen ilk pod siz olursunuz. B2 — resources.limits doğru kullanılıyor#
- ❗ Memory limit: mutlaka tanımla (OOM önle)
- ⚠️ CPU limit: controversial. Throttling ekler. Genellikle:
- Request = guaranteed minimum
- Limit = sadece burstable cap (ya da hiç koyma)
B3 — Request/limit oranı QoS sınıfını belirler#
requests == limits → Guaranteed (en yüksek priority)
requests < limits → Burstable
hiçbir şey → BestEffort (en önce evict)
B4 — VPA recommendation görüldü#
kubectl get vpa <APP> -o yaml | grep -A 10 'recommendation'
# Mevcut request'leri actual usage ile karşılaştır
B5 — HPA min/max replica#
B6 — Pod priority class#
- Sistem-kritik:
system-cluster-critical - Production app: custom
priorityClass: production-high - Best-effort batch:
priorityClass: low
B7 — Pod density (node başına pod) makul#
- Node max-pods (default 110) yetiyor mu?
- IP havuzu (CNI) yetiyor mu? (
kubectl get node -o yaml | grep podCIDR)
C. Security#
C1 — runAsNonRoot: true#
C2 — readOnlyRootFilesystem: true#
Yazılması gereken dizinler emptyDir ile mount. C3 — Capabilities düşürülmüş#
securityContext:
capabilities:
drop: ["ALL"]
# add: ["NET_BIND_SERVICE"] # sadece :80/:443 bind için gerekirse
C4 — allowPrivilegeEscalation: false#
C5 — Seccomp profili#
C6 — ServiceAccount least privilege#
- Default SA token mount edilmiyor:
automountServiceAccountToken: false - Kendi SA + Role + RoleBinding (sadece gerekli verb'ler)
cluster-adminasla pod'a verilmez
C7 — NetworkPolicy default-deny + explicit allow#
Bunu skip eden cluster'lar lateral movement saldırısına açıktır.C8 — Secrets Secret resource veya External Secrets#
- Plain
envile şifre yasak - Vault / AWS Secrets Manager + ESO
- Ya da SOPS-encrypted YAML + git'te
C9 — Pod Security Standards: restricted#
apiVersion: v1
kind: Namespace
metadata:
name: <NS>
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
C10 — Image imzalı + Kyverno verifyImages#
cosign signher CI/CD build sonrası- Cluster
Kyverno ClusterPolicy: verifyImagesile imzasızı reject
C11 — Tüm imajlar trusted registry'den#
# Kyverno policy'le enforce:
# image: ghcr.io/<ORG>/* veya <COMPANY_REGISTRY>/*
# docker.io/library/* doğrudan kullanma — proxy registry'den geç
D. Reliability & HA#
D1 — livenessProbe doğru tanımlı#
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 0 # startupProbe varsa
periodSeconds: 10
failureThreshold: 3
/health endpoint → cascade kill. D2 — readinessProbe doğru tanımlı#
DB bağlantısı, downstream warm-up okumalı; 503 → service'ten çıkar. D3 — startupProbe (yavaş başlangıç için)#
startupProbe:
httpGet:
path: /health/live
port: 8080
failureThreshold: 30 # 30 * 5s = 150 saniye'ye kadar tolerate
periodSeconds: 5
D4 — PodDisruptionBudget var#
apiVersion: policy/v1
kind: PodDisruptionBudget
spec:
minAvailable: 2 # voluntary disruption sırasında garanti
selector: ...
D5 — topologySpreadConstraints var#
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: <APP>
D6 — Pod anti-affinity (aynı node'a yığılma engel)#
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: <APP>
topologyKey: kubernetes.io/hostname
D7 — Persistent storage: StorageClass + retain policy#
volumeClaimTemplates: # StatefulSet'te
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: <CLASS> # gp3, ssd, vs
resources:
requests:
storage: 10Gi
reclaimPolicy: Retain (kazara silmede koruma). D8 — Backup test edilmiş#
- Velero veya snapshot tabanlı
- Restore'u en az ayda bir test et — hiç restore edilmemiş backup yoktur
E. Observability#
E1 — Metrics endpoint expose (Prometheus formatı)#
ServiceMonitor / PodMonitor (kube-prometheus-stack varsa).E2 — Structured logging (JSON)#
# stdout'a JSON yaz; log forwarder (Loki/ELK) parse eder
{"level":"error","ts":"2026-04-30T14:05:32Z","msg":"DB timeout","trace_id":"..."}
E3 — Trace context propagation (W3C traceparent)#
- OpenTelemetry SDK ekli
- HTTP client'lar
traceparentheader'ını forward eder - Logger trace_id'yi log'a ekler (auto-correlation)
E4 — SLO + alert tanımlı#
- En az 3 SLI: availability, latency p99, error rate
- SLO target (örn: %99.9, p99 < 500ms, error < %0.1)
- Multi-burn-rate alert (fast + slow burn)
E5 — Dashboard var#
- Golden 4 sinyal (latency, traffic, errors, saturation)
- Per-deployment annotation (deploy zamanları işaretli)
E6 — Alert routing yapılı#
- Sev-1 → PagerDuty
- Sev-2 → Slack #alerts
- Sev-3 → Ticket queue
- Alert fatigue önlemi: SLO-based, symptom-based
F. Operations & GitOps#
F1 — Manifest GitOps repo'da#
- Kubectl
applymanuel yapılmaz - ArgoCD/Flux Git'i izler
- Drift sürekli düzeltilir
F2 — Multi-env layout (Kustomize/Helm)#
F3 — Secret yönetimi GitOps-uyumlu#
- ESO + Vault/AWS SM
- Sealed Secrets (Bitnami)
- SOPS-encrypted (age/PGP)
F4 — Image update otomasyonu#
- Argo Image Updater veya Renovate ile k8s-config repo'sunda image bump PR
F5 — Migration job'ları kontrollü#
- Hook:
helm.sh/hook: post-install,pre-upgrade - Idempotent
- Timeout + retry policy
F6 — Cost label'ları zorunlu#
Kyverno ile enforce.✅ Pre-deploy Sanity (script'lenebilir)#
# 1. YAML valid
kubeconform -strict -summary deployment.yaml
# 2. Best practices linter
kube-linter lint deployment.yaml
# 3. Security scan
trivy config deployment.yaml
checkov -f deployment.yaml --framework kubernetes
# 4. Dry-run sunucu tarafı (admission policies dahil)
kubectl apply -f deployment.yaml --dry-run=server
# 5. Diff (canlı vs yeni)
kubectl diff -f deployment.yaml
🚦 "Bu prod'a çıkamaz" red flag'ler#
| 🚩 | Açıklama |
|---|---|
:latest tag | Rollback yok, immutability yok |
replicas: 1 (singleton non-leader-elected) | SPOF |
resources null | BestEffort QoS, evict riski |
runAsRoot | Container escape risk |
Namespace == default | Multi-tenant boundary yok |
livenessProbe HTTP / | DB query'li endpoint cascade kill |
| Secret env'de plain | Logger'da, env dump'ta sızıyor |
| NetworkPolicy yok | Lateral movement açık |
cluster-admin SA | Tek pod compromise = cluster pwn |
Bu 9 maddeden 1'i bile geçerse: PR red, canlıya çıkmaz.