云服务器网络优化方案
操作场景
为了提高程序的运行的性能,可以通过把云服务器上运行的某个进程,指定在某个CPU上工作,实现CPU性能调优。
为了获取更高的执行效率,应该保证一个CPU把一个完整的发送或者接收过程处理完,避免CPU切换。最好一个业务进程/线程固定在一个CPU、固定一个网卡发送队列,中断也使用这个CPU。对于跨NUMA的云服务器,应该尽量在一个NUMA上完成网络IO动作,避免NUMA间切换。(可以执行lscpu查看CPU相关信息判断是否跨NUMA。)
本节操作以C7.xlarge.2、CentOS 7.4操作系统的云服务器为例,介绍网络优化的方案。
其中C7.xlarge.2实例的网卡有0、1两个队列,有CPU0、CPU1、CPU2、CPU3四颗CPU。
场景一:单位CPU获取的带宽(Gbps)或者吞吐量(Mpps)最大
- 保证一个业务进程或线程固定在一个CPU。
taskset -pc cpu_id pid
- cpu_id为希望绑定的CPU,取值范围为[0, CPU最大数-1]。
- pid为希望绑核的业务进程。
例如,本例中使用iperf3作为业务进程,把两个进程分别绑定到CPU2和CPU3:
- 执行如下命令,获取iperf3进程状态。
ps aux | grep iperf3 | grep -v grep
[root@localhost ~]# ps aux | grep iperf3 | grep -v grep root 3161 9.7 0.0 9712 896 pts/0 R 08:22 0:01 iperf3 -c 192.168.1.61 -p 10000 -t 10000 -i 0 root 3163 9.0 0.0 9712 892 pts/0 S 08:22 0:01 iperf3 -c 192.168.1.61 -p 10001 -t 10000 -i 0
- 执行以下命令,指定进程运行在CPU2和CPU3上。
taskset -pc 3 3163
[root@localhost ~]# taskset -pc 2 3161 pid 3161's current affinity list: 0-3 pid 3161's new affinity list: 2 [root@localhost ~]# taskset -pc 3 3163 pid 3163's current affinity list: 0-3 pid 3163's new affinity list: 3
如果恢复配置到原来的状态,可以使用如下命令(0-3为初始设置时返回的current affinity list):
[root@localhost ~]# taskset -pc 0-3 3161 pid 3161's current affinity list: 2 pid 3161's new affinity list: 0-3 [root@localhost ~]# taskset -pc 0-3 3163 pid 3163's current affinity list: 3 pid 3163's new affinity list: 0-3
- 保证一个CPU固定使用一个网卡队列。
本例中让CPU2使用eth0的0号队列,CPU3使用eth0的1号队列。
- 修改前执行如下命令,首先查询原来的配置,便于恢复:
cat /sys/class/net/eth0/queues/tx-0/xps_cpus
cat /sys/class/net/eth0/queues/tx-1/xps_cpus
[root@localhost ~]# cat /sys/class/net/eth0/queues/tx-0/xps_cpus 0 [root@localhost ~]# cat /sys/class/net/eth0/queues/tx-1/xps_cpus 0
xps_cpus里显示的是CPUbitmask的16进制显示,0、1、2、3四个CPU对应的值分别是1、2、4、8。
- 通过如下的命令让CPU2使用eth0的0号队列,CPU3使用eth0的1号队列。
echo 4 > /sys/class/net/eth0/queues/tx-0/xps_cpus
echo 8 > /sys/class/net/eth0/queues/tx-1/xps_cpus
如需恢复原来的配置,使用如下命令:
echo 0 > /sys/class/net/eth0/queues/tx-0/xps_cpus
echo 0 > /sys/class/net/eth0/queues/tx-1/xps_cpus
- 修改前执行如下命令,首先查询原来的配置,便于恢复:
- 保证一个网卡队列的中断固定在一个CPU。这个CPU和业务进程的CPU相同。
- 首先查询eth0对应的virtio设备的名字,如下查询结果为virtio0。
[root@localhost ~]# ls /sys/class/net/eth0/device/driver/ | grep virtio virtio0
- 然后查询virtio0网卡使用的中断的名字,如下查询结果为24、25、26、27、28。
[root@localhost ~]# cat /proc/interrupts | grep virtio0 24: 0 0 0 0 PCI-MSI-edge virtio0-config 25: 105 52766 114 38738 PCI-MSI-edge virtio0-input.0 26: 79 49480 593063 403097 PCI-MSI-edge virtio0-output.0 27: 55 3594 46 36720 PCI-MSI-edge virtio0-input.1 28: 27277 1262879 0 12099829 PCI-MSI-edge virtio0-output.1
- 编辑irqbalance配置文件。
[root@localhost ~]# vim /etc/sysconfig/irqbalance
- 修改最后一行IRQBALANCE_ARGS为如下,这样irqbalance服务不再调度这几个中断。
IRQBALANCE_ARGS=--banirq=24-28
- 重启irqbalance服务。
[root@localhost ~]# service irqbalance restart
- 修改中断所在CPU,例如设置25、26号中断到CPU2; 27、28号中断到CPU3。
[root@localhost ~]# echo 2 > /proc/irq/25/smp_affinity_list [root@localhost ~]# echo 2 > /proc/irq/26/smp_affinity_list [root@localhost ~]# echo 3 > /proc/irq/27/smp_affinity_list [root@localhost ~]# echo 3 > /proc/irq/28/smp_affinity_list
- 首先查询eth0对应的virtio设备的名字,如下查询结果为virtio0。
场景二:获取的带宽(Gbps)或者吞吐量(Mpps)最高
场景二仅提供优化方案,需要具备相关网络知识,且根据云服务器实际情况和业务场景综合考虑具体操作。
建议首先尝试使用最优效率配置,即场景一:单位CPU获取的带宽(Gbps)或者吞吐量(Mpps)最大的操作方法。确认是否可以满足性能要求。当场景一的方法不满足需求的情况下,推荐使用本场景的方法。
- 对于即收又发的业务,可以把接收和发送中断分别绑定一个CPU,例如把3的25、26、27、28分别绑定到CPU0、1、2、3。
- 对于接收繁忙的业务,可以打开RPS(Receive Packet Steering),让一个中断的软中断分散到多个CPU。
场景三:获取最优的不丢包TCP带宽
场景三仅提供优化方案,需要具备相关网络知识,且根据云服务器实际情况和业务场景综合考虑具体操作。
为了获取稳定的TCP不丢包带宽,推荐从小到大修改,设置一个满足业务带宽要求的发送socket缓冲区大小。业务带宽要求应该小于云服务器规格的带宽大小,否则请更换更大规格的实例。
例如推荐从64K缓冲区开始,尝试64K、128K...socket缓冲区大小。
socket缓冲区大小设置示例:
int opt = 64 * 1024;
if (setsockopt(sk, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0
|| setsockopt(sk, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
log_err("socket buffer size set failed.");
close(sk);
return -1;
}
使用小的发送缓冲区大小,可以避免由于瞬时发送速率太高,超过flavor带宽限速而引起的丢包,从而获得更稳定的带宽。
- 对于iperf3程序,可以使用-w参数指定缓冲区大小。
- 对于4.9及以上内核版本,推荐把操作系统的TCP拥塞控制算法修改为BBR算法,这样可以在不修改socket缓冲区大小的情况下,获得一个稳定的TCP不丢包带宽。
BBR(Bottleneck Bandwidth and Round-trip propagation time)是一种拥塞控制算法,能够在传输最大带宽的同时保证最小的时延。Linux 4.9及以上内核版本自带了该算法,关于把操作系统内核协议栈拥塞控制算法修改为BBR的具体方法可以查询相关网站。
场景四:处理UDP接收丢包场景
场景四仅提供优化方案,需要具备相关网络知识,且根据云服务器实际情况和业务场景综合考虑具体操作。
- 执行以下命令,检查是否触发软中断丢包。
如果第二列计数值在增长时,则会判断为“软中断丢包”。当您的实例触发了软中断丢包时,可通过以下步骤进行排查及处理:
- 检查是否UDP接受缓冲区满导致丢包。
若您的实例因UDP接收缓冲区不足而导致丢包时,可通过以下步骤进行排查处理:
- 执行以下命令,检查UDP中receive buffer errors字段。
该字段表示由于接收缓冲区满而不能接收的UDP数据包数量。如果该值较高或者持续增加,可能是因为UDP接收缓冲区满而导致的丢包。
您可以尝试通过调大内核参数net.core.rmem_max和net.core.rmem_default来增加接收缓冲区的大小,并重启UDP程序以生效。
- 执行以下命令,临时调整内核参数。
sysctl -a | grep net.core.rmem_max
sysctl -a | grep net.core.rmem_default
修改:
sysctl -w net.core.rmem_max=2129920
sysctl -w net.core.rmem_default=2129920
具体数值与业务场景有关,可逐步增大尝试后再观测缓冲区丢包计数是否增长。
- 执行以下命令,检查UDP中receive buffer errors字段。
- 检查是否为CPU单核软中断高导致丢包。
通过执行top命令后按1查看每个CPU核的si列占比,确认是否为CPU单核软中断高导致未能及时接收数据。若是,您可以通过以下步骤进行排查处理:
- 选择开启RPS,使软中断分配更为均衡。
RPS采用软件模拟的方式,实现了多队列网卡所提供的功能,分散了在多CPU系统上数据接收时的负载,把软中断分到各个CPU处理,而不需要硬件支持,一般与RFS配合使用。RFS则是尽力保证同一会话仍然由“上次”的CPU处理,这样可以保证cache的热度,提高cache的命中率。
- 获取RPS/RFS默认设置。
执行以下命令,获取接收队列0所设置的CPU掩码,表示将接收队列0散列到指定CPU上,数值是按照16进制表示,如0、1、2、3四个CPU对应的值分别是1、2、4、8。
cat /sys/class/net/eth0/queues/rx-0/rps_cpus
执行以下命令命令获取接收队列0的最大栈/流程数。
cat /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
执行以下命令,获取内核可以操控的任意指定CPU可控制的最大栈/流程数,是一个系统参数。
cat /proc/sys/net/core/rps_sock_flow_entries
以上三个参数共同构成RPS/RFS功能。
- 开启RPS。
以下示例是将eth0网卡接收队列0散列到CPU2核,CPU3核使用eth0的1号接收队列,设置每个接收队列最大栈数为4096,CPU可控制的最大栈为65536:
echo 4 > /sys/class/net/eth0/queues/rx-0/rps_cpus
echo 8 > /sys/class/net/eth0/queues/rx-1/rps_cpus
echo 4096 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
echo 4096 > /sys/class/net/eth0/queues/rx-1/rps_flow_cnt
echo 65536 > /proc/sys/net/core/rps_sock_flow_entries
一般单网卡队列多CPU场景建议开启RPS以获得更优性能。
- 获取RPS/RFS默认设置。
- 检查业务程序是否会引发软中断分配不均匀。
如果是业务程序引发的软中断分配不均匀,可通过场景一:单位CPU获取的带宽(Gbps)或者吞吐量(Mpps)最大中保证一个业务进程或线程固定在一个CPU的操作方法进行业务绑核以缓解。
- 选择开启RPS,使软中断分配更为均衡。