alpine
背景
由于我们为 microvm 场景定制的内核是 25.9MB 的全内置 (Monolithic) 内核,因此 Rootfs 制作将遵循一个核心原则:彻底剔除所有内核模块和硬件固件。
Alpine 使用 musl libc 和 BusyBox,是目前启动最快的 Linux。
定制脚本
mkdir -p /home/sky/work/code/cloud-hypervisor/rootfs/alphine
cd /home/sky/work/code/cloud-hypervisor/rootfs/alphine
vi build_agent_rootfs_erofs.sh
内容为:
#!/bin/bash
set -e
# --- 配置参数 ---
ROOTFS_NAME="alpine_rootfs.erofs"
WORKING_DIR="./mnt_alpine"
ALPINE_VERSION="3.20.3"
ALPINE_TAR="alpine-minirootfs-${ALPINE_VERSION}-x86_64.tar.gz"
ALPINE_URL="https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/x86_64/${ALPINE_TAR}"
echo "🚀 开始自动化构建 EROFS + DAX 专用 Alpine Rootfs..."
# 1. 依赖检查
if ! command -v mkfs.erofs &> /dev/null; then
echo "❌ 错误: 未安装 erofs-utils。请运行: sudo apt install erofs-utils"
exit 1
fi
# 2. 下载原始 Rootfs 包
if [ ! -f "$ALPINE_TAR" ]; then
echo "📥 下载 Alpine 原始包..."
curl -L -O "$ALPINE_URL"
fi
# 3. 准备工作目录
echo "📂 准备构建目录..."
sudo rm -rf "$WORKING_DIR"
mkdir -p "$WORKING_DIR"
sudo tar -xzkf "$ALPINE_TAR" -C "$WORKING_DIR"
# 4. 进入 Chroot 进行内部定制
echo "🛠️ 开始内部定制..."
sudo cp /etc/resolv.conf "$WORKING_DIR/etc/resolv.conf"
sudo chroot "$WORKING_DIR" /bin/sh <<EOF
# 设置密码
echo "root:root" | chpasswd
# 1. 基础环境补全
mkdir -p /etc/init.d
mkdir -p /run/openrc
touch /run/openrc/softlevel
# 2. 优化终端:仅保留 hvc0
sed -i '/tty[0-9]/d' /etc/inittab
if ! grep -q "hvc0" /etc/inittab; then
echo "hvc0::respawn:/sbin/getty -L hvc0 115200 vt100" >> /etc/inittab
fi
# 3. 换源并安装 OpenRC
echo "https://mirrors.aliyun.com/alpine/v3.20/main" > /etc/apk/repositories
echo "https://mirrors.aliyun.com/alpine/v3.20/community" >> /etc/apk/repositories
apk add --no-cache openrc
# 4. 配置启动项
rc-update add devfs boot
rc-update add local boot
EOF
# 5. 注入 OverlayFS 极速启动逻辑 (针对 DAX 和写层优化)
echo "⚡ 注入 OverlayFS 启动逻辑..."
sudo tee "$WORKING_DIR/etc/init.d/rcS" <<EOF
#!/bin/sh
# 基础虚拟文件系统挂载
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
# 准备写层 (假设 /dev/vda 是通过 --disk 传入的 Ext4 镜像)
mkdir -p /mnt/write_layer
if mount -t ext4 -o noatime /dev/vda /mnt/write_layer; then
echo "✅ Writable layer (/dev/vda) detected."
# 准备 OverlayFS 结构
mkdir -p /mnt/write_layer/upper /mnt/write_layer/work
# 将 /var 叠加到写层,使得日志和配置可持久化
mount -t overlay overlay -o lowerdir=/var,upperdir=/mnt/write_layer/upper,workdir=/mnt/write_layer/work /var
else
echo "⚠️ Warning: No writable disk detected, using tmpfs for /var."
mount -t tmpfs tmpfs /var
fi
# 其他临时目录使用内存
mount -t tmpfs tmpfs /tmp
mount -t tmpfs tmpfs /run
echo "------------------------------------------------"
echo " Welcome to Agent MicroVM (EROFS + DAX) "
echo " Status: Rootfs is Read-Only | /var is Writable"
echo "------------------------------------------------"
# 执行正常的 init 流程
exec /sbin/init
EOF
sudo chmod +x "$WORKING_DIR/etc/init.d/rcS"
# 6. 极致清理
echo "🧹 执行最后瘦身清理..."
sudo rm -rf "$WORKING_DIR/lib/modules"/*
sudo rm -rf "$WORKING_DIR/lib/firmware"/*
sudo rm -rf "$WORKING_DIR/var/cache/apk"/*
sudo rm -f "$WORKING_DIR/etc/resolv.conf"
# 7. 构建 EROFS 镜像 (核心步骤)
# -d4: 4KB对齐,DAX 必需
# --fsid=rootfs: 方便内核识别
echo "💎 构建 EROFS 镜像..."
rm -f "$ROOTFS_NAME"
sudo mkfs.erofs -d4 "$ROOTFS_NAME" "$WORKING_DIR"
# 8. 完成
sudo rm -rf "$WORKING_DIR"
echo "✅ 构建完成!镜像文件: ${ROOTFS_NAME}"
ls -lh "$ROOTFS_NAME"
增加执行权限:
chmod +x build_agent_rootfs_erofs.sh
执行:
./build_agent_rootfs_erofs.sh
执行完成后,rootfs 文件会生成在当前目录下,大小为约 10MB。
-rw-r--r-- 1 root root 8.9M Feb 28 09:57 alpine_rootfs.erofs
测试
准备工作
准备工作目录和文件:
mkdir -p /home/sky/work/test/cloudhypervisor/agent
cd /home/sky/work/test/cloudhypervisor/agent
cp /home/sky/work/code/cloud-hypervisor/linux/vmlinux .
cp /home/sky/work/code/cloud-hypervisor/rootfs/alphine/alpine_rootfs.erofs .
准备可写层文件:
# 1. 创建一个 64MB 的原始镜像 (大小可根据日志量调整)
# 使用 seek 可以创建一个空洞文件,逻辑大小 64M,实际初始占用 0
dd if=/dev/zero of=write_layer_template.img bs=1M count=0 seek=64
# 2. 格式化为极致精简的 Ext4
# -O ^has_journal: 关键优化!禁用日志。
# 在只读根镜像+独立写层的架构下,日志会产生双倍 IO 开销,禁用它能大幅提升小文件写入速度。
# -O ^huge_file,^flex_bg: 移除针对大文件系统的冗余特性。
# -m 0: 将保留给 root 用户的 5% 空间设为 0,充分利用 64MB。
mkfs.ext4 -O ^has_journal,^huge_file,^flex_bg -m 0 write_layer_template.img
启动 microvm
mkdir microvm-1
cp write_layer_template.img microvm-1/write_layer.img
sudo /usr/local/bin/cloud-hypervisor \
--kernel ./vmlinux \
--memory size=1024M \
--pmem file=./alpine_rootfs.erofs \
--disk path=./microvm-1/write_layer.img \
--cpus boot=1 \
--console tty \
--cmdline "console=hvc0 root=/dev/pmem0 ro rootfstype=erofs init=/etc/init.d/rcS"
然后可以用帐号 root/root 登录(帐号密码在前面的脚本中有设置,可修改)。关机时注意 alphine Linux 默认不带 shutdown 命令,关机要用 poweroff 命令, 重启可以用普通的 reboot 命令。
如果想增加 shutdown 命令,可以:
apk update
apk add sysvinit
# 之后就能用 shutdown
shutdown -h now
shutdown -r now
先用 poweroff ,不额外安装了。
进程分析
这是启动单个 microvm 的进程信息:
ps -ef | grep cloud
root 1351503 1349610 0 14:50 pts/0 00:00:00 sudo /usr/local/bin/cloud-hypervisor --kernel ./vmlinux --memory size=1024M --pmem file=./alpine_rootfs.erofs --disk path=./microvm-1/write_layer.img --cpus boot=1 --console tty --cmdline console=hvc0 root=/dev/pmem0 ro rootfstype=erofs init=/etc/init.d/rcS
root 1351505 1351503 0 14:50 pts/1 00:00:00 sudo /usr/local/bin/cloud-hypervisor --kernel ./vmlinux --memory size=1024M --pmem file=./alpine_rootfs.erofs --disk path=./microvm-1/write_layer.img --cpus boot=1 --console tty --cmdline console=hvc0 root=/dev/pmem0 ro rootfstype=erofs init=/etc/init.d/rcS
root 1351506 1351505 1 14:50 pts/1 00:00:00 /usr/local/bin/cloud-hypervisor --kernel ./vmlinux --memory size=1024M --pmem file=./alpine_rootfs.erofs --disk path=./microvm-1/write_layer.img --cpus boot=1 --console tty --cmdline console=hvc0 root=/dev/pmem0 ro rootfstype=erofs init=/etc/init.d/rcS
root 1351509 1351506 0 14:50 pts/1 00:00:00 /usr/local/bin/cloud-hypervisor --kernel ./vmlinux --memory size=1024M --pmem file=./alpine_rootfs.erofs --disk path=./microvm-1/write_layer.img --cpus boot=1 --console tty --cmdline console=hvc0 root=/dev/pmem0 ro rootfstype=erofs init=/etc/init.d/rcS
这是一个多层级进程结构。虽然只运行了一个启动命令,但在 Linux 的 ps 输出中出现了 4 个相关的条目。这是由 sudo 的工作机制、Cloud Hypervisor 的内部架构以及 Linux 线程的处理方式共同决定的。
按照 PID (进程 ID) 和 PPID (父进程 ID) 的链路来拆解,进程链路树状:
| PID | PPID | 用户 | 角色 | 状态描述 |
|---|---|---|---|---|
| 1351503 | 1349610 | root | Sudo Shell | pts/0 输入命令后产生的第一个 sudo 进程。 |
| 1351505 | 1351503 | root | Sudo Runner | sudo 内部为了安全执行目标程序而分叉(fork)出的运行环境。 |
| 1351506 | 1351505 | root | CH Main (核心) | 这是真正的 MicroVM 主进程。它负责内存管理、设备初始化。 |
| 1351509 | 1351506 | root | CH Worker | 这是一个子线程(被 ps 显示为进程),通常负责异步 I/O 或 API 处理。 |
由于存在多余的 sudo 进程,因此大规模部署时不建议用 sudo。考虑到直接用 root 也不够安全,因此最好的解决方式是给运行 cloud hypervisor 的非 root 帐号 和 cloud-hypervisor 二进制文件赋予需要的权限。
以 sky 帐号为例,具体需要的操作有:
# 赋予 ch 网络管理、原始 IO 和内存锁定权限
sudo setcap 'cap_net_admin,cap_sys_rawio,cap_ipc_lock+ep' /usr/local/bin/cloud-hypervisor
# 设置设备访问权限
# Cloud Hypervisor 需要访问 /dev/kvm(虚拟化加速)和 /dev/vhost-vsock 等设备
# 将帐号加入 kvm 组
sudo usermod -aG kvm sky
# 为了确保每次开机 /dev/kvm 等设备都能被 sky 访问,创建一条规则
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0660"' | sudo tee /etc/udev/rules.d/99-kvm.rules
echo 'KERNEL=="vhost-vsock", GROUP="kvm", MODE="0660"' | sudo tee -a /etc/udev/rules.d/99-kvm.rules
sudo udevadm control --reload-rules && sudo udevadm trigger
# 确保你的 vmlinux、alpine_rootfs.erofs 以及 microvm-1/ 目录下的所有文件,属主都是 sky
# sudo chown -R sky:sky /home/sky/work/test/cloudhypervisor/agent
去掉 sudo,用 sky 帐号启动:
/usr/local/bin/cloud-hypervisor \
--kernel ./vmlinux \
--memory size=1024M \
--pmem file=./alpine_rootfs.erofs \
--disk path=./microvm-1/write_layer.img \
--cpus boot=1 \
--console tty \
--cmdline "console=hvc0 root=/dev/pmem0 ro rootfstype=erofs init=/etc/init.d/rcS quiet"
ps的信息如下:
ps -ef | grep cloud
sky 1352498 1351990 0 15:10 pts/0 00:00:00 /usr/local/bin/cloud-hypervisor --kernel ./vmlinux --memory size=1024M --pmem file=./alpine_rootfs.erofs --disk path=./microvm-1/write_layer.img --cpus boot=1 --console tty --cmdline console=hvc0 root=/dev/pmem0 ro rootfstype=erofs init=/etc/init.d/rcS
sky 1352501 1352498 0 15:10 pts/0 00:00:00 /usr/local/bin/cloud-hypervisor --kernel ./vmlinux --memory size=1024M --pmem file=./alpine_rootfs.erofs --disk path=./microvm-1/write_layer.img --cpus boot=1 --console tty --cmdline console=hvc0 root=/dev/pmem0 ro rootfstype=erofs init=/etc/init.d/rcS
去掉 sudo 后,现在的 ps 输出变得非常清爽且符合预期。中间那两层中转进程彻底消失了,直接看到的是 Cloud Hypervisor 本身。
| PID | PPID | 用户 | 角色 | 状态描述 |
|---|---|---|---|---|
| 1352498 | 1351990 | sky | 主进程 (Parent) | 这是你手动启动的那个二进制文件,负责资源协调。 |
| 1352501 | 1352498 | sky | 工作线程 (Child/Thread) | 它是主进程派生出的一个轻量级进程(LWP),通常处理 I/O 或信号。 |
内存分析
sudo pmap -x 1351506
1351506: /usr/local/bin/cloud-hypervisor --kernel ./vmlinux --memory size=1024M --pmem file=./alpine_rootfs.erofs --disk path=./microvm-1/write_layer.img --cpus boot=1 --console tty --cmdline console=hvc0 root=/dev/pmem0 ro rootfstype=erofs init=/etc/init.d/rcS
Address Kbytes RSS Dirty Mode Mapping
000055556aece000 4 0 0 ----- [ anon ]
000055556aecf000 8 8 8 rw--- [ anon ]
00007f8d66ff4000 8 0 0 ----- [ anon ]
00007f8d66ff6000 2052 4 4 rw--- [ anon ]
00007f8d671f7000 8 0 0 ----- [ anon ]
00007f8d671f9000 2052 20 20 rw--- [ anon ]
00007f8d673fa000 8 0 0 ----- [ anon ]
00007f8d673fc000 2052 4 4 rw--- [ anon ]
00007f8d675fd000 8 0 0 ----- [ anon ]
00007f8d675ff000 2052 2048 2048 rw--- [ anon ]
00007f8d67800000 10240 2752 0 rw-s- alpine_rootfs.erofs
00007f8d683fd000 8 0 0 ----- [ anon ]
00007f8d683ff000 2052 2048 2048 rw--- [ anon ]
00007f8d68600000 1048576 69632 69632 rw--- [ anon ]
00007f8da87fa000 8 0 0 ----- [ anon ]
00007f8da87fc000 2052 4 4 rw--- [ anon ]
00007f8da89fd000 8 0 0 ----- [ anon ]
00007f8da89ff000 2052 2048 2048 rw--- [ anon ]
00007f8da8c00000 368 368 0 r---- cloud-hypervisor
00007f8da8c5c000 3276 2940 0 r-x-- cloud-hypervisor
00007f8da8f8f000 576 384 0 r---- cloud-hypervisor
00007f8da901f000 600 260 260 rw--- cloud-hypervisor
00007f8da90b5000 8 8 8 rw--- [ anon ]
00007f8da910b000 4 4 4 rw--- [ anon ]
00007f8da910c000 4 0 0 ----- [ anon ]
00007f8da910d000 8 0 0 rw--- [ anon ]
00007f8da910f000 4 4 4 rw--- [ anon ]
00007f8da9110000 4 0 0 ----- [ anon ]
00007f8da9111000 8 0 0 rw--- [ anon ]
00007f8da9113000 124 56 56 rw--- [ anon ]
00007f8da9132000 8 8 8 rw-s- [ anon ]
00007f8da9134000 8 8 4 rw-s- [ anon ]
00007f8da9136000 4 0 0 ----- [ anon ]
00007f8da9137000 8 0 0 rw--- [ anon ]
00007f8da9139000 20 16 16 rw--- [ anon ]
00007f8da913e000 4 0 0 ----- [ anon ]
00007f8da913f000 8 0 0 rw--- [ anon ]
00007f8da9141000 4 0 0 ----- [ anon ]
00007f8da9142000 8 0 0 rw--- [ anon ]
00007f8da9144000 60 60 56 rw--- [ anon ]
00007f8da9153000 12 8 8 rw-s- [ anon ]
00007f8da9156000 4 4 4 rw-s- zero (deleted)
00007f8da9157000 84 80 80 rw--- [ anon ]
00007f8da916c000 4 0 0 ----- [ anon ]
00007f8da916d000 8 0 0 rw--- [ anon ]
00007f8da916f000 4 4 4 rw--- [ anon ]
00007f8da9170000 4 0 0 ----- [ anon ]
00007f8da9171000 8 0 0 rw--- [ anon ]
00007f8da9173000 72 72 72 rw--- [ anon ]
00007f8da9185000 4 0 0 ----- [ anon ]
00007f8da9186000 8 0 0 rw--- [ anon ]
00007f8da9188000 4 4 4 rw--- [ anon ]
00007f8da9189000 16 0 0 r---- [ anon ]
00007f8da918d000 8 4 0 r-x-- [ anon ]
00007ffe2b7bf000 132 60 60 rw--- [ stack ]
---------------- ------- ------- -------
total kB 1078736 82920 76464
启动脚本优化
/usr/local/bin/cloud-hypervisor \
--kernel ./vmlinux \
--memory size=1024M,shared=on \
--pmem file=./alpine_rootfs.erofs \
--disk path=./microvm-1/write_layer.img \
--cpus boot=1 \
--console tty \
--cmdline "console=hvc0 root=/dev/pmem0 ro rootfstype=erofs init=/etc/init.d/rcS quiet mitigations=off lpj=9676800 raid=noautodetect"
参考资料
和 gemini 的交互过程: