背景
随着容器化的普及,越来越多的服务开始用容器管理,这个排查问题的方式和传统物理机有了区别。容器可以做cpu的绑定。走单独cpu的进程队列。不能直接看到进程跑的慢,load高就判定为排队的影响。
观察load
传统的方式,我们可以选择nonvoluntary_ctxt_switches的数据来作为进程切换的衡量方式,这个值越大,被切换除去的概率越大。我们下面做个实验。 云机器2core。 第一组实验我们启动sysbench来看看单个的运行。
shell
sysbench --threads=10 --time=300 threads run
我们观测进程的nonvoluntary_ctxt_switches指标
bash
cat /proc/3121954/status | grep ctxt
voluntary_ctxt_switches: 2
nonvoluntary_ctxt_switches: 0
因为机器上就这么一个业务进程,可以看到没有非自愿的上下文切换。
第二组实验我们启动2个sysbench程序。再来观测nonvoluntary_ctxt_switches
bash
cat /proc/3132823/status |grep ctxt
voluntary_ctxt_switches: 4
nonvoluntary_ctxt_switches: 3
可以看到指标上涨了。但是不多。因为任务执行很快,cpu给的时间片是充足的,这个案例能更好的说明load的影响。 我们来观测load
lua
load average: 10.44, 5.01, 2.10
基于这个情况来看,我们的程序肯定是跑的慢了。 这里展示一下2组数据的吞吐。
第一组实验
yaml
Throughput:
events/s (eps): 688.9522
time elapsed: 300.0107s
total number of events: 206693
第二组实验
yaml
Throughput:
events/s (eps): 340.3187
time elapsed: 300.0100s
total number of events: 102099
结果符合预期,这也是我们之前排查问题的思路。我们接下来观测一下cpu绑定之后的结果。
cpu绑定
目前core有限,我们来模拟1个线程和100个线程的案例,先来看看1个线程和100个线程跑的结果。没绑定的情况。
这是单独跑的结果。
yaml
Throughput:
events/s (eps): 1147.2537
time elapsed: 30.0003s
total number of events: 34418
这是机器还有100个线程跑的结果
yaml
Throughput:
events/s (eps): 803.9569
time elapsed: 30.0004s
total number of events: 24119
可以看到数据是有下滑的。
lua
load average: 5.26, 1.91, 0.91
load是超过2了,说明cpu已经开始排队了。 我们开始针对2个进程做隔离。这里简单来模拟直接使用cgroup。cgroup0绑定cpu0,cgroup1绑定cpu1。我们再来看结果。然后1个线程的使用cpu0跑,100个线程的使用cpu1来跑
lua
load average: 3.41, 1.40, 0.89
load还是超过2,说明排队。
yaml
Throughput:
events/s (eps): 1074.8552
time elapsed: 30.0003s
total number of events: 32246
再来看这个结果,基本持平。 通过这个案例说明cpu绑定之后,排队之后,进程的运行并不受到load的影响。
解决方案
从上面来看,在隔离技术之下,load高并不一定带来影响。那我们应该如何去判断进程有没有被影响呢。我们想想load排队的本质cpu的调度,我们可以从cpu调度的角度,来进行分析。我们来试试观测1个线程的调度指标。
这是没有隔离的数据
rust
usecs : count distribution
0 -> 1 : 0 | |
2 -> 3 : 25519 |******** |
4 -> 7 : 117937 |****************************************|
8 -> 15 : 7792 |** |
16 -> 31 : 737 | |
32 -> 63 : 238 | |
64 -> 127 : 44 | |
128 -> 255 : 66 | |
256 -> 511 : 34 | |
512 -> 1023 : 38 | |
1024 -> 2047 : 22 | |
2048 -> 4095 : 1 | |
这里有隔离的数据
rust
usecs : count distribution
0 -> 1 : 0 | |
2 -> 3 : 61472 |****************************************|
4 -> 7 : 47169 |****************************** |
8 -> 15 : 2488 |* |
16 -> 31 : 538 | |
32 -> 63 : 131 | |
64 -> 127 : 41 | |
128 -> 255 : 46 | |
256 -> 511 : 44 | |
512 -> 1023 : 41 | |
1024 -> 2047 : 39 | |
2048 -> 4095 : 3 | |
可以看到调度分布是不一样的。隔离了之后调度的时间花费更少一些。
总结
在有容器隔离的环境下,load高并不一定会带来影响,一个一个来排查有没有设置隔离,相对比较麻烦,这里可以切换成cpu调度的观测,目前overhead还是比较高的,不适合长期开启,适合短期排查问题。