Dalam 15 menit ke depan, kita akan merakit stack ini dari nol sampai berjalan mulus. Pengalaman di lapangan membuktikan bahwa pod Next.js SSR sering kali tumbang bukan karena kurang kapasitas, melainkan karena manajemen memori V8 yang salah urus dan ketiadaan caching layer yang solid di Kubernetes. Bayangkan kita sedang menangani lonjakan trafik jutaan hit dari kampanye flash sale; jika konfigurasi Garbage Collection Node.js dibiarkan default, pod Anda akan mengalami OutOfMemory (OOM) dalam hitungan menit.
Mari kita bedah dan atasi masalah ini bersama-sama.
1. Menu Kita Hari Ini & Alat yang Perlu Disiapkan
Sebelum kita masuk ke ruang mesin, pastikan Anda sudah menyiapkan beberapa bahan utama di lingkungan kerja Anda:
- Klaster Kubernetes (bisa menggunakan Minikube untuk lokal, atau AWS EKS / GCP GKE untuk production).
kubectldanhelmyang sudah terhubung ke klaster.- Redis instance (bisa menggunakan Redis Operator atau managed service seperti ElastiCache).
- Metrik server seperti Metrics Server terpasang di Kubernetes untuk mendukung Horizontal Pod Autoscaler berbasis kustom.
[!IMPORTANT] Pastikan Node.js versi 18+ digunakan agar fitur manajemen memori V8 berjalan optimal.
2. Peta Jalan Singkat: Apa yang Sedang Kita Rakit
Perjalanan kita hari ini dirancang seefisien mungkin:
- Mengatur batas memori V8 Node.js di dalam kontainer Docker agar tidak rakus.
- Mengintegrasikan Redis dan Incremental Static Regeneration (ISR) untuk memangkas beban kerja SSR.
- Memasang Horizontal Pod Autoscaler (HPA) menggunakan custom metrics Prometheus.
- Melakukan uji beban dan verifikasi.
Mari kita mulai meracik fondasinya di langkah berikutnya.
3. Langkah 1: Mempersiapkan Fondasi & Environment
Hal pertama yang sering dilewatkan para insinyur junior adalah membiarkan V8 engine menggunakan batas memori default mesin. Di dalam kontainer Kubernetes dengan batas memori 2GB, V8 tetap berasumsi ia berjalan di atas mesin dengan RAM tak terbatas, lalu tiba-tiba CrashLoopBackOff karena OOMKilled.
Mari kita buat Dockerfile yang cerdas:
FROM node:18-alpine AS runner
WORKDIR /app
# Atur environment untuk produksi dan batasi memori V8 heap
ENV NODE_ENV=production
ENV NODE_OPTIONS="--max-old-space-size=1536"
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --chown=nextjs:nodejs .next/standalone ./
COPY --chown=nextjs:nodejs .next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
CMD ["node", "server.js"]
Perhatikan baris NODE_OPTIONS="--max-old-space-size=1536". Ini adalah rem tangan pengaman agar Node.js tahu persis kapan harus membersihkan tumpukan memori sebelum Kubernetes mematikan kontainer secara paksa.
4. Langkah 2: Meracik Konfigurasi Inti Tanpa Basa-basi
Sekarang pod kita sudah tahu batasan dirinya, saatnya meringankan bebannya menggunakan Redis dan ISR. Kita tidak ingin setiap request menghantam database secara langsung. Konfigurasikan Next.js agar melakukan fallback caching ke Redis cluster.
Buat file cache-handler.js di root proyek Anda:
const { createClient } = require('redis');
const client = createClient({ url: process.env.REDIS_URL });
client.connect().catch(console.error);
module.exports = async function cacheHandler(options) {
return {
async get(key) {
const value = await client.get(key);
return value ? JSON.parse(value) : null;
},
async set(key, data, ctx) {
await client.set(key, JSON.stringify({ value: data, lastModified: Date.now() }));
},
async revalidate(key) {
await client.del(key);
},
};
};
[!TIP] Gunakan Redis sentinel atau cluster terkelola untuk memastikan caching layer ini memiliki tingkat ketersediaan yang tinggi (high availability).
5. Langkah 3: Sentuhan Akhir Keamanan & Optimasi
Kubernetes deployment manifes adalah penentu akhir ketahanan sistem. Kita perlu mengatur resource requests dan limits yang presisi, serta mengonfigurasi Horizontal Pod Autoscaler (HPA) berbasis penggunaan memori kustom atau kueri kustom.
Berikut manifes deployment.yaml dan hpa.yaml yang siap pakai:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nextjs-ssr-app
spec:
replicas: 3
selector:
matchLabels:
app: nextjs-ssr
template:
metadata:
labels:
app: nextjs-ssr
spec:
containers:
- name: nextjs
image: your-docker-registry/nextjs-app:v1
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"
env:
- name: REDIS_URL
value: "redis://redis-master.database.svc.cluster.local:6379"
ports:
- containerPort: 3000
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nextjs-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nextjs-ssr-app
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 75
6. Tes Rasa: Memastikan Semuanya Berjalan Sempurna
Saatnya menguji racikan kita. Jalankan kubectl apply -f deployment.yaml dan pantau status pod menggunakan:
kubectl get pods -l app=nextjs-ssr -w
Lakukan load testing menggunakan k6 atau Apache Bench untuk melihat bagaimana HPA merespons lonjakan trafik. Jika Anda melihat pod baru berputar secara otomatis saat memori menyentuh 75%, selamat! Arsitektur Anda sudah siap menghadapi badai trafik.
7. Pertolongan Pertama: Jika Terjadi Kendala Kendala Kecil
- Problem: Pod sering restart dengan status
OOMKilledmeski--max-old-space-sizesudah diatur. - Solusi: Kurangi nilai heap V8 sedikit (misal ke
1280atau1024) karena kontainer juga membutuhkan ruang memori untuk overhead proses Node.js di luar V8 heap. - Problem: Redis mengalami koneksi putus-putus di bawah beban tinggi.
- Solusi: Pastikan Anda mengaktifkan connection pooling dan konfigurasi
maxclientsyang cukup pada instance Redis.