1. 故障现象与初步判断
那天早上刚到办公室,就收到监控系统发来的告警邮件:生产环境中的某台KVM虚拟机突然变成了"paused"状态。登录宿主机查看,确实看到虚拟机状态异常:
# virsh list --all Id Name State ---------------------------------- 3 prod-web-01 paused这台虚拟机承载着公司官网的业务,必须立即恢复。我首先尝试用virsh resume命令恢复虚拟机:
# virsh resume prod-web-01 error: Failed to resume domain prod-web-01 error: internal error: cannot resume domain: Input/output error错误信息显示是I/O错误,这提示可能是存储方面的问题。于是立即检查虚拟机日志:
# virsh dumpxml prod-web-01 | grep -i log <log file='/var/log/libvirt/qemu/prod-web-01.log'/> # tail -n 50 /var/log/libvirt/qemu/prod-web-01.log ... 2023-07-15 08:23:45.581+0000: shutting down, reason=failed 2023-07-15 08:23:45.632+0000: cannot write to '/var/lib/libvirt/images/prod-web-01.qcow2': No space left on device关键线索出现了——"No space left on device"。看来是磁盘空间耗尽导致虚拟机无法正常运行。
2. 宿主机磁盘空间排查
2.1 快速定位问题根源
立即检查宿主机磁盘使用情况:
# df -h Filesystem Size Used Avail Use% Mounted on /dev/sda1 50G 50G 0G 100% / ...根分区已经100%占满,这正是导致虚拟机I/O错误的直接原因。进一步分析空间占用:
# du -sh /* | sort -rh | head -n 5 25G /var 15G /usr 6.5G /home 3.2G /lib 1.1G /opt/var目录异常占用了25G空间,这明显不正常。继续深入:
# du -sh /var/* | sort -rh | head -n 5 18G /var/lib 4.2G /var/log 1.3G /var/cache ...最终定位到是/var/lib/libvirt/images目录下的虚拟机磁盘文件过大:
# ls -lh /var/lib/libvirt/images/ total 18G -rw------- 1 qemu qemu 18G Jul 15 08:20 prod-web-01.qcow22.2 空间占用的深层原因
这台虚拟机的磁盘配置原本只有50GB,为什么qcow2文件会膨胀到18GB?通过检查虚拟机配置:
# virsh domblklist prod-web-01 Target Source ----------------------------------------- vda /var/lib/libvirt/images/prod-web-01.qcow2查看磁盘格式和实际使用情况:
# qemu-img info /var/lib/libvirt/images/prod-web-01.qcow2 image: /var/lib/libvirt/images/prod-web-01.qcow2 file format: qcow2 virtual size: 50 GiB (53687091200 bytes) disk size: 18 GiB cluster_size: 65536 Format specific information: compat: 1.1 lazy refcounts: false refcount bits: 16 corrupt: false问题出在虚拟机内部的文件系统上。登录虚拟机检查(通过virt-rescue):
# virt-rescue -a /var/lib/libvirt/images/prod-web-01.qcow2 ><rescue> fdisk -l ><rescue> vgscan ><rescue> lvscan发现虚拟机内部分区使用率确实很高,但更严重的是存在大量被删除但仍被进程占用的文件:
><rescue> lsof | grep deleted nginx 1234 root 5r REG 253,0 4294967296 123456 /var/log/nginx/access.log (deleted) ...3. 紧急处理与恢复方案
3.1 立即释放磁盘空间
首先需要立即释放宿主机根分区的空间:
- 清理系统日志文件:
# journalctl --vacuum-size=200M # rm -f /var/log/*.gz- 删除旧的kernel包:
# package-cleanup --oldkernels --count=1- 清理yum缓存:
# yum clean all- 临时移动大文件(可选):
# mv /var/lib/libvirt/images/prod-web-01.qcow2 /mnt/backup/ # ln -s /mnt/backup/prod-web-01.qcow2 /var/lib/libvirt/images/3.2 恢复虚拟机运行
释放部分空间后,尝试恢复虚拟机:
# virsh start prod-web-01如果仍然失败,可以尝试:
- 使用qemu-img检查并修复磁盘:
# qemu-img check -r all /var/lib/libvirt/images/prod-web-01.qcow2- 转换磁盘格式释放空间:
# qemu-img convert -O qcow2 /var/lib/libvirt/images/prod-web-01.qcow2 /mnt/backup/prod-web-01-new.qcow2 # mv /mnt/backup/prod-web-01-new.qcow2 /var/lib/libvirt/images/prod-web-01.qcow23.3 虚拟机内部清理
成功启动虚拟机后,立即进行内部清理:
- 查找并清理大文件:
# find / -xdev -type f -size +100M -exec ls -lh {} \;- 清理日志文件:
# truncate -s 0 /var/log/*.log- 重启占用已删除文件的进程:
# systemctl restart nginx # systemctl restart mysql4. 根本解决方案与预防措施
4.1 宿主机存储规划最佳实践
分离存储分区:
- /var/lib/libvirt/images单独挂载大容量分区
- 系统分区保持精简
使用LVM管理存储:
# pvcreate /dev/sdb # vgcreate vg_vm /dev/sdb # lvcreate -L 500G -n lv_vm vg_vm # mkfs.xfs /dev/vg_vm/lv_vm # mount /dev/vg_vm/lv_vm /var/lib/libvirt/images- 设置磁盘空间监控:
# vim /etc/cron.hourly/check_disk #!/bin/bash THRESHOLD=90 df -h | awk '0+$5 >= '$THRESHOLD' {print $6}'4.2 虚拟机存储配置优化
- 启用磁盘空间监控:
<domain> ... <devices> <disk type='file' device='disk'> <driver name='qemu' type='qcow2'/> <source file='/var/lib/libvirt/images/prod-web-01.qcow2'/> <target dev='vda' bus='virtio'/> <iotune> <read_bytes_sec>10000000</read_bytes_sec> <write_bytes_sec>10000000</write_bytes_sec> </iotune> </disk> </devices> </domain>- 使用精简配置(thin provisioning):
# qemu-img create -f qcow2 -o preallocation=metadata prod-web-01.qcow2 50G- 定期压缩磁盘:
# qemu-img convert -O qcow2 -c prod-web-01.qcow2 prod-web-01-compressed.qcow24.3 日志管理策略
- 配置日志轮转:
# vim /etc/logrotate.d/nginx /var/log/nginx/*.log { daily missingok rotate 14 compress delaycompress notifempty create 0640 www-data adm sharedscripts postrotate [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid` endscript }使用日志收集系统:
- ELK Stack
- Graylog
- Loki
限制日志级别:
# vim /etc/rsyslog.conf *.info;mail.none;authpriv.none;cron.none /var/log/messages5. 高级排查技巧与工具
5.1 使用libguestfs工具集
- 检查虚拟机文件系统:
# virt-df -h prod-web-01- 直接编辑虚拟机文件:
# virt-edit -d prod-web-01 /etc/fstab- 批量清理文件:
# virt-ls -l -R -d prod-web-01 /var/log | grep -i '\.log$'5.2 KVM高级调试技巧
- 启用详细日志:
# virsh dumpxml prod-web-01 > prod-web-01.xml # vim prod-web-01.xml <domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'> ... <qemu:commandline> <qemu:arg value='-d'/> <qemu:arg value='guest_errors'/> </qemu:commandline> </domain> # virsh define prod-web-01.xml- 使用QEMU monitor:
# virsh qemu-monitor-command prod-web-01 --hmp "info block"- 性能监控:
# virt-top5.3 自动化监控方案
- Prometheus监控配置:
# vim /etc/prometheus/prometheus.yml scrape_configs: - job_name: 'libvirt' metrics_path: '/metrics' static_configs: - targets: ['localhost:9177']Grafana仪表板:
- 导入Libvirt仪表板ID: 10810
自定义告警规则:
# vim /etc/prometheus/rules.yml groups: - name: libvirt.rules rules: - alert: HighDiskUsage expr: libvirt_domain_info_disk_usage_bytes / libvirt_domain_info_disk_capacity_bytes > 0.8 for: 5m labels: severity: warning annotations: summary: "High disk usage on {{ $labels.domain }}" description: "Disk usage is {{ $value }}% for {{ $labels.domain }}"6. 经验总结与避坑指南
6.1 血泪教训
- 不要将所有虚拟机存储在系统分区
- 定期检查qcow2文件的实际大小与虚拟大小的比例
- 注意被删除但仍被进程占用的文件
- 日志文件是空间杀手,必须严格管理
- 监控系统必须覆盖宿主机磁盘空间指标
6.2 推荐工具链
空间分析:
- ncdu
- baobab
- du -sh * | sort -h
虚拟机管理:
- virt-manager
- cockpit-machines
- virsh
日志管理:
- logrotate
- systemd-journald
- rsyslog
6.3 性能调优参数
- I/O调度器设置:
# echo deadline > /sys/block/sda/queue/scheduler- KVM内核参数优化:
# vim /etc/sysctl.conf vm.swappiness = 10 vm.dirty_ratio = 20 vm.dirty_background_ratio = 10- QEMU性能参数:
<cpu mode='host-passthrough' check='none'/> <memoryBacking> <hugepages/> </memoryBacking> <ioThreads>4</ioThreads>