从 distroless 到 scratch:基础镜像的极简选择
镜像安全的尽头是基础镜像里到底有什么。distroless 和 scratch 代表两个方向的极简:前者是 Google 维护的只含运行时必需库的镜像族,后者干脆是空镜像。
先说适用性。Go 静态编译的二进制最适合 scratch:CGO_ENABLED=0 之后产物零依赖,镜像就是二进制本身,10M 上下。Java 应用选 distroless 的 java 变体,里面只有 JRE 和 CA 证书,没有 shell、没有包管理器。Python 的极简替代品还在演进,实践中用 slim 已经够用。
没有 shell 的代价要提前想清楚。docker exec sh 进不去,排障手段变成:kubectl debug 附加临时容器进同一 Pod 共享网络命名空间,或者把日志和诊断端点做扎实,让排障不需要登进容器。我们改造时给每个服务补齐了诊断用的 HTTP 端点,算是因祸得福。
安全收益直接体现在扫描报告上:原来 ubuntu 基础镜像常年挂着 40 多个 CVE,distroless 后降到个位数,且大多是低危。体积从 180M 到 22M 的那批 Go 服务,拉取速度也明显变快,冷启动一并受益。
落地节奏建议灰度:新服务直接上,存量服务在基础镜像升级窗口一起换,换之前在预发跑一轮完整回归,重点验证动态链接库和 CA 证书依赖。
还有一个细节:非 root 用户、只读根文件系统这些安全上下文和极简镜像是绝配,一起上收益最大。基础镜像越薄,能出安全问题的地方越少。
补充一个排障替代方案:临时容器里塞个轻量调试镜像,通过进程共享的方式进入目标容器命名空间,官方工具一条命令搞定。这个方案普及后,坚持用完整镜像的团队也接受了极简方案,毕竟调试需求有了正路,就不必为小概率场景背着大镜像。
极简镜像的另一个隐性收益是启动速度:批量扩容几百个副本时,三十兆和两百兆的镜像拉取时间差会被并发放大,扩容完成时间能差出一分钟以上,对弹性场景这就是竞争力。我们把镜像大小做进了服务资产信息,超过阈值的会出现在架构周会的待优化清单里,配一条自动卡点防止回弹,治理成果要靠机制守住。
- 点赞
- 收藏
- 关注作者
评论(0)