Containerize Laravel Framework with Docker Multi-Stage to Optimized Container Image Size
Background
Melakukan containerization pada suatu aplikasi merupakan salah satu hal yang dapat menunjang produktivitas developer, container dapat membantu mempercepat proses development, deployment, hingga delivery berbagai jenis aplikasi. Namun terdapat beberapa kasus bahwa aplikasi yang di container-kan memiliki ukuran images yang cukup besar, dikarenakan banyak package yang tidak digunakan atau pemilihan base image yang tidak ideal. Khususnya pada Laravel yang merupakan salah satu php framework populer, ia membutuhkan berbagai dependencies dan modules seperti composer, node, dan sebagainya.
Objective
Pada artikel ini penulis akan memberikan solusi bagaimana meminimalisir ukuran image container laravel dengan menggunakan metode multi-stage. Namun, penulis juga membatasi bahwa aplikasi yang akan dijalankan hanya contoh dan sederhana. Penulis juga berharap bahwa anda sudah mengenal sedikit tentang docker maupun laravel, sehingga anda dapat lebih memahami artikel ini. Penulis juga memberikan sedikit istilah terkait sebelum membahas artikel inti.
Terms
Dockerfile
Menyediakan file yang dibutuhkan untuk membangun docker image, setiap line merepresentasikan aksi seperti install software, konfigurasi, dan sebagainya. Source code Dockerfile dapat didistribusikan menggunakan git repository dan proses containerzation dapat diotomatisasi menggunakan tools CI/CD.
Docker Images
Adalah image file yang tidak dapat diubah dan persisten berdasarkan layer instruksi yang diberikan saat membangunnya. Setiap image merepresentasikan perintah, perubahan, instalasi, konfigurasi, dan sebagainya. Sehingga memungkinkan image sangat kompleks seperti berjalan diatas sistem operasi pada umumnya. Images dapat didistribusikan pada registries baik publik seperti docker hub maupun private registries seperti nexus, jfrog, dan sebagainya.
Docker Containers
Container mengisolasi aplikasi dan seluruh dependencies yang berjalan diatas sistem operasi. Semua hal yang anda butuhkan saat menjalankan vm atau bare metal dapat diinstall pada container yang lebih fleksibel dan cepat.
Container Orchestration
Orchestration berarti kemampuan untuk melakukan manajemen dan maintenance containers. Selain itu ia dapat lebih mudah digunakan untuk melakukan provisioning dan scale up resource. Tools populer yang dapat digunakan untuk orchestration seperti Kubernetes, Docker Swarm, dan sebagainya.
Laravel
Laravel adalah satu framework php yang digunakan untuk membantu pengembangan website. Dengan menggunakan framework, developer dapat terbantu dalam penyederhanaan proses pengembangan, performa website, keamanan, dan lebih dinamis.
Prerequisites
Operating System
Untuk sistem operasi, sesuaikan dengan kebutuhan anda. Pada artikel ini penulis menggunakan Linux Ubuntu 20.04. Jadi, anda perlu menyesuaikan untuk perintah dasar, path, atau hal lainnya yang dirasa berbeda. Namun untuk obyektif tutorial ini (Dockerfile) dapat digunakan secara general berbagai macam sistem operasi.
Docker
Multi-stage build tersedia mulai dari docker versi 17.05. Sebaiknya anda melakukan instalasi docker terbaru. Jika laptop anda belum terinstall docker, lakukan instalasi docker terlebih dahulu dengan mengikuti official guide docker berikut.
https://docs.docker.com/engine/install/ubuntu/
Composer
Composer pada laravel merupakan komponen penting yang digunakan untuk melakukan generate library package laravel. Berikut perintah instalasi composer.
$ sudo apt install composer
Atau anda dapat mengikuti official guide berikut untuk instalasi pada platform lainnya
https://getcomposer.org/doc/00-intro.md
Yarn
Pastikan anda sudah menginstal yarn sebagai manajemen dependencies laravel.
Installing Yarn
$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
$ echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
$ sudo apt update && sudo apt install yarn
Install Verification
$ yarn — version
Reference
https://classic.yarnpkg.com/en/docs/install/#debian-stable
NPM
Pada ubuntu, perintah yang digunakan cukup sederhana. Karena umumnya sudah ada dalam package manager apt.
$ sudo apt install npm
Laravel
Penulis menggunakan Laravel versi 5.0. Berikut adalah beberapa dependencies yang perlu dipersiapkan seperti node, composer, yarn, dan sebagainya.
Installing Composer
$ composer global require laravel/installer
Alias Laravel Binary
$ alias -p laravel=~/.config/composer/vendor/bin/laravel
Laravel Project Initiate
$ laravel new <project-name>
Reference
https://laravel.com/docs/5.0/installation
Implementation
Traditional Method
Objective
Pada konsep dockerize image tradisional (single stage) dalam satu image docker, anda harus memiliki setiap dependencies yang dibutuhkan seperti harus menginstall node js, composer, yarn, dan sebagainya. Sehingga docker image yang dihasilkan akan sangat berantakan, sulit dipelihara, dan bisa saja ukuran image menjadi lebih besar dari multi-stage untuk objective yang sama.
Script
Contoh dari pendekatan tradisional ini ada pada cuplikan Dockerfile berikut. Create Dockerfile, pastikan file berada di root directory laravel project.
$ vi Dockerfile
Isi dengan script berikut
# Base Image
FROM php:8.0-apache
# Install NPM
RUN apt-get update && apt-get install -y npm zip unzip
# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Copy Laravel app to Container
COPY . .
# Composer Install
RUN composer install \
--ignore-platform-reqs \
--no-interaction \
--no-plugins \
--no-scripts \
--prefer-dist
# NPM Install and Compile
RUN npm install \
&& npm run production
Lalu, anda dapat melakukan build image dengan contoh perintah berikut.
$ docker build -t <image-name>:<image-tag> .
Result
Ukuran image yang dihasilkan kurang lebih 588 MB untuk metode tradisional ini, anda dapat mengecek hasil-nya dengan perintah berikut:
$ docker images
Jalankan container untuk melihat hasil dari laravel container, misalkan berjalan diatas port 80. Maka perintah yang dapat digunakan adalah sebagai berikut:
$ docker run -it — expose 80 -p 80:80 <image-name:<image-tag>
Multi-Stage Build Method
Objective
Dengan menggunakan metode multi-stage build setiap stage akan menggunakan container image tertentu sesuai fungsinya. Hasil dari stage tersebut akan dialihkan ke stage berikutnya untuk di eksekusi menggunakan container images lainnya. Meminimalisir proses instalasi manual dengan perintah secara langsung pada satu docker images.
Preparation
Buat file .dockerignore, untuk mencegah penyalinan jalur yang ditentukan selama proses build. List berikut artinya file tidak akan diabaikan saat ada instruksi COPY atau ADD pada dockerfile.
.git/
vendor/
node_modules/
public/js/
public/css/
yarn-error.log
Misalkan ada perintah berikut COPY . /var/www/html. Artinya perintah tersebut akan melakukan copy seluruh file yang ada pada workdir, kecuali yang sudah di list pada .dockerignore.
Stage 1
Sesuai dengan tahapan instalasi laravel pada prerequisite. Tahap 1 adalah menyiapkan composer dengan menggunakan official image composer secara langsung. Berikut cuplikan Dockerfile tahap pertama.
# Stage 1 - PHP Dependencies
FROM composer:2 as vendor
COPY database/ database/
COPY composer.json composer.json
COPY composer.lock composer.lock
RUN composer install \
--ignore-platform-reqs \
--no-interaction \
--no-plugins \
--no-scripts \
--prefer-dist
Pada bagian “as vendor” akan digunakan sebagai referensi untuk menyalin seluruh hasil pada tahap 1 ke tahap berikutnya.
Stage 2
Pada tahap kedua ini bertujuan untuk menginstall dependencies frontend dengan menggunakan yarn atau npm. Tambahkan script berikut setelah script tahap pertama. Anda dapat mengidentifikasi directory public laravel dengan melakukan exec ke container. Tahap ini akan menghasilkan artefak frontend yang siap disalin ke tahap berikutnya.
# Stage 2 - Frontend Dependencies
FROM node:15.10-alpine as frontend
RUN mkdir -p /app/public
COPY package.json webpack.mix.js yarn.lock /app/
COPY resources/ /app/resources/
WORKDIR /app
RUN yarn install && yarn production
Stage 3
Tahap terakhir adalah menjalankan aplikasi menggunakan apache. Pada bagian ini juga terdapat aktifitas untuk merestore artefak tahap 1 (vendor), dan tahap 2 (frontend) ke directory /var/www/html yang sudah didefinisikan secara default oleh apache sebagai workdir. Anda dapat mengubahnya dengan memberikan konfigurasi tambahan pada file yang siap di copy dalam container.
# Stage 3 - Run Application
FROM php:8.0-apacheCOPY . /var/www/html
COPY --from=vendor /app/vendor/ /var/www/html/vendor/
COPY --from=frontend /app/public/js/ /var/www/html/public/js/
COPY --from=frontend /app/public/css/ /var/www/html/public/css/
COPY --from=frontend /app/mix-manifest.json /var/www/html/mix-manifest.jsonRUN chown -R www-data:www-data /var/www/html
Selain itu, jangan lupa untuk mengubah owner /var/www/html dan seluruh isinya pada UG www-data. Hal ini menghindari isu terkait file permissions.
Stage Summary
Setelah anda memahami konsep stage tersebut, berikut penulis sajikan hasil penggabungan dari seluruh stage yang ada pada Dockerfile.
Result
Lakukan docker build didalam root directory laravel project seperti perintah berikut.
$ docker build -t <image-name>:<image-tag> .
Ukuran image yang dihasilkan kurang lebih 488 MB untuk metode multistage, maka kurang lebih kita mengurangi sekitar 100 MB untuk melakukan membuat docker image laravel. Hal ini mungkin dapat di optimize kembali dengan memilih image yang lebih ringan. Anda dapat mengecek hasil docker build image dengan perintah berikut:
$ docker images
Jalankan container untuk melihat hasil dari laravel container, misalkan berjalan diatas port 80. Maka perintah yang dapat digunakan adalah sebagai berikut:
$ docker run -it — expose 80 -p 80:80 <image-name:<image-tag>
Kunjungi http://localhost pada browser anda untuk mengetahui hasilnya.
Artifactory
Dockerfile Repository
Source code sample app laravel dapat anda clone atau unduh pada repository berikut
https://github.com/divistant/container-php-laravel
Container Registry
https://hub.docker.com/r/divistant/php-laravel
Atau anda dapat langsung pull image dengan perintah berikut
$ docker pull divistant/php-laravel
DevOps Consultant
Jika anda memiliki kebutuhan containerize berbagai macam aplikasi, anda dapat menghubungi saya melalui email andrimuhyidin55@gmail.com. Semoga bisa membantu.