1. ovn 架构解析, 手动编译,启动集群,入门试验
- CMS: kube-ovn-controller 和 neutron
- OVN NB DB: 管理并存储集群逻辑网络资源
- OVN SB DB: 管理并存储集群逻辑流表( OVN-specific 格式)
- OVN Northbound: 承上启下,负责转化
- OVN-CONTROllER:上报信息,同步本节点需要的流表,转化为 openflow 流表,下发到 ovs-vswitchd,并存储到 - ovs-db
- OVS-VSWITCHED: 软件交换机
- OVS-DB:数据库管理
kube-ovn-controller 与 neutron 相当于 cms 的角色,
ovn-central 包括,ovn-northd,ovn-sb-db,ovn-nbdb, ovn-northd(启动依赖)ovn-sb-db,ovn-nbdb
参考: blog.csdn.net/fengcai_ke/…
流表相关:
datapath 类型:(logical switch 或者 logical router) 匹配报文方向: (ingress or egress) table id: (每个 table 实现不同功能)
/* OpenFlow table numbers.
*
* These are heavily documented in ovn-architecture(7), please update it if
* you make any changes. */
//table 0 专门用来将物理端口转换成其对应的逻辑端口
#define OFTABLE_PHY_TO_LOG 0
//table1-7 暂时还没用到
//table 8 用来对应 logical_flow ingress 方向的第一个 table,
//直到 table 32(8+24)
#define OFTABLE_LOG_INGRESS_PIPELINE 8 /* First of LOG_PIPELINE_LEN tables. */
//table33-36 暂时还没用到
//table37 用来存放输出端口为其他 chassis 的流表
#define OFTABLE_REMOTE_OUTPUT 37
//table38 用来存放输出端口为本 chassis 的流表
#define OFTABLE_LOCAL_OUTPUT 38
//table39 用来做 loopback 检查,即输出端口不能为输入端口
#define OFTABLE_CHECK_LOOPBACK 39
//table40 用来对应 logical_flow egress 方向的第一个table,
//直到 table 50(40+10)
#define OFTABLE_LOG_EGRESS_PIPELINE 40 /* First of LOG_PIPELINE_LEN tables. */
#define OFTABLE_SAVE_INPORT 64
//table65 用来将逻辑端口转换成物理端口
#define OFTABLE_LOG_TO_PHY 65
#define OFTABLE_MAC_BINDING 66
#define OFTABLE_MAC_LOOKUP 67
#define OFTABLE_CHK_LB_HAIRPIN 68
#define OFTABLE_CHK_LB_HAIRPIN_REPLY 69
#define OFTABLE_CT_SNAT_FOR_VIP 70
#define OFTABLE_GET_FDB 71
#define OFTABLE_LOOKUP_FDB 72
/* A stage within an OVN logical switch or router.
*
* An "enum ovn_stage" indicates whether the stage is part of a logical switch
* or router, whether the stage is part of the ingress or egress pipeline, and
* the table within that pipeline. The first three components are combined to
* form the stage's full name, e.g. S_SWITCH_IN_PORT_SEC_L2,
* S_ROUTER_OUT_DELIVERY. */
enum ovn_stage {
#define PIPELINE_STAGES \
/* Logical switch ingress stages. */ \
PIPELINE_STAGE(SWITCH, IN, PORT_SEC_L2, 0, "ls_in_port_sec_l2") \
PIPELINE_STAGE(SWITCH, IN, PORT_SEC_IP, 1, "ls_in_port_sec_ip") \
PIPELINE_STAGE(SWITCH, IN, PORT_SEC_ND, 2, "ls_in_port_sec_nd") \
PIPELINE_STAGE(SWITCH, IN, LOOKUP_FDB , 3, "ls_in_lookup_fdb") \
PIPELINE_STAGE(SWITCH, IN, PUT_FDB, 4, "ls_in_put_fdb") \
PIPELINE_STAGE(SWITCH, IN, PRE_ACL, 5, "ls_in_pre_acl") \
PIPELINE_STAGE(SWITCH, IN, PRE_LB, 6, "ls_in_pre_lb") \
PIPELINE_STAGE(SWITCH, IN, PRE_STATEFUL, 7, "ls_in_pre_stateful") \
PIPELINE_STAGE(SWITCH, IN, ACL_HINT, 8, "ls_in_acl_hint") \
PIPELINE_STAGE(SWITCH, IN, ACL, 9, "ls_in_acl") \
PIPELINE_STAGE(SWITCH, IN, QOS_MARK, 10, "ls_in_qos_mark") \
PIPELINE_STAGE(SWITCH, IN, QOS_METER, 11, "ls_in_qos_meter") \
PIPELINE_STAGE(SWITCH, IN, LB, 12, "ls_in_lb") \
PIPELINE_STAGE(SWITCH, IN, STATEFUL, 13, "ls_in_stateful") \
PIPELINE_STAGE(SWITCH, IN, PRE_HAIRPIN, 14, "ls_in_pre_hairpin") \
PIPELINE_STAGE(SWITCH, IN, NAT_HAIRPIN, 15, "ls_in_nat_hairpin") \
PIPELINE_STAGE(SWITCH, IN, HAIRPIN, 16, "ls_in_hairpin") \
PIPELINE_STAGE(SWITCH, IN, ARP_ND_RSP, 17, "ls_in_arp_rsp") \
PIPELINE_STAGE(SWITCH, IN, DHCP_OPTIONS, 18, "ls_in_dhcp_options") \
PIPELINE_STAGE(SWITCH, IN, DHCP_RESPONSE, 19, "ls_in_dhcp_response") \
PIPELINE_STAGE(SWITCH, IN, DNS_LOOKUP, 20, "ls_in_dns_lookup") \
PIPELINE_STAGE(SWITCH, IN, DNS_RESPONSE, 21, "ls_in_dns_response") \
PIPELINE_STAGE(SWITCH, IN, EXTERNAL_PORT, 22, "ls_in_external_port") \
PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 23, "ls_in_l2_lkup") \
PIPELINE_STAGE(SWITCH, IN, L2_UNKNOWN, 24, "ls_in_l2_unknown") \
\
/* Logical switch egress stages. */ \
PIPELINE_STAGE(SWITCH, OUT, PRE_LB, 0, "ls_out_pre_lb") \
PIPELINE_STAGE(SWITCH, OUT, PRE_ACL, 1, "ls_out_pre_acl") \
PIPELINE_STAGE(SWITCH, OUT, PRE_STATEFUL, 2, "ls_out_pre_stateful") \
PIPELINE_STAGE(SWITCH, OUT, LB, 3, "ls_out_lb") \
PIPELINE_STAGE(SWITCH, OUT, ACL_HINT, 4, "ls_out_acl_hint") \
PIPELINE_STAGE(SWITCH, OUT, ACL, 5, "ls_out_acl") \
PIPELINE_STAGE(SWITCH, OUT, QOS_MARK, 6, "ls_out_qos_mark") \
PIPELINE_STAGE(SWITCH, OUT, QOS_METER, 7, "ls_out_qos_meter") \
PIPELINE_STAGE(SWITCH, OUT, STATEFUL, 8, "ls_out_stateful") \
PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_IP, 9, "ls_out_port_sec_ip") \
PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_L2, 10, "ls_out_port_sec_l2") \
\
/* Logical router ingress stages. */ \
PIPELINE_STAGE(ROUTER, IN, ADMISSION, 0, "lr_in_admission") \
PIPELINE_STAGE(ROUTER, IN, LOOKUP_NEIGHBOR, 1, "lr_in_lookup_neighbor") \
PIPELINE_STAGE(ROUTER, IN, LEARN_NEIGHBOR, 2, "lr_in_learn_neighbor") \
PIPELINE_STAGE(ROUTER, IN, IP_INPUT, 3, "lr_in_ip_input") \
PIPELINE_STAGE(ROUTER, IN, DEFRAG, 4, "lr_in_defrag") \
PIPELINE_STAGE(ROUTER, IN, UNSNAT, 5, "lr_in_unsnat") \
PIPELINE_STAGE(ROUTER, IN, DNAT, 6, "lr_in_dnat") \
PIPELINE_STAGE(ROUTER, IN, ECMP_STATEFUL, 7, "lr_in_ecmp_stateful") \
PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 8, "lr_in_nd_ra_options") \
PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 9, "lr_in_nd_ra_response") \
PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 10, "lr_in_ip_routing") \
PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_ECMP, 11, "lr_in_ip_routing_ecmp") \
PIPELINE_STAGE(ROUTER, IN, POLICY, 12, "lr_in_policy") \
PIPELINE_STAGE(ROUTER, IN, POLICY_ECMP, 13, "lr_in_policy_ecmp") \
PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 14, "lr_in_arp_resolve") \
PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN , 15, "lr_in_chk_pkt_len") \
PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 16, "lr_in_larger_pkts") \
PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 17, "lr_in_gw_redirect") \
PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 18, "lr_in_arp_request") \
\
/* Logical router egress stages. */ \
PIPELINE_STAGE(ROUTER, OUT, UNDNAT, 0, "lr_out_undnat") \
PIPELINE_STAGE(ROUTER, OUT, SNAT, 1, "lr_out_snat") \
PIPELINE_STAGE(ROUTER, OUT, EGR_LOOP, 2, "lr_out_egr_loop") \
PIPELINE_STAGE(ROUTER, OUT, DELIVERY, 3, "lr_out_delivery")
#define PIPELINE_STAGE(DP_TYPE, PIPELINE, STAGE, TABLE, NAME) \
S_##DP_TYPE##_##PIPELINE##_##STAGE \
= OVN_STAGE_BUILD(DP_##DP_TYPE, P_##PIPELINE, TABLE),
PIPELINE_STAGES
#undef PIPELINE_STAGE
};
// 前 8 位表示 table id,第 8 位表示 pipeline,第 9 位表示 dp 类型
/* Returns an "enum ovn_stage" built from the arguments.
*
* (It's better to use ovn_stage_build() for type-safety reasons, but inline
* functions can't be used in enums or switch cases.) */
#define OVN_STAGE_BUILD(DP_TYPE, PIPELINE, TABLE) \
(((DP_TYPE) << 9) | ((PIPELINE) << 8) | (TABLE))
sbctl 查看整个集群的逻辑流表信息
root@master:~# ovn-sbctl lflow-list
Datapath: "ls1" (845314a0-ad79-4ac8-ac44-9fe2421478c2) Pipeline: ingress
//eth.src[40] 表示源mac为组播/广播地址
table=0 (ls_in_port_sec_l2 ), priority=100 , match=(eth.src[40]), action=(drop;)
table=0 (ls_in_port_sec_l2 ), priority=100 , match=(vlan.present), action=(drop;)
table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "ls1-vm1" && eth.src == {00:00:00:00:00:01}), action=(next;)
table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "ls1-vm2" && eth.src == {00:00:00:00:00:02}), action=(next;)
table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "ls1-vm3" && eth.src == {00:00:00:00:00:03}), action=(next;)
//match 1 表示匹配所有报文
2. ovn-northd
它将 ovn-nb-db 数据库中的逻辑网元配置转换成 ovn-sb-db 数据库中的集群流表。
3. ovn-nb-db
Northbound DB 是 OVN 和 CMS 之间的接口。CMS 产生 Northbound DB 里面的几乎所有的内容。ovn-northd 监听这个数据库的内容变化,然后转换并保存到 Southbound DB 里面。
Northbound DB中有将近二十张表,下面是一些常用表:
- Logical_Switch: 每行代表一个二层逻辑交换机。有两种类型的逻辑交换机,一种是全虚拟化网络,也叫 overlay 逻辑交换机,另一种用来和物理网络连接,称为桥接逻辑交换机。如果用来连接同 chassis 上的逻辑端口,这两种交换机的工作方式是一样的,但是如果用来连接不同 chassis 上的逻辑端口,方式就不一样了。overlay 逻辑交换机通过隧道来连接远端逻辑端口,而桥接逻辑交换机在 localnet 类型端口的帮助下,直接通过物理网络连接远端逻辑端口。每个桥接逻辑交换机有一个或者多个 localnet 类型的端口,此类型端口的地址类型为 unknown。在它上面可以配置 acl,qos 和 load balancer 等功能。
- Logical_Switch_Port: 每行代表一个逻辑交换机端口。
type 字段: 如果 type 为空,则表示一个 VM 接口,如果 type 为 router,则表示连接的是逻辑路由器; 如果 type 为 localnet,则表示连接外部网络(可能会绑定到多个 chassis); 如果类型为 l2gateway,则表示连接外部网络(只能绑定到指定 chassis上);等等
up 字段:此字段由 ovn-northd 修改,而不是 CMS 。如果逻辑端口绑定到了物理位置 (Southbound DB 中port_binding 表 up 字段为 true ),ovn-northd 就会设置 up 字段为 true。CMS通过此字段用来等待 VM 网络变成可用后,再启动 VM。但是有一个例外,如果端口类型为 router,这 up 字段永远是true。
- Logical_Router: 每行代表一个三层逻辑路由器。在它上面可以配置静态路由,策略,nat 和 load balancer 功能。它是在每个 chassis 上存在的,通过 openflow 流表实现。但是有一个例外,如果它通过 chassis 选项绑定指定了指定 chassis,则它就变为网关路由器。
- Logical_Router_Port: Logical_Router上的端口,每行代表一个逻辑路由器端口。通过 networks 指定它的网段。还能通过 ha_chassis_group 或者 gateway_chassis 将它设置为分布式网关端口。
- Gateway_Chassis: 用来关联到逻辑路由端口,实现分布式网关端口。发送到分布式网关端口的流量会被重定向到关联的 chassis,可以设置多行,即多个 chassis,同时指定优先级,优先级最高的生效。
- HA_Chassis_Group: 每行代表一组 chassis,这组 chassis 用来提供高可用。组里面的每个 chassis 使用另一个 table HA_Chassis 来表示。也是用来关联到逻辑路由端口,实现分布式网关端口。
- HA_Chassis: HA_Chassis_Group 组里面的 chassis,每行表示一个chassis,同时可以通过 priority 指定此chassis 的优先级。
- Load_Balancer : 每行代表一个load balancer。可以应用在逻辑交换机和路由器上。
- ACL: 每行代表一个 acl 规则。可以应用在逻辑交换机和 port group 上。
- NAT : 每行代表一个 nat 规则。可以应用在逻辑路由器上。
- DHCP_Options : 用来响应 VM 的 DHCP 请求。
4. ovn-sb-db
参考: blog.csdn.net/fengcai_ke/…
这个数据库包含了 OVN 的逻辑和物理的配置和状态。它位于 OVN 架构的核心位置,北向通过 ovn-northd 和 CMS 通信, 南向通过 ovn-controller 和 hypervisor 通信。
数据库,根据数据属性,大致分为如下几类:
- 物理网络: 物理网络相关的表主要包含 chassis 节点信息,比如节点的 hostname,ip地址和隧道类型。由节点上的ovn-controller 来更新这些表,相当于向集群注册自己的信息。 同时也可以获取到其他节点的连接信息。物理网络数据量一般比较小,不会频繁更新,所以可将这些数据复制到所有节点。物理网络相关的表主要包含 Chassis, Encap两张表。
- 逻辑网络: 逻辑网络相关的表包含逻辑交换机,逻辑路由器,acl,防火墙规则等逻辑网络资源转换到逻辑流表。 逻辑网络数据量可能很大,因为它包含逻辑端口,acl规则等信息。 为了提高扩展性,每个节点应该只接收和本节点相关的数据。逻辑网络相关的表有 Logical_Flow, Multicast_Group, Address_Group, DHCP_Options, DHCPv6_Options, and DNS tables。
- 逻辑-物理 网络绑定: 这些表用来绑定逻辑和物理网络。并且变化很频繁,比如在vm开始,关机或者迁移时,或者在容器环境中,变化更快。 主要有 Port_Binding and Datapath_Binding。
- mac 地址绑定: 用来绑定ip和mac地址。表为 MAC_Binding。
数据库表介绍:
- Chassis : 每行代表物理网络中的一个 hypervisor 节点。在每个节点上,由 ovn-controller 进程在 chassis 表中增加一行用来更新本节点的信息,包括本节点的 hostname,ip 地址和隧道类型( ip 和类型引用另一个 table encap),同时保存一份 Chassis 中其他行的信息,根据这些信息创建到其他节点的连接。
- Encap : Chassis 表中的 encaps 指向 Encap 表中的一行,用来保存本节点的 ip 地址和隧道类型。
- Logical_Flow : 每行代表一条逻辑流表。这些流表由 ovn-northd 进程更新,用来实现 Northbound DB 中指定的二层和三层拓扑。在每个节点上,再由 ovn-controller 进程将这些逻辑流表中和本节点相关的流表转换成openflow 流表,安装到ovs中。 逻辑流表使用 OVN-specific 格式。逻辑流表和 openflow 流表很像,但是逻辑流表中用的是逻辑端口和逻辑 datapath,而 openflow 流表用的是物理端口和物理 datapath。逻辑流表的默认动作是丢包。
- Datapath_Binding : 每行代表一个逻辑 datapath,比如一个逻辑交换机或者逻辑路由器。主要是为逻辑datapath 提供物理绑定。逻辑 datapath 实际上没有物理位置,即不会绑定到某个 chassis 上,所以这里说的物理绑定仅仅指的是和 tunnel_key 绑定。每个 datapath 会对应唯一的 tunnel_key,报文跨节点转发时,会将tunnel_key 封装到隧道报文中,以便另一个节点接收报文后,知道应该哪个 datapath (交换机或路由器)来处理。
- Port_Binding : 每行代表一个逻辑端口的具体实现。对于大部分逻辑端口来说,通常指绑定到指定的物理位置,比如将逻辑端口绑定到一个 VIF,此 VIF 为某个 hypervisor 上的 VM 的端口。
5. ovn-controller
ovn-controller 是运行在 chassis/hypervisor 上的后台进程,向上通过 OVSDB 协议连接到 OVN sourthbound数据库,向下通过 OVSDB 协议连接到 ovs 数据库,并通过 openflow 协议连接到 ovs-vswitchd。
以前我认为 external_ids 只是一个 meatadata, 对于 ovn, ovs 没有业务设计,目前看来可能不是: ovn-controller 从本地 ovs 数据库的 Open_vSwitch 表中获取下列参数:
external_ids:system-id:
# 会更新到 sbdb 的 Chassis 的 name 字段。ovn-controller 在运行过程中,不能动态修改 system-id。
# 有两种办法可以修改 system-id: 先停掉 ovn-controller,再修改。或者修改完后,收到删除 Chassis 表中旧的字段。
external_ids:hostname:
# 会更新到 sbdb 的 Chassis 的 hostname 字段
external_ids:ovn-bridge
# 用来连接逻辑端口的集成桥。如果不指定,则 ovn-controller 启动过程中会自动创建,集成桥名字默认为 br-int。
external_ids:ovn-remote:
# 指定连接 ovn southbound 数据库的方式。
external_ids:ovn-encap-type:
# 指定其他 chassis 连接本节点时用的隧道类型。可以同时指定多个隧道类型。
# 用来连接 chassis 的隧道类型有 geneve 和 stt,用来连接网关和 chassis 的隧道类型有 geneve,stt 和vxlan。
external_ids:ovn-encap-ip:
# 指定其他 chassis 连接本节点时用的 ip 地址。
external_ids:ovn-bridge-mappings:
# 键值对列表,用来映射物理网络名字到本地 ovs 网桥,意思为可通过此 ovs 网桥连接到物理网络名字所在的物理网络。
# 比如 phys‐net1:br-eth0, physnet2:br-eth1
OVN southbound 数据库
ovn-controller 从 sbdb 中读取内容来指定它的操作。同时也会更新下面的表
ovn-controller 的作用是将 sbdb 中的信息转换成当前节点的 chassis 上的 openflow 流表信息,而且只会将和本 chassis 相关的信息进行转换,所以 ovn-controller 首先得识别出来哪些信息是和本 chassis 相关的。比如一个 vm 的 vif 接口在本 chassis 上,则其对应的 logical_switch,logical_switch_port 都属于和本chassis 相关的信息。
Chassis:
# ovn-controller 启动时会在此表中创建一行,相当于上报自己的信息。
Encap:
# ovn-controller 启动时会在此表中创建一行,设置隧道类型和 ip,用来告诉其他 chassis 如何连接本节点。
Port_Binding:
# 一旦逻辑端口对应的物理实体创建在 chassis 上,此 chassis 上的 ovn-controller 就会将 Port_Binding中的 Chassis 字段更新为本节点。
MAC_Binding:
ovn-controller 会根据 put_arp 和 put_nd action 更新此表。
ovn-controller 转换流表: 参考: blog.csdn.net/fengcai_ke/…
6. ovn 通过(集中|高可用)网关 chassis 上公网
参考: blog.csdn.net/fengcai_ke/…
关于网关是集中式的还是分布式的?
东西向的流量都是分布式的
ovn的逻辑路由器是分布式的,这意味着它没有绑定到某个节点上,而是存在于所有节点上的,同时它是通过每个节点的 openflow 流表来实现的,所有 vm 之间的东西向流量可以在本节点就能找到目的节点。
南北向的流量
但是对于一些有状态的服务是有问题的,比如 SNAT 和 DNAT,这些服务需要在同一个节点上实现。为了解决这个问题,引入了网关路由器,其和逻辑路由器的区别是,网关路由器会通过 Logical_Router 表的选项 options:chassis 绑定到指定的节点上。
但 FIP 是分布式的,基于 localnet port, 可以实现本地直接进出,这种方式也叫 l2 gateway 通过 ovn 的 localnet 类型端口将 ovn 网络连接到外部网络。也是一种 l2gateway,但是相比 l2gateway 来说,localnet 类型的端口会在 vm 所在的 chassis上 都直接连接到外部网络,减少了东西向流量。 参考: blog.csdn.net/fengcai_ke/…
l2 gateway 参考: blog.csdn.net/fengcai_ke/…
参考: 分享放大价值: blog.csdn.net/fengcai_ke/…