目标

本文目标:基于OVN+OVS+KVM,实现两台宿主机之间的虚拟机组网。

安装OVN、OVS

采用源码安装方式,参考官方安装文档:https://github.com/ovn-org/ovn/blob/main/Documentation/intro/install/general.rst

# 下载ovn代码
git clone https://github.com/ovn-org/ovn.git
cd ovn
# 如果需要特定版本,可以用git切到特定的tag再做以下操作

# ovn依赖ovs,需要把ovs代码也下载下来,这步ovn已经指定了ovs版本,不再需要再做特殊处理
git submodule update --init

# 安装编译工具
sudo apt install autoconf automake libtool make

# 配置并安装ovs
cd ovs
./boot.sh
./configure
make
sudo make install

# 配置并安装ovn
cd ..
./boot.sh
./configure
make
sudo make install

# 添加ovn相关可执行文件路径到path环境变量
# 建议加到/etc/profile文件中
export PATH=$PATH:/usr/local/share/ovn/scripts

# 查看版本
ovn-appctl --version

配置环境

假设这两台节点上都按上述步骤安装好了ovn和ovs。在两个节点启动相关服务和配置,以下命令均在root账户下执行。

node1作为central节点,启动数据库三个服务:

# node1启动北桥数据库、ovn-northd、南桥数据库三个服务
ovn-ctl start_northd

两个hypervisor节点上都启动ovs:

# 创建所需目录
mkdir -p /usr/local/var/run/openvswitch
mkdir -p /usr/local/var/log/openvswitch

# 创建数据库
ovsdb-tool create /usr/local/etc/openvswitch/conf.db /usr/local/share/openvswitch/vswitch.ovsschema

# 启动ovsdb-server
ovsdb-server --remote=punix:/usr/local/var/run/openvswitch/db.sock --remote=db:Open_vSwitch,Open_vSwitch,manager_options --private-key=db:Open_vSwitch,SSL,private_key --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert --pidfile --detach

# 初始化ovsdb,针对于新创建的数据库才需要初始化
ovs-vsctl --no-wait init

# 环境变量建议写到/etc/profile
export DB_SOCK=/usr/local/var/run/openvswitch/db.sock
# 启动ovs-vswitchd
ovs-vswitchd unix:$DB_SOCK --pidfile --detach --log-file=/usr/local/var/log/openvswitch/ovs-vswitchd.log

# 验证ovs安装是否正确
# ovs-vsctl --version
ovs-vsctl (Open vSwitch) 2.17.90
DB Schema 8.3.0

两个节点上都启动ovn controller:

# 启动ovn controller
ovn-ctl start_controller
# ovn controler启动后会自动创建br-int网桥
ovs-vsctl show
# 输出如下
17aaf222-5a88-4416-a455-1663d9eeff57
    Bridge br-int
        fail_mode: secure
        datapath_type: system
        Port br-int
            Interface br-int
                type: internal

hypervisor连接central:

# central节点开放北向数据库端口6441,该端口主要给CMS plugins连接使用,非必须步骤,可不开启
ovn-nbctl set-connection ptcp:6641:10.0.12.7

# central节点开放南向数据库端口6442,该端口给ovn controller连接
ovn-sbctl set-connection ptcp:6642:10.0.12.7
# netstat -ntlp|grep 6642
tcp        0      0 10.0.12.7:6642          0.0.0.0:*               LISTEN      10007/ovsdb-server  

# node1上ovn-controller连接南向数据库
# ovn-remote:指定南向数据库连接地址
# ovn-encap-ip:指定ovs/controller本地ip
# ovn-encap-type:指定隧道协议,这里用的是geneve
# system-id:节点标识
ovs-vsctl set Open_vSwitch . external-ids:ovn-remote="tcp:10.0.12.7:6642" external-ids:ovn-encap-ip="10.0.12.7" external-ids:ovn-encap-type=geneve external-ids:system-id=node1

# node2上ovn-controller连接南向数据库
ovs-vsctl set Open_vSwitch . external-ids:ovn-remote="tcp:10.0.12.7:6642" external-ids:ovn-encap-ip="10.0.12.11" external-ids:ovn-encap-type=geneve external-ids:system-id=node2

# 查看南向数据库信息
ovn-sbctl show
# 输出如下:
Chassis node2
    hostname: VM-12-11-centos
    Encap geneve
        ip: "10.0.12.11"
        options: {csum="true"}
Chassis node1
    hostname: VM-12-7-centos
    Encap geneve
        ip: "10.0.12.7"
        options: {csum="true"}

此时,两个节点上的ovn controller都已连接上南向数据库,ovn环境安装完毕。

虚拟机组网实践

在每个节点上创建一个虚拟机,虚拟机添加网络设备,桥接到br-int:

# 允许kvm从br-int创建port作为网络设备
sudo apt install apparmor-utils
aa-complain /etc/apparmor.d/usr.sbin.libvirtd

# 虚拟机添加网络设备
virsh edit win10
# 添加内容如下:
<interface type='bridge'>
  <mac address='00:00:00:00:00:01'/>
  <source bridge='br-int'/>
  <virtualport type='openvswitch'></virtualport>
  <target dev='vm1'/>
  <model type='virtio'/>
  <driver name='vhost'/>
</interface>
# 为了获得更好的性能吞吐量和更少的延迟,建议 KVM guest 虚拟机在 libvirt配置文件中通过指定关键字<driver name='vhost'/>来使用较新的 vhost-net驱动程序,而不是较旧的半虚拟 virtio-net 驱动程序。vhost-net 使用内核虚拟机网络性能增强功能,该功能在虚拟机和主机系统之间移动网络数据包时使用Linux 内核而不是 QEMU。这避免了从内核到用户空间的进程上下文切换以提高整体性能。

# 启动虚拟机
virsh start win10

为了使node1和node2上两个连接到ovs交换机的ns能正常通信,我们借助ovn的逻辑交换机:

# 添加逻辑交换机
ovn-nbctl ls-add ls1

# 添加并设置用于连接node1的端口,注意mac地址要和虚拟机添加的网络设备匹配
ovn-nbctl lsp-add ls1 ls1-node1-vm1
ovn-nbctl lsp-set-addresses ls1-node1-vm1 "00:00:00:00:00:01 192.168.6.10"
ovn-nbctl lsp-set-port-security ls1-node1-vm1 "00:00:00:00:00:01 192.168.6.10"

# 添加并设置用于连接node2的端口,注意mac地址要匹配起来
ovn-nbctl lsp-add ls1 ls1-node2-vm1
ovn-nbctl lsp-set-addresses ls1-node2-vm1 "00:00:00:00:00:02 192.168.6.20"
ovn-nbctl lsp-set-port-security ls1-node2-vm1 "00:00:00:00:00:02 192.168.6.20"

# 查看逻辑交换机信息
ovn-nbctl show
# 输入如下:
switch d9bc0c6a-83cc-4919-a176-17e971e2f022 (ls1)
    port ls1-node2-ns1
        addresses: ["00:00:00:00:00:02 192.168.6.20"]
    port ls1-node1-ns1
        addresses: ["00:00:00:00:00:01 192.168.6.10"]

# node1上执行,vm1端口连接逻辑交换机端口
ovs-vsctl set interface vm1 external-ids:iface-id=ls1-node1-vm1

# node2上执行,vm1端口连接逻辑交换机端口
ovs-vsctl set interface vm1 external-ids:iface-id=ls1-node2-vm1

# 再次查看南向数据库信息,发现端口已连接
ovn-sbctl show
# 输出如下:
Chassis node2
    hostname: VM-12-11-centos
    Encap geneve
        ip: "10.0.12.11"
        options: {csum="true"}
    Port_Binding ls1-node2-vm1
Chassis node1
    hostname: VM-12-7-centos
    Encap geneve
        ip: "10.0.12.7"
        options: {csum="true"}
    Port_Binding ls1-node1-vm1

QoS限速

两种方式:

  1. 通过Open vSwitch给虚拟机限流(出流量)
# ingress_policing_rate 和 ingress_policing_burst 值全为0,则表示 不限流
# ingress_policing_rate 和 ingress_policing_burst 的默认单位是 Kbps
# ingress_policing_rate:应允许此VM发送的最大速率(以Kbps为单位)
# ingress_policing_burst: 监管算法的一个参数,用于指示此接口可以发送超出监管速率的最大数据量(以Kb为单位)
# 打开vSwitch的速率限制使用策略,它不对数据包进行排队。它会丢弃超出指定速率的任何数据包。指定更大的突发大小可以使算法更加宽容,这对于TCP等对丢弃数据包造成严重反应的协议非常重要。应避免将突发大小设置为小于MTU(例如,10 kb)。对于TCP流量,将突发大小设置为总策略速率的相当大的分数(例如,> 10%)有助于流更接近于实现全速率。如果将突发大小设置为总速率的很大一部分,则客户端实际上将经历略高于特定策略速率的平均速率。
ovs-vsctl set interface vnet1 ingress_policing_rate=1000
ovs-vsctl set interface vnet1 ingress_policing_burst=1000

# 查看信息
ovs-vsctl list interface vnet1
  1. 通过ovn-ctl对逻辑交换机的端口限速(经验证,限速似乎不准确)
# qos_max_rate 和 qos_burst 值全为0,则表示 不限流
# qos_max_rate 和 qos_burst 的默认单位是 bps
ovn-nbctl set logical_switch_port ls-veth1 options:qos_max_rate=1000000
ovn-nbctl set logical_switch_port ls-veth1 options:qos_burst=1000000

根据mac地址设置虚拟机静态IP

使用OVN实现DHCP功能

# 创建逻辑路由器
ovn-nbctl lr-add lr1
# 创建逻辑交换机
ovn-nbctl ls-add ls1
# 添加逻辑路由器端口
ovn-nbctl lrp-add lr1 lr1-ls1 00:00:00:00:00:30 192.168.8.1/24
# 添加逻辑交换机端口,并连接到逻辑路由器对应端口
ovn-nbctl lsp-add ls1 ls1-lr1
ovn-nbctl lsp-set-type ls1-lr1 router
ovn-nbctl lsp-set-addresses ls1-lr1 00:00:00:00:00:30
ovn-nbctl lsp-set-options ls1-lr1 router-port=lr1-ls1

# 创建dhcp选项
# cidr 虚拟 dhcp的网段
# server_id 虚拟 dhcp 服务器的 ip 地址
# server_mac 虚拟 dhcp 服务器的 MAC 地址
# lease_time DHCP 租约的生命周期
# router 提供有关默认网关的信息
ovn-nbctl create DHCP_Options cidr=192.168.8.0/24 options="\"server_id\"=\"192.168.8.1\" \"server_mac\"=\"00:00:00:00:00:30\" \"lease_time\"=\"3600\" \"router\"=\"192.168.8.1\""
# 查看DHCP选项
ovn-nbctl list dhcp-options
# 输出如下:
	_uuid               : 4fdee846-b3d9-47d4-bcae-8209d07f84be
	cidr                : "192.168.8.0/24"
	external_ids        : {}
	options             : {lease_time="3600", router="192.168.8.1", server_id="192.168.8.1", server_mac="00:00:00:00:00:30"}

# 设置逻辑交换机端口mac和ip(vm的端口)
ovn-nbctl lsp-add ls1 ls1-node1-vm1
ovn-nbctl lsp-set-addresses ls1-node1-vm1 "00:00:00:00:00:01 192.168.8.10"
ovn-nbctl lsp-set-port-security ls1-node1-vm1 "00:00:00:00:00:01 192.168.8.10"

# 逻辑交换机端口关联DHCP选项,通过 UUID 关联
ovn-nbctl lsp-set-dhcpv4-options ls1-node1-vm1 4fdee846-b3d9-47d4-bcae-8209d07f84be
ovn-nbctl lsp-get-dhcpv4-options ls1-node1-vm1

# 删除DHCP选项
ovn-nbctl dhcp-options-del 4fdee846-b3d9-47d4-bcae-8209d07f84be