跨过虚拟化边界:如何诊断虚拟机内的容器与智能体故障

2026-09-23 33 预计阅读时间: 1 分钟
来源: oschina.net AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:12 分钟

2026 上海开源软件应用创新大赛以“开源向实”为主题,ZSvirt 在智算云赛道提出了面向虚拟机内部容器与智能体工作负载的命题。这个方向触及了云平台运维中的一个真实难点:管理平台能看到虚拟机是否开机、CPU 是否繁忙,却不一定知道虚拟机内部的容器为什么退出,更难直接判断智能体为何超时、失去上下文或反复重试。

解决这类问题,不能只增加几个监控指标。诊断系统需要跨越宿主机、虚拟机、容器和应用四个层次,把同一时间窗口内的证据串成一条因果链。

故障往往不在报警出现的那一层

一次“智能体请求超时”,可能沿着完全不同的路径产生:

表面现象 可能的底层原因 关键证据
容器被重启 虚拟机内存不足触发 OOM 内核日志、容器退出码、cgroup 内存事件
推理速度突然下降 宿主机 CPU 超分或虚拟机发生 steal 虚拟机 CPU 指标、宿主机调度指标、请求延迟
工具调用失败 DNS、路由、代理或目标服务异常 DNS 查询耗时、连接错误、HTTP 状态码
智能体反复执行同一步骤 上下文丢失、模型响应异常或状态存储失败 trace ID、会话 ID、模型响应、状态读写日志
磁盘仍有空间但写入失败 inode 耗尽、只读挂载或块设备延迟过高 df -i、挂载参数、I/O pressure、内核日志

因此,诊断入口不应只是“虚拟机是否在线”。更有效的问题是:

  1. 故障发生在哪个时间窗口?
  2. 最早出现异常的是哪一层?
  3. 上下游是否使用同一个请求标识?
  4. 当前异常是资源不足、配置错误、依赖失败,还是应用逻辑问题?

尤其要避免看到容器退出便直接重启。重启可能恢复服务,也可能抹掉 /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/cpumemoryio
  • 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。不过,应明确采集动作是在虚拟机内部执行,还是由集群控制面发起,以免重复数据或权限边界混乱。

从采集数据走向自动诊断

自动诊断不应直接把全部日志交给大模型并接受一个结论。更可靠的流水线可以分成四步:

  1. 规则化采集:用固定 schema 表示指标、事件、配置变更和日志摘要。
  2. 时间关联:将宿主机、虚拟机、容器和请求事件映射到同一时间轴。
  3. 候选原因排序:先用确定性规则筛选,例如“OOM 记录 + 容器退出码 137 + 内存 pressure 上升”。
  4. 解释与建议:让智能体概括证据、指出缺失信息并生成只读验证命令,而不是直接执行重启、扩容或删除操作。

诊断结论还应区分三个等级:已证实、强相关和待验证。例如,“容器退出前 10 秒出现 cgroup OOM 事件”可以视为强证据;“CPU 较高,所以网络超时”通常只是弱相关。

智能体适合完成证据归纳、相似故障检索和排查步骤生成,但不应绕过权限系统。涉及重启虚拟机、调整资源配额、修改网络规则等动作时,应保留人工确认、操作审计和回滚方案。

落地时优先检查这几件事

面向虚拟机内部工作负载建设诊断能力时,可以先用以下清单约束方案:

  • 是否同时覆盖宿主机、虚拟机、容器和应用,而不是只装一个监控代理?
  • 所有节点和虚拟机的时钟是否同步?
  • VM ID、容器 ID、trace ID 和智能体 session ID 能否相互关联?
  • 故障发生后,系统能否在重启前保存短时间窗口内的证据?
  • 采集工具是否限制 CPU、内存、磁盘和网络开销?
  • 日志中是否包含提示词、token、凭据或用户隐私?
  • 自动生成的诊断结论是否展示原始证据与置信度?
  • 修复动作是否需要审批,并且能够回滚?

真正有价值的虚拟机故障诊断,不是把更多日志堆进一个页面,而是回答“哪一层先出问题、哪些证据支持这个判断、下一条验证命令是什么”。围绕这三个问题设计,才能让智能体成为工程师的排障助手,而不是另一个不透明的告警来源。


相关推荐