Bir SaaS müşterimiz 7 microservice (auth, api, worker, scheduler, notifier, reporter, gateway) ve 4 environment (dev/staging/uat/prod) ile çalışıyordu. Her servis için ayrı Helm chart, her environment için ayrı values dosyası — toplam 28 deployment kombinasyonu, manuel yönetilmesi imkansızdı. Bu yazı 4 hafta süren refactor’dan notlar.
Chart Yapısı: Umbrella Chart Pattern
charts/
├── platform/ # Umbrella chart
│ ├── Chart.yaml # Tüm sub-chart'ları dependency olarak listeler
│ ├── values.yaml # Common values
│ ├── values-dev.yaml
│ ├── values-staging.yaml
│ ├── values-uat.yaml
│ ├── values-prod.yaml
│ ├── templates/
│ │ ├── _helpers.tpl
│ │ ├── namespace.yaml
│ │ └── shared-configmap.yaml
│ └── charts/ # Sub-chart'lar
│ ├── auth/
│ ├── api/
│ ├── worker/
│ ├── scheduler/
│ ├── notifier/
│ ├── reporter/
│ └── gateway/
Her sub-chart kendi Chart.yaml + values.yaml + templates’lerine sahip. Umbrella tüm sub-chart’ları tek seferde deploy ediyor.
# platform/Chart.yaml
apiVersion: v2
name: platform
version: 1.4.0
appVersion: "2026.04"
dependencies:
- name: auth
version: 1.2.0
repository: "file://./charts/auth"
- name: api
version: 1.5.0
repository: "file://./charts/api"
- name: worker
version: 1.1.0
repository: "file://./charts/worker"
- name: postgresql
version: 14.0.0
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
- name: redis
version: 18.0.0
repository: "https://charts.bitnami.com/bitnami"
condition: redis.enabled
Values Hierarchy
Override sırası (sonraki öncekini ezer):
- Sub-chart values.yaml (default)
- Umbrella values.yaml (common override)
- Environment-specific values-prod.yaml
- –set CLI flag (CI/CD’den runtime values)
# platform/values.yaml (common, all envs)
global:
imageRegistry: registry.example.com
imagePullSecrets:
- name: regcred
domain: example.com
api:
replicaCount: 2
resources:
requests: {cpu: 100m, memory: 128Mi}
limits: {cpu: 500m, memory: 512Mi}
worker:
replicaCount: 1
postgresql:
enabled: true
auth:
database: app
primary:
persistence:
size: 20Gi
# platform/values-prod.yaml (production override)
global:
domain: app.example.com
api:
replicaCount: 6
resources:
requests: {cpu: 500m, memory: 512Mi}
limits: {cpu: 2000m, memory: 2Gi}
hpa:
enabled: true
minReplicas: 6
maxReplicas: 30
targetCPUUtilization: 70
worker:
replicaCount: 4
postgresql:
enabled: false # Prod'da Cosmos DB kullanılıyor, Helm postgres değil
externalDb:
enabled: true
host: postgres-prod.example.com
port: 5432
Deploy Komutları
# Dev'e deploy
helm upgrade --install platform-dev ./charts/platform
--namespace dev
--create-namespace
--values ./charts/platform/values.yaml
--values ./charts/platform/values-dev.yaml
--set global.imageTag=$(git rev-parse --short HEAD)
# Prod'a deploy
helm upgrade --install platform-prod ./charts/platform
--namespace production
--create-namespace
--values ./charts/platform/values.yaml
--values ./charts/platform/values-prod.yaml
--set global.imageTag=v2026.04.18
--atomic --timeout 10m
–atomic flag: deploy başarısız olursa otomatik rollback. –timeout 10m: 10 dakika içinde tüm pod’lar Ready olmazsa fail.
Secret Management: External Secrets Operator
Hassas secret’lar Helm values’a yazılmaz (commit ediliyor!). External Secrets Operator ile Azure Key Vault / AWS Secrets Manager / Vault’tan çekilir.
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: api-secrets
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: azure-keyvault
target:
name: api-secrets # Bu Kubernetes Secret oluşturulur
creationPolicy: Owner
data:
- secretKey: db-password
remoteRef:
key: prod-api-db-password
- secretKey: jwt-secret
remoteRef:
key: prod-api-jwt-secret
- secretKey: stripe-api-key
remoteRef:
key: prod-stripe-api-key
Helm chart sadece Secret reference ediyor (secretKeyRef), secret içeriği Key Vault’tan otomatik geliyor.
Helm Hooks: Pre-Install Migration
Database migration deployment öncesi çalışmalı. Helm hook ile:
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate-{{ .Release.Revision }}
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": before-hook-creation
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: registry.example.com/api:{{ .Values.global.imageTag }}
command: ["npm", "run", "db:migrate"]
envFrom:
- secretRef:
name: api-secrets
helm upgrade çalıştırıldığında: önce migrate job çalışır, başarılıysa pod’lar deploy edilir. Job fail ederse upgrade durur.
Helm Test: Smoke Test
apiVersion: v1
kind: Pod
metadata:
name: api-smoke-test
annotations:
"helm.sh/hook": test
spec:
containers:
- name: smoketest
image: curlimages/curl
command:
- sh
- -c
- |
curl -fsSL http://api/health || exit 1
curl -fsSL http://api/api/v1/status || exit 1
restartPolicy: Never
helm test platform-prod --namespace production
Deployment sonrası smoke test çalıştırır, fail ederse alarm.
Rollback Strategy
# Mevcut release history
helm history platform-prod -n production
# REVISION STATUS DESCRIPTION
# 14 deployed Upgrade complete
# 13 superseded Upgrade complete
# 12 superseded Upgrade complete
# Bir önceki sürüme rollback
helm rollback platform-prod 13 -n production --wait --timeout 5m
Helm her revision’ı ConfigMap olarak saklıyor (default 10), rollback bir komut.
ArgoCD ile GitOps
Manuel helm install yerine ArgoCD: Git repo’da Helm chart + values, ArgoCD sync eder, drift detect eder.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: platform-prod
spec:
destination:
namespace: production
server: https://kubernetes.default.svc
source:
repoURL: https://github.com/myorg/charts
targetRevision: main
path: charts/platform
helm:
valueFiles:
- values.yaml
- values-prod.yaml
parameters:
- name: global.imageTag
value: v2026.04.18
syncPolicy:
automated:
prune: true
selfHeal: true
Sahada Düşülen Üç Tuzak
- Sub-chart version’ı pin’lememek: Bitnami postgres chart’ı major version atladığında schema breaking. ~14.x.x gibi semver pin gerekli.
- helm template ile diff yapmamak: Production’a değişiklik geçmeden önce helm diff plugin ile farkı incele, sürpriz değişiklik yakala.
- Secret’ları Helm values’a koymak: chart commit’lendiğinde secret git history’de. External Secrets Operator veya Sealed Secrets şart.
CloudSpark olarak Helm chart tasarımı, multi-environment values yönetimi, ArgoCD entegrasyonu ve secret rotation mimarileri için danışmanlık veriyoruz.



