kvm guest虚拟机时间管理

为什么guest虚拟机需要精确的时钟

虚拟化涉及到guest虚拟机时钟精度的多项挑战:

  • 中断不能始终同时和即时发送给所有guest虚拟机。这就导致在虚拟机中的中断不是真实的中断。相反,虚拟机的中断是通过host主机注入到虚拟机中的。

  • host主机可能在运行其他虚拟机,或者其他进程。精确的时钟通常依赖中断,就不能始终保证。

guest虚拟机失去精确的时钟可能会导致网络应用和进程,会话校验,迁移,以及其他依赖时间戳的网络活动出现异常。

KVM通过为guest虚拟机提供一个paravirtual时钟(kvm-clock)来避免上述问题。然而,在影响时钟精度的操作,例如guest虚拟机迁移,需要非常仔细测试时间。

重要!!!

为避免上述问题,在host主机和guest虚拟机上,都要运行网络时钟协议(NTP)服务。在RHEL 6和早期版本,NTP是通过ntpd服务来是实现的。在最新的RHEL 7系统,时钟同步服务由ntpd或者chronyd服务实现。

注意:在虚拟机中,chrony有一些优势,详细可参考「Red Hat Enterprise Linux 7 System Administrator's Guide」章节Configuring NTP Using the chrony SuiteConfiguring NTP Using ntpd

chronydntpd具有优势的方便包括:

  • 只需要间歇性访问时钟源

  • 适合拥塞的网络而工作良好

  • 通常同步时钟更快更精确

  • ntpd可以更快调整主机时间

  • chronyd可以在Linux主机中以一个较大范围来调整时钟频率,也就是允许在主机时钟中断或不稳定时工作。例如,适合在虚拟机中

不过,ntpd在标准协议以及支持更多的时钟参考源上比chronyd具有优势。

guest虚拟机时间同步的机制

默认情况下,guest使用以下方式和hypervisor之间进行时间同步:

  • guest系统启动时,会从模拟的实时时钟(emulated Real Time Clock, RTC)读取时间

  • 当NTP服务初始化之后,它将自动同步guest时钟。之后,随着常规的guest操作,NTP在guest内部执行时钟校准

  • 当guest在暂停(pause)或者还原进程之后继续,通过管理软件(例如virt-manager)发出一个同步guest时钟到指定值的指令。这个同步工作值在QEMU guest agent安装在guest系统中并且支持这个功能的时候才会工作。这个guest时钟同步的值通常就是host主机的时钟值。

稳定时间戳计数器(Constant Time Stamp Counter, TSC)

现代Intel和AMD处理器提供了一个稳定时间戳计数器(Constant Time Stamp Counter, TSC)。这个稳定的TSC的计数频率不会随着CPU核心更改频率而改变,例如,节电策略导致的cpu主频降低不会影响TSC计数。一个CPU具有稳定的TSC频率对于使用TSC作为KVM guest的时钟源时非常重要的。

要查看CPU是否具有稳定的时间戳计数器(constant TSC)需要检查cpuinfo中是否有constant_tsc标志:

cat /proc/cpuinfo | grep constant_tsc

如果上述命令没有任何输出,则表明cpu缺少稳定的TSC特性,需要采用其他时钟源。详见下文。

windows虚拟机同时使用Real-Time Clock(RTC)和Time Stamp Counter(TSC)。对于Windows guest,Real-Time Clock可以用于取代TSC作为所有的时钟源来解决guest时间问题。详细设置和介绍见Red Hat Enterprise Linux 5 Virtualization Guide: Chapter 17. KVM guest timing management

针对没有稳定时间戳计数器(constant tsc)的主机配置

对于没有constant TSC主频的系统不能使用TSC作为虚拟机的时钟源,并且需要附加的配置。对于guest虚拟机,带有精确时间保持功能的电源管理功能接口必须被禁用,以在KVM中精确维持时间。

重要:这部分介绍只针对AMD revision F CPU

如果CPU缺少constant_tsc标志位,则需要关闭所有的电源管理功能。(电源管理会导致主频变化,如果CPU没有tsc会导致计时不准确)。

如果主机上TSC不稳定,深度的C状态或者迁移到其他具有更快TSC的主机就有可能会导致cpufreq变化。为了避免内核进入深度C状态(也就是深度节电状态),需要在内核启动参数加上processor.max_cstate=1

举例:

term Red Hat Enterprise Linux Server (2.6.18-159.el5)
        root (hd0,0)
    kernel /vmlinuz-2.6.18-159.el5 ro root=/dev/VolGroup00/LogVol00 rhgb quiet processor.max_cstate=1

要持久化这个修改,则编辑/etc/default/grub文件中GRUB_CMDLINE_LINUX配置。例如希望更改成激活emergency模式:

GRUB_CMDLINE_LINUX="emergency"

要禁用cpufeq(注意:只对没有constant_tsc的主机需要这样设置),安装kernel-tools然后激活cpupower.servicesystemctl enable cpupower.service)。如果想要在每次guest虚拟机启动时禁止这个服务,修改/etc/sysconfig/cpupower配置文件,修改CPUPOWER_START_OPTSCPUPOWER_STOP_OPTS两个参数。有关限制值可以在/sys/devices/system/cpu/<cpuid>/cpufreq/scaling_available_governors文件可以查看。详细信息参考Red Hat Enterprise Linux 7 Power Management Guide

Red Hat Enterprise Linux Guest所需的时间管理参数

对于不同的Red Hat Enterprise Linux guest虚拟机,需要设置对应的内核参数以确保时间正确同步。这些参数可以设置在guest虚拟机的/etc/grub2.cfg配置的/kernel行。

注意

Red Hat Enterprise Linux 5.5及以后版本,Red Hat Enterprise Linux 6.0及以后版本,以及Red Hat Enterprise Linux 7默认使用kvm-clock作为guest系统的时钟源。运行kvm-clock避免了附加的内核参数,所以Red Hat推荐使用kvm-clock

详细的不同guest操作系统设置内核参数见Chapter 8. KVM Guest Timing Management。当前大多数新操作系统版本默认使用kvm-clock就可以了,避免繁琐的内核参数设置。

更改系统的时钟源会影响系统性能,所以需要谨慎使用,实际解决方法建议排查影响系统稳定性的根本原因,避免修改虚拟机时钟源!

Clocksource tsc unstable (delta = xxxxxxxx ns)

近期在线维护KVM服务器,发现在虚拟机创建的过程中,会出现物理服务器上正在运行的虚拟机时间短暂偏移数秒到10秒左右,虽然虚拟机的ntp服务能够最终纠正,但是对运行的业务系统造成了困扰。

排查发现虚拟机创建的时候,在物理服务器的messages日志中显示PassThrough模式的ixgbe网卡的VF驱动会做一次Reload the VF driver to make this change effective。此时,运行的虚拟机的messages日志显示虚拟时钟源不稳定:

[2423264.824259] Clocksource tsc unstable (delta = 6824818792 ns).  Enable clocksource failover by adding clocksource_failover kernel parameter.

检查kvm虚拟机时钟源

# cat /sys/devices/system/clocksource/clocksource0/available_clocksource
kvm-clock tsc acpi_pm

#cat /sys/devices/system/clocksource/clocksource0/current_clocksource
kvm-clock

参考 [Howto] Fixing unstable clocksource in virtualised CentOS

在虚拟化之前的计算机时期是通过tick计数来测量时间的:操作系统初始化一个设备来发送终端 - 称为ticks - 作为一个持久的固定的频率。操作系统通过计算这个中断,例如每秒100次,来知晓经过了多少时间。

然而,在运行的虚拟机中,就不能保证虚拟机能够获得足够资源来保证固定的tick(滴答)速率。假设一个物理服务器运行了大量的虚拟机,就有可能在某个瞬间某些虚拟机不能活的足够资源来产生tick(滴答)。如果物理服务器的负载非常高,则ticks的一个backlog(后台日志)就会建立,并且可能不断增长。这就导致vm虚拟机的时钟延后。如果backlog过大,则这个ticks(滴答)甚至可能被抛弃,这样虚拟机的时钟源就会不稳定并且vm虚拟机时间就会落后。

当时钟源不稳定的时候,Linux就会尝试找出并报告这个现象,此时在日志中就会出现如下事件:

Clocksource tsc unstable (delta = -102057770 ns).  Enable clocksource failover by adding clocksource_failover kernel parameter.

要解决这个问题,我们需要首先找出哪些是可用的时钟源,以及当前使用的时钟源:

$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource
kvm-clock tsc hpet acpi_pm
$ cat /sys/devices/system/clocksource/clocksource0/current_clocksource
kvm-clock

解决这个时钟源的问题通常是添加另外一个时钟源,也就是failover clock source,例如hpetacpi_pm(这里可以看到报错显示是tsc unstable,所以接下来优选的顺序是hpetacpi_pm)。详细的时钟源解释见"Understanding the Linux Kernel, 3rd Edition" by Daniel P. Bovet, Marco Cesati。

  • 具体解决步骤 - 在KVM虚拟机的启动内核参数中添加failover clock source如下

kernel /vmlinuz-2.6.32-358.18.1.el6.x86_64 ro root=UUID=a4eea0d1-3150-4b3f-bc4b-204413280ac7 <其他内核参数> clocksource_failover=acpi_pm

然后重启虚拟机。

如果对虚拟机的时钟保持问题想进一步了解,可参考vmware的文档Timekeeping in VMware Virtual Machines

警告!

使用hpetacpi_pm时钟源会导致虚拟机性能下降;使用tsc作为时钟源能够提高guest虚拟机性能,但是带来的缺点是guest虚拟机时钟会偏移,所以务必要在guest虚拟机中运行ntpd服务,确保虚拟机时间精准。

guest虚拟机采用不同时钟源的性能测试

  • 可用guest时钟源及默认时钟源kvm-clock

# cat /sys/devices/system/clocksource/clocksource0/available_clocksource
kvm-clock tsc acpi_pm
# cat /sys/devices/system/clocksource/clocksource0/current_clocksource
kvm-clock
  • 测试性能指令:sysbench cpu --threads=8 --time=300 --cpu-max-prime=100000 run

  • 设置时钟源为kvm-clock,性能测试输出如下:

echo "kvm-clock" > /sys/devices/system/clocksource/clocksource0/current_clocksource

top输出:

top - 11:39:53 up 23:27,  3 users,  load average: 6.74, 2.48, 0.94
Tasks: 192 total,   1 running, 191 sleeping,   0 stopped,   0 zombie
Cpu0  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu1  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu2  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu3  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu4  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu5  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu6  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu7  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:  32880448k total,   854516k used, 32025932k free,    67620k buffers
Swap:        0k total,        0k used,        0k free,   475092k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                                                                                                                                                                         
25089 root      20   0 88160 2476 1416 S 799.7  0.0  14:38.32 sysbench

sysbench输出

CPU speed:
    events per second:   278.78

General statistics:
    total time:                          300.0210s
    total number of events:              83639

Latency (ms):
         min:                                 27.98
         avg:                                 28.69
         max:                                126.48
         95th percentile:                     33.12
         sum:                            2399991.83

Threads fairness:
    events (avg/stddev):           10454.8750/37.61
    execution time (avg/stddev):   299.9990/0.01
  • 设置时钟源为tsc,性能测试输出如下:

echo "tsc" > /sys/devices/system/clocksource/clocksource0/current_clocksource
CPU speed:
    events per second:   279.16

General statistics:
    total time:                          300.0256s
    total number of events:              83756

Latency (ms):
         min:                                 27.98
         avg:                                 28.65
         max:                                 43.79
         95th percentile:                     31.94
         sum:                            2399975.15

Threads fairness:
    events (avg/stddev):           10469.5000/37.98
    execution time (avg/stddev):   299.9969/0.01
  • 设置时钟源为acpi_pm,性能测试输出如下:

echo "acpi_pmtsc" > /sys/devices/system/clocksource/clocksource0/current_clocksource
CPU speed:
    events per second:   272.72

General statistics:
    total time:                          300.0241s
    total number of events:              81824

Latency (ms):
         min:                                 28.57
         avg:                                 29.32
         max:                                 43.81
         95th percentile:                     31.94
         sum:                            2398952.96

Threads fairness:
    events (avg/stddev):           10228.0000/29.83
    execution time (avg/stddev):   299.8691/0.01

上述性能测试可以看到,300秒内完成测试性能显示: tsc > kvm-clock > acpi_pm

| guest时钟源 | kvm-clock | tsc | acpi_pm | | 300秒完成event | 83639 | 83756 | 81824 | | 性能 | 基准 | +0.14% | -2.17% |

注意:这个测试只是一个举例,实际上性能对比需要case by case,针对某些特定不断访问时钟的应用,上述调整guest虚拟机时钟源会有比较大的差异,普通应用则可能差距不大或不明显。

参考

Last updated