2026 上海开源软件应用创新大赛以“开源向实”为主题,ZSvirt 在智算云赛道提出了面向虚拟机内部容器与智能体工作负载的命题。这个方向触及了云平台运维中的一个真实难点:管理平台能看到虚拟机是否开机、CPU 是否繁忙,却不一定知道虚拟机内部的容器为什么退出,更难直接判断智能体为何超时、失去上下文或反复重试。
解决这类问题,不能只增加几个监控指标。诊断系统需要跨越宿主机、虚拟机、容器和应用四个层次,把同一时间窗口内的证据串成一条因果链。
故障往往不在报警出现的那一层
一次“智能体请求超时”,可能沿着完全不同的路径产生:
| 表面现象 | 可能的底层原因 | 关键证据 |
|---|---|---|
| 容器被重启 | 虚拟机内存不足触发 OOM | 内核日志、容器退出码、cgroup 内存事件 |
| 推理速度突然下降 | 宿主机 CPU 超分或虚拟机发生 steal | 虚拟机 CPU 指标、宿主机调度指标、请求延迟 |
| 工具调用失败 | DNS、路由、代理或目标服务异常 | DNS 查询耗时、连接错误、HTTP 状态码 |
| 智能体反复执行同一步骤 | 上下文丢失、模型响应异常或状态存储失败 | trace ID、会话 ID、模型响应、状态读写日志 |
| 磁盘仍有空间但写入失败 | inode 耗尽、只读挂载或块设备延迟过高 | df -i、挂载参数、I/O pressure、内核日志 |
因此,诊断入口不应只是“虚拟机是否在线”。更有效的问题是:
- 故障发生在哪个时间窗口?
- 最早出现异常的是哪一层?
- 上下游是否使用同一个请求标识?
- 当前异常是资源不足、配置错误、依赖失败,还是应用逻辑问题?
尤其要避免看到容器退出便直接重启。重启可能恢复服务,也可能抹掉 /proc、临时文件、网络连接和容器状态等关键现场。
建立四层证据链
可以把诊断数据分成四层,并统一时间戳、实例标识和关联 ID。
1. 宿主机与虚拟化层
关注虚拟机生命周期、vCPU 调度、内存气球、块设备延迟、虚拟网卡丢包和存储后端状态。宿主机资源充足,不代表某个虚拟机没有受到 CPU 超分、I/O 抖动或网络队列拥塞的影响。
如果环境基于 libvirt,可以这样进行基础检查;使用其他虚拟化平台时,应替换为对应的只读接口:
VM="your-vm-name"
sudo virsh domstate "$VM"
sudo virsh domstats "$VM" --balloon --vcpu --block --interface
sudo virsh dumpxml "$VM" > "${VM}-definition.xml"
采集配置快照很重要,因为故障可能来自近期变更,例如 CPU 拓扑、磁盘缓存模式或网卡模型发生变化。
2. 虚拟机操作系统层
虚拟机内部需要观察 CPU、内存、I/O、文件系统、内核和网络。Linux PSI(Pressure Stall Information)尤其有用:CPU 使用率没有达到 100% 时,任务仍可能因内存回收或 I/O 等待而持续阻塞。
应至少采集:
/proc/pressure/cpu、memory和io;dmesg或 systemd journal 中的 OOM、块设备和网卡错误;- 磁盘容量与 inode 使用情况;
- 地址、路由、DNS 配置和 socket 摘要;
- NTP 或 chrony 状态,避免跨层日志因时钟漂移无法对齐。
3. 容器运行时层
除了容器是否运行,还要记录退出码、重启次数、资源限制、健康检查、挂载和运行时事件。退出码 137 常与 SIGKILL 有关,但不能仅凭这个数字认定发生了 OOM;还应核对内核日志、cgroup 事件和编排系统记录。
4. 智能体与应用层
智能体工作负载需要比普通 HTTP 服务更完整的上下文。建议在结构化日志中保留:
{
"timestamp": "2026-04-18T08:31:22.481Z",
"trace_id": "9af0d7f1c5124a3a",
"session_id": "session-42",
"vm_id": "vm-17",
"container_id": "8b6d...",
"agent_step": 6,
"tool_name": "inventory_lookup",
"model_latency_ms": 1840,
"tool_latency_ms": 5032,
"result": "timeout"
}
生产环境不要直接记录完整提示词、模型响应、密钥或用户隐私。更稳妥的做法是记录脱敏摘要、token 数量、模型版本、工具名称、耗时和错误分类,并为敏感调试数据设置严格的访问控制与保存期限。
一个可直接改造的虚拟机现场采集脚本
下面的脚本可以在 Linux 虚拟机内部运行,收集系统、网络、压力指标和常见容器运行时信息。它只执行读取操作,但部分日志需要 root 权限;建议先以普通用户运行,确有需要时再使用 sudo。
cat > collect-vm-diag.sh <<'EOF'
#!/usr/bin/env bash
set -Eeuo pipefail
OUT="${1:-vm-diag-$(date +%Y%m%d-%H%M%S)}"
mkdir -p "$OUT"
run() {
local name="$1"
shift
{
printf '$'
printf ' %q' "$@"
printf '\n'
"$@"
} >"$OUT/$name.txt" 2>&1 || true
}
run system uname -a
run uptime uptime
run failed_units systemctl --failed --no-pager
run memory free -h
run filesystems df -hT
run inodes df -ih
run mounts findmnt
run block_devices lsblk -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINTS
run pressure bash -c 'for f in /proc/pressure/cpu /proc/pressure/memory /proc/pressure/io; do echo "== $f =="; cat "$f"; done'
run network_addresses ip -details address
run network_routes ip route show table all
run sockets ss -s
run resolv_conf cat /etc/resolv.conf
run kernel_log dmesg --ctime
run journal journalctl -b --no-pager -n 1000
if command -v docker >/dev/null 2>&1; then
run docker_info docker info
run docker_ps docker ps -a --no-trunc
run docker_stats docker stats --no-stream
elif command -v podman >/dev/null 2>&1; then
run podman_info podman info
run podman_ps podman ps -a --no-trunc
run podman_stats podman stats --no-stream --all
elif command -v crictl >/dev/null 2>&1; then
run crictl_info crictl info
run crictl_ps crictl ps -a
run crictl_stats crictl stats
else
printf 'No supported container CLI found.\n' >"$OUT/container_runtime.txt"
fi
printf '%s\n' "$(date --iso-8601=seconds)" >"$OUT/collected_at.txt"
tar -czf "${OUT}.tar.gz" "$OUT"
printf 'Diagnostic bundle: %s.tar.gz\n' "$OUT"
EOF
chmod +x collect-vm-diag.sh
./collect-vm-diag.sh
执行后会生成类似 vm-diag-20260418-163122.tar.gz 的压缩包。投入实际环境前建议进行三项改造:
- 增加业务进程、智能体服务和指定容器的日志采集,但限制日志行数和时间范围;
- 对主机名、IP、环境变量、挂载路径和配置内容进行脱敏;
- 为采集包设置自动过期、加密和审计策略,避免诊断工具本身成为数据泄露入口。
如果容器运行在 Kubernetes 节点上,还可以补充 kubectl describe pod、Pod 事件和 kubectl logs --previous。不过,应明确采集动作是在虚拟机内部执行,还是由集群控制面发起,以免重复数据或权限边界混乱。
从采集数据走向自动诊断
自动诊断不应直接把全部日志交给大模型并接受一个结论。更可靠的流水线可以分成四步:
- 规则化采集:用固定 schema 表示指标、事件、配置变更和日志摘要。
- 时间关联:将宿主机、虚拟机、容器和请求事件映射到同一时间轴。
- 候选原因排序:先用确定性规则筛选,例如“OOM 记录 + 容器退出码 137 + 内存 pressure 上升”。
- 解释与建议:让智能体概括证据、指出缺失信息并生成只读验证命令,而不是直接执行重启、扩容或删除操作。
诊断结论还应区分三个等级:已证实、强相关和待验证。例如,“容器退出前 10 秒出现 cgroup OOM 事件”可以视为强证据;“CPU 较高,所以网络超时”通常只是弱相关。
智能体适合完成证据归纳、相似故障检索和排查步骤生成,但不应绕过权限系统。涉及重启虚拟机、调整资源配额、修改网络规则等动作时,应保留人工确认、操作审计和回滚方案。
落地时优先检查这几件事
面向虚拟机内部工作负载建设诊断能力时,可以先用以下清单约束方案:
- 是否同时覆盖宿主机、虚拟机、容器和应用,而不是只装一个监控代理?
- 所有节点和虚拟机的时钟是否同步?
- VM ID、容器 ID、trace ID 和智能体 session ID 能否相互关联?
- 故障发生后,系统能否在重启前保存短时间窗口内的证据?
- 采集工具是否限制 CPU、内存、磁盘和网络开销?
- 日志中是否包含提示词、token、凭据或用户隐私?
- 自动生成的诊断结论是否展示原始证据与置信度?
- 修复动作是否需要审批,并且能够回滚?
真正有价值的虚拟机故障诊断,不是把更多日志堆进一个页面,而是回答“哪一层先出问题、哪些证据支持这个判断、下一条验证命令是什么”。围绕这三个问题设计,才能让智能体成为工程师的排障助手,而不是另一个不透明的告警来源。