Build Container Aplikasi Laravel dengan FrankenPHP
Apa itu FrankenPHP? #
FrankenPHP adalah PHP server yang sebenarnya adalah sebuah modul dari Caddy web server. Karena Caddy ditulis dengan Go memungkinkan FrankenPHP menghadirkan fitur self-contained executable, yang mana aplikasi Laravel akan dibungkus dalam single binary untuk memuluskan proses Deployment.
Persiapan #
Pastikan project Laravel sudah siap, ditulisan ini saya menggunakan blog ini yang memang ditulis dengan Laravel, dengan catatan frontend asset di project blog ini di build dengan Bun.
Membuat Multi-Stage Container file #
FROM docker.io/oven/bun:1 AS asset-builder
COPY package.json bun.lockb /app/
WORKDIR /app
RUN bun install
COPY . .
RUN bun run build
FROM docker.io/serversideup/php:8.4-cli as vendor
COPY --chown=www-data:www-data . /var/www/html
RUN composer install --no-interaction --optimize-autoloader --ignore-platform-reqs --no-dev
FROM docker.io/dunglas/frankenphp:static-builder-musl-1.7.0 AS builder
ENV NO_COMPRESS=1
# Copy your app
WORKDIR /go/src/app/dist/app
COPY . .
# Remove the tests and other unneeded files to save space
# Alternatively, add these files to a .dockerignore file
RUN rm -Rf tests/
COPY --from=vendor /var/www/html /go/src/app/dist/app
COPY --from=asset-builder /app/public/build /go/src/app/dist/app/public/build
# Build the static binary, be sure to select only the PHP extensions you want
WORKDIR /go/src/app/
RUN EMBED=dist/app/ ./build-static.sh
FROM gcr.io/distroless/static-debian12
ARG build=dev
ENV BUILD $build
WORKDIR /app
COPY --from=builder /go/src/app/dist/frankenphp-linux-x86_64 blog
ENTRYPOINT ["/app/blog", "php-server"]Penjelasan Multi-Stage build #
Stage 1: Asset Builder #
Stage ini menggunakan Bun untuk build frontend assets (CSS, Javascript):
- Install dependencies dengan
bun install - Build assets dengan
bun run build - Hasil build akan di-copy ke stage selanjutnya
Stage 2: PHP Dependencies #
Stage ini berisi instalasi Composer dependencies, menggunakan base image dari serversideup:
- Install Composer dependencies dengan optimasi production
- Flag
--no-devuntuk skip development dependencies - Flag
--optimize-autoloaderuntuk performa lebih baik
Stage 3: Static Binary Build #
Membuat static binary dengan FrankenPHP:
- Menggunakan base image
dunglas/frankenphp:static-builder-musl-1.7.0 - Menggabungkan source code, Composer dependencies, dan frontend build assets
- Aktivasi variable
NO_COMPRESSuntuk menghindari proses kompression binary karena ini akan menambah waktu build container image - Menjalankan script
build-static.shdari Frankenphp untuk compile menjadi single executable
Stage 4: Final Runtime #
Stage terakhir yang menghasilkan image production
- Menggunakan base image
distroless/static-debian12 - Hanya berisi static binary hasil compile
Build Docker Image #
Jalankan perintah berikut untuk build image:
podman build --format=docker \
-t wayanjimmy/blog-10:latest .Menjalankan Container #
Setelah build selesai, pastikan container berjalan tanpa masalah:
podman container run -p 8080:80 \
--env-file=./.env --rm wayanjimmy/blog-10:latestExtract Static Binary #
Jika ingin mendapatkan static binary-nya saja:
podman create --name temp-container wayanjimmy/blog-10:latest
podman cp temp-container:/app/blog ./blog-binary
podman rm temp-containerSekarang file blog-binary bisa dijalankan langsung:
chmod +x blog-binary
./blog-binary php-serverKesimpulan #
Dengan menggunakan FrankenPHP dan multi-stage Container build, kita bisa mengubah aplikasi Laravel menjadi self-contained single binary. Ini akan memuluskan proses deployment, karena menghilangkan beberapa component yang harus dijalankan seperti nginx/apache dan php-fpm.
Mesti ukuran binary cukup besar (sekitar 199MB), namun kemudahan deployment yang didapat menurut saya cukup worth it. Approach ini sangat cocok untuk menyederhanakan komponen yang di deploy, dan seperti blog ini saya deploy ke server Fly.io.