第三部分-Dockerfile与镜像构建——13. Dockerfile 最佳实践
13. Dockerfile 最佳实践
1. 最佳实践概述
遵循 Dockerfile 最佳实践可以构建出更小、更安全、更高效的镜像,提升构建速度,减少部署时间和安全风险。
┌─────────────────────────────────────────────────────────────┐ │ Dockerfile 最佳实践金字塔 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 顶部 │ │ ┌─────────────┐ │ │ │ 安全 │ ← 安全加固 │ │ ┌──┴─────────────┴──┐ │ │ │ 可维护 │ ← 可读性、可维护 │ │ ┌──┴───────────────────┴──┐ │ │ │ 性能优化 │ ← 层缓存、并行构建 │ │ ┌──┴─────────────────────────┴──┐ │ │ │ 镜像体积优化 │ ← 多阶段构建、清理│ │ ┌──┴───────────────────────────────┴──┐ │ │ │ 基础原则 │ ← 单个进程、稳定│ │ └─────────────────────────────────────┘ │ │ 底部 │ └─────────────────────────────────────────────────────────────┘2. 基础原则
2.1 容器应该是短暂的
# ✅ 好:容器可以随时停止和销毁 # 应用状态应该存储在外部(数据库、存储卷) # ❌ 不好:容器内存储状态 # 不要将数据存储在容器可写层2.2 一个容器只运行一个进程
# ✅ 好:单一进程 FROM node:14-alpine CMD ["node", "app.js"] # ❌ 不好:多个进程 FROM ubuntu CMD ["sh", "-c", "nginx && node app.js"] # 应使用 docker-compose 管理多个进程2.3 使用具体标签,避免 latest
# ✅ 好:指定具体版本 FROM node:14.17.0-alpine FROM python:3.9.7-slim # ❌ 不好:使用 latest FROM node:latest FROM python:latest3. 镜像体积优化
3.1 选择合适的基础镜像
# 镜像大小对比(约) # ubuntu:20.04 → 72MB # debian:11-slim → 40MB # alpine:3.14 → 5.6MB # scratch → 0MB # ✅ 推荐:Alpine Linux FROM alpine:3.14 # 安装软件包 RUN apk add --no-cache nginx # ✅ 使用 slim 版本 FROM python:3.9-slim FROM node:14-slim # ✅ 对于静态编译的应用,使用 scratch FROM scratch COPY myapp /myapp CMD ["/myapp"]3.2 合并 RUN 命令
# ❌ 不好:多层 RUN apt-get update RUN apt-get install -y curl RUN apt-get install -y nginx RUN apt-get clean # ✅ 好:合并为单层 RUN apt-get update && \ apt-get install -y curl nginx && \ apt-get clean && \ rm -rf /var/lib/apt/lists/*3.3 清理缓存和临时文件
# ✅ 好:清理缓存 RUN apk add --no-cache nginx # ✅ 好:删除临时文件 RUN apt-get update && \ apt-get install -y build-essential && \ make && \ make install && \ apt-get remove -y build-essential && \ apt-get autoremove -y && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*4. 构建缓存优化
4.1 层缓存原则
# ✅ 好:变化最少的指令放在前面 FROM node:14-alpine # 先复制依赖文件(变化少) WORKDIR /app COPY package*.json ./ # 安装依赖(只有 package.json 变化才重新安装) RUN npm ci --only=production # 最后复制源代码(变化频繁) COPY . . CMD ["node", "app.js"]4.2 利用构建参数缓存
# ✅ 好:使用 ARG 控制缓存失效 FROM node:14-alpine ARG NPM_TOKEN ARG BUILD_DATE # npm token 变化不会影响前面层 COPY package.json ./ # RUN 变化 RUN if [ "$NPM_TOKEN" != "" ]; then \ echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc; \ fi && \ npm install && \ rm -f .npmrc COPY . . # 构建时传入 # docker build --build-arg NPM_TOKEN=xxx .4.3 指定构建上下文
# .dockerignore # 排除无关文件 node_modules .git *.log .DS_Store coverage dist .env # 减少上下文大小,加速构建5. 多阶段构建
# ✅ 好:多阶段构建分离编译和运行环境 # 阶段1:编译阶段 FROM golang:1.17-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o main . # 阶段2:运行阶段 FROM alpine:latest RUN apk --no-cache add ca-certificates COPY --from=builder /app/main /app/main ENTRYPOINT ["/app/main"]6. 安全最佳实践
6.1 使用非 root 用户
# ✅ 好:创建并切换到非 root 用户 FROM node:14-alpine # 创建应用用户 RUN addgroup -g 1000 -S nodejs && \ adduser -S nodejs -u 1000 # 设置目录权限 COPY --chown=nodejs:nodejs . /app WORKDIR /app # 切换到非 root 用户 USER nodejs CMD ["node", "app.js"]6.2 安全配置
# ❌ 不好:使用 root 用户 USER root # ✅ 好:指定安全的用户 USER nodejs # ✅ 好:只读文件系统配合 RUN chmod -R 755 /app && \ chown -R nodejs:nodejs /app # ✅ 好:删除不必要的 SUID/SGID RUN find / -type f \( -perm /6000 -o -perm /2000 \) -exec chmod 000 {} \; 2>/dev/null6.3 安全扫描
# 使用 Docker Scan 扫描镜像漏洞dockerscan myapp:latest# 使用 Trivytrivy image myapp:latest# 使用 Clairclair-scanner myapp:latest7. 可维护性
7.1 使用 LABEL
# ✅ 好:添加元数据标签 LABEL maintainer="team@example.com" \ version="1.0.0" \ description="My application" \ build-date="${BUILD_DATE}" \ vcs-url="https://github.com/example/myapp"7.2 使用 ARG 和 ENV
# ✅ 好:集中管理配置 ARG NODE_VERSION=14.17.0 ARG APP_HOME=/app FROM node:${NODE_VERSION}-alpine ENV NODE_ENV=production \ APP_PORT=8080 \ APP_HOME=/app WORKDIR ${APP_HOME}7.3 添加健康检查
# ✅ 好:配置健康检查 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8080/health || exit 1 # 或使用 wget HEALTHCHECK --interval=30s \ CMD wget --quiet --tries=1 --spider http://localhost:8080/health || exit 18. 开发环境 vs 生产环境
8.1 开发环境 Dockerfile
# Dockerfile.dev FROM node:14-alpine WORKDIR /app # 安装所有依赖 COPY package*.json ./ RUN npm install # 热重载支持 COPY . . EXPOSE 3000 # 开发命令 CMD ["npm", "run", "dev"]8.2 生产环境 Dockerfile
# Dockerfile.prod # 构建阶段 FROM node:14-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . RUN npm run build # 运行阶段 FROM node:14-alpine RUN addgroup -g 1000 -S nodejs && \ adduser -S nodejs -u 1000 WORKDIR /app COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules COPY package.json ./ USER nodejs EXPOSE 3000 HEALTHCHECK --interval=30s CMD node healthcheck.js CMD ["node", "dist/server.js"]9. 性能优化技巧
9.1 并行构建
# 使用 BuildKit 并行构建DOCKER_BUILDKIT=1dockerbuild.# 启用并行下载和构建dockerbuild--progress=plain --build-argBUILDKIT_INLINE_CACHE=1.9.2 缓存挂载
# 使用 --mount=type=cache(BuildKit 特性) # syntax = docker/dockerfile:1.2 FROM node:14-alpine WORKDIR /app # 缓存 node_modules RUN --mount=type=cache,target=/app/node_modules \ --mount=type=cache,target=/root/.npm \ npm install COPY . . CMD ["node", "app.js"]10. 检查清单
## Dockerfile 检查清单 - [ ] 使用具体版本标签,避免 latest - [ ] 选择合适的轻量级基础镜像(alpine/slim) - [ ] 合并 RUN 命令减少层数 - [ ] 清理 apt/apk 缓存 - [ ] 删除临时文件 - [ ] 配置 .dockerignore - [ ] 使用多阶段构建 - [ ] 使用非 root 用户运行 - [ ] 添加健康检查 - [ ] 添加 LABEL 元数据 - [ ] 利用构建缓存(顺序优化) - [ ] 固定依赖版本 - [ ] 扫描镜像漏洞 - [ ] 测试镜像 - [ ] 文档化构建参数11. 常见反模式
| 反模式 | 问题 | 解决方案 |
|---|---|---|
使用:latest标签 | 不可复现的构建 | 使用具体版本 |
| 在容器中存储数据 | 数据丢失风险 | 使用 Volume |
| 在一层中安装所有包 | 缓存失效 | 分离依赖和代码 |
| 使用 root 用户运行 | 安全风险 | 创建应用用户 |
| 忽略缓存 | 构建缓慢 | 优化层顺序 |
12. 小结
- 短暂容器:应用无状态
- 单进程:一个容器一个进程
- 具体标签:避免 latest
- 镜像体积:使用 alpine/slim,多阶段构建
- 构建缓存:稳定指令在前,经常变动的指令在后
- 安全加固:非 root 用户,健康检查
- 可维护性:LABEL、ARG、ENV、注释
- 开发/生产分离:不同 Dockerfile 或构建参数
