15.3.6 镜像优化与瘦身实践

镜像优化与瘦身的目标是在保证功能完整、可维护与可复现的前提下,尽量减少镜像体积、层数与攻击面,提高拉取与部署效率。实践中需要从基础镜像选择、多阶段构建、依赖裁剪、分层策略与运行时配置多维度入手,并通过量化验证形成可持续的优化流程。

原理草图(多阶段构建与分层):

文章图片

基础镜像选择与多阶段构建示例(Go 应用):

# 文件: Dockerfile
# 构建阶段:包含编译工具链
FROM golang:1.21-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# 说明:CGO_ENABLED=0 生成静态二进制,减少动态库依赖
RUN CGO_ENABLED=0 GOOS=linux go build -o /out/app ./cmd/app

# 运行阶段:使用最小化镜像
FROM gcr.io/distroless/static:nonroot
WORKDIR /app
COPY --from=builder /out/app /app/app
USER nonroot
ENTRYPOINT ["/app/app"]

构建与体积对比命令(含解释与预期效果):

# 构建镜像
docker build -t demo:slim .

# 查看镜像体积(关注 SIZE)
docker images | grep demo

# 查看分层与每层大小
docker history demo:slim
# 预期效果:层数减少、总大小显著低于包含编译工具链的镜像

层数与缓存控制示例(合并命令与清理缓存):

# 文件: Dockerfile片段(Debian/Ubuntu)
RUN apt-get update \
 && apt-get install -y --no-install-recommends curl ca-certificates \
 && rm -rf /var/lib/apt/lists/*
# 说明:
# 1) 合并指令减少层数
# 2) --no-install-recommends 避免多余依赖
# 3) 删除 apt 缓存目录,减少体积

依赖裁剪示例(Node.js 仅保留生产依赖):

# 文件: Dockerfile片段
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm ci --only=production
CMD ["node", "dist/index.js"]

.dockerignore 优化示例(避免无关文件进入镜像):

# 文件: .dockerignore
.git
node_modules
dist
*.log
tmp/

运行时安全与最小权限示例:

# 文件: Dockerfile片段
RUN adduser -D appuser
USER appuser
# 说明:使用非 root 运行降低攻击面

镜像分析与瘦身工具安装示例(dive):

# Linux 安装(基于包管理器)
# Debian/Ubuntu
sudo apt-get update && sudo apt-get install -y dive

# 分析镜像层与文件变化
dive demo:slim
# 预期效果:定位最大层与重复文件,指导进一步瘦身

常见问题与排错(含命令):
1) 体积仍过大:

# 查看哪些层最大
docker history --no-trunc demo:slim
# 查看镜像内文件占用
docker run --rm -it demo:slim /bin/sh -c "du -sh /* 2>/dev/null | sort -h"
# 说明:定位大目录(如 /usr/local、/var/cache),针对性清理

2) 缓存导致旧依赖残留:

# 强制不使用缓存构建
docker build --no-cache -t demo:slim .
# 清理构建缓存与无用镜像
docker builder prune -f
docker image prune -f

3) 多阶段构建中找不到产物:

# 进入构建阶段容器排查产物路径
docker build --target builder -t demo:builder .
docker run --rm -it demo:builder /bin/sh -c "ls -la /out"

量化验证与门禁建议(CI 中可复用):

# 计算镜像大小(字节),作为阈值判断
SIZE=$(docker image inspect demo:slim --format '{{.Size}}')
echo "Image size: $SIZE bytes"
# 预期:在 CI 中设置阈值,例如 < 200MB

综合实践清单:
- 统一基础镜像版本(避免漂移)。
- 多阶段构建分离构建与运行。
- 合并命令、清理缓存与临时文件。
- 严格 .dockerignore,避免冗余文件。
- 使用非 root 用户与最小权限。
- 在 CI 中引入镜像大小与安全扫描门禁。

练习:
1) 将一个包含编译工具链的单阶段 Dockerfile 改为多阶段构建,比较 docker images 中的 SIZE 变化。
2) 使用 dive 找出最大层并优化,记录优化前后体积对比。
3) 编写 .dockerignore,验证构建上下文大小是否下降(docker build 输出中的 Sending build context)。