OFFSET偏移
← 文章目录OFFSET / FIELD NOTESNO. 112

Dockerfile 与镜像构建

从入口命令、多阶段构建与缓存优化组织镜像配方。

所在专题Docker:从容器运行到应用编排 ↗
约 9 分钟阅读开始阅读

Dockerfile 是容器的"配方"。写好 Dockerfile,是高效使用容器的基础。


Dockerfile 基础#

第一个 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"]

构建与运行

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

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

核心指令速查#

指令 作用 示例
FROM 指定阶段基础镜像;全局 ARG 和 parser directives 可出现在首个 FROM 前 FROM node:20-alpine
WORKDIR 设置工作目录,不存在则自动创建 WORKDIR /app
COPY 从构建上下文或指定 stage/image/context 复制 COPY . /app/
ADD 类似 COPY,还支持 URL 下载和自动解压 tar ADD archive.tar.gz /app/
RUN 在镜像构建时执行命令 RUN apt update && apt install -y curl
ENV 设置环境变量(构建时和运行时都生效) ENV NODE_ENV=production
ARG 构建参数(只在构建时可用) ARG VERSION=1.0
EXPOSE 声明端口(文档性质,实际映射需 -p) EXPOSE 3000
CMD 容器启动时执行的命令(可被 docker run 末尾参数覆盖) CMD ["node", "server.js"]
ENTRYPOINT 容器入口程序(可用 --entrypoint 覆盖;exec 形式 CMD 通常作默认参数) ENTRYPOINT ["python"]
USER 切换运行用户(安全加固) USER appuser
HEALTHCHECK 健康检查命令 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 Build)#

以下片段彼此独立,基于示例项目的 requirements/package-lock 与入口文件;不能直接拼接成一个通用镜像。

目标:构建阶段用大镜像(含编译工具),最终产物用小镜像(只含运行时)。

单阶段(差)

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

多阶段(好)

# 阶段 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 与运行需求

效果:可减少构建工具和无关文件,实际体积、漏洞数量及运行兼容性需构建后检查。


层缓存优化#

Dockerfile 指令形成构建步骤与元数据,不是每条都生成文件系统层;RUN/COPY 等通常改变文件系统。缓存命中取决于指令、上下文及依赖输入。

层缓存规则

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

关键原则:把不常变的放前面,常变的放后面。

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

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

并行构建

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

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

优化技巧速查#

# 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(今天和明天拉的镜像可能不同)

构建秘密与入口行为#

ARG/ENV、COPY 和普通 RUN 都不能安全保存秘密;删除镜像中的文件也不保证旧层无秘密。构建凭据采用 BuildKit secret/SSH mounts,并检查是否被构建命令写入产物。用 exec 形式入口或初始化脚本末尾的 exec "$@" 正确传递停止信号。示例镜像标签是教学输入,并非当前受支持版本推荐。

参考与延伸

  1. Dockerfile 指令 ↗
  2. 多阶段构建 ↗
  3. 构建 secrets ↗

根据技术笔记重新整理,示例使用通用名称。

沿专题继续阅读

Docker:从容器运行到应用编排

  1. 01Docker 基础:镜像、容器与守护进程
  2. 02Docker 命令行:镜像、容器、卷与网络
  3. 03Dockerfile 与镜像构建
  4. 04Compose:定义多服务应用
  5. 05Docker 网络与存储
  6. 06Docker 生产实践:资源、安全与恢复
  7. 07NixOS 上声明 Docker 与容器
  8. 08Swarm:从服务副本到集群编排
  9. 09Docker 与 Podman:架构、兼容与共存
CONTINUE READING / 下一篇Compose:定义多服务应用

用服务、依赖、profiles、watch 和 secrets 管理单机应用。