Aktivasi opsi Prefork: true pada web framework Go Fiber di dalam klaster Kubernetes sering kali berakhir dengan status CrashLoopBackOff atau OOMKilled. Fitur prefork menduplikasi proses biner menggunakan mekanisme child process untuk memanfaatkan port sharing (SO_REUSEPORT). Model multi-proses ini bertabrakan dengan isolasi resource cgroup container dan mekanisme liveness probe Kubernetes.
Observasi Insiden: Sinyal Kerusakan pada Pod
Saat rilis bermasalah diluncurkan ke klaster, pod langsung mengalami restart berulang. Diagnostik awal dilakukan dengan memeriksa ringkasan status workload dan riwayat event pod.
kubectl get pods -l app=order-api
# Output menunjukkan CrashLoopBackOff dan restart bertambah
# NAME READY STATUS RESTARTS AGE
# order-api-7bbf977464-92xkq 0/1 CrashLoopBackOff 4 (85s ago) 3mPeriksa detail kegagalan menggunakan kubectl describe pod untuk mengekstrak termination reason dan K8s events:
kubectl describe pod order-api-7bbf977464-92xkqCari bagian Last State dan Events:
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
...
Events:
Warning Unhealthy 30s kubelet Liveness probe failed: HTTP probe failed with statuscode: 503
Warning BackOff 12s kubelet Back-off restarting failed containerExit code 137 menandakan sistem operasi (Linux OOM Killer) mematikan container secara paksa karena penggunaan memori melampaui batasan cgroup (limits.memory). Jika container tidak terbunuh oleh OOM, log Fiber sering kali menunjukkan kegagalan child process:
kubectl logs order-api-7bbf977464-92xkq --previous
# Log error:
# [Fiber-Prefork] Child process 42 terminated with exit code 1
# [Fiber-Prefork] Master process failed to bind port or spawn childTriage Cepat: Eksekusi Rollback Deployment
Langkah pertama pada level operasional adalah mengembalikan status deployment ke revisi kerja terakhir untuk memulihkan ketersediaan layanan.
- Batalkan deployment yang sedang bermasalah:
kubectl rollout undo deployment/order-api -n production - Pantau progress rollback sampai pod lama kembali melayani traffic:
kubectl rollout status deployment/order-api -n production - Verifikasi bahwa seluruh pod kembali berada pada status
1/1 Runningtanpa ada restart tambahan.
Postmortem Ringkas: Benturan Prefork dengan Cgroup
Go Fiber memanfaatkan model prefork untuk memaksimalkan utilisasi multi-core CPU dengan memanggil os/exec dari binary yang sama berulang kali. Benturan arsitektur terjadi di lingkungan container karena faktor-faktor berikut:
- Deteksi CPU Core Host vs Container Quota: Go secara default mendeteksi total CPU fisik node host (misalnya 32 core) melalui
runtime.NumCPU(), bukan kuota cgroup (misalnyalimits.cpu: 1000m). Fiber akan men-spawn 32 child process di dalam satu container yang hanya memiliki jatah 1 core. - Multiplikasi Alokasi Memori: Setiap child process menjalankan instance Go runtime independen dengan heap dan stack tersendiri. Jika base memory satu proses adalah 30MiB, spawning 16 child process langsung mengonsumsi 480MiB, menembus batas cgroup pod (misal limit 256MiB) dan memicu sinyal
SIGKILLdari kernel. - Kegagalan Liveness Probe: Spawning puluhan proses secara serentak menyebabkan CPU throttling parah. Akibatnya, server gagal merespons HTTP probe kubelet tepat waktu (timeout), memicu restart berulang dari kubelet meski memori mencukupi.
Pencegahan Teknis & Mitigasi Jangka Panjang
Model konkurensi native Go (goroutine dan M:N scheduler) sudah sangat efisien dalam menangani I/O bound workload dalam satu single OS process. Di Kubernetes, replikasi komputasi harus didelegasikan ke level pod melalui Horizontal Pod Autoscaler (HPA), bukan multi-process prefork di dalam satu container.
1. Proteksi Konfigurasi Bootstrap Go Fiber
Pasang guard rail pada bootstrap aplikasi untuk melarang aktivasi prefork secara eksplisit saat binary berjalan di dalam container Kubernetes.
package main
import (
"log"
"os"
"github.com/gofiber/fiber/v2"
)
func main() {
isKube := os.Getenv("KUBERNETES_SERVICE_HOST") != ""
enablePrefork := os.Getenv("ENABLE_PREFORK") == "true"
// ponytail: disallow prefork inside k8s. Use HPA instead.
if isKube && enablePrefork {
log.Println("WARN: Prefork diabaikan karena runtime berada di dalam Kubernetes.")
enablePrefork = false
}
app := fiber.New(fiber.Config{
Prefork: enablePrefork,
})
app.Get("/healthz", func(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusOK)
})
if err := app.Listen(":8080"); err != nil {
log.Fatalf("Gagal menjalankan server: %v", err)
}
}2. Delegasi Skalabilitas ke Kubernetes HPA
Jalankan container Go Fiber sebagai single-process pod, tentukan resource request dan limit secara realistis, lalu biarkan HPA menambah replika pod ketika beban trafik meningkat.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-api-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-api
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70Pendekatan single-process per pod menjaga overhead memori tetap deterministik, menghilangkan risiko perebutan socket listener antar-child process, dan menyelaraskan siklus hidup aplikasi dengan kontrol orkestrasi Kubernetes.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!