Giới thiệu
Nếu bạn là người dẫn dắt DevOps quản lý một đội ngũ container, việc hình ảnh bloat và thời gian xây dựng chậm là những kẻ giết chết năng suất âm thầm xuất hiện trong bất kỳ pipeline CI/CD nào. Trong hướng dẫn theo phong cách tutorial này, chúng ta sẽ cùng khám phá bảy kỹ thuật cụ thể để thu nhỏ hình ảnh Docker, tăng tốc độ xây dựng và giữ cho các runner của bạn luôn hoạt động tốt. Mục tiêu là các bước thực tiễn, có thể tái sử dụng mà bạn có thể áp dụng vào quy trình làm việc hiện tại mà không cần viết lại hoàn toàn.
1. Sử Dụng Multi-Stage Builds
Multi-stage builds cho phép bạn tách biệt môi trường xây dựng ra khỏi hình ảnh runtime. Điều này loại bỏ các trình biên dịch, trình quản lý gói và các tệp tạm thời khỏi artifact cuối cùng.
bash
# ---- Giai đoạn Xây dựng ----
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# ---- Giai đoạn Runtime ----
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production
CMD ["node", "dist/index.js"]
Hình ảnh kết quả chỉ bao gồm đầu ra đã biên dịch và các phụ thuộc runtime, thường giảm kích thước từ 60‑80%.
2. Tận Dụng Hiệu Quả Docker Build Cache
Docker lưu cache cho mỗi lớp dựa trên chính xác lệnh và ngữ cảnh của nó. Để tối đa hóa các lần truy cập cache:
- Sắp xếp các hướng dẫn từ ít thay đổi đến thường xuyên thay đổi nhất (ví dụ, cài đặt gói OS trước khi sao chép mã nguồn).
- Sử dụng
.dockerignoremột cách triệt để để ngăn các tệp không cần thiết làm hỏng cache. - Ghim phiên bản của hình ảnh cơ sở và các gói để tránh việc làm hỏng cache âm thầm.
bash
# .dockerignore
node_modules
.git
Dockerfile
*.log
Khi chỉ có các tệp nguồn thay đổi, Docker sẽ tái sử dụng các lớp trước đó, giảm thời gian xây dựng trong các lần chạy sau.
3. Chọn Hình Ảnh Cơ Sở Tối Thiểu
Alpine Linux là một hình ảnh cơ sở nhẹ phổ biến (≈5 MB). Tuy nhiên, hãy lưu ý về các vấn đề tương thích liên quan đến glibc. Nếu bạn cần một hệ điều hành đầy đủ tính năng, hãy xem xét scratch cho các binary Go hoặc các hình ảnh distroless cho Java và Python.
| Hình Ảnh Cơ Sở | Kích Thước Xấp Xỉ | Trường Hợp Sử Dụng Thông Thường |
|---|---|---|
alpine |
5 MB | Mục đích tổng quát, Node, Python |
scratch |
0 B | Các binary liên kết tĩnh |
distroless |
20‑30 MB | Java, .NET, Go (glibc) |
4. Xóa Các Tệp Thời Gian Xây Dựng Trong Một Lớp
Thay vì phân tán các lệnh rm -rf trên nhiều câu lệnh RUN (mỗi câu lệnh tạo ra một lớp mới), hãy kết hợp chúng lại:
bash
RUN apk add --no-cache build-base && \
make && \
make install && \
rm -rf /var/cache/apk/* /tmp/*
Điều này giữ cho hình ảnh cuối cùng gọn nhẹ và giảm số lượng lớp, cải thiện tốc độ kéo và hiệu quả lưu trữ.
5. Sử Dụng BuildKit Để Tăng Tốc và Lưu Cache
Engine BuildKit của Docker (có sẵn trong Docker 18.09+) giới thiệu tính năng lưu cache nâng cao, xử lý bí mật nội tuyến và thực thi song song. Bật tính năng này bằng cách thiết lập biến môi trường:
bash
export DOCKER_BUILDKIT=1
docker build -t myapp:latest .
BuildKit có thể giảm đáng kể thời gian xây dựng cho các dự án lớn, đặc biệt khi kết hợp với cờ --cache-from để tái sử dụng các lớp từ các lần xây dựng trước được lưu trữ trong một registry.
6. Lưu Cache Các Phụ Thuộc Tách Biệt
Đối với các ngôn ngữ có cây phụ thuộc nặng (Node, Python, Ruby), hãy lưu thư mục phụ thuộc trong một lớp riêng. Ví dụ cho Node:
bash
COPY package*.json ./
RUN npm ci --only=production
COPY . .
Khi chỉ có mã ứng dụng thay đổi, Docker sẽ tái sử dụng lớp npm ci đã lưu cache, tránh việc cài đặt lại hoàn toàn tất cả các gói.
7. Quét và Dọn Dẹp Hình Ảnh Định Kỳ
Ngay cả khi áp dụng các best practices, các hình ảnh trung gian không cần thiết vẫn có thể tích tụ trên các runner CI. Lên lịch cho một lần dọn dẹp vào ban đêm:
bash
# Xóa các hình ảnh không còn sử dụng
docker image prune -f
# Xóa các hình ảnh cũ hơn 30 ngày
docker image prune -a --filter "until=720h" -f
Tự động hóa bước này ngăn ngừa việc hết dung lượng đĩa và giữ cho hiệu suất của runner luôn ổn định.
Kết Hợp Tất Cả Trong Một Pipeline CI
Dưới đây là một workflow GitHub Actions tối thiểu minh họa các mẹo này:
bash
name: Docker Build & Push
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Thiết lập Docker BuildKit
run: echo "DOCKER_BUILDKIT=1" >> $GITHUB_ENV
- name: Đăng nhập vào Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
- name: Xây dựng và đẩy
run: |
docker build -t myorg/myapp:${{ github.sha }} .
docker push myorg/myapp:${{ github.sha }}
Workflow này tôn trọng việc lưu cache, sử dụng BuildKit và đẩy một hình ảnh gọn nhẹ đến registry.
Kết Luận
Tối ưu hóa hình ảnh Docker không phải là phép thuật mà chủ yếu là về việc tổ chức các lớp, lưu cache và dọn dẹp có kỷ luật. Bằng cách áp dụng bảy mẹo này, bạn sẽ thấy thời gian xây dựng CI nhanh hơn, các registry nhỏ hơn và việc triển khai mượt mà hơn. Để tìm hiểu sâu hơn về các best practices của Docker và một danh sách công cụ được chọn lọc, hãy truy cập https://lacidaweb.com – đây là một tài nguyên hữu ích cho các nhóm muốn tinh gọn quy trình container của mình.