Tối ưu Docker Image cho Production (Multi-stage builds)

· 4 min read

Trong môi trường development, chúng ta thường dùng laravel/sail hoặc các image đầy đủ tiện ích (full utility) để tiện debug. Nhưng khi deploy lên Production, kích thước và bảo mật là ưu tiên hàng đầu.

Một Docker image tồi cho Production thường:

  • Kích thước lớn (> 1GB) -> Deploy chậm, tốn băng thông.
  • Chứa source code thừa (node_modules, tests, git folder).
  • Chứa các công cụ không cần thiết (vi, nano, curl...) -> Tăng bề mặt tấn công (attack surface).
  • Chạy dưới quyền root.

Giải pháp là Multi-stage Builds.

Multi-stage Builds là gì?

Đơn giản là trong một Dockerfile bạn có nhiều lệnh FROM.

  • Stage 1: Dùng để Build (cần Composer, Node, NPM...).
  • Stage 2: Dùng để Run (chỉ cần PHP Runtime, Nginx).

Stage 2 sẽ copy kết quả từ Stage 1 sang, và vứt bỏ toàn bộ công cụ build.

Dockerfile mẫu cho Laravel Production

Dưới đây là một ví dụ Dockerfile tối ưu hóa cao độ:

# --- Stage 1: Build Assets (Frontend) ---
FROM node:20-alpine as frontend
WORKDIR /app
COPY package*.json vite.config.js ./
RUN npm ci
COPY resources/ ./resources/
COPY public/ ./public/
# Build assets vào folder public/build
RUN npm run build 

# --- Stage 2: Install Composer Deps ---
FROM composer:2.6 as vendor
WORKDIR /app
COPY composer.json composer.lock ./
# --no-dev: Không cài require-dev (phpunit, faker...)
# --optimize-autoloader: Tối ưu map class
RUN composer install \
    --no-dev \
    --no-interaction \
    --prefer-dist \
    --ignore-platform-reqs \
    --optimize-autoloader \
    --no-scripts

# --- Stage 3: Final Production Image ---
FROM php:8.3-fpm-alpine

# Cài đặt extension cần thiết cho Laravel
# Dùng install-php-extensions (script tiện ích) cho dễ
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions && \
    install-php-extensions pdo_mysql bcmath opcache redis intl zip exif

# Cấu hình PHP cho Prod
COPY ./docker/php/local.ini /usr/local/etc/php/conf.d/local.ini
COPY ./docker/php/opcache.ini /usr/local/etc/php/conf.d/opcache.ini

WORKDIR /var/www

# Copy vendor từ Stage 2
COPY --from=vendor /app/vendor/ ./vendor/

# Copy assets từ Stage 1
COPY --from=frontend /app/public/build/ ./public/build/

# Copy source code ứng dụng (trừ những gì trong .dockerignore)
COPY . .

# Setup quyền (quan trọng: không chạy root)
RUN chown -R www-data:www-data /var/www \
    && chmod -R 755 /var/www/storage

# Switch user
USER www-data

# Clean up
# (Trên Alpine, apk cache tự clear hoặc thêm rm -rf /var/cache/apk/*)

EXPOSE 9000
CMD ["php-fpm"]

Các điểm tối ưu chính

  1. Base Image Alpine: php:8.3-fpm-alpine chỉ nặng khoảng 50MB so với bản Debian/Ubuntu hàng trăm MB.
  2. Tách tầng Build JS: Node_modules rất nặng và chứa hàng nghìn file. Ta chỉ copy folder public/build cuối cùng.
  3. Tách tầng Vendor PHP: Ta chạy composer install --no-dev. Điều này loại bỏ PHPUnit, Mockery... khỏi production image.
  4. .dockerignore: Cực kỳ quan trọng. Phải ignore .git, tests, node_modules (nếu có ở local), storage/*.key...
  5. OPCache: Bắt buộc phải enable và config pre-load code PHP vào RAM để có hiệu năng cao nhất.

Kết hợp với Nginx

Thường ta sẽ có thêm một container Nginx (sidecar) hoặc build Nginx vào chung image (nếu dùng Supervisor). Mô hình chuẩn Kubernetes/ECS thường tách rời:

  • 1 Container chạy PHP-FPM (xử lý logic).
  • 1 Container chạy Nginx (phục vụ static file và proxy pass sang PHP-FPM).

Với Dockerfile trên, image cuối cùng của bạn có thể chỉ nặng tầm 100MB - 150MB (bao gồm cả code framework), so với 800MB+ nếu làm theo cách ngây thơ.

Kết luận

Docker Image nhỏ gọn không chỉ giúp tiết kiệm tiền lưu trữ (ECR, Docker Hub) mà còn giúp Autoscaling nhanh hơn (thời gian pull image giảm) và bảo mật hơn (ít component thừa). Đầu tư 1 lần vào Dockerfile chuẩn là khoản đầu tư sinh lời dài hạn cho dự án.

Bình luận