镜像构建与优化 #
镜像构建基础 #
docker build命令 #
bash
# 基本构建
docker build -t myapp:v1.0 .
# 指定Dockerfile路径
docker build -t myapp:v1.0 -f Dockerfile.prod .
# 指定构建上下文
docker build -t myapp:v1.0 /path/to/context
# 从URL构建
docker build -t myapp:v1.0 https://github.com/user/repo.git
# 从标准输入构建
cat Dockerfile | docker build -t myapp:v1.0 -
构建参数 #
bash
# 传递构建参数
docker build --build-arg VERSION=1.0.0 -t myapp:v1.0 .
# 指定目标阶段
docker build --target builder -t myapp:builder .
# 不使用缓存
docker build --no-cache -t myapp:v1.0 .
# 指定平台
docker build --platform linux/amd64 -t myapp:v1.0 .
BuildKit #
启用BuildKit #
bash
# 临时启用
DOCKER_BUILDKIT=1 docker build -t myapp:v1.0 .
# 永久启用(添加到 ~/.bashrc 或 ~/.zshrc)
export DOCKER_BUILDKIT=1
# 或在Docker Desktop中启用
# Settings > Docker Engine > 添加 "features": {"buildkit": true}
BuildKit优势 #
| 特性 | 说明 |
|---|---|
| 并行构建 | 独立层可并行构建 |
| 增量构建 | 只重建变化的部分 |
| 缓存优化 | 更智能的缓存策略 |
| 安全增强 | 支持secrets挂载 |
| 输出格式 | 支持多种输出格式 |
BuildKit示例 #
dockerfile
# syntax=docker/dockerfile:1
FROM alpine AS base
RUN apk add --no-cache curl
FROM base AS builder
COPY src/ /src/
RUN build-script.sh
FROM base AS production
COPY --from=builder /app /app
CMD ["./app"]
多阶段构建 #
基本概念 #
多阶段构建允许在一个Dockerfile中使用多个FROM语句,每个FROM开始一个新的构建阶段。
text
┌─────────────────────────────────────────────────────┐
│ 多阶段构建 │
├─────────────────────────────────────────────────────┤
│ │
│ Stage 1 (builder): │
│ ┌─────────────────────────────────────────────┐ │
│ │ FROM golang:1.20 AS builder │ │
│ │ WORKDIR /app │ │
│ │ COPY . . │ │
│ │ RUN go build -o myapp │ │
│ └─────────────────────────────────────────────┘ │
│ │ │
│ ↓ COPY --from=builder │
│ │
│ Stage 2 (production): │
│ ┌─────────────────────────────────────────────┐ │
│ │ FROM alpine:3.18 │ │
│ │ COPY --from=builder /app/myapp /app/ │ │
│ │ CMD ["./myapp"] │ │
│ └─────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────┘
Go应用示例 #
dockerfile
# 构建阶段
FROM golang:1.20-alpine AS builder
WORKDIR /app
# 复制依赖文件
COPY go.mod go.sum ./
RUN go mod download
# 复制源代码并构建
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
# 运行阶段
FROM alpine:3.18
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /root/
# 从构建阶段复制二进制文件
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]
前端应用示例 #
dockerfile
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 生产阶段
FROM nginx:alpine
# 复制构建产物
COPY --from=builder /app/dist /usr/share/nginx/html
# 复制nginx配置
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
选择特定阶段构建 #
bash
# 只构建到builder阶段
docker build --target builder -t myapp:builder .
# 构建最终镜像
docker build -t myapp:v1.0 .
镜像优化技巧 #
1. 选择合适的基础镜像 #
dockerfile
# 不推荐: 完整镜像
FROM ubuntu:22.04 # ~77MB
# 推荐: slim镜像
FROM python:3.11-slim # ~150MB
# 更推荐: alpine镜像
FROM python:3.11-alpine # ~50MB
# 最小: scratch(空镜像)
FROM scratch # 0MB
2. 减少镜像层数 #
dockerfile
# 不推荐: 多个RUN命令
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
RUN apt-get clean
# 推荐: 合并命令
RUN apt-get update && apt-get install -y \
curl \
vim \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
3. 利用构建缓存 #
dockerfile
# 不推荐: 先复制所有文件
COPY . /app
RUN npm install
# 推荐: 先复制依赖文件
COPY package*.json ./
RUN npm install
COPY . .
4. 清理不必要的文件 #
dockerfile
RUN apt-get update && apt-get install -y \
curl \
vim \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /tmp/* \
&& rm -rf /var/tmp/*
5. 使用.dockerignore #
text
# .dockerignore
node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
*.md
.env
coverage
.nyc_output
6. 使用多阶段构建 #
dockerfile
# 构建阶段包含构建工具
FROM node:18 AS builder
RUN npm install -g typescript
COPY . .
RUN npm run build
# 生产阶段只包含运行时
FROM node:18-alpine
COPY --from=builder /app/dist /app/dist
CMD ["node", "/app/dist/index.js"]
7. 压缩镜像层 #
dockerfile
# 使用squash选项(需要启用experimental)
# docker build --squash -t myapp:v1.0 .
# 或在Dockerfile中合并层
RUN commands && \
more commands && \
cleanup
镜像大小对比 #
优化前后对比 #
text
┌─────────────────────────────────────────────────────┐
│ 镜像大小对比 │
├─────────────────────────────────────────────────────┤
│ │
│ 优化前: │
│ FROM ubuntu:22.04 │
│ RUN apt-get update │
│ RUN apt-get install -y nodejs npm │
│ COPY . /app │
│ CMD ["node", "app.js"] │
│ 大小: ~500MB │
│ │
│ 优化后: │
│ FROM node:18-alpine │
│ WORKDIR /app │
│ COPY package*.json ./ │
│ RUN npm ci --only=production │
│ COPY . . │
│ CMD ["node", "app.js"] │
│ 大小: ~120MB │
│ │
│ 多阶段构建: │
│ FROM node:18-alpine AS builder │
│ WORKDIR /app │
│ COPY . . │
│ RUN npm ci && npm run build │
│ │
│ FROM node:18-alpine │
│ COPY --from=builder /app/dist /app │
│ CMD ["node", "/app/index.js"] │
│ 大小: ~50MB │
│ │
└─────────────────────────────────────────────────────┘
构建缓存优化 #
缓存机制 #
text
┌─────────────────────────────────────────────────────┐
│ 构建缓存机制 │
├─────────────────────────────────────────────────────┤
│ │
│ Dockerfile: │
│ 1. FROM node:18 ← 检查基础镜像 │
│ 2. WORKDIR /app ← 检查命令 │
│ 3. COPY package*.json ./ ← 检查文件是否变化 │
│ 4. RUN npm install ← 检查命令和上下文 │
│ 5. COPY . . ← 检查文件是否变化 │
│ 6. RUN npm run build ← 检查命令和上下文 │
│ │
│ 如果某层缓存失效,后续所有层都需要重建 │
│ │
└─────────────────────────────────────────────────────┘
缓存最佳实践 #
dockerfile
# 1. 把不常变化的放前面
FROM node:18-alpine
WORKDIR /app
# 2. 先复制依赖文件
COPY package*.json ./
RUN npm ci --only=production
# 3. 后复制经常变化的源代码
COPY src/ ./src/
COPY public/ ./public/
# 4. 最后执行构建
RUN npm run build
使用外部缓存 #
bash
# 从镜像拉取缓存
docker build --cache-from myapp:v1.0 -t myapp:v2.0 .
# 使用多个缓存源
docker build \
--cache-from myapp:v1.0 \
--cache-from myapp:latest \
-t myapp:v2.0 .
构建性能优化 #
并行构建 #
dockerfile
# syntax=docker/dockerfile:1
FROM alpine AS base
RUN apk add --no-cache curl
# 并行执行
FROM base AS builder1
RUN build-part1.sh
FROM base AS builder2
RUN build-part2.sh
# 合并结果
FROM base AS final
COPY --from=builder1 /part1 /
COPY --from=builder2 /part2 /
使用BuildKit secrets #
dockerfile
# syntax=docker/dockerfile:1
FROM alpine
# 挂载secrets(不会保存在镜像中)
RUN --mount=type=secret,id=npm_token \
export NPM_TOKEN=$(cat /run/secrets/npm_token) && \
npm config set //registry.npmjs.org/:_authToken $NPM_TOKEN && \
npm install
bash
# 构建时传入secret
docker build \
--secret id=npm_token,src=./.npm_token \
-t myapp:v1.0 .
使用BuildKit ssh #
dockerfile
# syntax=docker/dockerfile:1
FROM alpine
# 挂载ssh agent
RUN --mount=type=ssh \
apk add git && \
git clone git@github.com:user/repo.git
bash
# 构建时使用ssh
docker build --ssh default -t myapp:v1.0 .
镜像分析工具 #
docker history #
bash
# 查看镜像层历史
docker history myapp:v1.0
# 不截断输出
docker history --no-trunc myapp:v1.0
# 格式化输出
docker history --format "{{.CreatedBy}}\t{{.Size}}" myapp:v1.0
dive工具 #
bash
# 安装dive
brew install dive
# 分析镜像
dive myapp:v1.0
# CI模式
CI=true dive myapp:v1.0
docker scout #
bash
# 分析镜像
docker scout quickview myapp:v1.0
# 查看漏洞
docker scout cves myapp:v1.0
# 比较镜像
docker scout compare myapp:v1.0 myapp:v2.0
小结 #
本节学习了Docker镜像的构建和优化:
- BuildKit的使用
- 多阶段构建
- 镜像优化技巧
- 构建缓存优化
- 构建性能优化
下一步 #
接下来,让我们学习 镜像分发与存储,了解镜像的存储原理和分发策略。