前言:开源(Open Source,开放源码)被非盈利软件组织(美国的Open Source Initiative协会)注册为认证标记,并对其进行了正式的定义,用于描述那些源码可以被公众使用的软件,并且此软件的使用、修改和发行也不受许可证的限制。
一、OVS-DPDK 虚拟交换机
OVS-DPDK(Open vSwitch with DPDK)是一种高性能的虚拟交换机,它将DPDK(Data Plane Development Kit)和Open vSwitch技术相结合,用于在虚拟化环境中加速数据平面处理。OVS-DPDK采用用户空间数据包处理模式,利用DPDK提供高效、低延迟的数据包处理功能,并且可以与传统的内核态网络协议栈进行无缝衔接。
通过使用OVS-DPDK,可以实现更高的吞吐量和更低的延迟,从而提升网络性能。同时,OVS-DPDK还支持灵活配置和管理网络服务,并且可以与其他开源软件如OpenStack等集成使用。因此,在云计算、SDN、NFV等领域中得到了广泛应用。
要使用 ovs-dpdk,需要在node上构建 DPDK 并使用相应的 DPDK flag重新构建 ovs。OVS-DPDK需要从源码编译,因为高度依赖内核等所在机器的环境,并需要配置很多参数以达到高性能。这意味着很难提供一个ovs-dpdk docker镜像来满足所有情况。
OVS-DPDK需要大页内存作为资源。由于 DPDK 会剥夺系统对 nic 的控制权,我们需要一个 ovs-dpdk 专用的 nic 来传输容器网络,另一个 nic 用于普通主机网络。
普通Pod的ovs网络是在 pod 和 ovs 端口之间放置了一个 veth 对。veth 的一端移动到容器网络命名空间。不能用 OVS-DPDK 做到这一点。当我们请求一个新的 DPDK 端口时,我们最终会在 /var/run/openvswitch/ 这样的目录中得到一个类似 vhost-user 套接字文件的东西。它不能在命名空间之间移动,它必须被挂载到 pod 中就像一个普通的文件(由 Userspace-CNI 提供的功能)。所以不能使用 OVS-DPDK 作为默认网络。Pod对K8S API不可达,K8S不能对pod进行健康检查。因此,需要依赖 Multus 来连接多个网络。Kernel-OVS 仍然是默认网络。此外,Multus 允许为Pod提供 OVS-DPDK 网络。这是同一个 OVS 实例,但是 DPDK port位于另一个支持 DPDK 的bridge上。
ovs-dpdk创建br和port, ovs 集成网桥类型更改为 netdev ,端口类型更改为 dpdkvhostuser 并设置其他 ovs-dpdk 参数。
1.1准备工作
[root@backendcloud-fedora27 ~]# dnf groupinstall "Development Tools" [root@backendcloud-fedora27 ~]# dnf groupinstall "Virtualization" [root@backendcloud-fedora27 ~]# dnf install qemu [root@backendcloud-fedora27 ~]# dnf install automake tunctl kernel-tools pciutils hwloc numactl [root@backendcloud-fedora27 ~]# dnf install libpcap-devel [root@backendcloud-fedora27 ~]# dnf install numactl-devel [root@backendcloud-fedora27 ~]# dnf install libtool
1.2编译DPDK
[root@backendcloud-fedora27 ~]# tar xf dpdk-17.08.1.tar.xz [root@backendcloud-fedora27 ~]# ls anaconda-ks.cfg dpdk-17.08.1.tar.xz dpdk-stable-17.08.1 [root@backendcloud-fedora27 ~]# cd dpdk- -bash: cd: dpdk-: No such file or directory [root@backendcloud-fedora27 ~]# cd dpdk-stable-17.08.1/ [root@backendcloud-fedora27 dpdk-stable-17.08.1]# export DPDK_DIR=`pwd`/build [root@backendcloud-fedora27 dpdk-stable-17.08.1]# make config T=x86_64-native-linuxapp-gcc Configuration done using x86_64-native-linuxapp-gcc [root@backendcloud-fedora27 dpdk-stable-17.08.1]# sed -ri 's,(PMD_PCAP=).*,\1y,' build/.config [root@backendcloud-fedora27 dpdk-stable-17.08.1]# make
make 报错:
/usr/src/kernels/4.18.19-100.fc27.x86_64/Makefile:945: *** "Cannot generate ORC metadata for CONFIG_UNWINDER_ORC=y, please install libelf-dev, libelf-devel or elfutils-libelf-devel". Stop.
安装 elfutils-libelf-devel 解决:
[root@backendcloud-fedora27 dpdk-stable-17.08.1]# yum install -y elfutils-libelf-devel
编译OvS-DPDK
[root@backendcloud-fedora27 ~]# wget http://openvswitch.org/releases/openvswitch-2.8.1.tar.gz [root@backendcloud-fedora27 ~]# tar -xzvf openvswitch-2.8.1.tar.gz [root@backendcloud-fedora27 ~]# cd openvswitch-2.8.1/ [root@backendcloud-fedora27 openvswitch-2.8.1]# export OVS_DIR=`pwd` [root@backendcloud-fedora27 openvswitch-2.8.1]# sudo ./boot.sh [root@backendcloud-fedora27 openvswitch-2.8.1]# sudo ./configure --with-dpdk="$DPDK_DIR/" CFLAGS="-g -Ofast" [root@backendcloud-fedora27 openvswitch-2.8.1]# sudo make 'CFLAGS=-g -Ofast -march=native' -j10
Create OvS DB and Start OvS DB-Server
[root@backendcloud-fedora27 openvswitch-2.8.1]# pkill -9 ovs [root@backendcloud-fedora27 openvswitch-2.8.1]# rm -rf /usr/local/var/run/openvswitch [root@backendcloud-fedora27 openvswitch-2.8.1]# rm -rf /usr/local/etc/openvswitch/ [root@backendcloud-fedora27 openvswitch-2.8.1]# rm -f /usr/local/etc/openvswitch/conf.db [root@backendcloud-fedora27 openvswitch-2.8.1]# mkdir -p /usr/local/etc/openvswitch [root@backendcloud-fedora27 openvswitch-2.8.1]# mkdir -p /usr/local/var/run/openvswitch [root@backendcloud-fedora27 openvswitch-2.8.1]# cd $OVS_DIR [root@backendcloud-fedora27 openvswitch-2.8.1]# ./ovsdb/ovsdb-tool create /usr/local/etc/openvswitch/conf.db ./vswitchd/vswitch.ovsschema [root@backendcloud-fedora27 openvswitch-2.8.1]# ./ovsdb/ovsdb-server --remote=punix:/usr/local/var/run/openvswitch/db.sock --remote=db:Open_vSwitch,Open_vSwitch,manager_options --pidfile --detach [root@backendcloud-fedora27 openvswitch-2.8.1]# ./utilities/ovs-vsctl --no-wait init
Configure Fedora27 for OvS-DPDK
[root@backendcloud-fedora27 openvswitch-2.8.1]# vim /etc/default/grub ... GRUB_CMDLINE_LINUX_DEFAULT="default_hugepagesz=1G hugepagesz=1G hugepages=4 hugepagesz=2M hugepages=512 iommu=pt intel_iommu=on" [root@backendcloud-fedora27 openvswitch-2.8.1]# grub2-mkconfig -o /boot/grub2/grub.cfg Generating grub configuration file ... Found linux image: /boot/vmlinuz-4.18.19-100.fc27.x86_64 Found initrd image: /boot/initramfs-4.18.19-100.fc27.x86_64.img Found linux image: /boot/vmlinuz-0-rescue-98bddbf29a4f40009b8390e2c27a80ac Found initrd image: /boot/initramfs-0-rescue-98bddbf29a4f40009b8390e2c27a80ac.img done [root@backendcloud-fedora27 openvswitch-2.8.1]# reboot
可以设置cpu隔离:GRUB_CMDLINE_LINUX_DEFAULT=”default_hugepagesz=1G hugepagesz=1G hugepages=16 hugepagesz=2M hugepages=2048 iommu=pt intel_iommu=on isolcpus=1-27,29-55”
[root@backendcloud-fedora27 ~]# mkdir -p /mnt/huge [root@backendcloud-fedora27 ~]# mkdir -p /mnt/huge_2mb [root@backendcloud-fedora27 ~]# mount -t hugetlbfs hugetlbfs /mnt/huge [root@backendcloud-fedora27 ~]# mount -t hugetlbfs none /mnt/huge_2mb -o pagesize=2MB [root@backendcloud-fedora27 ~]# cat /proc/meminfo MemTotal: 17650724 kB MemFree: 11744728 kB MemAvailable: 11867184 kB ... HugePages_Total: 4 HugePages_Free: 4 HugePages_Rsvd: 0 HugePages_Surp: 0 Hugepagesize: 1048576 kB Hugetlb: 5242880 kB DirectMap4k: 124736 kB DirectMap2M: 5273600 kB DirectMap1G: 13631488 kB [root@backendcloud-fedora27 ~]# cat /proc/cmdline BOOT_IMAGE=/vmlinuz-4.18.19-100.fc27.x86_64 root=/dev/mapper/fedora-root ro rd.lvm.lv=fedora/root rhgb quiet default_hugepagesz=1G hugepagesz=1G hugepages=4 hugepagesz=2M hugepages=512 iommu=pt intel_iommu=on
配置OVS-DPDK
[root@backendcloud-fedora27 ~]# modprobe vfio-pci [root@backendcloud-fedora27 ~]# modprobe openvswitch [root@backendcloud-fedora27 ~]# cd openvswitch-2.8.1 [root@backendcloud-fedora27 openvswitch-2.8.1]# ./ovsdb/ovsdb-server --remote=punix:/usr/local/var/run/openvswitch/db.sock --remote=db:Open_vSwitch,Open_vSwitch,manager_options --pidfile --detach [root@backendcloud-fedora27 openvswitch-2.8.1]# ./vswitchd/ovs-vswitchd unix:/usr/local/var/run/openvswitch/db.sock --pidfile --detach 2022-09-22T06:09:08Z|00001|ovs_numa|INFO|Discovered 4 CPU cores on NUMA node 0 2022-09-22T06:09:08Z|00002|ovs_numa|INFO|Discovered 1 NUMA nodes and 4 CPU cores 2022-09-22T06:09:08Z|00003|reconnect|INFO|unix:/usr/local/var/run/openvswitch/db.sock: connecting... 2022-09-22T06:09:08Z|00004|reconnect|INFO|unix:/usr/local/var/run/openvswitch/db.sock: connected 2022-09-22T06:09:08Z|00005|dpdk|INFO|DPDK Disabled - Use other_config:dpdk-init to enable [root@backendcloud-fedora27 openvswitch-2.8.1]# ./utilities/ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-init=true
配置隔离cpu,memory提高DPDK性能,非必须。
[root@backendcloud-fedora27 openvswitch-2.8.1]# ./utilities/ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x10000001 [root@backendcloud-fedora27 openvswitch-2.8.1]# ./utilities/ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-lcore-mask=0xffffffeffffffe [root@backendcloud-fedora27 openvswitch-2.8.1]# ./utilities/ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-socket-mem="1024,1024"
Creating an OvS-DPDK Bridge and Ports
[root@backendcloud-fedora27 openvswitch-2.8.1]# ./utilities/ovs-vsctl show 52de1671-20cc-438c-be6a-d41e7923100b [root@backendcloud-fedora27 openvswitch-2.8.1]# ./utilities/ovs-vsctl add-br br0 -- set bridge br0 datapath_type=netdev [root@backendcloud-fedora27 openvswitch-2.8.1]# ./utilities/ovs-vsctl add-port br0 vhost-user1 -- set Interface vhost-user1 type=dpdkvhostuser [root@backendcloud-fedora27 openvswitch-2.8.1]# ./utilities/ovs-vsctl add-port br0 vhost-user2 -- set Interface vhost-user2 type=dpdkvhostuser [root@backendcloud-fedora27 openvswitch-2.8.1]# ./utilities/ovs-vsctl show 52de1671-20cc-438c-be6a-d41e7923100b Bridge "br0" Port "vhost-user1" Interface "vhost-user1" type: dpdkvhostuser Port "br0" Interface "br0" type: internal Port "vhost-user2" Interface "vhost-user2" type: dpdkvhostuser
Binding Nic Device to DPDK
[root@backendcloud-fedora27 openvswitch-2.8.1]# modprobe vfio-pci [root@backendcloud-fedora27 openvswitch-2.8.1]# cp ~/dpdk-stable-17.08.1/usertools/dpdk-devbind.py /usr/bin/ [root@backendcloud-fedora27 openvswitch-2.8.1]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 00:0c:29:7a:c2:a0 brd ff:ff:ff:ff:ff:ff inet 192.168.126.143/24 brd 192.168.126.255 scope global dynamic ens33 valid_lft 1442sec preferred_lft 1442sec inet6 fe80::7e7a:3d91:e5b0:4a63/64 scope link valid_lft forever preferred_lft forever 3: ens34: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 00:0c:29:7a:c2:aa brd ff:ff:ff:ff:ff:ff inet 192.168.126.144/24 brd 192.168.126.255 scope global dynamic ens34 valid_lft 1398sec preferred_lft 1398sec inet6 fe80::49fe:5d8c:8b86:95d8/64 scope link valid_lft forever preferred_lft forever 4: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000 link/ether 52:54:00:30:d1:c3 brd ff:ff:ff:ff:ff:ff inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0 valid_lft forever preferred_lft forever 5: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc fq_codel master virbr0 state DOWN group default qlen 1000 link/ether 52:54:00:30:d1:c3 brd ff:ff:ff:ff:ff:ff 6: ovs-netdev: <BROADCAST,PROMISC> mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether 22:0e:6b:c1:4c:91 brd ff:ff:ff:ff:ff:ff 7: br0: <BROADCAST,PROMISC> mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether 0e:3b:21:02:03:47 brd ff:ff:ff:ff:ff:ff [root@backendcloud-fedora27 openvswitch-2.8.1]# dpdk-devbind.py --status Network devices using DPDK-compatible driver ============================================ <none> Network devices using kernel driver =================================== 0000:02:01.0 '82545EM Gigabit Ethernet Controller (Copper) 100f' if=ens33 drv=e1000 unused=vfio-pci *Active* 0000:02:02.0 '82545EM Gigabit Ethernet Controller (Copper) 100f' if=ens34 drv=e1000 unused=vfio-pci *Active* ... [root@backendcloud-fedora27 openvswitch-2.8.1]# dpdk-devbind.py --bind=vfio-pci ens34 Routing table indicates that interface 0000:02:02.0 is active. Not modifying [root@backendcloud-fedora27 openvswitch-2.8.1]# yum install -y net-tools [root@backendcloud-fedora27 openvswitch-2.8.1]# ifconfig ens34 down [root@backendcloud-fedora27 openvswitch-2.8.1]# dpdk-devbind.py --bind=vfio-pci ens34 [root@backendcloud-fedora27 openvswitch-2.8.1]# dpdk-devbind.py --status Network devices using DPDK-compatible driver ============================================ 0000:02:02.0 '82545EM Gigabit Ethernet Controller (Copper) 100f' drv=vfio-pci unused= Network devices using kernel driver =================================== 0000:02:01.0 '82545EM Gigabit Ethernet Controller (Copper) 100f' if=ens33 drv=e1000 unused=vfio-pci *Active* ...
Using DPDK vhost-user Ports with VMs
去官网下载镜像并修改root密码:
[root@backendcloud-fedora27 ~]# virt-customize -a centos7vm1.qcow2 --root-password password:666666 -bash: virt-customize: command not found [root@backendcloud-fedora27 ~]# dnf install -y libguestfs-tools [root@backendcloud-centos9 ~]# virt-customize -a centos7vm1.qcow2 --root-password password:666666 [ 0.0] Examining the guest ... [ 6.8] Setting a random seed [ 6.8] Setting passwords [ 8.3] SELinux relabelling [ 12.9] Finishing off [root@backendcloud-centos9 ~]# virt-customize -a centos7vm2.qcow2 --root-password password:666666
启动两个dpdk vm:
qemu-system-x86_64 -m 1024 -smp 4 -cpu host,pmu=off -hda /root/centos7vm1.qcow2 -boot c -enable-kvm -no-reboot -net none -nographic \ -chardev socket,id=char1,path=/usr/local/var/run/openvswitch/vhost-user1 \ -netdev type=vhost-user,id=mynet1,chardev=char1,vhostforce \ -device virtio-net-pci,mac=00:00:00:00:00:01,netdev=mynet1 \ -object memory-backend-file,id=mem,size=1G,mem-path=/dev/hugepages,share=on \ -numa node,memdev=mem -mem-prealloc qemu-system-x86_64 -m 1024 -smp 4 -cpu host,pmu=off -hda /root/centos7vm2.qcow2 -boot c -enable-kvm -no-reboot -net none -nographic \ -chardev socket,id=char2,path=/usr/local/var/run/openvswitch/vhost-user2 \ -netdev type=vhost-user,id=mynet2,chardev=char2,vhostforce \ -device virtio-net-pci,mac=00:00:00:00:00:02,netdev=mynet2 \ -object memory-backend-file,id=mem,size=1G,mem-path=/dev/hugepages,share=on \ -numa node,memdev=mem -mem-prealloc
若上面的操作是在vmware上操作,需要加上上面额外的参数pmu=off,为了规避vmware的bug。若不加会报下面的错误:
[root@backendcloud-fedora27 ~]# qemu-system-x86_64 -m 1024 -smp 4 -cpu host -hda /root/centos7vm1.qcow2 -boot c -enable-kvm -no-reboot -net none -nographic \ > -chardev socket,id=char1,path=/usr/local/var/run/openvswitch/vhost-user1 \ > -netdev type=vhost-user,id=mynet1,chardev=char1,vhostforce \ > -device virtio-net-pci,mac=00:00:00:00:00:01,netdev=mynet1 \ > -object memory-backend-file,id=mem,size=1G,mem-path=/dev/hugepages,share=on \ > -numa node,memdev=mem -mem-prealloc qemu-system-x86_64: error: failed to set MSR 0x38d to 0x0 qemu-system-x86_64: /builddir/build/BUILD/qemu-2.10.2/target/i386/kvm.c:1806: kvm_put_msrs: Assertion `ret == cpu->kvm_msr_buf->nmsrs' failed. Aborted (core dumped)
1.3安装iperf3,并测试
[root@localhost ~]# iperf3 -s ----------------------------------------------------------- Server listening on 5201 ----------------------------------------------------------- Accepted connection from 192.168.0.2, port 48462 [ 5] local 192.168.0.1 port 5201 connected to 192.168.0.2 port 48464 [ ID] Interval Transfer Bandwidth [ 5] 0.00-1.00 sec 828 MBytes 6.95 Gbits/sec [ 5] 1.00-2.00 sec 775 MBytes 6.51 Gbits/sec [ 5] 2.00-3.00 sec 852 MBytes 7.15 Gbits/sec [ 5] 3.00-4.00 sec 1.03 GBytes 8.85 Gbits/sec [ 5] 4.00-5.00 sec 928 MBytes 7.79 Gbits/sec [ 5] 5.00-6.00 sec 905 MBytes 7.59 Gbits/sec [ 5] 6.00-7.00 sec 824 MBytes 6.91 Gbits/sec [ 5] 7.00-8.00 sec 962 MBytes 8.07 Gbits/sec [ 5] 8.00-9.00 sec 987 MBytes 8.28 Gbits/sec [ 5] 9.00-10.00 sec 856 MBytes 7.19 Gbits/sec [ 5] 10.00-10.04 sec 35.2 MBytes 7.97 Gbits/sec - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bandwidth [ 5] 0.00-10.04 sec 0.00 Bytes 0.00 bits/sec sender [ 5] 0.00-10.04 sec 8.80 GBytes 7.53 Gbits/sec receiver ----------------------------------------------------------- Server listening on 5201 ----------------------------------------------------------- [root@localhost ~]# iperf3 -c 192.168.0.1 Connecting to host 192.168.0.1, port 5201 [ 4] local 192.168.0.2 port 48464 connected to 192.168.0.1 port 5201 [ ID] Interval Transfer Bandwidth Retr Cwnd [ 4] 0.00-1.00 sec 861 MBytes 7.22 Gbits/sec 1087 177 KBytes [ 4] 1.00-2.00 sec 745 MBytes 6.26 Gbits/sec 566 182 KBytes [ 4] 2.00-3.00 sec 908 MBytes 7.61 Gbits/sec 755 184 KBytes [ 4] 3.00-4.00 sec 1.01 GBytes 8.66 Gbits/sec 824 212 KBytes [ 4] 4.00-5.00 sec 935 MBytes 7.85 Gbits/sec 589 165 KBytes [ 4] 5.00-6.00 sec 875 MBytes 7.34 Gbits/sec 514 194 KBytes [ 4] 6.00-7.00 sec 850 MBytes 7.13 Gbits/sec 718 188 KBytes [ 4] 7.00-8.00 sec 983 MBytes 8.25 Gbits/sec 949 158 KBytes [ 4] 8.00-9.00 sec 930 MBytes 7.80 Gbits/sec 649 180 KBytes [ 4] 9.00-10.00 sec 892 MBytes 7.48 Gbits/sec 668 147 KBytes - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-10.00 sec 8.80 GBytes 7.56 Gbits/sec 7319 sender [ 4] 0.00-10.00 sec 8.80 GBytes 7.56 Gbits/sec receiver iperf Done.
对比非OVS-DPDK虚拟机
[root@backendcloud-fedora27 ~]# yum -y install wget openssl-devel gcc make python-devel openssl-devel kernel-devel graphviz kernel-debug-devel autoconf automake rpm-build redhat-rpm-config libtool python-twisted-core python-zope-interface PyQt4 desktop-file-utils libcap-ng-devel groff checkpolicy selinux-policy-devel [root@backendcloud-fedora27 ~]# adduser ovs [root@backendcloud-fedora27 ~]# su - ovs [ovs@backendcloud-fedora27 ~]$ mkdir -p ~/rpmbuild/SOURCES [ovs@backendcloud-fedora27 ~]$ cd ~/rpmbuild/SOURCES [ovs@backendcloud-fedora27 ~]$ wget http://openvswitch.org/releases/openvswitch-2.5.10.tar.gz [ovs@backendcloud-fedora27 ~]$ tar -zxvf openvswitch-2.5.10.tar.gz [ovs@backendcloud-fedora27 ~]$ rpmbuild -bb --nocheck openvswitch-2.5.10/rhel/openvswitch-fedora.spec [ovs@backendcloud-fedora27 ~]$ exit [root@backendcloud-fedora27 ~]# yum localinstall yum localinstall /home/ovs/rpmbuild/RPMS/x86_64/openvswitch-2.5.10-1.fc27.x86_64.rpm -y [root@backendcloud-fedora27 ~]# ovs-vsctl --version ovs-vsctl (Open vSwitch) 2.5.10 Compiled Sep 22 2022 16:43:31 DB Schema 7.12.1 [root@backendcloud-fedora27 ~]# systemctl start openvswitch.service [root@backendcloud-fedora27 tmp]# ovs-vsctl show 0425b4a1-cfb0-4cbb-94a8-68bd581ce48e [root@backendcloud-fedora27 ~]# ovs-vsctl add-br br1 [root@backendcloud-fedora27 tmp]# ovs-vsctl show 0425b4a1-cfb0-4cbb-94a8-68bd581ce48e Bridge "br1" Port "br1" Interface "br1" type: internal ovs_version: "2.5.10" [root@backendcloud-fedora27 tmp]# cat test.xml <network> <name>test</name> <forward mode="bridge"/> <bridge name="br1"/> <virtualport type="openvswitch"/> </network> [root@backendcloud-fedora27 tmp]# virsh net-list --all Name State Autostart Persistent ---------------------------------------------------------- default active yes yes [root@backendcloud-fedora27 tmp]# virsh net-define test.xml Network test defined from test.xml [root@backendcloud-fedora27 tmp]# virsh net-list --all Name State Autostart Persistent ---------------------------------------------------------- default active yes yes test inactive no yes [root@backendcloud-fedora27 tmp]# virsh net-start test Network test started [root@backendcloud-fedora27 tmp]# virsh net-list --all Name State Autostart Persistent ---------------------------------------------------------- default active yes yes test active no yes [root@backendcloud-fedora27 tmp]# ovs-vsctl show 0425b4a1-cfb0-4cbb-94a8-68bd581ce48e Bridge "br1" Port "br1" Interface "br1" type: internal ovs_version: "2.5.10" [root@backendcloud-fedora27 tmp]# virt-install --virt-type kvm --name test-vm1 --ram 1024 --boot hd --disk path=c1.qcow2 --network network=test,mac=52:54:00:aa:69:dd --noautoconsole --keymap=en-us WARNING No operating system detected, VM performance may suffer. Specify an OS with --os-variant for optimal results. Starting install... Domain creation completed. [root@backendcloud-fedora27 tmp]# virt-install --virt-type kvm --name test-vm2 --ram 1024 --boot hd --disk path=c2.qcow2 --network network=test,mac=52:54:00:aa:69:de --noautoconsole --keymap=en-us WARNING No operating system detected, VM performance may suffer. Specify an OS with --os-variant for optimal results. Starting install... Domain creation completed. # 不加 "--keymap=en-us" kvm虚拟机会出现键盘乱码 [root@backendcloud-fedora27 tmp]# ovs-vsctl show 0425b4a1-cfb0-4cbb-94a8-68bd581ce48e Bridge "br1" Port "vnet0" Interface "vnet0" Port "br1" Interface "br1" type: internal Port "vnet1" Interface "vnet1" ovs_version: "2.5.10"
二、Lagopus虚拟交换机
Lagopus是一种开源的高性能虚拟交换机软件,由日本国立信息通信技术研究所(NICT)开发。它基于DPDK(Data Plane Development Kit)和OpenFlow协议,并采用了多线程、多核、流水线等技术,以实现高效的数据包处理。
Lagopus支持多种不同类型的网络接口和数据包格式,可以在物理服务器上或虚拟机中运行。它提供灵活的配置选项和API接口,使得用户可以轻松地进行自定义设置和扩展。同时,Lagopus还支持与其他SDN控制器集成使用。
由于其高性能、可定制化以及与其他开源软件兼容等特点,Lagopus在云计算、网络虚拟化、NFV等领域中具有广泛应用价值。
高性能软件 OpenFlow 1.3 交换机和路由器
特征
- Lagopus 交换机
- Best OpenFlow 1.3 compliant switch
- OpenFlow Switch Specification 1.3.4
- High performance software data plane with DPDK
- Lagopus 路由器
- 多个VRF
- VLAN交换
- DPDK支持
- 仍然是测试版
三、MoonGen数据包生成器
MoonGen是建立在一个脚本化的高速数据包生成libmoon。整个负载生成器由 Lua 脚本控制:发送的所有数据包均由用户提供的脚本制作。多亏了令人难以置信的快速 LuaJIT VM 和数据包处理库 DPDK,它可以在仅使用单个 CPU 内核的情况下用 64 字节数据包使 10 Gbit/s 以太网链路饱和。即使每个数据包都被 Lua 脚本修改,MoonGen 也能达到这个速率。它不依赖于重播相同缓冲区之类的技巧。
MoonGen 还可以接收数据包,例如,检查被测系统丢弃了哪些数据包。由于接收也完全由用户的 Lua 脚本控制,因此可用于实现高级测试脚本。例如,可以使用两个相互建立连接的 MoonGen 实例。此设置可用于对防火墙等中间设备进行基准测试。
MoonGen 重点关注四个要点:
- 高性能和多核扩展:每个 CPU 内核每秒 > 2000 万个数据包
- 灵活性:每个数据包都是由用户提供的 Lua 脚本实时制作的
- 精确和准确的时间戳:在商品硬件上以亚微秒精度进行时间戳
- 精确和准确的速率控制:在商品硬件上可靠地生成任意流量模式
MoonGen 建立在libmoon 之上,它是 DPDK 的 Lua 包装器。
用户可以为他们的实验编写自定义脚本。建议在脚本中使用硬编码的设置特定常量。脚本就是配置,为脚本编写一个复杂的配置界面是无关紧要的。或者,有一个简化(但功能较弱)的命令行界面可用于快速测试。
下图显示了架构以及如何处理多核支持。
执行从必须在用户脚本中定义的主任务开始。此任务在使用的 NIC 上配置队列和过滤器,然后启动一个或多个从属任务。
请注意,Lua 没有任何对多线程的本机支持。因此,MoonGen 会为每个线程启动一个新的且完全独立的 LuaJIT VM。新的 VM 接收序列化参数:要执行的函数和参数,例如要从中发送数据包的队列。线程仅通过底层库共享状态。
示例脚本quality-of-service-test.lua展示了如何使用此线程模型来实现典型的负载生成任务。它通过发送两种不同类型的数据包来实现 QoS 测试并测量它们的吞吐量和延迟。它通过启动两项数据包生成任务来实现:一项用于后台流量,一项用于优先流量。第三个任务用于对传入的数据包进行分类和计数。
四、FastClick处理程序
FastClick是一种高性能的用户空间点击处理程序,用于优化网络应用程序中的数据包接收和处理。它最初由EPFL(瑞士洛桑联邦理工学院)开发,旨在提高网络应用程序的吞吐量和响应时间。
FastClick通过将数据包处理逻辑移动到用户空间,并使用自定义算法来实现快速匹配和过滤,从而实现了比内核中处理更高效的数据包接收和处理。它支持多线程操作,并具有灵活、可配置的流水线模型,可以轻松地进行定制以适应不同类型的网络应用程序。
FastClick已被广泛用于高性能网络设备(例如路由器、交换机等)和分布式系统中,以提供快速、可靠的数据包处理功能。
4.1安装fastclick
安装fastclick可以使用npm,Component和Bower。另外也提供了Ruby版的gem fastclick-rails以及.NET提供了NuGet package。最直接的可以在页面引入fastclick js文件。如:
在页面直接引入fastclick.js
<script type='application/javascript' src='/path/to/fastclick.js'></script>
使用npm安装
npm install fastclick
初始化FastClick实例
初始化FastClick实例建议在页面的DOM文档加载完成后。
纯Javascript版
if ('addEventListener' in document) { document.addEventListener('DOMContentLoaded', function() { FastClick.attach(document.body); }, false); }
jQuery版
$(function() { FastClick.attach(document.body); });
类似Common JS的模块系统方式
var attachFastClick = require('fastclick'); attachFastClick(document.body);
调用require('fastclick')会返回FastClick.attach函数。
使用needsclick过滤特定的元素
如果页面上有一些特定的元素不需要使用fastclick来立刻触发点击事件,可以在元素的class上添加needsclick:
<a class="needsclick">Ignored by FastClick</a>
不需要使用fastclick的情况
以下这几种情况是不需要使用fastclick:
1、FastClick是不会对PC浏览器添加监听事件
2、Android版Chrome 32+浏览器,如果设置viewport meta的值为width=device-width,这种情况下浏览器会马上出发点击事件,不会延迟300毫秒。
<meta name="viewport" content="width=device-width, initial-scale=1">
3、所有版本的Android Chrome浏览器,如果设置viewport meta的值有user-scalable=no,浏览器也是会马上出发点击事件。
4、IE11+浏览器设置了css的属性touch-action: manipulation,它会在某些标签(a,button等)禁止双击事件,IE10的为-ms-touch-action: manipulation
五、OpenFastPath数据包处理框架
OpenFastPath(OFP)是一个高性能的数据包处理框架,可以用于构建网络应用程序和设备,例如路由器、交换机等。它提供了一个用户空间的数据包处理引擎,可以通过与多种硬件平台和操作系统集成来实现快速、可扩展的网络应用程序。
OFP支持多个协议栈(例如TCP/IP、UDP/IP等),并提供了一系列基本功能模块,如ARP缓存管理、IP地址管理、ACL过滤等。它还支持虚拟化和容器化部署,并具有灵活的API和可扩展的插件架构,可以轻松地定制以适应不同类型的网络应用程序需求。
OFP最初由Linux基金会主导开发,并已成为Linux基金会项目之一。目前,它已经被广泛应用于各种高性能网络设备和云计算环境中。
fastpath 的介绍:
1. 提供了 Workgroup 协议的实现,Workgroup 的概念就是专门对应在线客服这个典型场景了。这是企业或组织机构的客服需求的核心概念和功能,类似于呼叫中心。
2.Server 端的历史记录存储。默认 Openfire 本身是不记录信息历史记录的,只记录离线留言。注意,离线消息和消息历史记录是两个不同的概念,离线消息是对方不在线的情况,server 端先保存起来,等对方上线后再发给他,发完了消息在 server 端就被删除了;而消息历史记录是只 server 端记录的所有对话消息,当然客户端也可以实现在客户端上自己的历史记录。
fasthpath:主要分为两端:agent - 客服,user - 用户,为人工客服实现了排队路由等基本的呼叫中心功能。
fasthpath:实现的原理就是以技能组为标准对用户加入对应的技能组在路由给对应技能组的客服人员,客服人员收到 offer 之后,可以接受,fastpath 就可以创建 openfire 的聊天室邀请用户和 agent 加入进来进行会话,实现客服功能。其中 fastpath 还提供了一些其他的附加功能:比如转接,路由过程指定客服,设置最大聊天室等等,这些功能可以再以后开发过程中有需要的情况下查看源码即可了解。
下面给出最简单的:user 去排队,客服加入技能组,来显示整个的客服功能。
这是 openfire 提供的插件,在 smack 中也提供了对应的 api,这里提供一个 agent 端的实例
/** * 加入技能组 * @param workGroupName * @return */ public boolean joinWorkGroup(String workGroupName,int maxChats){ boolean bResult=false; agentSession=new AgentSession(workGroupName,connection); agentSession.addInvitationListener(new WorkgroupInvitationListener(){ public void invitationReceived(WorkgroupInvitation workgroupInvitation) { // System.out.println("workgroupInvitation.getWorkgroupName():"+workgroupInvitation.getWorkgroupName()); // System.out.println("workgroupInvitation.getMessageBody():"+workgroupInvitation.getMessageBody()); // System.out.println("workgroupInvitation.getMetaData():"+workgroupInvitation.getMetaData()); // System.out.println("workgroupInvitation.getInvitationSender():"+workgroupInvitation.getInvitationSender()); // System.out.println("workgroupInvitation.getGroupChatName():"+workgroupInvitation.getGroupChatName()); joinRoom(workgroupInvitation.getGroupChatName()); // System.out.println("name:"+workgroupInvitation.getGroupChatName()+",id:"+workgroupInvitation.getSessionID()); /*try { agentSession.sendRoomTransfer(RoomTransfer.Type.user, "10110@kfas1", workgroupInvitation.getSessionID(), "转接"); } catch (XMPPException e) { // TODO Auto-generated catch block e.printStackTrace(); }*/ } }); agentSession.addOfferListener(new OfferListener(){ public void offerReceived(Offer offer) { System.out.println("offer.getUserJID();"+offer.getUserJID()); System.out.println("offer.getContent();"+offer.getContent()); offer.accept(); } public void offerRevoked(RevokedOffer revokedOffer) { System.out.println("revokedOffer.getReason():"+revokedOffer.getReason()); System.out.println("revokedOffer.getUserJID():"+revokedOffer.getUserJID()); } }); agentSession.addQueueUsersListener(new QueueUsersListener(){ public void averageWaitTimeUpdated(WorkgroupQueue workgroupQueue, int averageWaitTime) { // System.out.println("averageWaitTime:"+averageWaitTime); // System.out.println("workgroupQueue.getAverageWaitTime():"+workgroupQueue.getAverageWaitTime()); // System.out.println("workgroupQueue.getCurrentChats():"+workgroupQueue.getCurrentChats()); // } public void oldestEntryUpdated(WorkgroupQueue workgroupQueue, Date oldestEntry) { // System.out.println("oldestEntry:"+oldestEntry.toString()); } public void statusUpdated(WorkgroupQueue workgroupQueue, Status status) { // System.out.println("status:"+status.toString()); } public void usersUpdated(WorkgroupQueue workgroupQueue, Set uers) { for (Iterator iterator = uers.iterator(); iterator .hasNext();) { Object user = (Object) iterator.next(); System.out.println("user:"+user.toString()); } } }); try { agentSession.setOnline(true); /*Presence presence=new Presence(Presence.Type.available); presence.setTo("demo@workgroup.kftest2"); presence.setPriority(1); connection.sendPacket(presence); System.out.println("presence OK");*/ agentSession.setStatus(Presence.Mode.available,maxChats,"OK"); System.out.println(agentSession.getMaxChats()); } catch (XMPPException e) { e.printStackTrace(); } bResult=true; return bResult; }
用户 user 端的加入排队实例,也是基于 smack 包编写,可以发现 smack 都提供了对应的对象进行编程:
public void joinQueue(String workgroupName, Map metaData) { workgroup = new Workgroup(workgroupName, connection); //监听技能组中队列的事件 //这里监听,由于workGroup监听connection的包,如果第二次初始化,第一次的队列的监听还会触发 this.listenForQueue(); workgroupInvitationListener = new WorkgroupInvitationListener(){ public void invitationReceived(WorkgroupInvitation workgroupInvitation) { String room = workgroupInvitation.getGroupChatName(); joinRoom(room); } }; workgroup.addInvitationListener(workgroupInvitationListener); if (workgroup != null) { try { workgroup.joinQueue(metaData, userid); } catch (XMPPException e) { //异常的情况也继续排队,由于技能没有agent会service-unavailable(503) log.error("[>"+callInfo.getJid()+"<]: Unable to join chat queue.",e); } } } /** * 监听队列情况 * */ public void listenForQueue(){ queueListener =new QueueListener(){ //加入队列成功返回基础消息 public void joinedQueue() { } //离开队列事件 public void departedQueue() { } //队列位置变化 public void queuePositionUpdated(int currentPosition) { } //队列时间变化 public void queueWaitTimeUpdated(int secondsRemaining) { } }; workgroup.addQueueListener(queueListener); }