容器隔离性带来的问题--容器化Java应用比虚机启动速度慢
引发的问题
同等配置下,虚机中的java 服务的启动速度,要比容器快很多(将近两倍)
实测数据
在同是1c1g的虚机和容器中,虚机启动时间大概在1min20s,容器启动时间大概在2min40s。
排查思路
怀疑网络
最开始怀疑是网络问题,因为业务依赖外部数据库,在容器和虚机中ping、telnet外部数据库,能通而且延迟差不多。
咨询熟悉java的小伙伴,说 spingboot可能有潜在的外部网络请求延迟(如请求Spring官网等),请求可能多次失败超时,不影响服务启动,但会影响启动时间。通过在虚机和容器中抓包,抓到了一个外部域名,但是虚机容器中都可以正常联通。包括修改域名服务器,都没有效果
硬件差异
排查问题陷入僵局后,咨询小伙伴的建议,涛哥提出是不是因为硬件差异导致的?这是个新的思路,之前只关注了软件层面的。
google了下,确实有人遇到了因为cpu频率的差异,导致虚机和容器中业务性能的差异。查看了容器和虚机所在主机的cpu频率后,进一步证实了涛哥的猜想,cpu频率确实有将近一倍的差异。根据文章中提供的解决办法,通过修改cpu的工作模式,从
powersave到performance,来提高cpu的工作频率。命令如下:
# 查看cpu频率 # lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 48 On-line CPU(s) list: 0-47 Thread(s) per core: 2 Core(s) per socket: 12 Socket(s): 2 NUMA node(s): 2 Vendor ID: GenuineIntel CPU family: 6 Model: 79 Model name: Intel(R) Xeon(R) CPU E5-2650 v4 @ 2.20GHz Stepping: 1 CPU MHz: 2494.133 CPU max MHz: 2900.0000 CPU min MHz: 1200.0000 BogoMIPS: 4389.67 Virtualization: VT-x L1d cache: 32K L1i cache: 32K L2 cache: 256K ··· # 查看cpu工作模式 # cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor powersave powersave ... # 修改cpu工作模式 # cpupower -c all frequency-set -g performance # 查看每个cpu的频率 # grep -i mhz /proc/cpuinfo cpu MHz : 1870.495 cpu MHz : 2348.156 cpu MHz : 2160.900 cpu MHz : 1918.896 ···
在修改完cpu工作模式后,cpu MHz确实有很大的提高,但是实测容器中业务启动时间并没有预期的和虚机中的速度一样,只有一点优化。看来cpu MHz不是决定的影响因素。
后来详细查了一下,cpu MHz是个不断浮动的素质,cpu性能要看CPU max MHz和工作模式。两台宿主机的cpu型号是一致的,改动cpu工作模式影响有限
容器对java的隔离缺陷
在之前容器化java业务的时候就遇到了OOMKilled,以及Runtime.getRuntime().availableProcessors()获取的cpu核数问题。当时通过引入了lxcfs,以及替换jvm libnumcpus.so文件,通过环境变量注入cpu核数来解决这个问题。
在怀疑是隔离引起的问题后,对比了虚机和容器中java进程的线程数,发现确实有比较大的差异。命令如下:
# 虚机中 ··· [root@data-message-b69c847c7-sjlrx /]# cat /proc/136/status |grep Threads Threads: 42 [root@data-message-b69c847c7-sjlrx /]# cat /proc/136/status |grep Threads Threads: 42 [root@data-message-b69c847c7-sjlrx /]# cat /proc/136/status |grep Threads Threads: 42 [root@data-message-b69c847c7-sjlrx /]# cat /proc/136/status |grep Threads Threads: 42 [root@data-message-b69c847c7-sjlrx /]# cat /proc/136/status |grep Threads Threads: 42 [root@data-message-b69c847c7-sjlrx /]# cat /proc/136/status |grep Threads Threads: 42 ··· # 容器中 ··· [root@data-message-79bb65797d-ffsfb /]# cat /proc/42/status |grep Threads Threads: 74 [root@data-message-79bb65797d-ffsfb /]# cat /proc/42/status |grep Threads Threads: 74 [root@data-message-79bb65797d-ffsfb /]# cat /proc/42/status |grep Threads Threads: 76 [root@data-message-79bb65797d-ffsfb /]# cat /proc/42/status |grep Threads Threads: 76 [root@data-message-79bb65797d-ffsfb /]# cat /proc/42/status |grep Threads Threads: 76 ···
解决办法
使用包含了cpu-online /sys/devices/system/cpu/online
的lxcfs(我们之前引入的lxcfs还未支持cpu-online)
在引入新版lxcfs cpu-online后,线程数下降明显,启动速度有明显的改善,达到和虚机同等水平。
LXCFS 3.1.2 has been released
Virtualize /sys/devices/system/cpu/online
LXCFS now also partially virtualizes sysfs. The first file to virtualize is /sys/devices/system/cpu/online per container.
结论
容器java进程启动慢的最终原因,还是容器的隔离性不够,导致jvm启动过多的线程,线程频繁切换带来的性能下降。目前使用包含cpu-online的lxcfs能解决这个问题。