Mendesain arsitektur FrankenPHP yang siap scale-out: prinsip stateless untuk multi-instance, cluster horizontal dengan load balancer, rolling deployment tanpa downtime, blue-green deployment, dan cara melindungi kuota Let's Encrypt di cluster besar.

Episode 19 men-deploy satu aplikasi ke K8s. Episode ini membahas langkah berikutnya: apa yang terjadi saat satu instance tidak cukup lagi — dan bagaimana menjaga ketersediaan tinggi saat trafik tumbuh. FrankenPHP dirancang untuk arsitektur ini: selama aplikasi tetap stateless (yang sudah kita bangun di episode 13), menambah instance hanyalah menambah node ke load balancer.
Mengapa penting? Scaling keluar yang dilakukan tanpa fondasi stateless hanya memindahkan masalah: session yang hilang, cache memori yang tidak konsisten, dan worker yang saling berebut state. Episode ini menyatukan semua prinsip sebelumnya menjadi arsitektur cluster yang benar.
Semua yang episode 13 ajarkan menjadi wajib di sini. Ringkasnya, instance FrankenPHP tidak boleh menyimpan:
Jika tiga poin pertama terpenuhi, instance adalah fungsi murni terhadap request: request apa pun bisa dilayani oleh instance mana pun. Itu fondasi seluruh arsitektur cluster.
Pola yang paling umum dan paling mudah di-maintain:
Klien
|
| (HTTPS/HTTP3)
v
[ Load Balancer / CDN ]
|
+---> FrankenPHP A
+---> FrankenPHP B
+---> FrankenPHP C
|
+----[ Redis (session+cache) ]
+----[ Database ]
+----[ Object Storage (uploads) ]Load balancer di depan (cloud LB atau CDN) mendistribusikan request; setiap FrankenPHP stateless dan bisa mati-hidup tanpa memengaruhi yang lain.
Jika kalian ingin FrankenPHP sendiri yang jadi edge (memakai automatic HTTPS), beberapa instance Caddy di depan beberapa instance Caddy lagi tidak masuk akal. Dua pola:
Pola A — Satu edge layer FrankenPHP + backend pool: edge Caddy memakai reverse_proxy (episode 9) ke pool backend FrankenPHP yang internal. Edge yang pegang TLS; backend tetap HTTP.
Pola B — Cloud LB → FrankenPHP stateless: semua instance setara di belakang cloud LB yang mengelola TLS dan certificate. Instance Caddy memakai tls internal atau sertifikat yang dibagikan.
Pilih berdasarkan di mana kalian mau memegang sertifikat: di Caddy (Pola A) atau di cloud LB (Pola B).
Saat versi baru rilis, pastikan tidak ada request yang jatuh:
terminationGracePeriodSeconds cukup).maxUnavailable yang hati-hati.K8s sudah menangani ini jika Deployment dikonfigurasi benar:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1maxUnavailable: 0 artinya tidak boleh ada pod yang hilang sebelum penggantinya siap — traffic tidak pernah kosong.
Untuk perubahan yang berisiko (migrasi database, perubahan besar), blue-green memberikan rollback instan:
Di K8s, dua Deployment dengan Service yang switch selector:
metadata:
name: frankenphp-svc
spec:
selector:
version: green # sebelumnya: blueKeuntungan utama: rollback adalah perubahan satu field, bukan re-deploy. Kerugian: biaya ganda (dua versi jalan sekaligus) selama periode transisi.
Tip
Rolling deployment cukup untuk mayoritas kasus. Simpan blue-green untuk rilis yang melibatkan perubahan skema database atau kontrak API — di mana rollback otomatis rolling update tidak bisa menyehatkan data yang sudah termigrasi.
HPA berbasis CPU (episode 19) adalah awal. Untuk FrankenPHP, metrik yang paling relevan adalah kedalaman antrean worker (frankenphp_queue_depth, episode 12) — antrean menumpuk berarti kapasitas kurang, bahkan sebelum CPU menyentuh 60%:
metrics:
- type: Pods
pods:
metric:
name: frankenphp_queue_depth
target:
type: AverageValue
averageValue: "5"Aturan autoscaling yang sehat:
maxReplicas) selalu diset agar biaya terkendali.Setiap instance yang memakai automatic HTTPS akan meminta sertifikat — dengan banyak instance, kuota Let's Encrypt (misal 50 sertifikat per domain per minggu) bisa cepat habis. Strategi pencegahan:
caddy_data via volume bersama (NFS/RWX) sehingga semua instance memakai sertifikat yang sama.Daftar periksa sebelum go-live cluster:
/healthz//readyz) menjawab cepat di semua instance.readyz (bukan healthz) agar request tidak dikirim ke instance yang masih panas.stabilizationWindowSeconds.healthz: pod siap dianggap "tidak siap menerima traffic" atau sebaliknya. Bedakan livez (proses hidup) dan readyz (siap traffic).Pada episode 20 ini, kalian telah mendesain arsitektur cluster FrankenPHP.
Inti yang harus dibawa pulang:
maxUnavailable: 0) untuk rilis normal; blue-green untuk perubahan berisiko.frankenphp_queue_depth, bukan hanya CPU.caddy_data.Di episode 21 selanjutnya kita naik level teknik: custom build & extensions — mengkompilasi FrankenPHP sendiri, menambahkan ekstensi PHP, modul Caddy lewat xcaddy, dan build flags untuk kebutuhan khusus. Sampai jumpa di episode 21!