OFFSET偏移
← All articlesOFFSET / FIELD NOTESNO. 112

Dockerfiles and image builds

Organize image recipes around entry commands, multi-stage builds, and cache optimization.

English translation · Code and commands follow the Chinese original.

IN THESE READING PATHSDocker: from running containers to application orchestration ↗
About 4 min readStart reading

A Dockerfile is a container’s "recipe". Writing good Dockerfiles is fundamental to using containers efficiently.


Dockerfile basics#

Your first Dockerfile

# 基于什么镜像开始
FROM python:3.12-slim

# 设置工作目录(后续命令都在这个目录执行)
WORKDIR /app

# 复制依赖文件(先复制 requirements 是为了利用层缓存)
COPY requirements.txt .

# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY . .

# 声明容器运行时监听的端口(文档性质,不影响运行)
EXPOSE 8000

# 容器启动时执行的命令
CMD ["python", "app.py"]

Building and running

# 构建镜像
docker build -t myapp:1.0 .
#           └─┘ └─────┘ └─┘
#        打标签   名称:版本   构建上下文(此例为当前目录;Dockerfile 可由 -f 另选)

# 运行
docker run -d -p 8000:8000 myapp:1.0

Core instruction quick reference#

Instruction Purpose Example
FROM Specifies the base image for a stage; global ARG instructions and parser directives may appear before the first FROM FROM node:20-alpine
WORKDIR Sets the working directory, creating it automatically if it does not exist WORKDIR /app
COPY Copies from the build context or a specified stage/image/context COPY . /app/
ADD Similar to COPY, with additional support for URL downloads and automatic tar extraction ADD archive.tar.gz /app/
RUN Runs commands while building the image RUN apt update && apt install -y curl
ENV Sets environment variables (available at both build time and runtime) ENV NODE_ENV=production
ARG Build arguments (available only at build time) ARG VERSION=1.0
EXPOSE Declares a port (documentation only; actual publishing requires -p) EXPOSE 3000
CMD The command executed when the container starts (can be overridden by arguments at the end of docker run) CMD ["node", "server.js"]
ENTRYPOINT The container entry point (can be overridden with --entrypoint; exec-form CMD usually provides default arguments) ENTRYPOINT ["python"]
USER Switches the execution user (security hardening) USER appuser
HEALTHCHECK Health check command HEALTHCHECK --interval=30s CMD curl -f http://localhost/ || exit 1

CMD vs ENTRYPOINT

# 方式 1:只有 CMD(可被覆盖)
CMD ["python", "app.py"]
# docker run myapp node server.js  ← 会覆盖 CMD

# 方式 2:ENTRYPOINT + CMD(可用 --entrypoint 覆盖 ENTRYPOINT,CMD 作为默认参数)
ENTRYPOINT ["python"]
CMD ["app.py"]
# docker run myapp --version  ← 相当于 python --version

# 方式 3:ENTRYPOINT 执行脚本
ENTRYPOINT ["/docker-entrypoint.sh"]
# 脚本可以做初始化(数据库迁移、权限设置),最后 exec "$@" 启动 CMD

Multi-stage builds#

The following snippets are independent examples based on each sample project’s requirements/package-lock and entry-point files. They cannot simply be concatenated into a single general-purpose image.

Goal: Use a large image containing compilation tools for the build stage, and a small image containing only the runtime for the final artifact.

Single-stage (poor)

FROM node:20
WORKDIR /app
COPY . .
RUN npm ci && npm run build
CMD ["node", "dist/index.js"]
# 最终镜像包含构建依赖和源码,具体大小以实际构建测量

Multi-stage (better)

# 阶段 1:构建(build stage)
FROM node:20 AS builder
WORKDIR /app
COPY package*.json .
RUN npm ci
COPY . .
RUN npm run build

# 阶段 2:运行(production stage)
FROM node:20-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json .
RUN npm ci --omit=dev
USER node
CMD ["node", "dist/index.js"]
# 最终阶段只装生产依赖和运行产物;需验证本机模块 ABI 与运行需求

Effect: This can reduce build tools and unrelated files. Check the actual image size, vulnerability count, and runtime compatibility after building.


Optimizing the layer cache#

Dockerfile instructions create build steps and metadata; not every instruction creates a filesystem layer. RUN/COPY and similar instructions usually change the filesystem. Cache hits depend on the instructions, context, and dependency inputs.

Layer cache rules

FROM python:3.12-slim        # 层 1
WORKDIR /app                 # 层 2
COPY requirements.txt .      # 层 3  ← 如果 requirements.txt 没变,这层用缓存
RUN pip install -r requirements.txt  # 层 4  ← 依赖不装在上层,也用缓存
COPY . .                     # 层 5  ← 源代码变了,这层和后面都重新构建
CMD ["python", "app.py"]     # 层 6

Key principle: Put infrequently changing content first and frequently changing content last.

# ✅ 好:先 COPY 依赖文件(不常变),再 COPY 代码(常变)
COPY package.json package-lock.json .
RUN npm ci
COPY . .

# ❌ 差:先 COPY 全部(轻微改动就废掉所有缓存)
COPY . .
RUN npm ci

Parallel builds

# 启用 BuildKit(Docker 23+ 默认, 更老版本需要设置)
export DOCKER_BUILDKIT=1

# BuildKit 可并行不相互依赖的阶段/构建图节点;同阶段有先后依赖的 RUN 仍顺序执行
docker build -t myapp .

Optimization tips at a glance#

# 1. 合并 RUN 减少层数(用 && 连接)
RUN apt-get update && apt-get install -y curl vim && rm -rf /var/lib/apt/lists/*

# 2. 使用 .dockerignore 排除不需要的文件
# .dockerignore:
# node_modules
# .git
# *.md
# .env

# 3. 指定非 root 用户
RUN useradd -m appuser
USER appuser

# 4. 用 Alpine 等精简镜像(需验证 musl、原生依赖与调试工具兼容性)
FROM node:20-alpine  # 大小依版本及架构
# vs FROM node:20     # 实际大小需检查

# 5. 用 slim 变体(平衡大小和兼容性)
FROM python:3.12-slim  # 大小依版本及架构
# vs FROM python:3.12   # 实际大小需检查

# 6. 明确版本;示例版本是历史示意,标签仍可变,生产可进一步锁 digest
FROM node:20.11.1-alpine3.19
# 而不是 FROM node:latest(今天和明天拉的镜像可能不同)

Build secrets and entry-point behavior#

ARG/ENV, COPY, and ordinary RUN instructions cannot safely hold secrets; deleting a file from an image does not guarantee that earlier layers contain no secrets. Use BuildKit secret/SSH mounts for build credentials, and check that build commands do not write them into artifacts. Use an exec-form entry point or exec "$@" at the end of an initialization script to forward stop signals correctly. The example image tags are teaching inputs, not recommendations for currently supported versions.

References & further reading

  1. Dockerfile instructions ↗
  2. Multi-stage builds ↗
  3. Build secrets ↗

Adapted from technical notes. Examples use generic names.

Continue along a reading path

Docker: from running containers to application orchestration

  1. 01Docker basics: images, containers, and the daemon
  2. 02The Docker command line: images, containers, volumes, and networks
  3. 03Dockerfiles and image builds
  4. 04Compose: defining multi-service applications
  5. 05Docker networking and storage
  6. 06Docker in production: resources, security, and recovery
  7. 07Declaring Docker and containers on NixOS
  8. 08Swarm: from service replicas to cluster orchestration
  9. 09Docker and Podman: architecture, compatibility, and coexistence
CONTINUE READING / NEXT ARTICLECompose: defining multi-service applications

Manage single-host applications with services, dependencies, profiles, watch, and secrets.