Series Dockerize Laravel Thực chiến #6: Trạm cuối – Tuyệt kỹ Multi-stage Build cho Production
1. Multi-stage Build là gì? Tại sao phải dùng nó?
Hãy tưởng tượng quá trình build một dự án Laravel giống như việc nướng bánh. Để nướng bánh (chạy Laravel), bạn cần bột mì, trứng, máy đánh trứng, lò nướng (đó là Composer, Node.js, git, unzip...). Nhưng khi giao cái bánh cho khách hàng ăn (chạy trên Server Production), bạn không cần phải gói theo cái máy đánh trứng hay cái lò nướng! Bạn chỉ cần giao cái bánh thành phẩm.
Multi-stage Build cho phép bạn định nghĩa nhiều "giai đoạn" (stage) trong cùng một file Dockerfile.
- Giai đoạn 1 (Lò nướng): Dùng image composer khổng lồ để tải toàn bộ thư viện (vendor).
- Giai đoạn 2 (Thành phẩm): Dùng image php-fpm-alpine siêu nhẹ. Sau đó, chỉ copy đúng thư mục vendor đã được xử lý xong từ Giai đoạn 1 sang.
Kết quả: Từ một Image nặng 1GB (chứa đủ thứ rác biên dịch), Image Production của bạn chỉ còn chưa tới 150MB!
2. Viết lại Dockerfile cho môi trường Production
Hãy tạo một file mới mang tên docker/php/Dockerfile.prod để chuyên phục vụ cho lúc deploy. Cấu trúc sẽ chia làm 2 giai đoạn rõ rệt:
# ==========================================
# GIAI ĐOẠN 1: Cài đặt thư viện (Vendor)
# ==========================================
FROM composer:2.6 as builder
WORKDIR /app
# Copy các file quản lý thư viện vào trước để tận dụng Docker Cache
COPY composer.json composer.lock ./
# Chạy lệnh install. Bỏ qua các thư viện dev (như phpunit) để giảm dung lượng
RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist
# Sau đó mới copy toàn bộ source code vào
COPY . .
# Chạy autoload lại một lần nữa cho chuẩn xác
RUN composer dump-autoload --optimize
# ==========================================
# GIAI ĐOẠN 2: Đóng gói Image Production
# ==========================================
# Sử dụng bản Alpine cực nhẹ của PHP
FROM php:8.2-fpm-alpine
# Cài đặt các extension bắt buộc cho hệ thống (PDO MySQL, Redis...)
RUN set -ex \
&& apk --no-cache add \
libzip-dev \
libpng-dev \
&& docker-php-ext-install pdo_mysql gd zip bcmath \
&& apk add --no-cache pcre-dev $PHPIZE_DEPS \
&& pecl install redis \
&& docker-php-ext-enable redis
WORKDIR /var/www/html
# COPY MÃ NGUỒN TỪ GIAI ĐOẠN 1 SANG
COPY --from=builder /app /var/www/html
# Chuyển quyền sở hữu cho user www-data (quan trọng để không bị lỗi Permission)
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 775 /var/www/html/storage /var/www/html/bootstrap/cache
# Expose port 9000 cho Nginx gọi vào
EXPOSE 9000
# Chạy PHP-FPM
CMD ["php-fpm"]
3. Tinh chỉnh lại Docker Compose cho Server
Trên server thật, file docker-compose.yml của bạn sẽ rất "gọn gàng". Bạn không cần map volume mã nguồn nữa. Bạn cũng không cần mở port của MySQL hay Redis ra ngoài Internet để tránh bị hacker tấn công, chỉ mở duy nhất port 80/443 của Nginx.
version: '3.8'
services:
webserver:
image: nginx:alpine
container_name: laravel_web_prod
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- app
networks:
- prod_network
app:
build:
context: .
dockerfile: docker/php/Dockerfile.prod
container_name: laravel_app_prod
restart: always
environment:
- APP_ENV=production
- APP_DEBUG=false
networks:
- prod_network
# Các service db, redis, queue-worker, scheduler giữ nguyên logic nhưng bỏ đi các port mapping ra ngoài.
# ...
networks:
prod_network:
driver: bridge
4. Triển khai lên máy chủ thật
Khi đẩy code lên một con VPS mới tinh tại AWS hay DigitalOcean, quy trình deploy của bạn giờ đây chỉ gồm 2 bước:
- Copy file .env.production vào server.
- Gõ lệnh:
docker-compose -f docker-compose.prod.yml up -d --build
Hệ thống sẽ tự động tải các Image nhẹ nhất, tự động cài thư viện (mà server không cần có Composer), tự động cấp quyền Đọc/Ghi, và bật toàn bộ hệ thống lên một cách trơn tru, bảo mật. Bạn hoàn toàn có thể nhúng lệnh này vào Jenkins Pipeline để nó tự động chạy mỗi khi có code mới được merge vào nhánh main.
Lời kết cho Hành trình Dockerize Laravel
Chúc mừng bạn! Chúng ta đã hoàn tất một chặng đường dài và cực kỳ chất lượng.
Từ việc xóa bỏ thói quen dùng XAMPP để làm quen với kiến trúc LEMP Stack nguyên bản (Bài 1 & 2), đến việc thuần hóa Artisan và sửa lỗi phân quyền (Bài 3). Chúng ta cũng không quên xây dựng sức mạnh ngầm cho hệ thống với Redis Queue Worker (Bài 4) và tự động hóa vận hành bằng Cronjob Scheduler (Bài 5). Cuối cùng, kỹ thuật Multi-stage Build ở bài học hôm nay đã chốt lại toàn bộ bức tranh kiến trúc Enterprise.
Khi bạn làm chủ được bộ khung này, việc quản lý một ứng dụng có vài chục ngàn lượt truy cập hay việc kết nối các Microservices phức tạp sẽ không còn là nỗi ám ảnh. Máy tính cá nhân của bạn sẽ luôn gọn gàng, và code của bạn tung lên bất kỳ server Linux nào cũng sẽ chạy chính xác 100%.
Chúc bạn giữ vững tư duy hệ thống này và ứng dụng thành công vào những dự án Backend quan trọng sắp tới!
All rights reserved