Skip to content

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元数据maintainerorg.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 .

镜像瘦身技巧

  1. 选择合适的基础镜像:优先 alpineslimdistroless
  2. 多阶段构建:编译与运行分离。
  3. 合并 RUN 层RUN apt-get update && apt-get install -y xxx && rm -rf /var/lib/apt/lists/*
  4. 清理包缓存:删除 /var/lib/apt/lists/*~/.cachenode_modules 中 dev 依赖。
  5. 使用 .dockerignore:避免把 .gitnode_modules、本地日志打入上下文。
  6. 最小化层数:非必要的独立指令合并。

查看镜像大小:

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 1000USER nonroot)。
  • 使用 HEALTHCHECK 暴露真实健康状态。
  • 固定基础镜像 digest(FROM node:20-alpine@sha256:xxxx)以保证可复现。
  • 扫描镜像漏洞(见 Docker 安全)。

最佳实践

  1. 一个容器只跑一个进程(PID 1)。
  2. exec 形式(["cmd", "arg"])而非 shell 形式,便于信号处理。
  3. 生产镜像打语义化 tag(v1.2.32026-08-28-abcdef),避免 latest
  4. 把可变数据写入 VOLUME 或挂载,避免写入可写层。