一、工具定位vm-builder 是一个 Go CLI 工具核心功能将任意 Docker 镜像转换为可在 QEMU VM 中运行的磁盘镜像qcow2。它把容器化应用打包成一个完整的虚拟机环境使用busybox init 作为初始化系统通过虚拟 CD-ROM 传递启动命令。---二、Go 主流程main.go1. 参数解析与校验L155-L208--src 源 Docker 镜像必需如 alpine:3.19--dst 输出 Docker 镜像名不传则自动派生 vm-{src}--file 导出 qcow2 磁盘文件路径可选--spec 自定义 YAML 规格文件可选--daemon-image neonvm-daemon 镜像必需编译时注入或命令行指定--target-arch linux/amd64 | linux/arm64必需--pull 强制拉取源镜像--build-arg 传递给 Docker build 的构建参数关键前置校验- NeonvmDaemonImage 必须设置通过 ldflags 编译注入或 --daemon-image 覆盖- --target-arch 必须在 amd64/arm64 中选一个- 读取并验证 --spec YAML 文件imageSpec 结构体包含 Commands、Files、Build、Merge、ShutdownHook2. Docker 凭证加载L210-L224从 ~/.docker/config.json 加载认证信息构建 authConfigs 映射用于后续镜像拉取和推送。3. Docker 客户端建立L226-L262- 支持自定义 Docker contextdocker context use xxx 设置的远程 daemon- 通过 client.FromEnv client.WithAPIVersionNegotiation() 连接 Docker daemon4. 源镜像拉取L264-L333- 检查本地是否已有 --src 镜像- 若无或 --pull 为 true则调用 cli.ImagePull 拉取- 智能处理 docker.io 的 legacy 配置兼容问题5. 构建模板上下文L335-L417关键步骤从源镜像提取 imageSpec.ConfigEntrypoint、Cmd、Env、User并进行双重 shell 转义因为要嵌入到 shell 脚本中。如果提供了 --spec 文件- 将 spec 中的 Build/Merge/Dockerfile 片段填入模板- 将 spec 中的 Commands 转换为 inittab 条目每个命令带 ulimit -c unlimited- 将 spec 中定义的文件hostPath 或 content添加到 tar 构建上下文6. 生成 Docker 构建上下文L378-L442将所有嵌入式文件通过 Go text/template 渲染后打包进 tarball将所有嵌入式文件通过 Go text/template 渲染后打包进 tarball文件名来源作用Dockerfileimg.Dockerfile多阶段构建定义helper.move-bins.sh嵌入移动二进制及依赖库到 /neonvm/vmstart嵌入容器启动脚本生成 vmstarter.sh 并执行vminit嵌入系统初始化挂载、udev、网络等vmshutdown嵌入优雅关闭协调删除 vmstart.allowed、等待进程退出vmacpi嵌入ACPI 电源按钮事件处理inittab嵌入busybox init 配置vector.yaml嵌入指标采集配置chrony.conf嵌入时间同步配置sshd_config嵌入SSH 服务配置udev-init.sh嵌入virtio 端口设备初始化resize-swap.sh嵌入动态调整交换空间set-disk-quota.sh嵌入磁盘配额管理7. 调用 Docker BuildL444-L482设置构建参数 VM_BUILDER_DISK_SIZE 和 VM_BUILDER_TARGET_ARCH调用 cli.ImageBuild 执行多阶段 Docker 构建输出目标镜像如 vm-alpine:3.19。8. 可选导出 qcow2 磁盘文件L484-L542如果指定了 --file1. 用刚构建的镜像创建一个临时容器2. 从容器中复制 /disk.qcow2 文件3. 解压 tar 流写入指定的 .qcow2 文件4. 移除临时容器---三、Dockerfile 多阶段构建img.DockerfileStage 1: rootdisk ←从 --src 源镜像开始Stage 2: rootdisk-mod ←在 rootdisk 上安装 neonvm 运行时组件Stage 3: builder ←用 qemu-img 创建 .qcow2 磁盘Stage 4: 最终镜像 ←只包含 disk.qcow2极简构建细节- 从 busybox 镜像获取基础 shell- 从 Alpine 安装必要工具acpid, udev, agetty, su-exec, flock 等- 将系统二进制通过 helper.move-bins.sh 复制到 /neonvm/bin/依赖库放到 /neonvm/lib/- 安装 Vector指标采集、chrony时间同步、sshdSSH 访问、quota 工具- 从 neonvm-daemon 镜像复制 neonvmdVM 运行守护进程- 使用 mkfs.ext4 qemu-img convert 生成 qcow2 磁盘---四、VM 运行时生命周期启动阶段busybox init →inittab/etc/inittab 定义了 init 进程管理的全部服务按顺序1. ::sysinit:/neonvm/bin/vminit ←系统初始化挂载、udev、网络2. ::once:/neonvm/bin/touch /neonvm/vmstart.allowed ←允许 vmstart 启动3. ::once:cat /proc/version ←打印内核版本4. ::respawn:udhcpc ←DHCP 获取 IP5. ::respawn:udevd ←udev 设备管理器6. ::wait:udev-init.sh ←等待 udev 初始化完成7. ::respawn:acpid ←ACPI 事件处理器8. ::respawn:vector ←指标采集代理9. ::respawn:chronyd ←时间同步10. ::respawn:sshd ←SSH 服务器11. ::respawn:neonvmd ←NeonVM 守护进程与宿主机通信12. ::respawn:vmstart ←启动容器工作负载Docker entrypoint/cmd13. [spec.commands...] ←用户自定义 inittab 命令14. agetty on ttyS0/ttyAMA0 ←串口登录amd64/arm6415. ::shutdown:vmshutdown ←关机钩子工作负载启动vmstart# 1. 生成 /neonvm/bin/vmstarter.sh# - 写入环境变量来自源镜像 Config.Env# - 追加 /neonvm/runtime/env.sh如果有# - 追加 /neonvm/runtime/command.sh 或使用源镜像 Entrypoint# - 追加 /neonvm/runtime/args.sh 或使用源镜像 Cmd# 2. 通过 flock 互斥 vmstart.allowed 标志检查执行# su-exec user sh vmstarter.sh这相当于 Docker 的 --restartalways 行为——ini的 respawn 策略会在容器退出后自动重启。优雅关闭ACPI 关机流程这是整个设计中最精妙的部分详见 doc/vm-builder/README.md 的 TLA 模型K8s 发送 SIGTERM →vmrunner 发送 ACPI 电源按钮事件→QEMU 转发给 Guest Kernel→acpid 收到 button/power 事件→执行 /neonvm/bin/poweroff→busybox poweroff 发送 SIGUSR2 给 init→init 停止 respawn、执行 shutdown 动作→运行 vmshutdown1. rm /neonvm/vmstart.allowed 阻止新的 vmstart 启动2. flock 等待 vmstarter.sh 退出确保容器进程已停3. ShutdownHook 执行用户自定义清理→init 发送 SIGTERM →等待 1s →SIGKILL 所有进程→调用内核 poweroff核心同步机制vmstart.allowed标志文件 vmstart.lockflock 文件锁确保优雅关机时不会启动新容器进程。---五、关键设计决策总结设计点实现方式最小化 VM 体积busybox 静态编译二进制仅安装必要组件跨架构支持通过 --target-arch 选择 busybox/alpine 的 digest构建时指定 platform容器命令注入虚拟 CD-ROMiso9660挂载到 /neonvm/runtime传递 command/args/env容器重启语义inittab 的 respawn 策略 ≈ Docker --restartalways优雅关机ACPI 事件 → poweroff → SIGUSR2 → init shutdown → flock 协调可定制性--spec YAML 允许自定义 Dockerfile 片段、额外文件、自定义命令指标/监控Vector 采集 host_metrics 暴露 Prometheus 端点时间同步chrony 配置 PHCPrecision Hardware Clock