穆琪的博客

一个程序员的自传

【转】ceph性能调优历程--系统篇

ceph调优是一个比较漫长且多坑的过程,故开辟一个调优系列,记录下各种历程,既为了做笔记,也为了给后人提供经验吧

系统 - cpu调优

cpu调优这部份主要是两大块:
1、关闭处理器的节能模式,需要在bios里进行设置,可以将空闲cpu睡眠也一并关闭掉
2、将ceph相关进程绑定在指定的cpu core上并且让cpu只为ceph进程服务
对于第2点,绑定进程的好处,一方面是符合当前的硬件设计,尤其是硬件体系结构中的cache体系,绑定了core,就能很大程度避免进程切换到其他的core上,它的cache命中就会更高,尤其是带有numa设计的设备,做隔离能够比较明显地提高性能,另外一方面绑定core使得ceph进程更快地得到资源,在长期高负载运行的环境,更具稳定性和效率
当下比较主流的进程绑定core的方式有:
1、taskset
2、cgroup
3、numactl
当然,在程序代码中绑定core也是可以的,不过在ceph中实现是几乎无可能的,所以这里就不列举了

taskset

tasket通过给定的pid,将指定的程序限定运行在指定的core上,效果其实也不错,缺点就是程序运行之后再进行绑定,如果程序启动时已经在某个core上运行,而绑定的core是跨物理处理器,这种情况下绑定core就没有什么意义了;当然,taskset也可以启动某个进程来实现在进程一开始就锁定cpu core,但是taskset有明显的缺陷,那就是它不能指定程序从哪里分配内存,虽然从最近的地方分配内存是系统会尽量做的事,但是并不能保证它的内存使用是最优的,而且redhat官方也指出:

1
2
3
important

taskset does not guarantee local memory allocation. If you require the additional performance benefits of local memory allocation, Red Hat recommends using numactl instead of taskset.


cgroup

crgoup是内核级的隔离手段,通过为指定的程序集指定它的子系统,来实现对程序使用资源的控制,它更擅长用来限制程序的资源使用量,例如使用cpu不超过某个值,或者内存使用不超过某个量,当然,通过配置与cpu的亲和性,也可以用来限制程序的运行core,但是仍不是最好的方案,国外有些资料甚至认为cgroup其实是备选方案,并不是很推荐的方案

numactl

numa(非一致内存访问)方式是首推的方案,它实现的控制、绑定更底层,并且在使用方式上更简单、灵活,关于numa相关的知识网上有不少,这里推荐一篇写得很不错的cpu拓扑及ceph使用时的优化,目前绝大部分的服务器设备都会使用numa架构进行设计,主要是因为当cpu的core(超线程)数量超过10个之后,SMP架构带来的总线共享冲突会导致访存性能瓶颈。使用numactl对radosgw进程进行绑定的具体步骤:
1、验证设备是否开启numa

1
2
3
4
5
6
[root@ceph-c204 tanweijie]# grep -i numa /var/log/dmesg
[    0.000000] NUMA: Initialized distance table, cnt=2
[    0.000000] NUMA: Node 0 [mem 0x00000000-0x7fffffff] + [mem 0x100000000-0x107fffffff] -> [mem 0x00000000-0x107fffffff]
[    0.000000] Enabling automatic NUMA balancing. Configure with numa_balancing= or the kernel.numa_balancing sysctl
[    1.275254] pci_bus 0000:00: on NUMA node 0
[    1.278298] pci_bus 0000:80: on NUMA node 1


2、查看设备的numa分布情况,如果没有该命令,请安装yum install -y numactl

1
2
3
4
5
6
7
8
9
10
11
12
[root@ceph-c204 tanweijie]# numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 28 29 30 31 32 33 34 35 36 37 38 39 40 41
node 0 size: 64838 MB
node 0 free: 53858 MB
node 1 cpus: 14 15 16 17 18 19 20 21 22 23 24 25 26 27 42 43 44 45 46 47 48 49 50 51 52 53 54 55
node 1 size: 65536 MB
node 1 free: 47716 MB
node distances:
node   0   1
 0:  10  21
 1:  21  10


3、查看具体的处理器情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[tanweijie@ceph-c204 tanweijie]# lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                56
On-line CPU(s) list:   0-55
Thread(s) per core:    2
Core(s) per socket:    14
Socket(s):             2
NUMA node(s):          2
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 79
Model name:            Intel(R) Xeon(R) CPU E5-2660 v4 @ 2.00GHz
Stepping:              1
CPU MHz:               2399.921
CPU max MHz:           3200.0000
CPU min MHz:           1200.0000
BogoMIPS:              4000.09
Virtualization:        VT-x
L1d cache:             32K
L1i cache:             32K
L2 cache:              256K
L3 cache:              35840K
NUMA node0 CPU(s):     0-13,28-41
NUMA node1 CPU(s):     14-27,42-55
Flags:                 fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdseed adx smap xsaveopt cqm_llc cqm_occup_llc


可以看到,笔者的设备中有两个node,每个node有一个物理cpu,超线程后,共有56个core,这里要注意一点,cpu的core在相同的node里,有可能是不连续的,在绑定的时候别搞错了;由此可以确定cpu core的分布情况,绑定的时候尽量使用同一个socket的core
4、执行命令查看当前程序的调度策略:

1
2
[tanweijie@ceph-c204 tanweijie]# for i in $(pgrep radosgw) ; do taskset -pc $i ; done
pid 111835's current affinity list: 0-55 '


5、在/usr/lib/systemd/system/中找到对应的启动文件,例如要绑定第一个socket:
使用numactl来启动进程:

1
ExecStart=/usr/bin/numactl -C 14-27,42-55 -m 1 /usr/bin/radosgw -f --cluster...


这样就可以将rgw进程绑定在14-27,42-55一共28个core上面了,同时指定了rgw进程只能从node 1分配内存,这里会有个问题,如果指定的进程无法从指定的node里分配到内存,即分配失败,有可能会导致进程异常,或者直接被杀,所以在进行规划的时候要注意,分配core的时候要预测好进程消耗的内存上限
6、重新读取配置并重启服务:

1
2
3
systemctl daemon-reload
systemctl restart ceph-radosgw@rgw.$(hostname -s).service
systemctl status ceph-radosgw@rgw.$(hostname -s).service -l


7、再次查看目标进程的numa调度策略:

1
2
[root@gz-open-dw-c204 tanweijie]# for i in $(pgrep radosgw) ; do taskset -pc $i ; done
pid 111996's current affinity list: 14-27,42-55 '


8、htop查看实际效果

让cpu只为ceph服务

最佳的办法就是用isolcpus参数将cpu隔离出去,系统的调度器就再也不会把进程往指定的core上调度,然后再使用捆绑的方式将我们的进程绑定到隔离出来的cpu上,再使用一些手工方法将系统中断隔离出去,实现ceph进程对cpu的完全独占,岂不美哉!可惜的是,经过测试,在使用isolcpus将cpu隔离出去之后,再使用numactl来对进程进行绑定是行不通的,使用numactl -C xx指定那部分core,libnuma会报错,out of range,原因是numactl读取core的方式的限制,它只能从系统已经识别并允许调度的core上面进行指定,因此不可以指定那部分已经isolcpus的core,笔者尝试与numactl的代码贡献者联系,想看看有没有可能修改代码,重新编译来实现,毕竟numactl是最好的cpu隔离方式,对方给出的解答是,在较新版本的numactl提供了一个-a参数,可以使numactl检测到所有的cpu核心;另外一种方案就是使用taskset来启动程序,运行在isolcpus指定的隔离出来的cpu上,这种方式笔者尝试过,效果也不错,但是内存分配的方式实在不如意,大概是强迫症吧;这里最后介绍一下将系统中断转给某些cpu的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
修改/etc/sysconfig/irqbalance文件:
[root@ceph ~]# cat /etc/sysconfig/irqbalance
# irqbalance is a daemon process that distributes interrupts across
# CPUS on SMP systems. The default is to rebalance once every 10
# seconds. This is the environment file that is specified to systemd via the
# EnvironmentFile key in the service unit file (or via whatever method the init
# system you're using has. '
#
# ONESHOT=yes
# after starting, wait for a minute, then look at the interrupt
# load and balance it once; after balancing exit and do not change
# it again.
#IRQBALANCE_ONESHOT=

#
# IRQBALANCE_BANNED_CPUS
# 64 bit bitmask which allows you to indicate which cpu's should
# be skipped when reblancing irqs. Cpu numbers which have their
# corresponding bits set to one in this mask will not have any
# irq's assigned to them on rebalance
#
IRQBALANCE_BANNED_CPUS=000000,000000ff

#
# IRQBALANCE_ARGS
# append any args here to the irqbalance daemon as documented in the man page
#
#IRQBALANCE_ARGS=

就是通过指定IRQBALANCE_BANNED_CPUS来进行中断的处理指定


系统 - 网络调优

网络调优方面主要是针对网卡以及中间设备,例如交换机,进行调整,具体调整参数要根据具体硬件来确定,这里仅对笔者的硬件环境给出建议
网络环境:
1、网卡 - 40Gbps的ib卡(PCIe卡拓展),目前只有一个
2、交换机为24口的ib交换机4036E
网络能进行调节的地方也不多,首先是开启ib卡的巨帧模式,即调节ib卡的MTU为65520;ib卡千兆网卡不同,它使用命令

1
ifconfig ib1 mtu 65520


调节mtu会报错,起初以为是网卡驱动不支持,但是,ib卡能到40Gbps,怎么会连巨帧都不支持呢?经过调查,发现linux kernel官方提供的资料显示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Datagram vs Connected modes

 The IPoIB driver supports two modes of operation: datagram and
 connected.  The mode is set and read through an interface's
 /sys/class/net/<intf name>/mode file.

 In datagram mode, the IB UD (Unreliable Datagram) transport is used
 and so the interface MTU has is equal to the IB L2 MTU minus the
 IPoIB encapsulation header (4 bytes).  For example, in a typical IB
 fabric with a 2K MTU, the IPoIB MTU will be 2048 - 4 = 2044 bytes.

 In connected mode, the IB RC (Reliable Connected) transport is used.
 Connected mode takes advantage of the connected nature of the IB
 transport and allows an MTU up to the maximal IP packet size of 64K,
 which reduces the number of IP packets needed for handling large UDP
 datagrams, TCP segments, etc and increases the performance for large
 messages.

 In connected mode, the interface's UD QP is still used for multicast
 and communication with peers that don't support connected mode. In
 this case, RX emulation of ICMP PMTU packets is used to cause the
 networking stack to use the smaller UD MTU for these neighbours.'


才发现是打开方式不对,应该是给mode文件注入模式,才能修改它的mtu,于是:

1
2
echo "connected" > /sys/class/net/ib1/mode
ifdown ib1 ; ifup ib1


再次查看ib卡的mtu,发现mtu已经改好了~

1
2
3
4
5
6
8: ib1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65520 qdisc mq state UP qlen 1024
   link/infiniband a0:00:03:00:fe:80:00:00:00:00:00:00:00:02:c9:03:00:0e:46:96 brd 00:ff:ff:ff:ff:12:40:1b:ff:ff:00:00:00:00:00:00:ff:ff:ff:ff
   inet 172.25.59.204/24 brd 172.25.59.255 scope global ib1
      valid_lft forever preferred_lft forever
   inet6 fe80::202:c903:e:4696/64 scope link
      valid_lft forever preferred_lft forever


注意,这样修改后,重启系统后ib卡仍会恢复到datagram模式,解决办法就是修改文件/etc/infiniband/openib.conf中的SET_IPOIB_CM=yes,即默认开启连接模式;但是,对ceph节点修改接口的mtu不总是能提高性能,典型的场景就是节点通信链路上的交换设备不支持巨帧,那就适得其反了,因此,需要对节点经过的交换设备修改配置,使其支持巨帧转发,这样一来就能充分利用ib卡的性能了;这里就简单说下,4036E ib交换机的巨帧模式开启,是通过交换机的管理口,对交换机的接口mtu进行设置,它只能将mtu设置最大为4096,可能是这款交换机太老了吧~不过,从实测效果来看,性能还可以,用iperf3测试显示,单口链路速度稳定在26Gbps左右;,当然,交换机成为了链路瓶颈;
网络方面,除了链路上的优化外,系统的协议栈相关参数也可以进行优化,尤其是ib链路开启了巨帧模式,进行tcp协议栈优化以发挥巨帧模式的最大性能是十分有必要的,这里可以给出ib卡巨帧模式下linux网络优化的参考:

1
2
3
4
5
6
7
8
9
10
11
net.ipv4.tcp_timestamps=0
net.ipv4.tcp_sack=0
net.core.netdev_max_backlog=250000
net.core.rmem_max=16777216
net.core.wmem_max=16777216
net.core.rmem_default=16777216
net.core.wmem_default=16777216
net.core.optmem_max=16777216
net.ipv4.tcp_mem="16777216 16777216 16777216"
net.ipv4.tcp_rmem="4096 87380 16777216"
net.ipv4.tcp_wmem="4096 65536 16777216"


参考资料来自ipoib_release_notes
对于网络优化,还有一种更彻底的优化方案,就是通过使用dpdk方式,直接脱离协议栈,能实现更高的网络性能,不过dpdk又是另外一个大项目,除非对性能要求苛刻,否则投入产出比估计不高

系统 - 内存调优

内存方面,主要是考虑一下几个方面:
1、尽量不让程序使用swap分区
2、尽量让程序访问最快的内存
3、尽量加大程序的cache

尽量不让程序使用swap分区

有些比较极端的用户可能会直接关闭、禁用swap分区,这么一来当然会使得程序不会去使用swap分区而完全使用内存,性能会有不少提升,但是,一旦系统的可用内存过低,程序无法正常分配到内存时,可能会引发各种各样的问题,甚至会直接宕机,这种高风险的方式是非常不建议使用的,比较稳妥的办法就是告诉系统,尽量使用内存,是在是没有办法的时候,才去使用swap分区,这样可以最大化避免程序被杀或者宕机,同时,将swap分区文件建立在ssd上面,也能获得较好的表现
让系统尽量不用swap的方法:
1、在sysctl中加入规则并重新读入配置

1
2
echo "vm.swappiness = 0" >> /etc/sysctl.conf
sysctl -p


这就告诉系统,当内存消耗殆尽之后才开始使用swap分区
2、执行命令

1
sync


将必要数据数据写入硬盘,因为接下来我们要完全释放swap的空间,在swap中的数据应立即写入磁盘避免丢失
3、接下来

1
swapoff -a


关闭swap,这一步将会释放掉所有swap的占用并禁用掉swap, 如果swap占用较多,可能需要比较长的时间来进行同步
4、最后

1
swapon -a


重新将swap打开,此时swap完全free,同时因为设置了内存消耗殆尽后才开始使用swap分区,所以在内存规划合理的情况下,swap被用到的几率非常小

尽量让程序访问最快的内存

由上述的numactl绑定cpu的知识可知,在numa模式下,跨node访问内存速度会有很大差异,所以在绑定程序,尤其是rgw和osd这类进程的时候,最好同时注意到它们的所属node的关系,并且在使用numactl绑定的时候,同时指定-m参数,即指定进程从哪个node上分配内存,这样就能让程序运行得更快

尽量加大程序的cache

加大程序的cache其实是ceph层面的调优,主要是通过加大bluestore cache size 的值来指定cache,在笔者的环境中,bluestore cache size指定为1G,而rgw的rgw lru cache使用了10000,在500个线程(python)并发上传965KB文件的情况下,两节点共12个osd时,基本可以达到600MB的集群读写带宽,当然程序不完整,暂时还不能测试延时情况
以上就是系统级的调优方案,目前仍在探索中前进,未完待续。

参考资料

Working With NUMA/CPU Pinning
Managing Process Affinity in Linux


© 鲁ICP备18041558号
Powered by Z-BlogPHP & Yiwuku.com