通过多阶段构建、非 root 用户和依赖锁定降低镜像体积与攻击面。
核心思路
构建阶段安装工具链,运行阶段只复制产物;固定基础镜像版本并定期扫描漏洞。删除缓存与无用包,避免把密钥、源码历史和调试工具带入最终镜像。
落地步骤
容器默认使用只读根文件系统和非 root 用户,需要写入的目录单独挂载。镜像越小,拉取越快,但可调试性也会下降,应准备独立诊断镜像。
验证清单
实施后不要只确认“能运行”,还应检查错误率、延迟、资源占用和异常路径。把验证命令、预期结果和回滚方式写进变更记录,后续维护会轻松很多。
小结
好的工程实践不是堆叠工具,而是用清晰边界、可观察结果和可逆变更降低长期成本。先解决最重要的问题,再逐步自动化。
可执行实践
采用多阶段构建并固定基础镜像 digest;运行镜像创建非 root 用户,删除包缓存和构建工具。用 SBOM 与漏洞扫描记录依赖,结合签名或准入策略限制未知镜像。
验证与退出条件
验证容器在只读根文件系统、丢弃 Linux capabilities 和禁用提权后仍可运行。若必须写文件,只挂载明确的临时目录。
实践场景
以“把一个常规应用镜像收敛到最小运行权限”为例。实施前先保存当前版本、配置和关键指标基线,写明成功条件与停止条件;涉及生产数据或外部写入时,先在隔离环境验证。
分步实施
使用多阶段构建,只复制运行产物;固定基础镜像摘要,创建非 root 用户,移除包管理器与无关工具。每完成一步都记录可观察结果,不把命令返回零等同于业务正确。
故障演练
扫描已知漏洞,并尝试写根目录、获取额外 capability 和启动 shell,验证运行时策略能拒绝。演练应使用可清理的测试数据,并验证告警、日志和恢复路径,而不只是确认系统最终又能访问。
观测与回滚
比较镜像体积、高危漏洞和启动失败;新镜像异常时回滚摘要,不复用 latest 标签。变更单中应写清负责人、观察窗口、阈值和回滚触发器,避免故障发生后再临时争论。
完成后把使用的版本、关键命令、观测窗口和结果写入变更记录。没有验证证据的“已完成”不能作为上线结论。
第二轮实践复核
可复用交付物: 固定摘要的镜像、SBOM 与扫描报告。复用时先固定软件版本、依赖锁文件、测试数据规模和运行环境;文章中的命令包含占位符,必须替换成自己的非生产目标后再执行。
最低验证: docker build --pull --no-cache -t app:test . && docker image inspect app:test
执行记录要保存命令、退出码、关键输出、观察窗口和回滚结果。若验证会产生写入,先使用隔离环境或 dry-run;任何失败都不能通过扩大权限、关闭校验或跳过测试来“解决”。
完整实战:构建可审计的 Go 服务镜像
多阶段构建把编译工具和运行时分开,依赖版本由模块锁定,最终镜像只带二进制和必要证书:
1 | FROM golang:1.25-alpine AS build |
版本仅作示例,生产构建应固定可复现的基础镜像摘要,并由自动化更新。不要为了通过扫描盲目删除 CA、时区或调试所需能力;先定义运行契约,再用临时调试容器排障。
镜像只是运行边界的一部分
1 | securityContext: |
应用若需临时写入,挂载容量受限的专用目录,不能退回可写根文件系统。密钥在运行时注入,不通过 ARG、ENV 或层文件进入镜像历史。
| 验收 | 证据 |
|---|---|
| 来源 | 基础镜像摘要、构建提交、签名/证明 |
| 内容 | SBOM、许可证、无意外工具和凭据 |
| 漏洞 | 可利用性评估、修复版本、例外到期日 |
| 权限 | 非 root、只读根、无多余 capability |
| 行为 | 健康检查、信号退出、临时目录和证书可用 |
扫描结果要区分运行时可达性与纯构建依赖,但不能靠忽略规则永久隐藏高风险项。用测试 Pod 验证只读根、无 shell、网络策略和优雅退出。灰度按镜像摘要部署并监控启动失败、OOM、权限拒绝和业务错误;回滚到上一个已签名摘要,而不是复用可变标签。
一手资料
资料与版本边界复核日期:2026-08-22。工具行为可能演进,实施前以锁定版本对应的官方文档为准。
评论