镜像既是应用的分发载体,也是攻击面与信任边界。镜像小了、安全了、可验证了,部署才谈得上可靠;镜像里藏着漏洞、悬空的基础镜像、无签名来源,等于把后门和风险一起交付。本指南从构建到扫描再到签名与供应链信任,建立一条「构建得快、镜像得小、来源可验、内容可信」的镜像产线。
目录
- 1. 高效构建:BuildKit 与缓存的艺术
- 2. 多阶段构建与镜像瘦身
- 3. 镜像漏洞扫描与安全基线
- 4. SBOM 与软件物料清单
- 5. 镜像签名:cosign 与供应链信任
- 6. 私有仓库与拉取策略
- 7. 镜像准入:用 Polymer 在集群侧强制签名
- 8. 最佳实践与常见坑
1. 高效构建:BuildKit 与缓存的艺术
1.1 为什么默认启用 BuildKit
传统 docker build 用的是旧引擎,难以并行、缓存脆弱。BuildKit 提供并发执行、挂载缓存、远程缓存等能力:
DOCKER_BUILDKIT=1 docker build -t app:latest .
# 或 Docker 23+ 默认启用
docker buildx build --platform linux/amd64,linux/arm64 -t app:latest .
1.2 缓存关键:让"稀疏变化的层"放前面
缓存命中靠不变量。把很少变化的依赖层放前面、频繁变化的源代码层放后面:
# 差:整层拷贝源码,任何改动都炸缓存
COPY . /src
RUN npm install # 源码一变,依赖全部重装
# 好:依赖层独立,只有 package.json 变化才重装
COPY package.json package-lock.json /app/
RUN npm ci
COPY . /app
1.3 BuildKit 缓存挂载(经典鸡生蛋)
npm ci 本身要网络,用 RUN --mount=type=cache 缓存下载:
RUN --mount=type=cache,target=/root/.npm \
npm ci --ignore-scripts
一句话:构建 = 用最小不变量分组 + Cache mount + 多架构并发——缓存命中是最便宜的优化。
2. 多阶段构建与镜像瘦身
2.1 多阶段:构建工具不进运行镜像
编译后的应用只需要运行产物,不需要依赖和编译器。多阶段把"构建环境"与"运行镜像"分离:
# ---------- 阶段 1:构建 ----------
FROM golang:1.22 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server .
# ---------- 阶段 2:运行(极简) ----------
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /app/server /server
USER nonroot
ENTRYPOINT ["/server"]
2.2 显著瘦身手段
| 手段 | 效果 |
|---|---|
| 多阶段构建 | 甩掉编译器/依赖 |
| 用 distroless / alpine | 去掉 shell、包管理器 |
| 合并 RUN、清理 apt 缓存 | 减层数、减体积 |
| 选合适的基础镜像 | 用官方 slim 版 |
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/* # 关键:清理缓存
2.3 安全与体积的平衡
alpine(musl)有时与某些 CGO 库不兼容;distroless 无 shell 不便排障。推荐:常规服务用较省体积的发行版或 distroless,需要调试失败保留 slim。
一句话:多阶段 + distroless = 只交付运行所需——又小又安全,构建依赖不泄漏到生产镜像里。
3. 镜像漏洞扫描与安全基线
3.1 扫描工具
| 工具 | 特点 |
|---|---|
| Trivy | 全面、快、OSS,可做 CI 门禁 |
| Grype | Anchore 开源,Syft 配套 |
| Clair | Red Hat 的仓库级扫描 |
| Snyk | 商业,含运行时与依赖 |
3.2 在 CI 里做门禁
把扫描做成阻断性步骤,严重影响高危漏洞就不让推送:
# GitHub Actions 示例
- name: 扫描镜像
uses: aquasecurity/trivy-action@master
with:
image-ref: ghcr.io/me/app:latest
severity: CRITICAL,HIGH
format: table
exit-code: '1' # 高危存在即失败
3.3 信任与基线
- 不追求"零漏洞"(基础系统总有低危项),而是设可控基线(阻止高/危急);
- 对基础镜像与应用依赖分仓扫描;
- 建立已知可接受风险清单,注明原因与时间。
一句话:扫描 = CI 门禁(高危阻断)+ 基线化(可接受风险清单)——让镜像在进仓库前就"体检"。
4. SBOM:镜像的物料清单
4.1 什么是 SBOM
SBOM(Software Bill of Materials,软件物料清单) 列出镜像内所有组件、版本、许可证与来源。它让"这个镜像里到底有什么"可审计,是供应链可见性的基础。
4.2 生成 SBOM
# 用 Syft 生成
syft ghcr.io/me/app:latest -o cyclonedx-json > app-sbom.json
# 或 Trivy
trivy image --format cyclonedx --output app-sbom.json ghcr.io/me/app:latest
4.3 SBOM 的用途
| 用途 | 说明 |
|---|---|
| 漏洞追踪 | 组件级排查漏洞影响范围 |
| 合规审计 | 许可证与成分审查 |
| 供应链信任 | 与签名一起构成"原料可追溯" |
| 平台治理 | 作为镜像资产的一部分存档 |
一句话:SBOM = 镜像的户口本——没有它,“里面有什么"是不可审计的;让漏洞排查从"猜"变成"查”。
5. 镜像签名:cosign 与供应链信任
5.1 为什么要签名
镜像从构建方到集群要经过仓库、传输、拉取多个环节。若无人验证,攻击者可在任一环节替换镜像。签名 保证镜像的签发者与完整性可被校验。
5.2 使用 cosign 签名
# 生成密钥
cosign generate-key-pair
# 签名
cosign sign --key cosign.key ghcr.io/me/app:latest
# 校验
cosign verify --key cosign.pub ghcr.io/me/app:latest
# Verification true / 失败会有明确错误
5.3 SLSA 与可复现供应链
SLSA(Supply-chain Levels for Software Artifacts)定义从"完整"到"规范"的信任等级。将构建输入、签名、SBOM 关联,形成可验证、可复现的供应链:
源码 → 信任构建(记录构建元数据)→ 生成 SBOM → cosign 签名(+SBOM 一起)→ 推送仓库 → 部署时校验
一句话:签名 = 给镜像盖章,让平台知道"这是可信签发者发布的";加上 SBOM,供应链从"来源不明"变成"逐层可验"。
6. 私有仓库与拉取策略
6.1 使用容器仓库策略
- 用 私有仓库 + 访问控制(registry + IAM/凭证);
- 开启 内容信任(Notary/DCT)或 加固访问;
- 用 镜像拉取镜像(imagePullPolicy)控制缓存策略:
spec:
containers:
- name: app
image: registry.example.com/team/app:1.2.3
imagePullPolicy: IfNotPresent # 或 Always,避免用旧缓存
6.2 仓库级扫描与策略
在云厂商或制品库侧开启扫描 + 策略(如阻断高危镜像出仓),作为 CI 的补充防线。
一句话:仓库 = 鉴权 + 扫描 + 拉取策略——把好第一道,不让带毒镜像散发出。
7. 镜像准入:在集群侧强制签名
7.1 为什么需要准入策略
CI 扫描 + 签名做了,但部署时谁保证用的是签名镜像?若有人绕过 CI 直接创建 Pod,可能用脏镜像。ImagePolicyWebhook(或 Gatekeeper/President)在集群侧强制校验。
7.2 用准入策略做防线
- 用 Open Policy Agent / Gatekeeper 规则:只允许签名
cosign verify通过的镜像; - 设 imagePullPolicy 强制 Always/fixed-tag,禁止
latest在非 dev 命名空间; - 对关键命名空间 禁止允许源不明的镜像。
一句话:构建门禁 + 仓库扫描 + 集群准入 三层防线,让"脏镜像"到不了运行时。
8. 最佳实践与常见坑
8.1 常见坑
| 坑 | 现象 | 对策 |
|---|---|---|
| 用 docker build 忘构建缓存 | 构建慢 | 用 BuildKit + 缓存 mount |
| 不瘦身 | 镜像过大 | 多阶段 + distroless |
| 从来不扫描 | 带漏洞上线 | CI 加门禁 |
| 无 SBOM | 漏洞影响面难查 | 生成 SBOM 存档 |
| 无签名 | 镜像可能被替换 | cosign 签名 + 校验 |
| 用 latest | 无法复现 | 固定不可变 tag |
8.2 端到端流水线
源码提交 → 信任构建(记录元数据)
→ SBOM 生成(Syft/Trivy)
→ 漏洞扫描(高危阻断)
→ cosign 签名(镜像 + SBOM)
→ 推私有仓(仓库扫描)
→ 部署时集群准入校验签名
→ 运行镜像可追溯
构建快、镜像小、扫描全、签名真、准入严——一条面向生产可解释、可审计的镜像产线。
总结:镜像治理 = 高效构建(快)+ 多阶段瘦身(小)+ 漏洞扫描(安全)+ SBOM(可见)+ 签名(可信)+ 准入(强制)。让镜像是可信的交付包袱,而不是攻击面。镜像之所以可信,是因为每一个环节都被验证和记录。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。