容器毕竟是一种轻量级的实现方式,所以其封闭性不如虚拟机技术。
1、容器环境的资源隔离性
举个例子:
物理机/宿主机有 96 个 CPU 内核、256GB 物理内存,容器限制的资源是 4 核 8G,那么容器内部的 JVM 进程看到的内核数和内存数是多少呢?
目前来说,JVM 看到的内核数是 96,内存值是 256G。
这会造成一些问题,基于 CPU 内核数 availableProcessors 的各种算法都会受到影响,比如默认 GC 线程数:假如啥都不配置,JVM 看见 96 个内核,设置 GC 并行线程数为 96*5/8~=60
,但容器限制了只能使用 4 个内核资源,于是 60 个并行 GC 线程来争抢 4 个机器内核,造成严重的 GC 性能问题。
同样的道理,很多线程池的实现,根据内核数量来设置并发线程数,也会造成剧烈的资源争抢。如果容器不限制资源的使用也会造成一些困扰,比如下面介绍的坏邻居效应。基于物理内存 totalPhysicalMemorySize 和空闲内存 freePhysicalMemorySize 等配置信息的算法也会产生一些奇怪的 Bug。
2、JDK 对容器的支持和限制
最新版的 JDK 加入了一些修正手段。
新版 JDK 支持 Docker 容器的 CPU 和内存限制:
可以增加 JVM 启动参数来读取 Cgroups 对 CPU 的限制:
Hotspot 是一个规范的开源项目,关于 JDK 的新特性,可以阅读官方的邮件订阅,例如:
其他版本的 JDK 特性,也可以按照类似的命名规范,从官网的 Mailing Lists 中找到:
关于这个问题的排查和分析,请参考前面的章节[《JVM 问题排查分析调优经验》]。
3、坏邻居效应
有共享资源的地方,就会有资源争用。在计算机领域,共享的资源主要包括:
在多租户的公有云环境中,会存在一种严重的问题,称为“坏邻居效应”(noisy neighbor phenomenon)。当一个或多个客户过度使用了某种公共资源时,就会明显损害到其他客户的系统性能。(就像是小区宽带一样)
吵闹的坏邻居(noisy neighbor),用于描述云计算领域中,用来描述抢占共有带宽,磁盘 I/O、CPU 以及其他资源的行为。
坏邻居效应,对同一环境下的其他虚拟机/应用的性能会造成影响或抖动。一般来说,会对其他用户的性能和体验造成恶劣的影响。
云,是一种多租户环境,同一台物理机,会共享给多个客户来运行程序/存储数据。
坏邻居效应产生的原因,是某个虚拟机/应用霸占了大部分资源,进而影响到其他客户的性能。
带宽不足是造成网络性能问题的主要原因。在网络中传输数据严重依赖带宽的大小,如果某个应用或实例占用太多的网络资源,很可能对其他用户造成延迟/缓慢。坏邻居会影响虚拟机、数据库、网络、存储以及其他云服务。
有一种避免坏邻居效应的方法,是使用裸机云(bare-metal cloud)。裸机云在硬件上直接运行一个应用,相当于创建了一个单租户环境,所以能消除坏邻居。虽然单租户环境避免了坏邻居效应,但并没有解决根本问题。超卖(over-commitment)或者共享给太多的租户,都会限制整个云环境的性能。
另一种避免坏邻居效应的方法,是通过在物理机之间进行动态迁移,以保障每个客户获得必要的资源。此外,还可以通过 存储服务质量保障(QoS,quality of service)控制每个虚拟机的 IOPS,来限制坏邻居效应。通过 IOPS 来限制每个虚拟机使用的资源量,就不会造成某个客户的虚机/应用/实例去挤占其他客户的资源/性能。
有兴趣的同学可以查看:
[谈谈公有云的坏邻居效应](https://github.com/cncounter/translation/blob/master/tiemao_2016/45_noisy_neighbors/noisy_neighbor_cloud _performance.md)