Men-deploy RoadRunner ke container: membangun image multi-stage dari binary resmi dan PHP, menyusun Docker Compose dengan healthcheck, lalu men-deploy ke Kubernetes dengan Deployment, HPA, dan graceful shutdown memakai SIGTERM.

Setelah di episode 20 kita menyetel performa, pada episode kali ini kita mengepak semuanya: Docker & Kubernetes. RoadRunner dirancang container-friendly — satu binary + worker PHP dalam satu image, dengan pola shutdown yang bersih untuk orkestrasi modern.
Mengapa episode ini penting? Karena hampir semua production 2026 berjalan di container. Memahami image yang benar, healthcheck yang jujur, dan graceful shutdown adalah syarat agar RoadRunner tidak memutus request saat Kubernetes melakukan rolling update.
Strategi: stage pertama menyiapkan PHP + dependensi, stage kedua menggabungkannya dengan binary RoadRunner dari image resmi:
FROM ghcr.io/roadrunner-server/roadrunner:2025.1.15 AS rr
FROM php:8.4-cli AS build
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-interaction --prefer-dist \
&& composer dump-autoload --optimize
FROM php:8.4-cli
COPY --from=rr /usr/bin/rr /usr/local/bin/rr
COPY --from=build /app/vendor /app/vendor
COPY --from=build /app/composer.json /app/composer.lock /app/
COPY .rr.yaml app.php /app/
WORKDIR /app
RUN echo "opcache.enable_cli=1" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.jit=tracing" >> /usr/local/etc/php/conf.d/opcache.ini
EXPOSE 8080
CMD ["rr", "serve", "-c", ".rr.yaml"]Poin penting:
:cli cukup — tidak butuh FPM, worker dijalankan sebagai proses CLI.rr di-copy dari image resmi roadrunner (versi di-pin).CMD menjalankan rr serve sebagai PID 1 — penting untuk sinyal (baca di bawah).Untuk development/lokal, susun compose.yaml dengan healthcheck:
services:
app:
build: .
ports:
- "8080:8080"
healthcheck:
test: ["CMD", "rr", "jobs:list"]
interval: 10s
timeout: 5s
retries: 3
depends_on:
redis:
condition: service_healthy
redis:
image: redis:7
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5Healthcheck memakai rr jobs:list — perintah yang ringan dan memvalidasi server + RPC hidup. Alternatif: hit endpoint HTTP /health yang disediakan aplikasi.
Tip
Healthcheck harus memvalidasi kesiapan melayani, bukan sekadar proses hidup. rr jobs:list (atau HTTP GET /health) lebih jujur daripada pgrep rr yang hanya mengecek proses ada.
Manifest Kubernetes dengan readiness probe dan resource limit:
apiVersion: apps/v1
kind: Deployment
metadata:
name: rr-app
labels:
app: rr-app
spec:
replicas: 3
selector:
matchLabels:
app: rr-app
template:
metadata:
labels:
app: rr-app
spec:
terminationGracePeriodSeconds: 30
containers:
- name: app
image: registry.example.com/rr-app:2025.1.15
ports:
- containerPort: 8080
envFrom:
- secretRef:
name: app-secrets
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 3
periodSeconds: 5
livenessProbe:
httpGet:
path: /health/live
port: 8080
periodSeconds: 15Ditambah Service:
apiVersion: v1
kind: Service
metadata:
name: rr-app
spec:
selector:
app: rr-app
ports:
- port: 80
targetPort: 8080Saat Kubernetes menghapus pod (rolling update, scaling down), ia mengirim SIGTERM lalu menunggu terminationGracePeriodSeconds. RoadRunner menangani SIGTERM dengan:
Karena itu: jalankan rr sebagai PID 1 (CMD ["rr", ...] tanpa wrapper), dan jangan kirim sinyal lain lebih dulu. Worker PHP juga harus menangani Worker::stop() untuk flush data — pola yang kita bahas di episode 4.
Warning
Jangan pakai kill -9 / force sebagai langkah pertama. Kubernetes memotong terminationGracePeriodSeconds hanya jika SIGTERM tidak selesai tepat waktu. Beri nilai yang cukup (30-60s) untuk aplikasi dengan request yang berjalan lama.
Autoscale berbasis CPU atau metric kustom (episode 12):
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: rr-app
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: rr-app
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60Catatan penting: scaling horizontal (replica) berbeda dengan menambah num_workers di dalam satu pod. Mulai dengan replica yang sesuai beban, lalu set num_workers per pod moderat — jangan double-count resource.
Pada episode 21 ini, kalian telah men-deploy RoadRunner ke container.
Inti yang harus dibawa pulang:
rr dari image resmi, versi di-pin.rr jobs:list atau HTTP /health.kill -9 dulu.Di episode 22 selanjutnya, kita belajar cluster, multisite & high availability — memperbanyak replica, session dan queue terdistribusi, strategi zero-downtime deployment, dan menjaga konsistensi antar node. Sampai jumpa di episode 22!