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-alpineSử 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 curlExec 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 formENTRYPOINT - 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=5432Sử 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_VERSIONSử 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 /appWORKDIR 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/tcpEXPOSE 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 appgroupSwitch 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 1Disable 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.