Multi-Environment Configuration #

Hampir semua sistem produksi butuh lebih dari satu environment — minimal development, staging, dan production. Tantangannya: konfigurasi harus berbeda per environment (URL database, resource limits, feature flags) tapi struktur dasarnya sama. Tanpa strategi yang jelas, konfigurasi mudah drift — staging tidak lagi mencerminkan production, dan bug hanya muncul di production karena ada yang berbeda.

Pendekatan 1: Namespace per Environment #

Cara paling sederhana: buat namespace terpisah untuk setiap environment dalam satu cluster.

Cluster tunggal:
  Namespace: development
    Deployment: api (1 replica, resource kecil)
    ConfigMap: app-config (LOG_LEVEL=debug, DB_HOST=dev-postgres)

  Namespace: staging
    Deployment: api (2 replica, resource menengah)
    ConfigMap: app-config (LOG_LEVEL=info, DB_HOST=staging-postgres)

  Namespace: production
    Deployment: api (5 replica, resource besar)
    ConfigMap: app-config (LOG_LEVEL=warn, DB_HOST=prod-postgres)

Kelebihan: sederhana, satu cluster untuk semua. Kekurangan: production di-share dengan non-production — ada risiko blast radius jika ada masalah di cluster.


Pendekatan 2: Kustomize — Overlay per Environment #

Kustomize adalah tool bawaan kubectl yang memungkinkan kamu mendefinisikan base konfigurasi dan overlay (modifikasi) per environment tanpa duplikasi manifest.

Struktur direktori dengan Kustomize:

k8s/
├── base/                          # konfigurasi dasar yang sama di semua env
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── configmap.yaml
│   └── kustomization.yaml
│
└── overlays/
    ├── development/
    │   ├── kustomization.yaml     # patch untuk development
    │   └── configmap-patch.yaml
    ├── staging/
    │   ├── kustomization.yaml
    │   └── configmap-patch.yaml
    └── production/
        ├── kustomization.yaml
        ├── configmap-patch.yaml
        └── hpa.yaml               # HPA hanya di production
# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 1
  template:
    spec:
      containers:
      - name: api
        image: my-api:latest
        resources:
          requests:
            cpu: "100m"
            memory: "128Mi"
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- configmap.yaml
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production          # override namespace
bases:
- ../../base
resources:
- hpa.yaml                     # tambah HPA yang tidak ada di base
patches:
- path: configmap-patch.yaml
- target:
    kind: Deployment
    name: api
  patch: |-
    - op: replace
      path: /spec/replicas
      value: 5                 # override replicas
    - op: replace
      path: /spec/template/spec/containers/0/resources/requests/cpu
      value: "500m"            # override resource    
images:
- name: my-api
  newTag: v2.1.0              # pin image tag di production
# overlays/production/configmap-patch.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  LOG_LEVEL: "warn"            # override dari base yang mungkin "debug"
  DB_HOST: "postgres.production.svc.cluster.local"
  MAX_CONNECTIONS: "100"
# Deploy ke production
kubectl apply -k k8s/overlays/production/

# Preview apa yang akan di-deploy (dry-run)
kubectl diff -k k8s/overlays/production/

# Generate manifest lengkap tanpa apply (untuk review)
kubectl kustomize k8s/overlays/production/

Pendekatan 3: Helm Values per Environment #

Helm menggunakan file values.yaml sebagai konfigurasi default, dan kamu bisa override dengan file values per environment.

helm-chart/
├── Chart.yaml
├── templates/
│   ├── deployment.yaml
│   ├── service.yaml
│   └── configmap.yaml
├── values.yaml                # default values
├── values-development.yaml    # override untuk development
├── values-staging.yaml        # override untuk staging
└── values-production.yaml     # override untuk production
# values.yaml (defaults)
replicaCount: 1
image:
  tag: latest
resources:
  requests:
    cpu: "100m"
    memory: "128Mi"
config:
  logLevel: "debug"
  maxConnections: 10
  dbHost: "postgres-dev"
autoscaling:
  enabled: false
# values-production.yaml
replicaCount: 5
image:
  tag: "v2.1.0"              # pin versi di production
resources:
  requests:
    cpu: "500m"
    memory: "512Mi"
  limits:
    cpu: "2000m"
    memory: "2Gi"
config:
  logLevel: "warn"
  maxConnections: 100
  dbHost: "postgres.production.svc.cluster.local"
autoscaling:
  enabled: true
  minReplicas: 5
  maxReplicas: 20
# Deploy ke production dengan values production
helm upgrade --install api ./helm-chart \
  --namespace production \
  --values values-production.yaml \
  --set image.tag=$(git rev-parse --short HEAD)   # override tag dengan commit hash

# Lihat values yang aktif
helm get values api -n production

Mencegah Configuration Drift #

Configuration drift terjadi saat konfigurasi di production secara diam-diam berbeda dari apa yang ada di repository. Strategi untuk mencegahnya:

1. GitOps — satu-satunya cara deploy adalah via Git
   Semua perubahan konfigurasi harus melalui pull request
   Tidak ada kubectl apply manual ke production
   Tools: ArgoCD, Flux

2. Immutable ConfigMap names
   Beri versi pada nama ConfigMap:
   app-config-v2.1.0 (bukan app-config yang bisa diubah kapan saja)
   Update berarti buat ConfigMap baru dan update referensi di Deployment

3. Drift detection
   ArgoCD secara otomatis deteksi perbedaan antara Git dan cluster
   Alert saat ada resource yang out-of-sync

4. Regular reconciliation
   Secara periodik bandingkan state cluster dengan state Git
   kubectl diff -k overlays/production/ untuk lihat perbedaan

Pola: Konfigurasi Shared vs Per-Environment #

Tidak semua konfigurasi perlu berbeda per environment. Pisahkan yang truly shared dari yang environment-specific:

# ConfigMap shared — sama di semua environment
apiVersion: v1
kind: ConfigMap
metadata:
  name: shared-config
data:
  SERVICE_NAME: "api-server"
  API_VERSION: "v2"
  SUPPORTED_LOCALES: "en,id,ms"

---
# ConfigMap per-environment
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config        # nama sama di semua namespace, konten berbeda
data:
  APP_ENV: "production"   # atau "staging", "development"
  LOG_LEVEL: "warn"       # berbeda per env
  DB_HOST: "..."          # berbeda per env
  REPLICAS: "5"           # berbeda per env

Ringkasan #

  • Namespace per environment untuk isolasi simpel — mudah diterapkan tapi semua environment berbagi cluster; risiko blast radius lebih tinggi.
  • Kustomize untuk DRY configuration — definisikan sekali di base, override hanya yang berbeda di overlays; tidak ada duplikasi manifest; bawaan kubectl.
  • Helm values per environment untuk chart yang kompleksvalues-production.yaml override values.yaml; cocok untuk chart yang sudah ada atau deployment Helm yang sudah mapan.
  • GitOps mencegah configuration drift — semua perubahan via Git; tidak ada manual kubectl apply ke production; ArgoCD/Flux memastikan cluster selalu sinkron dengan repository.
  • Pisahkan shared dari per-environment — tidak semua konfigurasi perlu berbeda; identifikasi apa yang benar-benar environment-specific untuk mengurangi duplikasi.
  • Pin image tag di production — jangan gunakan latest di production; gunakan commit hash atau semantic version untuk reproducibility dan rollback yang reliable.

← Sebelumnya: Configuration Hot Reload   Berikutnya: Anti-Pattern Configuration →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact