主题
Dockerfile 编写
Dockerfile 是构建镜像的蓝图。本文详解常用指令、多阶段构建、镜像瘦身与缓存优化技巧。
基础结构
dockerfile
# 指定基础镜像
FROM node:20-alpine
# 设置工作目录
WORKDIR /app
# 拷贝依赖清单
COPY package*.json ./
# 安装依赖(利用缓存)
RUN npm ci --omit=dev
# 拷贝源码
COPY . .
# 暴露端口
EXPOSE 3000
# 启动命令
CMD ["node", "server.js"]常用指令
| 指令 | 作用 | 说明 |
|---|---|---|
FROM | 基础镜像 | 必须是第一条非注释指令;FROM scratch 表示空镜像 |
WORKDIR | 设置工作目录 | 不存在则自动创建,推荐使用绝对路径 |
COPY | 拷贝文件 | 推荐用 COPY 而非 ADD(ADD 会自动解压 tar/远程下载,行为隐式) |
ADD | 拷贝并解压/下载 | 仅在需要自动解压 tar 时使用 |
RUN | 执行命令并提交层 | 多条命令用 && 合并减少层数 |
ENV | 设置环境变量 | 可被后续指令及运行中的容器读取 |
ARG | 构建时变量 | 仅在 build 阶段有效,不进入镜像 |
EXPOSE | 声明端口 | 文档性声明,实际映射仍需 -p |
VOLUME | 声明挂载点 | 防止写入容器可写层 |
USER | 切换用户 | 安全性关键,见 Docker 安全 |
HEALTHCHECK | 容器健康检查 | 配合编排工具做存活判断 |
ENTRYPOINT | 容器入口 | 与 CMD 组合可传参;ENTRYPOINT 不可被 run 参数覆盖 |
CMD | 默认命令/参数 | 可被 docker run 参数覆盖 |
LABEL | 元数据 | 如 maintainer、org.opencontainers.image.source |
.dockerignore | 排除文件 | 类似 .gitignore,减少构建上下文 |
ENTRYPOINT 与 CMD 的组合
dockerfile
ENTRYPOINT ["/bin/app"]
CMD ["--port", "8080"]
# 运行 docker run img --port 9090 会覆盖 CMD多阶段构建
多阶段构建能在同一 Dockerfile 中使用多个 FROM,仅把最终需要的产物拷入精简镜像,显著减小体积。
dockerfile
# ---- 构建阶段 ----
FROM golang:1.22 AS builder
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server .
# ---- 运行阶段 ----
FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/server /server
EXPOSE 8080
USER nonroot:nonroot
ENTRYPOINT ["/server"]构建时可用 --target 指定阶段(常用于 CI 中单独跑测试阶段):
bash
docker build --target builder -t myapp:test .镜像瘦身技巧
- 选择合适的基础镜像:优先
alpine、slim、distroless。 - 多阶段构建:编译与运行分离。
- 合并 RUN 层:
RUN apt-get update && apt-get install -y xxx && rm -rf /var/lib/apt/lists/*。 - 清理包缓存:删除
/var/lib/apt/lists/*、~/.cache、node_modules中 dev 依赖。 - 使用
.dockerignore:避免把.git、node_modules、本地日志打入上下文。 - 最小化层数:非必要的独立指令合并。
查看镜像大小:
bash
docker images
docker history myapp:latest缓存优化
Docker 按层缓存,指令顺序影响缓存命中率。原则:变动频率低的指令放前面。
dockerfile
# 先拷依赖清单并安装(依赖变动少,缓存命中高)
COPY package*.json ./
RUN npm install
# 再拷源码(源码变动频繁,仅使这一层及之后失效)
COPY . .构建参数联动缓存:
bash
docker build --build-arg NODE_ENV=production -t myapp .禁用缓存:
bash
docker build --no-cache -t myapp .安全基线
- 尽量以非 root 用户运行(
USER 1000或USER nonroot)。 - 使用
HEALTHCHECK暴露真实健康状态。 - 固定基础镜像 digest(
FROM node:20-alpine@sha256:xxxx)以保证可复现。 - 扫描镜像漏洞(见 Docker 安全)。
最佳实践
- 一个容器只跑一个进程(PID 1)。
- 用
exec形式(["cmd", "arg"])而非 shell 形式,便于信号处理。 - 生产镜像打语义化 tag(
v1.2.3、2026-08-28-abcdef),避免latest。 - 把可变数据写入
VOLUME或挂载,避免写入可写层。