Bỏ qua menu, vào nội dung chính

Dockerfile chuẩn: bảo mật, dung lượng image và cache

Dockerfile best practices đầy đủ: multi-stage build, layer cache, non-root user, scan vulnerability, distroless. Code production-ready cho Node.js.

3 phút đọc

Dockerfile best practices chuẩn 2026 quyết định phần lớn chất lượng image khi chạy thật. Bài này đi qua 10 quy tắc + Dockerfile mẫu cho Node.js, Python, Go ở cuối.

Tài liệu Docker: best practices khi viết Dockerfile
Tài liệu Docker: best practices khi viết Dockerfile (docs.docker.com), ảnh chụp ngày 27/09/2026.

1. Luôn dùng multi-stage build

Tách build dependencies khỏi runtime. Image final chỉ chứa app + runtime deps:

dockerfileFROM node:22-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:22-slim
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --omit=dev
USER node
CMD ["node", "dist/server.js"]

Stage builder có TypeScript, dev deps, source. Stage cuối chỉ có dist + production deps. Image cuối ~200MB thay vì 800MB.

2. Pin version base image

dockerfile# ❌ Sai - không reproducible
FROM node:latest
FROM node:22

# ✓ Đúng - cụ thể
FROM node:22.5.1-slim
# Tốt hơn - pin digest
FROM node:22.5.1-slim@sha256:abc123...

Renovate/Dependabot tự bump version có PR - anh review từng update.

3. Tận dụng layer cache

Đặt instruction ít thay đổi trước:

dockerfile# ❌ Sai - sửa code = re-run npm ci
COPY . .
RUN npm ci

# ✓ Đúng - sửa code không invalidate npm ci
COPY package*.json ./
RUN npm ci
COPY . .

4..dockerignore đầy đủ

textnode_modules
npm-debug.log
.git
.gitignore
.env*
.next
.cache
*.md
.vscode
.idea
__tests__
coverage
.DS_Store

Không chỉ vì size -.env không được vào image. Secret leak vào registry là incident phổ biến.

5. Chạy non-root user

dockerfileFROM node:22-slim
RUN groupadd -r app && useradd -r -g app -u 1001 app
WORKDIR /app
COPY --chown=app:app . .
USER app
CMD ["node", "server.js"]

Image official Node có user node sẵn (UID 1000). Dùng USER node là đủ.

6. Handle signal đúng - dùng exec form

dockerfile# ❌ Sai - shell form, không nhận SIGTERM
CMD node server.js

# ✓ Đúng - exec form, app trực tiếp PID 1
CMD ["node", "server.js"]

Khi shell form, node là child của shell - SIGTERM gửi shell, không tới app. App không graceful shutdown được.

7. Combine RUN để giảm layer

dockerfile# ❌ Tạo 3 layer
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get clean

# ✓ 1 layer + dọn cache trong cùng layer
RUN apt-get update \
  && apt-get install -y curl \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/*

8. HEALTHCHECK cho orchestrator

dockerfileHEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  CMD curl -f http://localhost:3000/healthz || exit 1

K8s/ECS dùng để restart container khi unhealthy. Endpoint /healthz nên kiểm DB connection, không chỉ "200 OK".

9. KHÔNG bao giờ ARG/ENV cho secret

dockerfile# ❌ Secret leak vào image layer
ARG DATABASE_PASSWORD
ENV DB_PASS=$DATABASE_PASSWORD

# ✓ Dùng BuildKit secret mount
# syntax=docker/dockerfile:1.4
RUN --mount=type=secret,id=db_pass \
  cat /run/secrets/db_pass | npm run migrate

Build với:

bashDOCKER_BUILDKIT=1 docker build --secret id=db_pass,src=./db_pass.txt .

10. Scan image trước khi push

bash# Trivy
trivy image alodev-api:latest

# Snyk
snyk container test alodev-api:latest

# Docker Scout (built-in)
docker scout cves alodev-api:latest

Tích hợp CI: fail build nếu có CRITICAL vuln chưa patch.

Full example: Node.js production

dockerfile# syntax=docker/dockerfile:1.6
FROM node:22.5.1-slim AS deps
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
  npm ci --omit=dev

FROM node:22.5.1-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
COPY . .
RUN npm run build

FROM gcr.io/distroless/nodejs22-debian12
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package.json ./
EXPOSE 3000
USER nonroot
CMD ["dist/server.js"]

Distroless: không có shell, không apt, gần như không attack surface. Image ~120MB.

Kết luận

10 quy tắc Dockerfile best practices: multi-stage, pin version, layer cache, non-root, exec form, combine RUN, healthcheck, secret mount, scan. Áp dụng đầy đủ, image anh sẽ nhỏ hơn 3x và an toàn hơn nhiều. Đọc thêm Docker cơ bản nếu mới bắt đầu, và CI/CD GitHub Actions để tự động build + scan.

Nhận bài mới

Để lại email để nhận bài mới của series Kiến thức lập trình, hoặc theo dõi bằng RSS.

RSS Kiến thức lập trình

Email chỉ dùng để gửi bài mới, xem Chính sách bảo mật.

Trao đổi về dự án

Cần tư vấn?