WEBKT

边缘节点瘦身实战:将 Kata 容器 VM 镜像从 300MB 压缩到 128MB 的裁剪方案

7 0 0 0

背景:当 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 编译优化

使用 -O2CONFIG_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 降至 18MBvmlinuz 压缩后),启动时间从 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 生产环境注意事项

⚠️ 风险点与缓解措施

  1. 调试能力丧失:裁剪后的内核缺少 CONFIG_KALLSYMS,崩溃时无符号表

    • 缓解:保留一份带调试信息的 vmlinux 在 host 侧用于离线分析
  2. 硬件兼容性:特定边缘设备的网卡驱动被误删

    • 缓解:在 configuration.toml 中配置 kernel_modules=[] 允许动态注入
  3. 安全特性削弱:关闭了 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(社区轻量级镜像草案)

云原生包工头 边缘计算内核裁剪

评论点评