虚拟化环境中 VM 的时间同步是很重要的,时间失去同步可能导致诸如 Git、APT、SSL/TLS 等很多服务出现异常,而虚拟机的系统时间还容易受到休眠和在线迁移的影响。

在生产环境常用的 Hyper-V 和 QEMU/KVM 中,如何优雅地使虚拟机的系统时间和外部时间保持同步呢?


虚拟机中的时间同步大致有两种方案:

  • 和网络时间(NTP 服务器)同步
  • 和宿主机时间同步

大多数系统中已经默认配置了第一种 NTP 时间同步,然而,虚拟机中使用第一种方法需要可靠的网络连接,且更容易受到外部因素(休眠、在线迁移等)影响。

而第二种方法只需要保证宿主机时间精确(Azure 和其他大多数 VPS 的服务商其实也都做到了这个),然后让虚拟机跟随宿主机时钟同步即可,使用这种方法通常精准度会更高,性能开销也稍低一些。

所以在此只介绍第二种方法,适用于 Hyper-V 和 QEMU/KVM,包括 Proxmox VE 等基于 QEMU 魔改的虚拟机也可以,这些虚拟机程序和 VM 时间同步都使用 PTP 协议,简单的介绍一下 PTP:

PTP

即 Precision Time Protocol,精确时间协议,PTP 提供微秒级的精度,适用于要求极高时间同步的场景,而常见的 NTP 通常只提供毫秒级的精度,PTP 使用主从模式,主时钟负责同步所有从时钟,通常更适用于局域网环境;NTP 较简单,适合广域网和互联网同步。

确保宿主机时间精确之后,照着下面的说明为虚拟机开启时间同步

宿主机操作

在宿主机上为虚拟机启用 PTP 时间同步设备

  • Hyper-V
    虚拟机设置里找到 Integration Services,勾上 Time Synchronization,保存
    理论上所有 Hyper-V 版本都支持这个
    Hyper-V Time Synchronization Option
  • QEMU/KVM
    默认应该是自动启用的,如果你坚信他没启用,可以试试把 -device kvm-ptp 塞进 QEMU 参数(大模型说的,笔者感觉这个参数没用,加了可能反而会炸)
    翻了一下源码和提交记录,QEMU 是从 7ec6832(6.0.50 或 6.1 的开发版本) ,2021 年 6 月 18 日开始支持时间同步特性的,太老的版本也许是没有的

Linux VM

Linux 中推荐使用 chrony 来设置时间同步,部分发行版自带了不支持 PTP 协议的 systemd-timesyncd,少部分发行版默认就提供了配置好的 chrony

我们这时候通过 chronyc tracking 命令来检查 chrony 的工作状态:

  • text
1
2
3
4
5
6
7
8
9
10
11
12
13
Reference ID    : 50484330 (PHC0)
Stratum : 3
Ref time (UTC) : Thu Oct 17 16:26:56 2024
System time : 0.000000000 seconds fast of NTP time
Last offset : -0.000001475 seconds
RMS offset : 0.000000613 seconds
Frequency : 0.008 ppm slow
Residual freq : -0.019 ppm
Skew : 0.064 ppm
Root delay : 0.000000001 seconds
Root dispersion : 0.000010623 seconds
Update interval : 8.0 seconds
Leap status : Normal

主要看第一行 Reference ID 即可:

  • 如果括号内显示的是 PHCx,说明系统已经配置好了和宿主机的时间同步(部分云厂商会帮你做好这件事),然后你大致就可以关闭本页面了(?
  • 如果括号内只有域名或者 IP,说明系统只配置了 NTP 同步,继续按照下面的方法配置即可
  • 当然你可能会直接得到一个 command not found,说明你没有安装 chrony,可以参考下面的命令来安装,不同发行版安装方法不同,以 Debian 为例:
    • sh
    1
    apt install chrony

    安装 chrony 会自动移除 systemd-timesyncd 和其他时间同步工具

检查 PTP 设备

运行 ls /dev/ptp_* -lah 来检查 VM 里是否已经有 PTP 设备

  • sh
1
2
3
4
# Hyper-V
lrwxrwxrwx 1 root root 4 Oct 17 13:17 /dev/ptp_hyperv -> ptp0
# QEMU/KVM
lrwxrwxrwx 1 root root 4 Oct 17 13:50 /dev/ptp_kvm -> ptp0

如果报错 No such file or directory,尝试载入虚拟机的 PTP 模块

  • Hyper-V
    • sh
    1
    2
    modprobe hv_utils
    # echo hv_utils >> /etc/modules # Load module on boot
    (不过理论上正常系统检测到 Hyper-V 应该会自动载入的…真的会有默认不载入的吗
  • QEMU/KVM
    • sh
    1
    2
    modprobe ptp_kvm
    echo ptp_kvm >> /etc/modules # Load module on boot
    (倒是 QEMU 的貌似不会自动载入

添加时钟源

PTP 设备出现以后就可以开始配置 chrony 了:

先编辑 /etc/chrony/chrony.conf,注释掉所有以 poolsourcedir 开始的行,然后添加硬件时钟:

  • Hyper-V
    • sh
    1
    2
    3
    echo "maxupdateskew 100.0
    refclock PHC /dev/ptp_hyperv poll 3 dpoll -2 offset 0 stratum 2
    makestep 1.0 -1" > /etc/chrony/conf.d/hv.conf
  • QEMU/KVM
    • sh
    1
    2
    3
    echo "maxupdateskew 100.0
    refclock PHC /dev/ptp_kvm poll 3 dpoll -2 offset 0 stratum 2
    makestep 1.0 -1" > /etc/chrony/conf.d/kvm.conf

完成配置

重启 chrony,查看时间同步信息

  • text
1
2
3
systemctl restart chrony
sleep 30
chronyc tracking

第一行显示 PHCx 说明已配置完成,可以看到 PTP 时间同步的延迟低至 0.000000001 seconds,大致能比 NTP 高 10 的 5-7 次方

Windows VM

(待补充)