Appearance
镜像优化与瘦身
镜像体积越小,拉取速度越快,部署越迅速,安全攻击面也越小。本节介绍系统性的镜像瘦身技巧。
分析镜像大小
查看基础信息
bash
# 查看镜像大小
docker images myapp
# 查看每一层的大小
docker history myapp
# 详细分层分析(需安装 dive)
dive myapp安装 dive 分析工具
dive 是一个交互式镜像层分析工具:
bash
# macOS
brew install dive
# Linux
curl -OL https://github.com/wagoodman/dive/releases/latest/download/dive_linux_amd64.tar.gz
tar -xzf dive_linux_amd64.tar.gz
sudo mv dive /usr/local/bin/
# 使用
dive myapp:latest优化策略一:选择更小的基础镜像
常见镜像大小对比
node:20 ~1.1GB
node:20-bullseye ~1.0GB
node:20-slim ~250MB
node:20-alpine ~60MBdockerfile
# 从 1.1GB 优化到 60MB,只改一行
FROM node:20-alpine # 而不是 FROM node:20Distroless 镜像
Google 维护的极简镜像,只包含运行时,不含 shell:
dockerfile
FROM golang:1.21 AS builder
# ... 构建过程 ...
FROM gcr.io/distroless/static:nonroot AS runtime
COPY --from=builder /app /app
USER nonroot
CMD ["/app"]优化策略二:多阶段构建
参考多阶段构建章节。核心原则:构建工具不进入最终镜像。
优化策略三:合并层并清理缓存
dockerfile
# 未优化:每层都有缓存残留
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
# 优化:合并 + 清理
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
curl \
vim \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*各包管理器的清理命令
dockerfile
# Alpine APK(--no-cache 直接跳过缓存)
RUN apk add --no-cache curl vim
# npm
RUN npm ci --only=production \
&& npm cache clean --force
# pip
RUN pip install --no-cache-dir -r requirements.txt
# Maven(清理本地仓库缓存)
RUN mvn package -DskipTests \
&& rm -rf /root/.m2/repository优化策略四:精简 .dockerignore
dockerignore
# 开发依赖(最大的节省)
node_modules/
vendor/
.venv/
__pycache__/
# 版本控制
.git/
.gitignore
.svn/
# 文档和测试
README*.md
docs/
tests/
*.test.js
*.spec.ts
# 构建产物(用于多阶段构建,不需要复制)
dist/
build/
out/
# 敏感文件
.env
.env.*
*.pem
*.key
# 日志
*.log
logs/
# IDE
.vscode/
.idea/
*.swp优化策略五:只复制必要文件
dockerfile
# 不推荐:复制所有文件
COPY . .
# 推荐:只复制必要的文件
COPY src/ ./src/
COPY package.json package-lock.json ./
COPY tsconfig.json ./
# 或者用 .dockerignore 排除不需要的文件,再 COPY . .优化策略六:使用 --squash 压缩层(实验性)
bash
# 将所有层压缩为一层(需要开启实验性功能)
docker build --squash -t myapp .实战:Node.js 镜像从 1.1GB 到 60MB
优化前:
dockerfile
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
EXPOSE 3000
CMD ["node", "app.js"]大小:约 1.1GB
优化后:
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
# 阶段三:运行时(只有 Alpine + 代码 + 依赖)
FROM node:20-alpine AS runtime
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --from=deps --chown=app:app /app/node_modules ./node_modules
COPY --from=builder --chown=app:app /app/dist ./dist
USER app
EXPOSE 3000
CMD ["node", "dist/app.js"]大小:约 60MB(节省 94%)
实战:Java 镜像优化
优化前:
dockerfile
FROM maven:3.9-eclipse-temurin-17
# ... 包含完整 JDK + Maven + 源码大小:约 800MB
优化后:
dockerfile
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
# 使用 JRE 而非 JDK(仅运行时)
FROM eclipse-temurin:17-jre-alpine AS runtime
WORKDIR /app
COPY --from=builder /build/target/app.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]大小:约 180MB(节省 77%)
镜像大小参考目标
| 应用类型 | 未优化 | 优化后 | 目标 |
|---|---|---|---|
| Node.js | 1.1GB | 60-100MB | < 100MB |
| Python | 1GB | 80-150MB | < 200MB |
| Java | 800MB | 150-250MB | < 300MB |
| Go | 800MB | 5-20MB | < 20MB |
| Nginx 静态 | 200MB | 20-50MB | < 50MB |
持续监控镜像大小
在 CI/CD 中加入镜像大小检查:
bash
#!/bin/bash
# check-image-size.sh
IMAGE="myapp:latest"
MAX_SIZE_MB=200
SIZE=$(docker image inspect $IMAGE --format='{{.Size}}')
SIZE_MB=$((SIZE / 1024 / 1024))
echo "Image size: ${SIZE_MB}MB"
if [ $SIZE_MB -gt $MAX_SIZE_MB ]; then
echo "ERROR: Image size ${SIZE_MB}MB exceeds limit ${MAX_SIZE_MB}MB"
exit 1
fi总结
镜像瘦身的优先级策略:
- 选择更小的基础镜像(Alpine/slim/distroless)—— 收益最大
- 多阶段构建,构建工具不进最终镜像
- 合并 RUN 层,安装后清理缓存
- 配置 .dockerignore,排除不必要文件
- 只复制必要文件
通过以上方法,大多数镜像可以减小 70-95% 的体积。