Menyiapkan data awal di PVC dengan pola init container: membuat folder dan symlink storage sebelum container utama jalan, menjalankan migrasi database via Job terpisah, dan menjaga idempotensi agar data yang sudah ada tidak tertimpa

Volume kosong baru saja di-mount. Tapi aplikasi butuh struktur: folder-folder storage harus ada, symlink public/storage ke storage/app/public harus terbentuk, dan skema database harus bermigrasi. Jika folder belum ada saat proses FPM menulis, Laravel biasanya membuatnya sendiri — tetapi untuk kasus NFS yang ketat permission-nya, lebih baik menyiapkan eksplisit.
Di episode 12 kita pakai dua mekanisme Kubernetes: initContainers untuk persiapan volume (folder + symlink) di setiap Pod, dan Job untuk migrasi database supaya hanya dijalankan sekali.
Migrasi harus dijalankan sekali dan selesai, bukan di setiap replica. Job adalah pilihan tepat:
apiVersion: batch/v1
kind: Job
metadata:
name: laravel-migrate
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: ghcr.io/<user>/laravel-media:1.0.0
command: ["php", "artisan", "migrate", "--force"]kubectl apply -f job-migrate.yaml
kubectl get job laravel-migrate
kubectl logs job/laravel-migrate--force dipakai agar migrasi berjalan tanpa prompt interaktif di environment produksi.
Jika ada direktori statis (misal avatar default, sample gallery) yang harus tersedia di volume saat pertama kali, seed dilakukan lewat init container (lihat di bawah), atau sekali dengan kubectl cp/rsync dari Pod. Untuk konsistensi dan reproducibility, lebih baik seed lewat init container yang idempoten.
Init container berjalan sebelum container utama dan harus selesai sukses. Gunakan untuk menyiapkan volume:
spec:
template:
spec:
initContainers:
- name: prepare-storage
image: busybox:1.36
command:
- /bin/sh
- -c
- |
mkdir -p /var/www/storage/app/uploads
mkdir -p /var/www/storage/app/public
mkdir -p /var/www/storage/framework/sessions
mkdir -p /var/www/storage/framework/cache
mkdir -p /var/www/storage/framework/views
[ -d /var/www/public/storage ] || ln -sfn /var/www/storage/app/public /var/www/public/storage
volumeMounts:
- name: rwx-media
mountPath: /var/www/storage
containers:
- name: laravel-web
image: ghcr.io/<user>/laravel-media:1.0.0
volumeMounts:
- name: rwx-media
mountPath: /var/www/storagePola ini menjamin setiap Pod yang baru lahir (scale out, rolling update) melihat struktur volume yang lengkap sebelum container utama dianggap siap oleh readiness probe.
Important
Perhatikan penggunaan [ -d ... ] || ln -sfn ... — ini idempotency guard. Symlink hanya dibuat jika belum ada, sehingga volume yang sudah terisi tidak rusak oleh inisialisasi ulang.
Dua prinsip yang melindungi data:
mkdir -p (aman), symlink dengan guard [ -d ... ] ||, file seed hanya ditulis jika belum ada.Urutan deploy yang benar:
kubectl apply -f job-migrate.yaml
kubectl wait --for=condition=complete job/laravel-migrate --timeout=180s
kubectl apply -f deployment-laravel.yamlTunggu migrasi selesai sebelum deployment naik — aplikasi tidak layak menerima traffic bila tabel belum ada.
Warning
Jangan jalankan php artisan migrate dari Pod replica biasa tanpa status Job. Saat replicas: 3 melakukan rollout serentak, tiga proses migrate berjalan bersamaan → deadlock atau kegagalan sebagian. Job + kubectl wait adalah pola yang aman.
Pada episode 12 ini, kalian telah menyiapkan data awal secara disiplin:
Inti yang harus dibawa pulang:
php artisan migrate --force).[ -d ... ] || ln -sfn ...) agar idempoten dan tidak menimpa data.Di episode 13 selanjutnya kita akan bedah sisi keamanan dan kompatibilitas paling licin: ekspor NFS, permissions, dan UID mapping — root squash, all_squash vs no_root_squash, membatasi client di /etc/exports, serta memperbaiki permission dengan chown dan stat. Sampai jumpa di episode 13!