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 →