强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

02 - 基础镜像选择

02 - 基础镜像选择:Alpine、Distroless、Scratch 与多架构

2.1 为什么基础镜像如此重要

FROM 是 Dockerfile 中的第一条指令(ARG 除外),它决定了镜像的起点。基础镜像的选择直接影响:

维度影响
镜像体积不同基础镜像体积差异可达 100 倍以上
攻击面包含的工具越多,潜在漏洞越多
构建兼容性某些库在特定发行版上可能不可用
调试能力越精简的镜像越难直接进入调试
多架构支持是否支持 arm64、arm/v7 等架构

2.2 常见基础镜像对比

镜像基于体积包管理器Shell调试工具典型场景
ubuntu:22.04Ubuntu~77MBapt✅ bash✅ 丰富开发/需要大量系统包
debian:bookworm-slimDebian~75MBapt✅ bash⚠️ 基础通用生产环境
alpine:3.19Alpine~7MBapk✅ sh⚠️ 基础追求极致精简
distroless/staticGoogle~2MB静态编译程序
distroless/baseGoogle~20MB动态链接程序
scratch0MB静态二进制
busyboxBusyBox~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/baseglibc + CA 证书 + 时区Python、Node.js
distroless/base-nossl无 OpenSSL不需要 TLS 的程序
distroless/java17JRE 17 + baseJava 17 应用
distroless/java21JRE 21 + baseJava 21 应用
distroless/nodejs20Node.js 20 + baseNode.js 20 应用
distroless/python3Python 3 + basePython 应用
distroless/ccC/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 / $TARGETARCHCPU 架构amd64, arm64
$TARGETVARIANT架构变体v7(arm/v7)

业务场景:如果你的服务需要同时在 x86 服务器和 ARM 边缘设备上运行,使用 docker buildx 构建多架构镜像并推送到 Registry,用户只需 docker pull myapp 就能自动获取匹配当前架构的镜像。

2.7 官方镜像与第三方镜像

镜像来源优先级

  1. Docker Official Images(如 python:3.12-slim)—— 官方维护,安全性高
  2. Verified Publishers(如 bitnami/nginx)—— 经过验证的发布者
  3. Docker-Sponsored OSS —— 开源项目赞助
  4. 社区镜像 —— 需仔细审查

镜像标签策略

# ❌ 使用 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 — 文件复制指令的深度对比与缓存策略。