Anti-Pattern Ecosystem & Tooling #

Tools yang seharusnya membantu bisa menjadi beban jika digunakan dengan cara yang salah. Helm yang terlalu kompleks lebih sulit di-debug dari plain YAML. GitOps yang tidak konsisten lebih buruk dari tidak ada GitOps sama sekali. Artikel ini membahas anti-pattern yang paling sering muncul dalam penggunaan tools dan ekosistem Kubernetes — bukan dari tools-nya yang salah, tapi dari cara penggunaannya.

Anti-Pattern 1: Helm Chart Spaghetti #

# ANTI-PATTERN: template yang penuh dengan conditional kompleks
{{- if and .Values.ingress.enabled (not .Values.service.disabled) }}
{{- if or (eq .Values.env "production") (and (eq .Values.env "staging") .Values.ingress.stagingEnabled) }}
{{- if .Values.tls.enabled }}
{{- if or (eq .Values.tls.provider "cert-manager") (and (eq .Values.tls.provider "manual") .Values.tls.secretName) }}
apiVersion: networking.k8s.io/v1
kind: Ingress
...
{{- end }}
{{- end }}
{{- end }}
{{- end }}
Konsekuensi:
  → `helm template` menghasilkan output yang sulit diprediksi
  → Bug tersembunyi di kombinasi values yang jarang diuji
  → Onboarding developer baru: "gimana cara debug template ini?"
  → values.yaml dengan 200+ parameter yang tidak ada yang tahu semua fungsinya
  → Tim akhirnya takut ubah chart, biarkan nilai default yang mungkin salah
Solusi:
  → Buat template sederhana dengan values yang minimal
  → Logika kompleks → pisah jadi beberapa chart terpisah
  → Gunakan Kustomize untuk variasi environment, bukan nested conditionals
  → Aturan: jika orang baru tidak bisa pahami template dalam 10 menit,
    terlalu kompleks

Anti-Pattern 2: Kustomize Berakhir Jadi Copy-Paste #

ANTI-PATTERN: struktur direktori yang "Kustomize" tapi sebenarnya duplikasi

k8s/
├── development/
│   ├── deployment.yaml   ← copy dari production dengan modifikasi
│   ├── service.yaml      ← copy dari production
│   └── configmap.yaml    ← copy dari production
└── production/
    ├── deployment.yaml
    ├── service.yaml
    └── configmap.yaml

Tanda-tanda anti-pattern:
  → Tidak ada folder base/
  → File yang "sama" tapi ada di dua tempat
  → "Update deployment di production, jangan lupa update juga yang di dev"
  → Drift antar environment karena seseorang lupa sync
Solusi:
  → Buat base/ yang benar-benar dipakai sebagai base
  → Overlay hanya berisi yang benar-benar berbeda
  → Jika overlay lebih besar dari base, pertanyakan ulang strukturnya
  → Test: hapus overlay, bisa base masih deploy sendiri? Harus bisa.

Anti-Pattern 3: kubectl Langsung ke Production #

# ANTI-PATTERN: manual kubectl ke production tanpa review

# "Cepat aja langsung apply, nanti commit ke git"
kubectl apply -f hotfix.yaml -n production

# "Oh perlu cek sesuatu"
kubectl exec -it database-pod -- psql -c "UPDATE users SET ..."

# "Kita butuh scale up sekarang"
kubectl scale deployment api --replicas=20 -n production

# Semua ini tidak ada di Git, tidak ada di review, tidak ada di audit log
Konsekuensi:
  → State cluster tidak sinkron dengan Git (GitOps violation)
  → ArgoCD/Flux akan rollback perubahan ini saat sync berikutnya
  → Tidak ada audit trail: siapa yang scale ke 20? kapan? kenapa?
  → Tidak bisa reproduce state cluster dari scratch
  → Hotfix yang "nanti di-commit" sering tidak pernah di-commit

Untuk hotfix darurat yang legitimate:
  → Buat commit ke branch hotfix, push, PR review cepat
  → Trigger manual sync di ArgoCD setelah merge
  → Bukan: apply langsung dan "nanti sync"

Anti-Pattern 4: Satu Namespace untuk Semua Environment #

# ANTI-PATTERN: semua environment di satu namespace
kubectl get pods -n default
# api-dev-abc123
# api-staging-def456
# api-production-ghi789
# database-dev
# database-production
# ...semua campur
Konsekuensi:
  → Tidak ada isolasi resource quota: workload dev bisa "mencuri" resource
    dari production
  → NetworkPolicy sulit karena semua campur
  → RBAC sulit: developer bisa tidak sengaja access production
  → Billing dan cost allocation tidak bisa dilakukan per environment
  → `kubectl get pods` memberikan daftar yang membingungkan

Solusi:
  Satu namespace per environment per team:
  → production, staging, development
  → Atau: team-a-production, team-a-staging
  ResourceQuota per namespace untuk isolasi resource
  RBAC per namespace untuk isolasi akses

Anti-Pattern 5: Over-Engineering Toolchain #

ANTI-PATTERN: toolchain yang lebih kompleks dari kebutuhan

Startup dengan 3 engineer dan 5 microservice:
  → ArgoCD + Flux (dua GitOps operator sekaligus)
  → Helm + Kustomize untuk semua chart
  → Istio service mesh (untuk 5 service yang semuanya di satu tim)
  → OPA Gatekeeper + Kyverno (dua policy engine)
  → Jaeger + Zipkin (dua tracing backend)
  → EFK + Loki (dua logging stack)

Konsekuensi:
  → Lebih banyak waktu dihabiskan untuk maintenance toolchain
    daripada feature development
  → Debugging: masalah di aplikasi atau di toolchain?
  → Onboarding engineer baru: "kita pakai apa saja?"
  → Biaya infra untuk tools lebih besar dari aplikasi itu sendiri
Prinsip: pilih satu tool per use case
  GitOps: ArgoCD atau Flux, bukan keduanya
  Package management: Helm atau Kustomize (atau kombinasi deliberate)
  Policy: Kyverno atau OPA, bukan keduanya
  Tracing: Jaeger atau Tempo, bukan keduanya
  Logging: Loki atau EFK, bukan keduanya
  Mulai simpel, tambah kompleksitas saat ada kebutuhan nyata

Anti-Pattern 6: Tidak Ada Version Pinning untuk Tools #

# ANTI-PATTERN: Helm chart dengan version tidak di-pin
dependencies:
- name: redis
  version: "*"          # ← version terbaru, apapun itu
  repository: "..."

- name: postgresql
  version: ">=14.0.0"   # ← semua versi >= 14
  repository: "..."
# ANTI-PATTERN: kubectl manifest tanpa pin image
image: nginx             # ← latest tag, tidak terkontrol
image: postgres:14       # ← bisa berubah jika 14.x dirilis

# ANTI-PATTERN: Terraform/tools tanpa version lock
terraform {
  required_providers {
    kubernetes = {
      source = "hashicorp/kubernetes"
      # tidak ada version = terbaru selalu
    }
  }
}
Konsekuensi:
  → `helm dependency update` hari ini dan besok menghasilkan output berbeda
  → "Kenapa tiba-tiba deploy production berubah?"
    "Oh, ada patch release postgresql chart"
  → Tidak bisa reproduce build dari bulan lalu

Solusi:
  → Pin ke versi spesifik: version: "18.6.2"
  → Gunakan Renovate atau Dependabot untuk update terencana
  → Commit Chart.lock dan package-lock.json ke Git

Anti-Pattern 7: Mengabaikan Kapasitas Cluster saat Deploy Baru #

# ANTI-PATTERN: deploy large workload tanpa cek kapasitas
kubectl apply -f ml-training-job.yaml  # minta 100 CPU cores
# → Cluster hanya punya 20 cores tersedia
# → Semua Pod Pending
# → Cluster autoscaler butuh waktu untuk scale up
# → Pod production yang ada bisa terdampak jika autoscaler tidak terkonfigurasi dengan benar
Konsekuensi:
  → Pod production yang butuh reschedule tidak bisa karena resource penuh
  → Cluster autoscaler scale up tapi node baru butuh 2-5 menit
  → Selama itu, ada workload yang Pending atau Evicted

Sebelum deploy workload besar:
  kubectl describe nodes | grep -A 5 "Allocated resources"
  kubectl top nodes
  kubectl get nodes -o custom-columns=\
    "NAME:.metadata.name,CPU:.status.capacity.cpu,MEM:.status.capacity.memory"

  Atau hitung dulu:
  Tersedia = (total capacity) - (reserved untuk system) - (yang sudah terpakai)
  Bandingkan dengan resource requests workload baru

Ringkasan #

  • Helm template yang sederhana lebih baik dari yang canggih — conditional yang dalam dan values yang banyak membuat chart sulit dipahami dan di-debug; simpelkan atau pisah jadi chart terpisah.
  • Kustomize base harus benar-benar dipakai sebagai base — jika overlay lebih besar dari base, atau ada copy-paste antar environment, strukturnya salah.
  • Tidak ada kubectl langsung ke production — semua perubahan melalui Git; untuk hotfix sekalipun, commit dulu baru trigger sync; bukan apply dulu baru commit nanti.
  • Satu namespace per environment — isolasi resource, RBAC, dan NetworkPolicy menjadi jauh lebih mudah; tidak ada Pod production dan development yang bercampur.
  • Pilih satu tool per use case — dua GitOps operator, dua logging stack, atau dua policy engine tidak memberikan double benefit; hanya menambah kompleksitas dan confusion.
  • Pin semua versi — Helm chart dependencies, image tags, dan tool versions harus di-pin ke versi spesifik; gunakan Renovate/Dependabot untuk update terencana, bukan floating version.

← Sebelumnya: Managed Kubernetes   Berikutnya: Resource Right-Sizing →

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