Chuyển đến nội dung chính

Bài 5: Dockerfile từ A đến Z

Hướng dẫn chi tiết tất cả Dockerfile instructions: FROM, RUN, COPY, ADD, CMD, ENTRYPOINT, ENV, ARG, WORKDIR, EXPOSE, VOLUME, USER, HEALTHCHECK, LABEL, SHELL, STOPSIGNAL. Best practices và anti-patterns khi viết Dockerfile.

🔒 DevSecOps — Bài 5 Bài 5: Dockerfile từ A đến Z

Docker từ Cơ bản đến Nâng cao

Phần 2: Dockerfile và Image Optimization

xdev.asia

1. Dockerfile là gì?

Dockerfile là một text file chứa tất cả các instructions (hướng dẫn) để Docker tự động build một image. Mỗi instruction tạo ra một layer trong image.

2. Tất cả Dockerfile Instructions

2.1. FROM - Base image

Mọi Dockerfile đều bắt đầu bằng FROM, xác định base image:

# Sử dụng image với tag cụ thể (recommended)
FROM node:20-alpine

Sử dụng digest cho reproducible builds

FROM node@sha256:abc123...

Multi-platform

FROM --platform=linux/amd64 node:20-alpine

Scratch - empty base (cho static binaries)

FROM scratch

2.2. RUN - Chạy commands

# Shell form (chạy qua /bin/sh -c)
RUN apt-get update && apt-get install -y curl

Exec form

RUN ["apt-get", "install", "-y", "curl"]

Best practice: Gộp nhiều RUN thành một để giảm layers

RUN apt-get update &&
apt-get install -y --no-install-recommends
curl
wget
git &&
rm -rf /var/lib/apt/lists/*

2.3. COPY vs ADD

# COPY - Copy files/directories từ build context
COPY package.json ./
COPY src/ ./src/
COPY --chown=node:node . .

ADD - Giống COPY nhưng thêm:

- Tự động extract tar archives

- Hỗ trợ URL (nhưng không recommended)

ADD archive.tar.gz /app/ ADD https://example.com/file.txt /tmp/ # Không nên dùng

Best practice: Luôn dùng COPY trừ khi cần extract tar

2.4. CMD vs ENTRYPOINT

# CMD - Default command khi container start
# Có thể bị override bởi docker run arguments
CMD ["node", "server.js"]           # Exec form (recommended)
CMD node server.js                   # Shell form

ENTRYPOINT - Command luôn được chạy

docker run arguments sẽ được append vào ENTRYPOINT

ENTRYPOINT ["node", "server.js"] # Exec form

Kết hợp ENTRYPOINT + CMD

ENTRYPOINT = executable, CMD = default arguments

ENTRYPOINT ["python", "manage.py"] CMD ["runserver", "0.0.0.0:8000"]

→ python manage.py runserver 0.0.0.0:8000

docker run myapp migrate

→ python manage.py migrate

CMD ENTRYPOINT
Override Dễ dàng với docker run args Cần --entrypoint flag
Use case Default command, có thể thay đổi Container luôn chạy cùng executable
Best for General purpose images Single-purpose containers

2.5. ENV - Environment Variables

# Set environment variables
ENV NODE_ENV=production
ENV APP_PORT=3000
ENV DB_HOST=localhost DB_PORT=5432

Sử dụng trong Dockerfile

RUN echo "Running in $NODE_ENV mode"

2.6. ARG - Build-time Variables

# Khai báo build argument
ARG NODE_VERSION=20
ARG APP_VERSION

Sử dụng ARG

FROM node:${NODE_VERSION}-alpine LABEL version=${APP_VERSION}

Build với arguments

docker build --build-arg NODE_VERSION=22 --build-arg APP_VERSION=1.0 .

ARG vs ENV:

  • ARG: Chỉ available trong build time, không tồn tại trong container

  • ENV: Available cả build time và runtime trong container

2.7. WORKDIR - Working Directory

# Set working directory
WORKDIR /app

WORKDIR tạo directory nếu chưa tồn tại

WORKDIR /app/src

Có thể dùng nhiều WORKDIR

WORKDIR /app COPY . . WORKDIR /app/client RUN npm install

2.8. EXPOSE - Declare ports

# Khai báo port (documentation only)
EXPOSE 3000
EXPOSE 80/tcp
EXPOSE 443/tcp

EXPOSE không tự publish port!

Vẫn cần -p flag khi docker run

2.9. VOLUME - Declare mount points

# Khai báo volume mount point
VOLUME /data
VOLUME ["/data", "/logs"]

2.10. USER - Set user

# Tạo non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

Switch sang non-root user

USER appuser

Hoặc dùng UID:GID

USER 1000:1000

2.11. HEALTHCHECK

# Health check cho container
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:3000/health || exit 1

Disable healthcheck

HEALTHCHECK NONE

2.12. LABEL - Metadata

LABEL maintainer="[email protected]"
LABEL version="1.0"
LABEL description="My awesome application"
LABEL org.opencontainers.image.source="https://github.com/user/repo"

2.13. SHELL - Change default shell

# Đổi shell mặc định (default: ["/bin/sh", "-c"])
SHELL ["/bin/bash", "-c"]
RUN echo "Now using bash"

2.14. STOPSIGNAL

# Signal để stop container (default: SIGTERM)
STOPSIGNAL SIGQUIT

3. .dockerignore

File .dockerignore loại bỏ files khỏi build context, giúp build nhanh hơn:

# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
Dockerfile
docker-compose*.yml
.env
.env.*
*.md
LICENSE
.vscode
.idea
coverage
.nyc_output
dist
build

4. Dockerfile Best Practices

4.1. Tận dụng layer caching

# ❌ Bad: Copy tất cả trước khi install dependencies
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm ci

✅ Good: Copy package.json trước, tận dụng cache

FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm ci COPY . .

4.2. Giảm số layers

# ❌ Bad: Nhiều RUN tạo nhiều layers
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y wget
RUN rm -rf /var/lib/apt/lists/*

✅ Good: Gộp thành 1 RUN

RUN apt-get update &&
apt-get install -y --no-install-recommends curl wget &&
rm -rf /var/lib/apt/lists/*

4.3. Chạy với non-root user

FROM node:20-alpine
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --chown=app:app . .
RUN npm ci --only=production
USER app
CMD ["node", "server.js"]

4.4. Sử dụng specific tags

# ❌ Bad: latest tag có thể thay đổi
FROM node:latest

✅ Good: Pin version cụ thể

FROM node:20.11-alpine3.19

5. Ví dụ tổng hợp

# Production Dockerfile cho Node.js
FROM node:20-alpine AS base
LABEL maintainer="[email protected]"

Install dependencies

FROM base AS deps WORKDIR /app COPY package*.json ./ RUN npm ci --only=production

Build

FROM base AS build WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build

Production

FROM base AS production RUN addgroup -S app && adduser -S app -G app WORKDIR /app

COPY --from=deps --chown=app:app /app/node_modules ./node_modules COPY --from=build --chown=app:app /app/dist ./dist COPY --chown=app:app package.json ./

ENV NODE_ENV=production EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=10s --retries=3
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

USER app CMD ["node", "dist/server.js"]

6. Tổng kết

Trong bài này, bạn đã nắm được:

  • Tất cả Dockerfile instructions và cách sử dụng

  • CMD vs ENTRYPOINT, COPY vs ADD, ARG vs ENV

  • .dockerignore để tối ưu build context

  • Best practices: layer caching, non-root user, specific tags

Bài tiếp theo sẽ hướng dẫn multi-stage builds và các kỹ thuật tối ưu Docker image.