k8s环境增加实例,其它实例延迟变高(1)
k8s环境通常会在一台物理机上部署各种服务,各服务通过cgroup限制自身CPU、内存的使用,保障隔离性。 在线上发现一台机器,一旦启动其它实例,原有实例的延迟就会升高。 此时整机的CPU使用率不足30%,IO也很少,传统视角看,整机的负载非常低。
1 实例部署现状
这台机器有2个socket,每个socket 26个core,开启了超线程,整机看有104个CPU。 我们把延迟升高的那个实例称作A。
- socket0的CPU使用率在32%左右,socket1在15%左右,两个socket的CPU使用率非常不均衡;
- 实例A做了numa亲和,运行在socket0,CPU使用率在30%左右(1500%-1700% 按照52个CPU折算)。
2 实例A受影响的情况
- 实例A的IPC之前稳定在0.75左右,新增实例后在0.5-0.6间波动;
- 实例A的CPU使用率之前稳定在1200,新增实例后在1300-1800%间波动;
实例A CPU使用率的变化解释了实例A延迟的上升,也表明受到了新增实例的干扰。 但是从整机层面看,新增加实例后,CPU使用率上升不到1%,变化非常小,所以实例A受到的干扰,应该不是来自CPU的争用。
3 内存带宽
在讨论机器负载时,内存带宽通常是一个被忽略的因素。当内存带宽超过某个值(通常在50%左右),内存延迟会急剧上升,则IPC下降,CPU使用率升高。
内存带宽的变化:
- socket0的单通道带宽从7G上升到12G;
- socket1的单通道带宽几乎没有变化,在4G左右。
socket0的单通道带宽12G,差不多50%左右,应该是它导致了实例A延迟的上升。
前面提到socket1 CPU使用率低,新增加的实例,如果没有numa亲和,内核通常会调度到socket1运行,保证两个socket CPU使用的均衡,理论上socket1的带宽应该上升才对。
4 内存分配的均衡
numactl -H
观察两个socket的内存情况,socket1的free内存极少,socket0则较多。
一个合理的猜测是,因为socket0的free内存较多,新增实例的内存分配总是发生在socket0,无论新增实例运行在socket0,还是socket1,内存带宽的消耗总是发生在socket0。(内核可能为了避免跨socket访问内存,而牺牲掉CPU使用的均衡)
因为使用的内存中pagecache很多,通过 echo 3 | tee /proc/sys/vm/drop_caches
释放pagecache,增加socket1的free内存,可用避免跨socket分配内存。
这个操作后的变化:
- socket0的单通道带宽恢复到7G,socket1的单通道带宽上升到6G;
- 实例A的CPU使用率恢复到1200%,业务延迟恢复;
- 两个socket的cpu使用率变得均衡。
疑惑 socket0的单通道带宽下降5G,而socket1的单通道带宽只上升2G,这两个值有点对不上。
如何保证socket的内存均衡呢?一个可行的方法是,机器的所有组件都放到cgroup中,限制每个cgroup的内存,这样可以保证pagecache的隔离。
5 总结
因为pagecache导致socket间free内存不均衡,出现了跨socket分配(使用)内存的情况,导致socket间内存带宽不均衡,其中一个socket内存带宽饱和,影响了业务可用性。