边缘节点瘦身实战:将 Kata 容器 VM 镜像从 300MB 压缩到 128MB 的裁剪方案
背景:当 Kata 遇到边缘计算
在边缘 Kubernetes 集群中,我们曾遇到一个典型困境:某工业网关设备仅有 8GB 内存和 32GB eMMC 存储,而 Kata Containers 默认的 kata-containers.img 加上 vmlinuz 占用超过 300MB,再加上业务镜像,磁盘空间瞬间告急。
经过三个月的迭代优化,我们最终将 Kata VM 运行时依赖压缩到了 128MB(kernel 18MB + initrd 110MB),同时保持了完整的容器运行时兼容性。以下是完整的裁剪路径。
一、Kata VM 镜像构成分析
Kata 启动一个 Pod 需要两部分:
- Kernel:通常为
vmlinuz-5.10.x,默认 60-120MB - Guest Image:包含 kata-agent 的根文件系统,默认 200MB+
裁剪策略围绕三个维度展开:内核模块化精简、根文件系统轻量化、压缩算法优化。
二、内核裁剪:从 120MB 到 18MB
2.1 基线配置采集
首先基于实际硬件采集必要模块清单。在目标边缘设备(ARM64 或 x86-64)启动标准内核后执行:
make localmodconfig
# 生成仅包含当前加载模块的 .config
这会生成一个基准配置,但通常仍包含大量边缘场景不需要的驱动(如 WiFi、蓝牙、声卡、显卡)。
2.2 关键裁剪配置
针对边缘计算场景(通常为 headless 服务器,特定网卡/存储),我们在 .config 中进行了以下调整:
# 禁用大型子系统
CONFIG_SOUND=n
CONFIG_USB_SUPPORT=n # 如无 USB 外设需求
CONFIG_DRM=n # 无需图形
CONFIG_WLAN=n # 通常使用有线网络
CONFIG_IEEE802154=n
CONFIG_CFG80211=n
# 精简调试信息(减小 40% 体积)
CONFIG_DEBUG_INFO=n
CONFIG_DEBUG_KERNEL=n
CONFIG_KALLSYMS=n # 生产环境可关闭,调试时开启
CONFIG_MODULE_SIG=n # 边缘私有关闭
# 网络协议精简(仅保留必要)
CONFIG_IPV6=n # 如环境仅需 IPv4
CONFIG_DECNET=n
CONFIG_ATM=n
# 文件系统裁剪(仅保留容器所需)
CONFIG_EXT4_FS=y # 必须
CONFIG_OVERLAY_FS=y # 容器层叠必需
CONFIG_XFS_FS=n
CONFIG_BTRFS_FS=n # 边缘通常不需要
CONFIG_NFS_FS=n # 如无 NFS 存储
2.3 编译优化
使用 -O2 和 CONFIG_CC_OPTIMIZE_FOR_SIZE(对应 make menuconfig 中的 "Optimize for size"):
make -j$(nproc) CFLAGS="-Os -fno-stack-protector -U_FORTIFY_SOURCE" \
LOCALVERSION="-kata-edge"
效果:Kernel 从 120MB 降至 18MB(vmlinuz 压缩后),启动时间从 850ms 降至 220ms。
三、根文件系统:从 200MB 到 110MB
3.1 基础镜像选择
放弃 Ubuntu/CentOS,选择 Alpine Linux 或 自编译 BusyBox:
# 使用 Alpine 作为基础(原始镜像仅 5MB)
FROM alpine:3.18 AS rootfs
# 安装最小依赖(注意:kata-agent 需要 musl 或 glibc)
RUN apk add --no-cache \
ca-certificates \
tzdata \
libc6-compat # 如 agent 静态编译则不需要
更激进的方案是使用 scratch + 静态编译 kata-agent:
# 静态编译 kata-agent(Go)
cd kata-containers/src/agent
make KATA_RUST_AGENT=no \
AGENT_INIT=yes \
LIBC=gnu \
STATIC=yes
# 生成单二进制文件,仅依赖 libc(可进一步使用 musl 完全静态)
3.2 深度裁剪清单
在 rootfs 中执行以下清理(对比原始体积):
| 组件 | 原始体积 | 裁剪后 | 操作 |
|---|---|---|---|
| Systemd | 45MB | 0MB | 替换为 kata-agent 内置 init |
| Bash + coreutils | 15MB | 1.2MB | 使用 BusyBox 1.36.1(静态编译) |
| glibc 共享库 | 28MB | 8MB | 使用 strip --strip-unneeded,移除 locales |
| Kernel modules | 60MB | 12MB | 仅保留 virtio-net、virtio-blk、9pfs |
| 文档/手册 | 8MB | 0MB | 删除 /usr/share/{doc,man,info} |
关键脚本示例:
# 激进的库文件清理(在 rootfs 目录内执行)
find . -type f -name "*.so*" -exec strip --strip-unneeded {} \;
rm -rf ./usr/share/locale/* ./usr/lib/locale/*
rm -rf ./var/cache/apk/* ./tmp/*
# 移除不必要的设备节点(由 kata-runtime 动态创建)
rm -rf ./dev/*
3.3 initrd vs 裸镜像权衡
边缘场景推荐使用 initrd(ramdisk) 而非 qcow2/raw 镜像:
- 无需块设备驱动,启动更快
- 内存压力可控(边缘 Pod 密度低,通常 < 20 个节点)
生成优化后的 initrd:
cd /path/to/rootfs
find . | cpio -H newc -o | zstd -19 -T0 > ../kata-containers-initrd.img
# 使用 zstd 替代 gzip,压缩率提升 15%,解压速度提升 3 倍
效果:rootfs 从 200MB 压缩至 110MB(zstd 压缩后)。
四、高级优化:突破 150MB 限制
若目标进一步压缩至 < 150MB 总和,需采用以下策略:
4.1 DAX(Direct Access)与 PMEM 优化
利用 Kata 的 DAX 支持,将部分只读数据通过 host 直接映射,减少 guest 内存占用:
# /etc/kata-containers/configuration.toml
[hypervisor.qemu]
valid_entropy_sources = ["/dev/urandom"]
enable_dax = true
virtio_fs_extra_args = ["--thread-pool-size=1"]
4.2 内核模块延迟加载
将非关键驱动(如特定网卡的高级功能)打包为独立 ko 文件,通过 initrd 中的 modprobe 按需加载,而非编译进内核:
# 在 initrd 中延迟加载
echo "modprobe virtio_net" >> /init
4.3 镜像分层与 CRI-O 集成
配合 CRI-O 的 VM 镜像缓存机制,避免每个 Pod 重复拉取:
# /etc/crio/crio.conf
[crio.runtime.runtimes.kata-qemu]
allowed_annotations = ["io.kubernetes.cri-o.Devices", "io.katacontainers.config.agent.kernel_modules"]
五、验证与回退方案
5.1 功能验证清单
# 1. 启动时间测试
crictl runp pod-config.yaml
# 目标:从创建到 Ready < 3s(边缘设备)
# 2. 功能完整性测试
kubectl exec -it pod -- ls /proc/self/fd # 验证 9p/virtiofs 工作
kubectl exec -it pod -- ping 8.8.8.8 # 验证网络
kubectl exec -it pod -- df -h # 验证存储
# 3. 资源监控
ps aux | grep qemu # 观察 QEMU 进程 RSS
ls -lh /var/lib/kata-images/ # 确认镜像体积
5.2 生产环境注意事项
⚠️ 风险点与缓解措施:
调试能力丧失:裁剪后的内核缺少
CONFIG_KALLSYMS,崩溃时无符号表- 缓解:保留一份带调试信息的 vmlinux 在 host 侧用于离线分析
硬件兼容性:特定边缘设备的网卡驱动被误删
- 缓解:在
configuration.toml中配置kernel_modules=[]允许动态注入
- 缓解:在
安全特性削弱:关闭了
CONFIG_MODULE_SIG和部分 Stack Protector- 缓解:边缘内网环境配合镜像签名(containerd 的 NRI 插件验证)
5.3 回滚配置
始终保留原始镜像作为 fallback:
[hypervisor.qemu]
kernel = "/usr/share/kata-containers/vmlinuz-kata-edge" # 裁剪版
initrd = "/usr/share/kata-containers/kata-containers-initrd-edge.img"
# 若失败,快速切换至:
# kernel = "/usr/share/kata-containers/vmlinuz.container" # 原版
# image = "/usr/share/kata-containers/kata-containers.img"
六、最终成果对比
| 指标 | 标准 Kata | 裁剪后 | 收益 |
|---|---|---|---|
| VM 镜像总大小 | 320MB | 128MB | ↓ 60% |
| 冷启动时间 | 2.8s | 1.1s | ↓ 61% |
| 内存开销(空 Pod) | 128MB | 96MB | ↓ 25% |
| 磁盘 I/O 峰值 | 45MB/s | 12MB/s | ↓ 73% |
这套方案已在某智能交通边缘节点(NVIDIA Jetson AGX + K3s)稳定运行 6 个月,单节点可承载 40+ Kata Pod,较之前提升 2.5 倍密度。
GitHub 参考实现:kata-containers/kata-containers#5678(社区轻量级镜像草案)