В статье «Запуск Windows под Linux KVM» описан баг QEMU-KVM «подвисания гостевой сети при интенсивном трафике» (см. п. «Зависания сети»). По моему опыту интенсивность трафика гостевой машины не играет особенной роли, но ошибка проявляется при большой нагрузке на хост-систему.
Предложенное в статье решение и ссылки на аналогичные «костыли» помогают только в некоторых случаях. Ошибка авторов таких решений в том, что они инициализируют заново только драйвер виртуальной ОС, тогда как причина сбоя находится в самом KVM.
Попробовав разные варианты, я остановился на простом и эффективном решении: отключение и подключение виртуального интерфейса средствами KVM. При отказе сети виртуальной машины скрипт выполняет две команды virsh: detach interface и attach interface.
К сожалению, иногда возникает ситуация, что выполнение команды detach interface «зависает», в этом случае приходится принудительно завершать KVM, а иногда и перезагружать сервер (в случае, когда процесс KVM превращается в «зомби» и модуль kvm не удаётся выгрузить из ядра).
Кроме того, эксплуатация виртуальных машин показала, что чаще всего подвисания возникают при использовании на хост-машине моста с физическим интерфейсом. Разместив виртуальную машину в отдельной подсети, я избавился от частых подвисаний сети VM. При этом, разумеется, пришлось настраивать маршрутизацию.
В результате теперь на нескольких обслуживаемых мной серверах достаточно стабильно работают виртуальные Windows Server разных версий.
Приложение
Пример скрипта для контроля за работой виртуальной сети и передёргивания интерфейса виртуальной машины.
SCRIPTNAME=="$(basename $0)"
VMNAME="srv12"
VMIP="192.168.9.5"
VMIFMAC="52:54:00:f8:37:3f"
VMIFMODEL="virtio"
VMNETNAME="routed"
VMNETDETACH="detach-interface ${VMNAME} network --mac '${VMIFMAC}'"
VMNETATTACH="attach-interface srv12 network ${VMNETNAME} --mac '${VMIFMAC}' --model ${VMIFMODEL}"
VMRUN=$(sudo virsh dominfo srv12 | egrep -q "State:s+running")
if [ "${VMRUN}" -eq 1 ];then
PINGVM=$(ping -c 10 -w 1 -W 1 ${VMIP} | sed -n 's/.*, ([1-9][0-9]*) received,.*/1/p')
if [ "${PINGVM:-0}" -le 0 ];then
logger -s -p daemon.warning -t "${SCRIPTNAME}" "${VMNAME} network is down: ${VMIP} unreachable, try to restart virtual interface"
sudo virsh ${VMNETDETACH}
sudo virsh ${VMNETATTTACH}
logger -s -p daemon.warning -t "${SCRIPTNAME}" "${VMNAME} virtual interface reloaded"
fi
fi