Appearance
镜像构建最佳实践
1. 选择合适的基础镜像
优先使用官方镜像
dockerfile
# 推荐:官方维护,安全更新及时
FROM node:20-alpine
FROM python:3.12-slim
FROM nginx:alpine
# 不推荐:非官方镜像,来源不明
FROM someuser/node-custom选择最小化基础镜像
| 镜像 | 大小 | 适用场景 |
|---|---|---|
ubuntu:22.04 | ~77MB | 需要完整系统工具 |
debian:slim | ~75MB | 轻量 Debian 环境 |
alpine:3.19 | ~7MB | 最小化场景 |
distroless | ~2MB | 安全生产环境 |
scratch | 0 | 静态编译程序 |
dockerfile
# 不推荐:体积大
FROM ubuntu:22.04
# 推荐:Alpine,体积小
FROM node:20-alpine
# 推荐:slim 版本
FROM python:3.12-slim固定镜像版本
dockerfile
# 不推荐:latest 标签可能引入破坏性变更
FROM node:latest
# 推荐:固定具体版本,确保可重现
FROM node:20.11.0-alpine3.19
FROM python:3.12.2-slim-bookworm2. 合理利用构建缓存
Docker 构建时,从变化的层开始重新执行,之前的层使用缓存。
将变化频率低的操作放前面
dockerfile
# 不推荐:代码变化会导致依赖重新安装
FROM node:20-alpine
WORKDIR /app
COPY . . # 代码经常变化
RUN npm install # 每次都重新安装
# 推荐:先复制依赖文件,再复制代码
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./ # 依赖变化频率低
RUN npm install # 依赖不变时使用缓存
COPY . . # 代码变化只影响这层及以后分离不同类型的文件
dockerfile
FROM python:3.12-slim
WORKDIR /app
# 依赖文件(变化少)
COPY requirements.txt .
RUN pip install -r requirements.txt
# 配置文件(偶尔变化)
COPY config/ ./config/
# 应用代码(经常变化)
COPY src/ ./src/3. 减少镜像层数
每条 RUN 指令创建一个新层,合并减少层数:
dockerfile
# 不推荐:多条 RUN 创建多个层
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
RUN rm -rf /var/lib/apt/lists/*
# 推荐:合并成一条 RUN
RUN apt-get update \
&& apt-get install -y \
curl \
vim \
&& rm -rf /var/lib/apt/lists/*4. 清理缓存减小镜像大小
安装软件后清理包管理器缓存:
dockerfile
# APT(Debian/Ubuntu)
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
curl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# YUM/DNF(CentOS/RHEL)
RUN yum install -y curl \
&& yum clean all \
&& rm -rf /var/cache/yum
# Alpine APK
RUN apk add --no-cache curl
# pip
RUN pip install --no-cache-dir -r requirements.txt
# npm
RUN npm ci --only=production && npm cache clean --force5. 使用 .dockerignore
# .dockerignore
.git/
.gitignore
node_modules/
dist/
build/
coverage/
*.log
.env
.env.local
.DS_Store
README.md
docs/
tests/
__pycache__/
*.pyc
*.pyo
.pytest_cache/
.venv/好处:
- 减少构建上下文大小,加快构建速度
- 避免敏感文件(.env)进入镜像
- 避免
node_modules等大目录不必要地复制
6. 以非 root 用户运行
dockerfile
# Alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# Debian/Ubuntu
RUN groupadd --system appgroup \
&& useradd --system --group appgroup appuser
USER appuser
# 使用已存在的用户(Node.js 官方镜像有 node 用户)
USER node7. 设置有意义的 LABEL
dockerfile
LABEL org.opencontainers.image.title="My Application" \
org.opencontainers.image.description="A brief description" \
org.opencontainers.image.version="1.0.0" \
org.opencontainers.image.authors="team@example.com" \
org.opencontainers.image.url="https://example.com" \
org.opencontainers.image.source="https://github.com/org/repo" \
org.opencontainers.image.created="2024-01-01T00:00:00Z"8. 正确使用 COPY 和 ADD
dockerfile
# 推荐:COPY 更透明
COPY package.json .
COPY src/ ./src/
# 仅在需要解压时用 ADD
ADD archive.tar.gz /app/
# 不推荐:用 ADD 下载 URL(难以缓存和验证)
ADD https://example.com/file.txt /app/
# 推荐替代方案:
RUN curl -fsSL https://example.com/file.txt -o /app/file.txt9. 优化 CMD 和 ENTRYPOINT
dockerfile
# 不推荐:shell 格式无法接收信号
CMD node app.js
# 推荐:exec 格式,可以正确接收 SIGTERM
CMD ["node", "app.js"]
# 推荐:使用 ENTRYPOINT + CMD 提供灵活性
ENTRYPOINT ["node"]
CMD ["app.js"]10. 添加健康检查
dockerfile
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1完整最佳实践示例
dockerfile
# 阶段一:安装依赖
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production \
&& npm cache clean --force
# 阶段二:构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 阶段三:生产镜像
FROM node:20-alpine AS production
LABEL org.opencontainers.image.title="My App" \
org.opencontainers.image.version="1.0.0"
ENV NODE_ENV=production \
PORT=3000
# 创建非 root 用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
# 从前面的阶段复制
COPY --from=deps --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
# 只复制需要的文件
COPY --chown=appuser:appgroup package.json .
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/main.js"]检查镜像质量
bash
# 查看镜像大小
docker images myapp
# 查看镜像层详情
docker history --no-trunc myapp
# 使用 dive 工具分析镜像(需单独安装)
dive myapp
# 使用 docker scout 扫描漏洞(Docker Desktop 内置)
docker scout cves myapp总结
镜像构建最佳实践清单:
- [ ] 使用官方基础镜像,固定具体版本
- [ ] 优先选择 alpine 或 slim 版本
- [ ] 使用多阶段构建分离构建和运行环境
- [ ] 合理安排指令顺序,最大化缓存利用
- [ ] 合并 RUN 指令,安装后清理缓存
- [ ] 配置 .dockerignore 排除无关文件
- [ ] 使用非 root 用户运行应用
- [ ] 添加有意义的 LABEL
- [ ] CMD/ENTRYPOINT 使用 exec 格式
- [ ] 添加 HEALTHCHECK
下一节介绍镜像优化与瘦身的进阶技巧。