Halo Talenta Digital! Selamat datang kembali di blog Ruang Developer, kali ini kita akan belajar bagaimana cara menggunakan multi stage build saat membuat docker image untuk project laravel kita. Untuk mengikuti materi ini, saya harap kamu sudah mengetahui apa itu docker ya. Oke, langsung kita mulai.
Dockerfile
Ketika kita ingin membuat docker image, kita menuliskan perintah-perintah yang digunakan untuk membangun image yang ingin kita buat dalam sebuah file bernama Dockerfile. Di dalam file Dockerfile, kita menggunakan sebuah base image sesuai dengan stack teknologi project kita. Misalnya, ketika kita membuat image untuk project laravel, maka kita bisa menggunakan base image dari apache. Ketika project kita menggunakan Node JS, maka kita menggunakan base image dari Node. Berikut ini contoh Dockefile untuk project Node JS.
# Menggunakan base image node:alpine
FROM node:alpine
# Membuat directory app, setting working directory
WORKDIR /usr/src/app
# Copy file package.json dan package-lock.json (jika ada)
COPY package*.json ./
# Menginstal dependency
RUN npm install
# Bundle source
COPY . .
# Expose port (for documentation)
EXPOSE 3000
# Run the app
CMD [ "node", "src/app.js"]
Apa Itu Multi Stage Build?
Multi stage build adalah menggunakan lebih dari satu statement FROM
dalam Dockerfile. Tiap statement FROM
menggunakan base image yang berbeda sesuai dengan stack yang digunakan. Pada contoh Dockerfile di atas, hanya terdapat satu buah statement FROM
untuk menggunakan image node:alpine
. Hal ini disebabkan karena contoh projectnya hanya menggunakan stack Node JS saja. Lalu bagaimana jika project kita menggunakan laravel?
Seperti yang kita ketahui, laravel merupakan full stack web framework. Di dalam project laravel, biasanya kita menggunakan composer untuk depenedency management Node JS untuk compile asset. Ini berarti kita menggunakan dua buah stack yang berbeda pada project laravel. Sehingga, ketika kita ingin membuat docker image untuk project laravel, kita bisa menggunakan multi stage build.
Apa Keuntungan Menggunakan Multi Stage Build?
Sebenarnya, kita bisa membuat docker image untuk larave tanpa multi stage build, yaitu dengan menginstall semua stack teknologi dalam satu buah base image. Namun, hal ini akan membuat ukuran docker image kita akan menjadi lebih besar. Hal ini akan menjadi sebuah pemborosan ketika kita mendeploy aplikasi kita di server production. Ukuran image yang besar tentunya memerlukan resource server yang besar pula, sehingga berakibat pada biaya server yang membengkak.
Membuat Multi Stage Build Untuk Project Laravel
Oke, setelah penjelasan singkat tentang multi stage build, kita akan mencoba membuat multi stage build untuk project laravel. Sebagai informasi, di sini saya akan menggunakan laravel versi 8.0.
Dalam root folder project, buat sebuah file bernama .dockerignore
yang berisi kode berikut:
vendor/
node_modules/
File tersebut berfungsi untuk mengabaikan file saat melakukan perintah copy dalam Dockerfile.
Berikutnya, masih dalam root folder project, buatlah sebuah file baru bernama Dockerfile
(tanpa ekstensi). Berikut ini beberapa perintah yang harus kita tulis dalam file Dockerfile
.
Stage 0: Vendor
Pada stage ini kita akan menggunakan Composer sebagai base image dan menginstall dependency untuk project laravel kita.
FROM composer:latest AS vendor
WORKDIR /app
COPY composer.json composer.json
COPY composer.lock composer.lock
RUN composer install \
--no-interaction \
--no-plugins \
--no-scripts \
--no-dev \
--prefer-dist
COPY . .
RUN composer dump-autoload
Stage 1: Frontend
Pada stage ini kita akan menggunakan Node JS sebagai base image dan menginstall node modules.
FROM node:alpine AS frontend
WORKDIR /app
COPY resources/css ./resources/css
COPY resources/js ./resources/js
COPY package*.json webpack.mix.js ./
RUN mkdir -p /app/public
RUN npm install
RUN npm run production
Stage 2: App
Pada stage ini kita mengumpulkan kembali file-file yang telah kita install sebelumnya ke dalam satu buah base image yang digunakan untuk menjalankan aplikasi laravel yaitu php apache.
FROM php:8.0-apache
COPY . /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.json
RUN chown -R www-data:www-data /var/www/html
Berikut ini hasil keseluruhan file Dockerfile
untuk project kita.
FROM composer:latest AS vendor
WORKDIR /app
COPY composer.json composer.json
COPY composer.lock composer.lock
RUN composer install \
--no-interaction \
--no-plugins \
--no-scripts \
--no-dev \
--prefer-dist
COPY . .
RUN composer dump-autoload
FROM node:alpine AS frontend
WORKDIR /app
COPY resources/css ./resources/css
COPY resources/js ./resources/js
COPY package*.json webpack.mix.js ./
RUN mkdir -p /app/public
RUN npm install
RUN npm run production
FROM php:8.0-apache
COPY . /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.json
RUN chown -R www-data:www-data /var/www/html
Sekarang kita bisa coba membuild imagenya menggunakan perintah berikut:
docker build -t <name>:<tag> .
Sebagai perbandingan, saya juga membuild project yang sama, namun dengan menggunakan cara tradisional yaitu satu stage saja dengan Dockerfile berikut:
FROM php:8.0-apache
# NPM
RUN apt-get update && apt-get install -y npm zip unzip
# Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Copy project
COPY . .
# Composer Install
RUN composer install \
--ignore-platform-reqs \
--no-interaction \
--no-plugins \
--no-scripts \
--prefer-dist
# NPM Install dan compile production
RUN npm install \
&& npm run production
Berikut ini hasil perbandingan ukuran image yang dibuild menggunakan multi stage (tag: 1.0) dan single stage (tag: traditional)
image name | tag | image id | size |
---|---|---|---|
laravel-docker | traditional | b7c3f1233f02 | 1.08GB |
laravel-docker | 1.0 | 4e12df5bace0 | 498MB |
Wow, dapat kita lihat perbedaan yang cukup besar ya untuk ukuran image.
Kesimpulan
Oke, setelah kita mencoba multi stage build. Kita mendapatkan ukuran image yang cukup lebih kecil dibandingkan dengan menggunakan cara tradisional. Maka dari itu, mulai sekarang kita biasakan untuk menggunakan multi stage build jika ingin membuild docker image untuk project laravel. Hal ini selain hemat resource di production, juga hemat resource di komputer local kita.