0
点赞
收藏
分享

微信扫一扫

DPVS-FullNAT模式部署篇

捌柒陆壹 2022-02-27 阅读 100

本文主要介绍在CentOS7.9系统上部署DPVS的FullNAT模式和在RealServer上安装toa模块获取客户端的真实IP。

此前的文章已经介绍过DPVS简介与部署以及DPDK在DPVS中的应用及原理分析,有需要的同学可以先补一下相关的内容。由于之前的文章中的部署步骤只介绍到了DPVS的部署,并没有涉及相关的各种负载均衡模式的配置,以及时间过去大半年之后,DPVS的版本和对应的DPDK版本都有所更新,因此这里再重新详细写一篇新的部署教程。

1、准备工作

在正式开始安装之后我们需要先对机器的硬件参数进行一些调整,DPVS官方对硬件有一定的要求(主要是因为底层使用的DPDK),dpdk官方给出了一份支持列表,虽然支持性列表上面的平台支持得很广泛,但是实际上兼容性和表现最好的似乎还是要Intel的硬件平台。

1.1 硬件部分

1.1.1 硬件参数

  • 机器型号: PowerEdge R630
  • CPU:两颗 Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz
  • 内存:16G*8 DDR4-2400 MT/s(Configured in 2133 MT/s),每个CPU64G,共计128G
  • 网卡1:Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection (rev 01)
  • 网卡2:Intel Corporation Ethernet 10G 2P X520 Adapter (rev 01)
  • 系统:CentOS Linux release 7.9.2009 (Core)
  • 内核:3.10.0-1160.36.2.el7.x86_64

1.1.2 BIOS设置

开始之前,先进入BIOS中关闭超线程和启用NUMA策略。其中DPVS是非常典型的CPU繁忙型应用(进程所在的CPU使用率一直都是100%),为了保证性能,建议关闭CPU的超线程设置。同时因为DPVS使用的是我们手动分配的大页内存,为了保证CPU亲和性,最好在BIOS中直接打开NUMA策略。

1.1.3 网卡PCI ID

使用dpvsPMD驱动接管网卡之后,如果网卡的数量较多,容易搞混,最好提前记录下对应的网卡名MAC地址PCI ID,避免后面操作的时候搞混。

使用lspci命令可以查看对应网卡的PCI ID,接着我们可以查看/sys/class/net/这个目录下对应网卡名目录下的device文件,就能够得知网卡对应的PCI ID。最后就可以把网卡名-MAC地址-PCI ID三个参数串起来。

$ lspci | grep -i net
01:00.0 Ethernet controller: Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection (rev 01)
01:00.1 Ethernet controller: Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection (rev 01)

$ file /sys/class/net/eth0/device
/sys/class/net/eth0/device: symbolic link to `../../../0000:01:00.0'

1.2 软件部分

1.2.1 系统软件

# 编译安装dpvs需要使用的工具以及查看CPU NUMA信息的工具
$ yum group install "Development Tools" 
$ yum install patch libnuma* numactl numactl-devel kernel-devel openssl* popt* libpcap-devel -y
# 如果需要ipvsadm支持ipv6需要安装libnl3-devel
$ yum install libnl libnl-devel libnl3 libnl3-devel -y


# 注意kernel以及相应的kernel组件的版本需要和现在使用的kernel版本相对应
$ uname -r
3.10.0-1160.36.2.el7.x86_64
$ rpm -qa | grep kernel | grep "3.10.0-1160.36.2"
kernel-3.10.0-1160.36.2.el7.x86_64
kernel-devel-3.10.0-1160.36.2.el7.x86_64
kernel-tools-libs-3.10.0-1160.36.2.el7.x86_64
kernel-debug-devel-3.10.0-1160.36.2.el7.x86_64
kernel-tools-3.10.0-1160.36.2.el7.x86_64
kernel-headers-3.10.0-1160.36.2.el7.x86_64

1.2.2 dpvs和dpdk

# dpvs我们直接使用git从github拉取最新的版本
$ git clone https://github.com/iqiyi/dpvs.git
# dpdk我们从官网下载18.11.2版本,放到dpvs目录下方便操作
$ cd dpvs/
$ wget https://fast.dpdk.org/rel/dpdk-18.11.2.tar.xz
$ tar -Jxvf dpdk-18.11.2.tar.xz

完成上述步骤之后就可以开始下面的安装了。

2、安装步骤

2.1 DPDK安装

2.1.1 安装dpdk-patch

在dpvs文件夹的patch目录下面有对应支持的dpdk版本的patch补丁,如果不清楚自己到底需要哪个补丁,官方的建议是全部安装

$ ll dpvs/patch/dpdk-stable-18.11.2
total 44
-rw-r--r-- 1 root root  4185 Jul 22 12:47 0001-kni-use-netlink-event-for-multicast-driver-part.patch
-rw-r--r-- 1 root root  1771 Jul 22 12:47 0002-net-support-variable-IP-header-len-for-checksum-API.patch
-rw-r--r-- 1 root root  1130 Jul 22 12:47 0003-driver-kni-enable-flow_item-type-comparsion-in-flow_.patch
-rw-r--r-- 1 root root  1706 Jul 22 12:47 0004-rm-rte_experimental-attribute-of-rte_memseg_walk.patch
-rw-r--r-- 1 root root 16538 Jul 22 12:47 0005-enable-pdump-and-change-dpdk-pdump-tool-for-dpvs.patch
-rw-r--r-- 1 root root  2189 Jul 22 12:47 0006-enable-dpdk-eal-memory-debug.patch

安装patch的操作也非常的简单

# 我们首先把所有的patch复制到dpdk的根目录下面
$ cp dpvs/patch/dpdk-stable-18.11.2/*patch dpvs/dpdk-stable-18.11.2/
$ cd dpvs/dpdk-stable-18.11.2/
# 然后我们按照patch的文件名顺序依次进行安装
$ patch -p 1 < 0001-kni-use-netlink-event-for-multicast-driver-part.patch
patching file kernel/linux/kni/kni_net.c
$ patch -p 1 < 0002-net-support-variable-IP-header-len-for-checksum-API.patch
patching file lib/librte_net/rte_ip.h
$ patch -p 1 < 0003-driver-kni-enable-flow_item-type-comparsion-in-flow_.patch
patching file drivers/net/mlx5/mlx5_flow.c
$ patch -p 1 < 0004-rm-rte_experimental-attribute-of-rte_memseg_walk.patch
patching file lib/librte_eal/common/eal_common_memory.c
Hunk #1 succeeded at 606 (offset 5 lines).
patching file lib/librte_eal/common/include/rte_memory.h
$ patch -p 1 < 0005-enable-pdump-and-change-dpdk-pdump-tool-for-dpvs.patch
patching file app/pdump/main.c
patching file config/common_base
patching file lib/librte_pdump/rte_pdump.c
patching file lib/librte_pdump/rte_pdump.h
$ patch -p 1 < 0006-enable-dpdk-eal-memory-debug.patch
patching file config/common_base
patching file lib/librte_eal/common/include/rte_malloc.h
patching file lib/librte_eal/common/rte_malloc.c

2.1.2 dpdk编译安装

$ cd dpvs/dpdk-stable-18.11.2
$ make config T=x86_64-native-linuxapp-gcc
$ make 

# 出现Build complete [x86_64-native-linuxapp-gcc]的字样就说明make成功

$ export RTE_SDK=$PWD
$ export RTE_TARGET=build

2.1.3 配置hugepage

和其他的一般程序不同,dpvs使用的dpdk并不是从操作系统中索要内存,而是直接使用大页内存(hugepage),极大地提高了内存分配的效率。hugepage的配置比较简单,官方的配置过程中使用的是2MB的大页内存,这里的28672指的是分配了28672个2MB的大页内存,也就是一个node对应56GB的内存,一共分配了112GB的内存,这里的内存可以根据机器的大小来自行调整。但是如果小于1GB可能会导致启动报错。

# for NUMA machine
$ echo 28672 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
$ echo 28672 > /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages

$ mkdir /mnt/huge
$ mount -t hugetlbfs nodev /mnt/huge

# 需要开机自动挂载的话可以在
$ echo "nodev /mnt/huge hugetlbfs defaults 0 0" >> /etc/fstab

# 配置完成后我们可以看到内存的使用率立马上升了
$ free -g	# 配置前
              total        used        free      shared  buff/cache   available
Mem:            125           1         122           0           1         123
$ free -g	# 配置后
              total        used        free      shared  buff/cache   available
Mem:            125         113          10           0           1          11
# 使用numactl查看内存状态也可以看到确实是两边的CPU内存各分配了56G
$ numactl -H
available: 2 nodes (0-1)
node 0 cpus: 0 2 4 6 8 10 12 14 16 18
node 0 size: 64184 MB
node 0 free: 4687 MB
node 1 cpus: 1 3 5 7 9 11 13 15 17 19
node 1 size: 64494 MB
node 1 free: 5759 MB
node distances:
node   0   1
  0:  10  21
  1:  21  10

2.1.4 配置ulimit

默认情况下系统的ulimit限制打开的文件描述符数量如果太小会影响dpvs正常运行,因此我们将其调大一些:

$ ulimit -n 655350
$ echo "ulimit -n 655350" >> /etc/rc.local
$ chmod a+x /etc/rc.local

2.2 挂载驱动模块

首先我们需要让系统挂载我们已经编译好的dpdk驱动(PMD驱动),然后再将网卡使用的默认驱动换为我们这里编译好的PMD驱动

$ modprobe uio
$ insmod /path/to/dpdk-stable-18.11.2/build/kmod/igb_uio.ko
$ insmod /path/to/dpdk-stable-18.11.2/build/kmod/rte_kni.ko carrier=on

dpdk-stable-18.11.2/usertools目录下有一些辅助我们安装使用dpdk的脚本,我们可以用它们来降低配置的复杂度,这里我们可以使用dpdk-devbind.py脚本来变更网卡的驱动

# 首先我们关闭我们需要加载PMD驱动的网卡
$ ifdown eth{2,3,4,5}

# 查看网卡状态,注意要特别关注网卡对应的PCI ID,下面只截取部分有用的输出结果
$ ./usertools/dpdk-devbind.py --status
Network devices using kernel driver
===================================
0000:04:00.0 '82599ES 10-Gigabit SFI/SFP+ Network Connection 10fb' if=eth2 drv=ixgbe unused=igb_uio
0000:04:00.1 '82599ES 10-Gigabit SFI/SFP+ Network Connection 10fb' if=eth3 drv=ixgbe unused=igb_uio
0000:82:00.0 'Ethernet 10G 2P X520 Adapter 154d' if=eth4 drv=ixgbe unused=igb_uio
0000:82:00.1 'Ethernet 10G 2P X520 Adapter 154d' if=eth5 drv=ixgbe unused=igb_uio

从上面的输出结果我们可以看到目前的网卡使用的是ixgbe驱动,而我们的目标是让其使用igb_uio驱动。注意如果这个时候系统的网卡太多,前面我们记录下来的网卡名-MAC地址-PCI ID三个参数就可以派上用场了。

# 对需要使用dpvs的网卡加载特定的驱动
$ ./usertools/dpdk-devbind.py -b igb_uio 0000:04:00.0
$ ./usertools/dpdk-devbind.py -b igb_uio 0000:04:00.1
$ ./usertools/dpdk-devbind.py -b igb_uio 0000:82:00.0
$ ./usertools/dpdk-devbind.py -b igb_uio 0000:82:00.1

# 再次检查是否加载成功,下面只截取部分有用的输出结果
$ ./usertools/dpdk-devbind.py --status
Network devices using DPDK-compatible driver
============================================
0000:04:00.0 '82599ES 10-Gigabit SFI/SFP+ Network Connection 10fb' drv=igb_uio unused=ixgbe
0000:04:00.1 '82599ES 10-Gigabit SFI/SFP+ Network Connection 10fb' drv=igb_uio unused=ixgbe
0000:82:00.0 'Ethernet 10G 2P X520 Adapter 154d' drv=igb_uio unused=ixgbe
0000:82:00.1 'Ethernet 10G 2P X520 Adapter 154d' drv=igb_uio unused=ixgbe

2.3 DPVS安装

$ cd /path/to/dpdk-stable-18.11.2/
$ export RTE_SDK=$PWD
$ cd /path/to/dpvs
$ make 
$ make install
# 查看bin目录下的二进制文件
$ ls /path/to/dpvs/bin/
dpip  dpvs  ipvsadm  keepalived

# 注意查看make过程中的提示信息,尤其是keepalived部分,如果出现下面的部分则表示IPVS支持IPv6
Keepalived configuration
------------------------
Keepalived version       : 2.0.19
Compiler                 : gcc
Preprocessor flags       : -D_GNU_SOURCE -I/usr/include/libnl3
Compiler flags           : -g -g -O2 -fPIE -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -O2
Linker flags             : -pie -Wl,-z,relro -Wl,-z,now
Extra Lib                : -lm -lcrypto -lssl -lnl-genl-3 -lnl-3
Use IPVS Framework       : Yes
IPVS use libnl           : Yes
IPVS syncd attributes    : No
IPVS 64 bit stats        : No



# 为了方便管理可以将相关的操作命令软链接到/sbin下方便全局执行
$ ln -s /path/to/dpvs/bin/dpvs /sbin/dpvs
$ ln -s /path/to/dpvs/bin/dpip /sbin/dpip
$ ln -s /path/to/dpvs/bin/ipvsadm /sbin/ipvsadm
$ ln -s /path/to/dpvs/bin/keepalived /sbin/keepalived

# 检查dpvs相关命令能否正常工作,注意其他命令要在dpvs进程启动后才能正常使用
$ dpvs -v
dpvs version: 1.8-10, build on 2021.07.26.15:34:26

2.4 配置dpvs.conf

dpvs/conf目录下面有着各种配置方式的dpvs配置文件范例,同时在dpvs.conf.items文件中记录了所有的参数,建议同学们全部阅读一遍了解了基本语法之后再进行配置。默认的dpvs启动的配置文件的是/etc/dpvs.conf

这里简单摘几个部分出来说一下(!为注释符号):

  • 日志的格式可以手动调成DEBUG并且修改日志输出的位置方便定位问题

    global_defs {
        log_level   DEBUG
        log_file    /path/to/dpvs/logs/dpvs.log
    }
    
  • 如果需要定义多个网卡,可以参考这个配置

    netif_defs {
        <init> pktpool_size     1048575
        <init> pktpool_cache    256
    
        <init> device dpdk0 {
            rx {
                queue_number        16
                descriptor_number   1024
                rss                 all
            }
            tx {
                queue_number        16
                descriptor_number   1024
            }
            fdir {
                mode                perfect
                pballoc             64k
                status              matched
            }
            kni_name                dpdk0.kni
        }
    
        <init> device dpdk1 {
            rx {
                queue_number        16
                descriptor_number   1024
                rss                 all
            }
            tx {
                queue_number        16
                descriptor_number   1024
            }
            fdir {
                mode                perfect
                pballoc             64k
                status              matched
            }
            kni_name                dpdk1.kni
        }
    
        <init> device dpdk2 {
            rx {
                queue_number        16
                descriptor_number   1024
                rss                 all
            }
            tx {
                queue_number        16
                descriptor_number   1024
            }
            fdir {
                mode                perfect
                pballoc             64k
                status              matched
            }
            kni_name                dpdk2.kni
        }
    
        <init> device dpdk3 {
            rx {
                queue_number        16
                descriptor_number   1024
                rss                 all
            }
            tx {
                queue_number        16
                descriptor_number   1024
            }
            fdir {
                mode                perfect
                pballoc             64k
                status              matched
            }
            kni_name                dpdk3.kni
        }
    
    }
    
  • 多个网卡的同一个收发队列共用同一个CPU

        <init> worker cpu1 {
            type    slave
            cpu_id  1
            port    dpdk0 {
                rx_queue_ids     0
                tx_queue_ids     0
            }
            port    dpdk1 {
                rx_queue_ids     0
                tx_queue_ids     0
            }
            port    dpdk2 {
                rx_queue_ids     0
                tx_queue_ids     0
            }
            port    dpdk3 {
                rx_queue_ids     0
                tx_queue_ids     0
            }
        }
    
  • 如果需要单独指定某个CPU来处理ICMP数据包,可以在该worker的参数中添加icmp_redirect_core

        <init> worker cpu16 {
            type    slave
            cpu_id  16
            icmp_redirect_core
            port    dpdk0 {
                rx_queue_ids     15
                tx_queue_ids     15
            }
        }
    

DPVS进程启动后可以直接在Linux系统的网络配置文件中对相应的网卡进行配置,使用起来和其他的eth0之类的网卡是完全一样的。

运行成功之后,使用dpip命令和正常的ipifconfig命令都能够看到对应的dpdk网卡,IPv4和IPv6网络都能够正常使用。下图只截取部分信息,IP和MAC信息已脱敏,IPv6信息已摘除。

$ dpip link show
1: dpdk0: socket 0 mtu 1500 rx-queue 16 tx-queue 16
    UP 10000 Mbps full-duplex auto-nego
    addr AA:BB:CC:23:33:33 OF_RX_IP_CSUM OF_TX_IP_CSUM OF_TX_TCP_CSUM OF_TX_UDP_CSUM

$ ip a
67: dpdk0.kni: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether AA:BB:CC:23:33:33 brd ff:ff:ff:ff:ff:ff
    inet 1.1.1.1/24 brd 1.1.1.255 scope global dpdk0.kni
       valid_lft forever preferred_lft forever
       
$ ifconfig dpdk0.kni
dpdk0.kni: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 1.1.1.1  netmask 255.255.254.0  broadcast 1.1.1.255
        ether AA:BB:CC:23:33:33  txqueuelen 1000  (Ethernet)
        RX packets 1790  bytes 136602 (133.4 KiB)
        RX errors 0  dropped 52  overruns 0  frame 0
        TX packets 115  bytes 24290 (23.7 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

3、配置FullNat

为了校验我们的DPVS能够正常工作,这里我们参考官方的配置文档,先配置一个最简单的双臂模式的FNAT。参考官方的架构图并修改其中的IP地址信息我们可以得到下面的简单架构图。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yWrJXOM5-1645782758324)(https://resource.tinychen.com/20210728123202.svg)]

这里我们使用dpdk2网卡作为wan口,dpdk0网卡作为lan口

# 首先我们把VIP 10.0.96.204 加到dpdk2网卡(wan)上
$ dpip addr add 10.0.96.204/32 dev dpdk2

# 接着我们需要添加两条路由,分为是wan口网段的路由和到RS机器网段的路由
$ dpip route add 10.0.96.0/24 dev dpdk2
$ dpip route add 192.168.229.0/24 dev dpdk0
# 最好再加一条到网关的默认路由保证ICMP数据包的回包能跑通
$ dpip route add default via 10.0.96.254 dev dpdk2

# 使用RR算法建立转发规则
# add service <VIP:vport> to forwarding, scheduling mode is RR.
# use ipvsadm --help for more info.
$ ipvsadm -A -t 10.0.96.204:80 -s rr

# 这里为了方便测试我们只添加一台RS
# add two RS for service, forwarding mode is FNAT (-b)
$ ipvsadm -a -t 10.0.96.204:80 -r 192.168.229.1 -b

# 添加LocalIP到网络中,FNAT模式这里需要
# add at least one Local-IP (LIP) for FNAT on LAN interface
$ ipvsadm --add-laddr -z 192.168.229.204 -t 10.0.96.204:80 -F dpdk0


# 然后我们查看一下效果
$ dpip route show
inet 192.168.229.204/32 via 0.0.0.0 src 0.0.0.0 dev dpdk0 mtu 1500 tos 0 scope host metric 0 proto auto
inet 10.0.96.204/32 via 0.0.0.0 src 0.0.0.0 dev dpdk2 mtu 1500 tos 0 scope host metric 0 proto auto
inet 10.0.96.0/24 via 0.0.0.0 src 0.0.0.0 dev dpdk2 mtu 1500 tos 0 scope link metric 0 proto auto
inet 192.168.229.0/24 via 0.0.0.0 src 0.0.0.0 dev dpdk0 mtu 1500 tos 0 scope link metric 0 proto auto
inet 0.0.0.0/0 via 10.0.96.254 src 0.0.0.0 dev dpdk2 mtu 1500 tos 0 scope global metric 0 proto auto

$ dpip addr show
inet 10.0.96.204/32 scope global dpdk2
     valid_lft forever preferred_lft forever
inet 192.168.229.204/32 scope global dpdk0
     valid_lft forever preferred_lft forever

$ ipvsadm  -ln
IP Virtual Server version 0.0.0 (size=0)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.0.96.204:80 rr
  -> 192.168.229.1:80              FullNat 1      0          0
$ ipvsadm  -G
VIP:VPORT            TOTAL    SNAT_IP              CONFLICTS  CONNS
10.0.96.204:80    1
                              192.168.229.204       0          0

然后我们在RS上面启动一个nginx,设置返回IP和端口号,看看效果:

    server {
        listen 80 default;

        location / {
            default_type text/plain;
            return 200 "Your IP and port is $remote_addr:$remote_port\n";
        }

    }

直接对VIP使用ping和curl命令进行测试:

$ ping -c4 10.0.96.204
PING 10.0.96.204 (10.0.96.204) 56(84) bytes of data.
64 bytes from 10.0.96.204: icmp_seq=1 ttl=54 time=47.2 ms
64 bytes from 10.0.96.204: icmp_seq=2 ttl=54 time=48.10 ms
64 bytes from 10.0.96.204: icmp_seq=3 ttl=54 time=48.5 ms
64 bytes from 10.0.96.204: icmp_seq=4 ttl=54 time=48.5 ms

--- 10.0.96.204 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 8ms
rtt min/avg/max/mdev = 47.235/48.311/48.969/0.684 ms

$ curl 10.0.96.204
Your IP and port is 192.168.229.204:1033

可以发现不管在什么机器上面都只会返回LIP的IP和端口号,如果需要获取用户的真实IP,那么就需要安装TOA模块

4、RS安装TOA模块

目前开源社区提供toa模块的版本比较多,这里我们为了保证兼容性,直接使用dpvs官方提供的toauoa模块,根据他们的官方描述,他们的toa模块是从Alibaba TOA中剥离出来

由于我们这里的RS机器和DPVS机器都是使用版本的CentOS7系统,因此我们可以直接在DPVS机器上面编译toa模块,再复制到各个RS机器上使用

$ cd /path/to/dpvs/kmod/toa/
$ make

顺利编译完成之后会在当前目录下生成一个toa.ko模块文件,这就是我们需要的文件,直接使用insmod命令加载模块然后检查

$ insmod toa.ko
$ lsmod  | grep toa
toa                   279641  0

确保开机加载模块,可以在rc.local文件中加入下面的指令

/usr/sbin/insmod /path/to/toa.ko
# for example:
# /usr/sbin/insmod /home/dpvs/kmod/toa/toa.ko

除了toa模块之外,还有针对UDP协议的uoa模块,和上面的toa模块编译安装过程完全一致,这里不再赘述。

在RS机器上面加载了toa模块后我们再次使用curl测试效果:

$ curl 10.0.96.204
Your IP and port is 172.16.0.1:62844

至此,整个DPVS的FullNat模式就算是部署完成并且能够正常工作了。由于DPVS支持非常多的配置组合,后面会再专门写一篇关于IPv6、nat64、keepalived、bonding、Master/Backup模式的配置。

举报

相关推荐

0 条评论