02 - 基础镜像选择
02 - 基础镜像选择:Alpine、Distroless、Scratch 与多架构
2.1 为什么基础镜像如此重要
FROM 是 Dockerfile 中的第一条指令(ARG 除外),它决定了镜像的起点。基础镜像的选择直接影响:
| 维度 | 影响 |
|---|---|
| 镜像体积 | 不同基础镜像体积差异可达 100 倍以上 |
| 攻击面 | 包含的工具越多,潜在漏洞越多 |
| 构建兼容性 | 某些库在特定发行版上可能不可用 |
| 调试能力 | 越精简的镜像越难直接进入调试 |
| 多架构支持 | 是否支持 arm64、arm/v7 等架构 |
2.2 常见基础镜像对比
| 镜像 | 基于 | 体积 | 包管理器 | Shell | 调试工具 | 典型场景 |
|---|---|---|---|---|---|---|
ubuntu:22.04 | Ubuntu | ~77MB | apt | ✅ bash | ✅ 丰富 | 开发/需要大量系统包 |
debian:bookworm-slim | Debian | ~75MB | apt | ✅ bash | ⚠️ 基础 | 通用生产环境 |
alpine:3.19 | Alpine | ~7MB | apk | ✅ sh | ⚠️ 基础 | 追求极致精简 |
distroless/static | ~2MB | ❌ | ❌ | ❌ | 静态编译程序 | |
distroless/base | ~20MB | ❌ | ❌ | ❌ | 动态链接程序 | |
scratch | 空 | 0MB | ❌ | ❌ | ❌ | 静态二进制 |
busybox | BusyBox | ~4MB | ❌ | ✅ sh | ⚠️ 极简 | 嵌入式/临时容器 |
2.3 Alpine Linux
Alpine Linux 是最流行的精简基础镜像,基于 musl libc 和 BusyBox。
优点
- 体积小(约 5MB 压缩)
- 内置包管理器 apk
- 安全导向(默认启用 PaX 和 SSP)
注意事项
# ❌ 常见陷阱:musl libc 兼容性问题
FROM alpine:3.19
RUN apk add --no-cache python3 py3-pip
# 某些 Python 包包含 C 扩展,可能在 musl 上编译失败
# ✅ 解决方案一:使用兼容层
FROM alpine:3.19
RUN apk add --no-cache python3 py3-pip build-base python3-dev
# ✅ 解决方案二:使用 wolfi(musl + glibc 兼容性更好)
FROM cgr.dev/chainguard/python:latest-dev
Alpine 安装依赖的正确姿势
FROM alpine:3.19
# ✅ 合并 apk add 与清理,使用 --no-cache 不产生索引缓存
RUN apk add --no-cache \
ca-certificates \
tzdata \
curl \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone
DNS 问题排查
Alpine 在某些网络环境下可能出现 DNS 解析问题:
# 如果遇到 DNS 问题,手动指定 DNS
RUN echo "nameserver 8.8.8.8" >> /etc/resolv.conf && \
apk add --no-cache curl
2.4 Distroless
Google 维护的 Distroless 镜像只包含应用运行时所需的最小依赖,没有包管理器、Shell 和其他系统工具。
Distroless 镜像变体
| 镜像 | 内容 | 适用场景 |
|---|---|---|
distroless/static | 仅 glibc 静态链接 | Go 静态编译、Rust |
distroless/base | glibc + CA 证书 + 时区 | Python、Node.js |
distroless/base-nossl | 无 OpenSSL | 不需要 TLS 的程序 |
distroless/java17 | JRE 17 + base | Java 17 应用 |
distroless/java21 | JRE 21 + base | Java 21 应用 |
distroless/nodejs20 | Node.js 20 + base | Node.js 20 应用 |
distroless/python3 | Python 3 + base | Python 应用 |
distroless/cc | C/C++ 运行时 | C/C++ 应用 |
使用 Distroless 的示例
# Go 应用使用 Distroless
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /app/server .
# 生产阶段:使用 Distroless
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]
调试 Distroless 镜像
由于没有 Shell,调试 Distroless 镜像需要特殊方法:
# 方法一:使用 debug 变体(内置 busybox shell)
docker run --rm -it gcr.io/distroless/static-debian12:debug sh
# 方法二:使用 ephemeral 容器(Kubernetes)
kubectl debug -it my-pod --image=gcr.io/distroless/static:debug --target=my-container
# 方法三:使用 docker cp 复制调试工具
docker create --name temp myimage:latest
docker cp /usr/bin/strace temp:/tmp/
docker start -ai temp
2.5 Scratch
scratch 是一个空镜像(0 字节),不包含任何文件。适合静态编译的二进制程序。
FROM scratch
COPY server /server
COPY ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]
Scratch 的限制
- 没有 Shell(无法 exec 进入容器)
- 没有 CA 证书(HTTPS 请求会失败,需要手动复制)
- 没有时区数据(需要手动复制
/usr/share/zoneinfo) - 没有
/etc/passwd(无法解析用户名)
Go 静态编译 + Scratch 完整示例
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# 静态编译,包含 CA 证书
RUN CGO_ENABLED=0 GOOS=linux go build \
-ldflags="-s -w" \
-o /server .
FROM scratch
# 从 builder 阶段复制 CA 证书
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# 复制时区数据
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
# 创建基本的 passwd 文件(非 root 运行)
COPY --from=builder /etc/passwd /etc/passwd
# 复制编译好的二进制
COPY --from=builder /server /server
USER nobody
EXPOSE 8080
ENTRYPOINT ["/server"]
2.6 多架构镜像(Multi-Architecture)
随着 ARM 服务器(如 AWS Graviton、Apple Silicon)的普及,多架构支持变得重要。
查看镜像支持的架构
# 查看镜像的 manifest 列表
docker manifest inspect nginx:latest | jq '.manifests[] | {digest, platform}'
# 输出示例:
# { "digest": "sha256:abc...", "platform": { "architecture": "amd64", "os": "linux" } }
# { "digest": "sha256:def...", "platform": { "architecture": "arm64", "os": "linux" } }
构建多架构镜像
# 创建/使用 BuildKit builder
docker buildx create --name multiarch --driver docker-container --use
# 同时构建 amd64 和 arm64
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t myapp:latest \
--push .
多架构 Dockerfile 编写
# 使用 ARG 获取构建平台信息
FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS builder
ARG TARGETPLATFORM
ARG TARGETOS
ARG TARGETARCH
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# 根据目标架构交叉编译
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 \
go build -ldflags="-s -w" -o /server .
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /server /server
ENTRYPOINT ["/server"]
$BUILDPLATFORM vs $TARGETPLATFORM
| 变量 | 说明 | 示例 |
|---|---|---|
$BUILDPLATFORM | 执行构建的机器架构 | linux/amd64 |
$TARGETPLATFORM | 目标运行平台 | linux/arm64 |
$BUILDOS / $TARGETOS | 操作系统 | linux |
$BUILDARCH / $TARGETARCH | CPU 架构 | amd64, arm64 |
$TARGETVARIANT | 架构变体 | v7(arm/v7) |
业务场景:如果你的服务需要同时在 x86 服务器和 ARM 边缘设备上运行,使用
docker buildx构建多架构镜像并推送到 Registry,用户只需docker pull myapp就能自动获取匹配当前架构的镜像。
2.7 官方镜像与第三方镜像
镜像来源优先级
- Docker Official Images(如
python:3.12-slim)—— 官方维护,安全性高 - Verified Publishers(如
bitnami/nginx)—— 经过验证的发布者 - Docker-Sponsored OSS —— 开源项目赞助
- 社区镜像 —— 需仔细审查
镜像标签策略
# ❌ 使用 latest 标签(不可重现构建)
FROM python:latest
# ✅ 使用具体版本号
FROM python:3.12.2-slim-bookworm
# ✅ 使用 digest(最精确)
FROM python:3.12.2-slim-bookworm@sha256:abc123...
# ✅ 使用带日期的标签(部分镜像提供)
FROM python:3.12-slim-bookworm-20240101
| 标签策略 | 可重现性 | 安全补丁 | 推荐场景 |
|---|---|---|---|
latest | ❌ | 自动 | 仅限本地实验 |
3.12 | ⚠️ | 自动 | 开发环境 |
3.12.2 | ✅ | 手动 | 生产环境 |
digest | ✅ | 手动 | 安全要求极高 |
2.8 基础镜像选型决策树
需要 Shell 进入调试?
├── 是 → 需要包管理器安装额外工具?
│ ├── 是 → Alpine(追求体积小)/ Debian Slim(兼容性好)
│ └── 否 → BusyBox / distroless debug
└── 否 → 程序是静态编译的?
├── 是 → scratch(零依赖)/ distroless/static
└── 否 → 需要特定运行时?
├── Java → distroless/java21
├── Node → distroless/nodejs20
├── Python → distroless/python3
└── 通用 → distroless/base
2.9 常见问题与排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
exec format error | 架构不匹配 | 使用 --platform 指定或构建多架构镜像 |
not found(静态编译程序) | musl/glibc 不兼容 | 使用 CGO_ENABLED=0 编译或选择匹配的 libc |
| Alpine 上 Python 包安装失败 | musl 编译问题 | 安装 build-base 或使用 Debian Slim |
| 时区显示不正确 | 缺少时区数据 | 复制 tzdata 或设置 TZ 环境变量 |
| HTTPS 请求失败 | 缺少 CA 证书 | 安装 ca-certificates 包 |
2.10 扩展阅读
上一章:01 - Dockerfile 概述 下一章:03 - COPY 与 ADD — 文件复制指令的深度对比与缓存策略。