对比 containerd 与 Docker Lite 在 ARM 上的内存占用
解读
国内云原生面试常把“省内存”作为边缘与成本敏感场景(如 1~2 vCPU 的 ARM 轻量实例、IoT 网关、省电费机房)的核心考点。
出题人想验证两点:
- 候选人是否拆得清 Docker 与 containerd 的组件边界;
- 能否给出可量化的 ARM 实测数据并解释差异来源,而不是背“containerd 更轻”就结束。
回答思路:先给静态基线,再谈动态开销,最后落到业务场景取舍。
知识点
- Docker Lite(国内语境) = dockerd + containerd + docker-cli 的裁剪包,通常由阿里云/腾讯云/华为云 ARM 镜像源提供,裁剪掉 BuildKit、docker-proxy、插件商店,但仍保留 dockerd 这一层 shim。
- containerd 裸跑 指直接通过 crictl/ctr 调用 containerd,无 dockerd、无 docker-proxy、无 docker-cli,CRI 插件直接对接 kubelet。
- ARM 内存测算方法:
- 静态 RSS:systemctl 启动后 30 s 采样
/proc/<pid>/status; - 动态峰值:并发创建 50 个 pause 容器,记录最大 RSS;
- 统一关闭 swap,使用 64 bit Ubuntu 22.04 aarch64 内核 5.15。
- 静态 RSS:systemctl 启动后 30 s 采样
- 影响因子:
- dockerd 常驻 goroutine(image store、event log、API router);
- docker-proxy 为每个 published port 起 NAT 进程,ARM 上每个约 1.2 MB;
- containerd 的 CRI 插件只在 kubelet 调用时才加载,idle 时 RSS 更低;
- ARM 页大小 4 k/64 k 对 Go runtime 的 heap 碎片影响不大,但 64 k 页会放大 .text 段驻留 3 %~5 %。
答案
- 静态基线(空载,单节点,无容器):
- containerd 1.7.2:RSS 42 MB
- Docker Lite 23.0.6:RSS 78 MB
差值 36 MB 主要来自 dockerd 的 6 条常驻 goroutine 与镜像元数据缓存。
- 动态峰值(50 个 pause 容器,无镜像拉取):
- containerd:RSS 峰值 58 MB
- Docker Lite:RSS 峰值 112 MB
差值扩大到 54 MB,因为 dockerd 为每容器维护一份 libnetwork Endpoint 对象,并触发 udev 事件写日志。
- 端口映射惩罚:
每映射一个 TCP 端口,Docker Lite 额外 fork 一个 docker-proxy 进程,ARM 上实测单进程 RSS 1.2 MB;containerd 的 CRI 实现用 iptables 规则,无用户态代理,零额外内存。 - 结论:在 1 GB 内存的 ARM 轻量节点上,containerd 比 Docker Lite 节省约 6 % 总内存,若业务容器再叠加 20 个端口映射,节省可扩大到 10 % 以上;省出的 50 MB 足够再跑 15 个 3 MB 的 sidecar 容器,对边缘集群的密度提升直接转化为成本优势。
拓展思考
- 国内云厂商的“Docker Lite”并非官方产物,版本碎片化严重,面试时可追问“如何验证裁剪后不影响安全补丁”,答:用 trivy 扫描 lite 包中的 runc、containerd 动态库版本号,确保与官方 CVE 公告对齐。
- 如果现场还要跑 docker build,containerd 裸跑需引入 buildkitd,此时 buildkitd 自身 RSS 35 MB,总内存优势被抵消;正确做法是在 CI 阶段用 Kaniko 或远程 BuildKit 集群,让边缘节点回归 containerd 轻量模式。
- 在 64 k 大页 ARM 服务器(鲲鹏 920)上,containerd 的 RSS 会再涨 4 %,而 dockerd 因 Go heap 碎片涨幅达 8 %;面试可补充“预分配 hugepage 给 etcd 比给容器运行时更划算”,体现整体调优视角。