← 中文资源

简体中文版本

安全地把运行时镜像迁移到 distroless

从完整基础镜像迁到 distroless 缩小攻击面,用独立 debug 标签保留排障入口,并在 CI 验证。

阅读英文原文

目标

典型的应用镜像为了跑一个进程而装了一整个 Linux 发行版:包管理器、shell、各种工具,全是应用从未用到的攻击面与 CVE 噪声。distroless 镜像只含运行时与应用本身。迁移通常只是 Dockerfile 两行改动,真正的工作是保住“没法再 sh 进去”之后的排障能力。

步骤要点

  1. 记录基线:当前镜像体积与 trivy HIGH/CRITICAL 数量(如 950MB / 212 个 HIGH)。
  2. 最终阶段换成 gcr.io/distroless/nodejs22 并以 nonroot 运行,多阶段构建只拷应用产物。
  3. 同步构建 :debug 变体(内含 busybox)发布到仓库,事故时仍可 exec 排障。
  4. 对新镜像跑冒烟套件,证明没有系统包也能正常服务。
  5. 复测体积与 CVE(约 120MB / 个位数 HIGH),并把这两个数字设成 CI 门禁防回潮。

排错提示

容器立刻崩溃并报难懂的错误,多半是应用 shell 调了已不存在的系统工具,从日志找到子进程调用改用库实现;没法调试运行中容器正是 distroless 的本意,请用 debug 标签或 kubectl debug 临时容器;健康检查依赖 wget/curl 会失效,改成语言级端点探活或 TCP 探针。

适用边界

移除 shell 动了你的调试习惯,debug 标签必须是流程的一部分而非事后补救;依赖原生扩展时需选匹配运行时的 distroless 变体,否则退回 slim。

代码示例

FROM gcr.io/distroless/nodejs22
WORKDIR /app
COPY --from=build /app /app
USER nonroot
CMD ["server.js"]

# Incidents still need a shell: build a second tag
FROM gcr.io/distroless/nodejs22:debug