Kubectl ko 下可以执行 nbctl,sbctl,vsctl,ofctl,trace 等等命令
# kubectl ko --help
kubectl ko {subcommand} [option...]
Available Subcommands:
[nb|sb] [status|kick|backup|dbstatus|restore] ovn-db operations show cluster status, kick stale server, backup database, get db consistency status or restore ovn nb db when met 'inconsistent data' error
nbctl [ovn-nbctl options ...] invoke ovn-nbctl
sbctl [ovn-sbctl options ...] invoke ovn-sbctl
vsctl {nodeName} [ovs-vsctl options ...] invoke ovs-vsctl on the specified node
ofctl {nodeName} [ovs-ofctl options ...] invoke ovs-ofctl on the specified node
dpctl {nodeName} [ovs-dpctl options ...] invoke ovs-dpctl on the specified node
appctl {nodeName} [ovs-appctl options ...] invoke ovs-appctl on the specified node
tcpdump {namespace/podname} [tcpdump options ...] capture pod traffic
trace {namespace/podname} {target ip address} [target mac address] {icmp|tcp|udp} [target tcp or udp port] trace ovn microflow of specific packet
diagnose {all|node} [nodename] diagnose connectivity of all nodes or a specific node
env-check check the environment configuration
tuning {install-fastpath|local-install-fastpath|remove-fastpath|install-stt|local-install-stt|remove-stt} {centos7|centos8}} [kernel-devel-version] deploy kernel optimisation components to the system
reload restart all kube-ovn components
先获取到 leader pod,如 ovn-nb-leader,和 ovn-sb-leader
kubectl get pod -n kube-system -l ovn-nb-leader=true | grep ovn-central | head -n 1 | awk '{print $1}'
kubectl get pod -n kube-system -l ovn-sb-leader=true | grep ovn-central | head -n 1 | awk '{print $1}'
然后进入到 leader pod 中执行
kubectl exec "$OVN_NB_POD" -n $KUBE_OVN_NS -c ovn-central -- ovn-nbctl "$@"
kubectl exec "$OVN_SB_POD" -n $KUBE_OVN_NS -c ovn-central -- ovn-sbctl "$@"
即查看流表命令 ovn-sbctl lflow-list == kubectl ko sbctl lflow-list
展示流表匹配流程
Kubectl ko trace {namespace/podname} {target ip address} [target mac address] {icmp|tcp|udp} [target tcp or udp port]
kubectl ko trace default/pod8 10.96.0.10 udp 53
// 即 trace pod8 访问 10.96.0.10 的 udp 53 端口
ls: 通过 pod annotation 获取到的 logical_switch
Inport:pod 的 logical_switch_port 的命名
Mac:通过 annotation 获取到的 mac
gwMac:通过 ovn 查询 logical_route_port 看 mac (underlay 模式,需要访问网关获取)
af:4 or 6
type:tcp or udp
kubectl exec "$OVN_SB_POD" -n $KUBE_OVN_NS -c ovn-central -- ovn-trace --ct=new "$ls" "inport == \"$podName.$namespace\" && ip.ttl == 64 && eth.src == $mac && ip$af.src == $podIP && eth.dst == $gwMac && ip$af.dst == $dst && $type.src == 10000 && $type.dst == $4"
kubectl exec ovn-central-7f7c579877-qlkbb -n kube-system -c ovn-central -- ovn-trace --ct=new ovn-default "inport == \"pod8.default\" && ip.ttl == 64 && eth.src == 00:00:00:1B:3E:8B && ip4.src == 10.16.3.47 && eth.dst == 00:00:00:63:B3:F9 && ip4.dst == 10.96.0.10 && udp.src == 10000 && udp.dst == 53"
# udp,reg14=0x1e,vlan_tci=0x0000,dl_src=00:00:00:1b:3e:8b,dl_dst=00:00:00:63:b3:f9,nw_src=10.16.3.47,nw_dst=10.96.0.10,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=10000,tp_dst=53
ingress(dp="ovn-default", inport="pod8.default")
------------------------------------------------
0. ls_in_port_sec_l2 (northd.c:5607): inport == "pod8.default", priority 50, uuid 59013590
next;
6. ls_in_pre_lb (northd.c:6002): ip4 && ip4.dst == 10.96.0.0/12, priority 100, uuid 95834334
reg0[2] = 1;
next;
7. ls_in_pre_stateful (northd.c:6042): reg0[2] == 1 && ip4 && udp, priority 120, uuid b6ecaf0b
reg1 = ip4.dst;
reg2[0..15] = udp.dst;
ct_lb_mark;
ct_lb_mark
----------
8. ls_in_acl_hint (northd.c:6119): ct.new && !ct.est, priority 7, uuid 325fce8b
reg0[7] = 1;
reg0[9] = 1;
next;
9. ls_in_acl (northd.c:6713): ip && (!ct.est || (ct.est && ct_mark.blocked == 1)), priority 1, uuid 6a459a10
reg0[1] = 1;
next;
12. ls_in_lb (northd.c:7028): ct.new && ip4.dst == 10.96.0.10 && udp.dst == 53, priority 120, uuid fba6a6c7
reg0[1] = 0;
reg1 = 10.96.0.10;
reg2[0..15] = 53;
ct_lb_mark(backends=10.16.0.48:53,10.16.0.5:53);
ct_lb_mark /* default (use --ct to customize) */
------------------------------------------------
14. ls_in_after_lb (northd.c:7165): ip4.dst == 10.16.0.5, priority 50, uuid 92e4c3a1
eth.dst = 00:00:00:3e:8e:4f;
next;
16. ls_in_pre_hairpin (northd.c:7253): ip && ct.trk, priority 100, uuid 2478265d
reg0[6] = chk_lb_hairpin();
reg0[12] = chk_lb_hairpin_reply();
*** chk_lb_hairpin_reply action not implemented
next;
25. ls_in_l2_lkup (northd.c:8674): eth.dst == 00:00:00:3e:8e:4f, priority 50, uuid 49fad5a5
outport = "coredns-6d8c4cb4d-6c4rb.kube-system";
output;
egress(dp="ovn-default", inport="pod8.default", outport="coredns-6d8c4cb4d-6c4rb.kube-system")
----------------------------------------------------------------------------------------------
0. ls_out_pre_lb (northd.c:6012): ip, priority 100, uuid dfdb1c2b
reg0[2] = 1;
next;
2. ls_out_pre_stateful (northd.c:6062): reg0[2] == 1, priority 110, uuid 953b71bb
ct_lb_mark;
ct_lb_mark /* default (use --ct to customize) */
------------------------------------------------
3. ls_out_acl_hint (northd.c:6182): ct.est && ct_mark.blocked == 0, priority 1, uuid bc0f3aca
reg0[10] = 1;
next;
9. ls_out_port_sec_l2 (northd.c:5704): outport == "coredns-6d8c4cb4d-6c4rb.kube-system", priority 50, uuid c717b137
output;
/* output to "coredns-6d8c4cb4d-6c4rb.kube-system", type "" */
稍后会结合功能讲解流表,这里不再赘述
流表示例如下:
Datapath: "join" (73d52ffc-620d-4fc5-b6b3-19becad7623c) Pipeline: ingress
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 == "join-ovn-cluster"), action=(next;)
table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "node-master"), action=(next;)
table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "node-worker"), action=(next;)
table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "node-worker2"), action=(next;)
table=1 (ls_in_port_sec_ip ), priority=0 , match=(1), action=(next;)
table=2 (ls_in_port_sec_nd ), priority=0 , match=(1), action=(next;)
table=3 (ls_in_lookup_fdb ), priority=0 , match=(1), action=(next;)
table=4 (ls_in_put_fdb ), priority=0 , match=(1), action=(next;)
table=5 (ls_in_pre_acl ), priority=110 , match=(eth.dst == $svc_monitor_mac), action=(next;)
......
Datapath:logical_router or logical_switch
Pipeline:ingress or egress,代表流方向。 Ovn 流水线 pipleline 的方式处理
table=0(ls_in_port_sec_l2 ):table 索引和名称
priority:在该 table 里的优先级,越大越高
match:匹配规则
action:操作
Ovs 流表查看:kubectl ko ofctl master dump-flows br-int
广播/组播 源 mac 为 xxxxxxx1 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx,广播是特殊的组播包为全1,像 01:02:03:04:05:06 就是组播包源 mac;
match=(eth.src[40]), action=(drop;) 第一条意思就是 组播包丢弃
vlan.present 带 vlan 的包
match=(vlan.present), action=(drop;) 带 vlan 丢弃
inport == “node-master” 从 node-master port 进来的包
Node-master 是 logical_switch_port
# kubectl ko vsctl master list Interface | grep -C 20 ovn0 // 在 master 看 ovs port
// 缩略
external_ids : {iface-id=node-master, ip="100.64.0.2,dd:100:64::2", ovn-installed="true", ovn-installed-ts="1663729450921"}
mac_in_use : "00:00:00:21:85:b9"
mtu : 1400
name : ovn0
type : internal
可以看到 在 master 节点 ovn0 在 ovs 上的配置,iface-id 即 lsp 标记。
match=(inport == “node-master”), action=(next;) 从 node-master 进来的包送到下一张表。
outport == “coredns-6d8c4cb4d-6c4rb.kube-system” 从 lsp 出去的包
ip4.dst == 100.64.0.2 / ip6.dst == dd: 100:64::2 / eth.dst == 00:00:00:21:85:b9 / ip6.src == dd: 100:64::/64 / ip4.src == 127.0.0.0/8 / ip4.src == {10.16.0.1, 10.16.255.255} / icmp4.type == 8 && icmp4.code == 0 / icmp6.type == 128 && icmp6.code == 0 / ip4 / ip6 / arp/ arp.tpa == 100.64.0.1 / arp.op == 1
nd || nd_rs || nd_ra || mldv1 || mldv2
reg0[2] == 1 && ip4 && tcp
reg0: 寄存器0[2] 是 1 且 是 ipv4 tcp。
ct.new, ct.est,ct.trk,ct.rel,ct.rpl
ct:ovs 的 conntrack 功能增加了 ct 流表的概念,将需要跟踪状态的报文提交进 ct 里去,标记连接状态,供后续报文查询连接状态使用。
参考:http://www.openvswitch.org//support/dist-docs/ovs-fields.7.txt
new:数据包来自一个新的连接
est:数据包来自一个已经建立的连接
rel:数据包与一个已存在的连接相关联
rpl:数据包来自一个连接的回复方向
ink:数据包状态是无效的
trk:数据包已经过 conntrack
Datapath: "join" (73d52ffc-620d-4fc5-b6b3-19becad7623c) Pipeline: ingress
...
table=25(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:21:85:b9), action=(outport = "node-master"; output;)
示例流表即到 join 上的流,如果目的 mac 是 master 节点的 ovn0 网卡的 mac,就从 node-master 这个 lsp 丢出。
ovs 寄存器,当一个数据包进入交换机时,所有的寄存器都被清零,用户可以通过 Action 的指令修改寄存器中的值。
一般有 reg(32 bits,reg0-reg15),xreg(64 bits,xreg0-xreg7),xxreg(128 bits,xxreg0-xxreg3);
xxreg ipv6 广泛使用
路由时 | lb 时 | Arp or ipv6 ND | 其他 | |
---|---|---|---|---|
reg0 | 下一跳 ipv4 地址 | reg0[11],查询是否 inport 是否在 mac learning table | reg0[1] :是否需要 tracker 流 for acl;reg0[2]:是否需要 tracker 流 for LB;reg0[3]:dhcp 配置是否获取成功;reg0[4]:dns 是否解析成功;reg0[5]:IPv6 RA 配置是否获取成功;reg0[6] 和reg0[12]:一个是 检查 lb_hairpin,一个是检查 lb_hairpin_reply。reg0[7]:acl 可能 allow,且流是被追踪的;reg0[8]:acl 可能 allow,且流是未被追踪的;reg0[9]:acl 可能 drop;reg0[10]:acl 可能是 drop,且需要改 tracker;reg0[13]:stateful 流 | |
reg1 | 网关 ipv4 地址,所选择的 router_port IPv4 地址。 | vip | ||
reg2 | 前 16 位:vip_port;transport port | |||
reg7 | 从 Logical Router Port 来的流量在 lr_in_ip_routing_pre table 中设置为 0; | |||
reg8 | 前 16 位:ecmp group id;后 16 位:ecmp number id | |||
reg9 | reg9[16…31]:目的端口,LB 的 DNAT 会通过此值获得端口。 (不止 lb,发往 ovs contrack 的所有) | 去 OVN_Southbound mac_binding 表查询;reg9[2] = 1,代表已有或者 不需要 mac_learn。 |
具体使用介绍 https://github.com/ovn-org/ovn/blob/main/northd/ovn-northd.8.xml
分为 logical switch 和 logical router 表,且 ingress 和 egress 不同。
table=1 (ls_in_port_sec_ip ):准入控制
table=5/6 (ls_in_pre_acl/ls_in_pre_lb): prepares flows acl 和 lb
table=8 (ls_in_acl_hint):设置 hints,为后续处理设置一些提示
table=9 (ls_in_acl):acl
table=10/11 (ls_in_qos_mark/ls_in_qos_meter):nbctl 里的 qos table,关于 dscp marking。未使用
table=12 (ls_in_lb):LB 规则
Name: kube-dns
Namespace: kube-system
Selector: k8s-app=kube-dns
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.96.0.10
IPs: 10.96.0.10
Port: dns 53/UDP
TargetPort: 53/UDP
Endpoints: 10.16.0.48:53,10.16.0.5:53
Port: dns-tcp 53/TCP
TargetPort: 53/TCP
Endpoints: 10.16.0.48:53,10.16.0.5:53
table=13 (ls_in_acl_after_lb):配置 acl 时选项 --apply-after-lb,字面意思,未使用
table=14 (ls_in_after_lb):after lb 的操作
table=16 (ls_in_pre_hairpin) 配置 lb 后,如果 匹配 ip && ct.trk(ct 跟踪),则需要 hairpinned
table=17 (ls_in_nat_hairpin):需要 hairpin 的,snat 操作
table=18 (ls_in_hairpin):hairpin 操作
table=19 (ls_in_arp_rsp):arp 回复流表
table=20 (ls_in_dhcp_options):获取 dhcp 配置,对应 subnet 创建时的 dhcpOptions 的配置。
table=21 (ls_in_dhcp_response):dhcp 回复
table=22/23 (ls_in_dns_lookup/ls_in_dns_response):查询 dns 配置和回复 dns 请求,未使用
table=24 (ls_in_external_port):从 external logical ports,如 localnet 来的流,未使用
table=25 (ls_in_l2_lkup):实际的交换行为
table=26 (ls_in_l2_unknown):未知流的处理
如 match=(outport == “none”), action=(drop;),未设置 output 的流,drop。
Egress
table=0 (ls_out_pre_lb):与 ingress ls_in_pre_lb 类似
table=1 (lr_in_lookup_neighbor):arp 和 ipv6 nd,判断是否需要学习 mac binding。如果已有不需要学习,设置 reg9[2]。
table=2 (lr_in_learn_neighbor):判断是否 reg9[2] == 1,如果不为 1,则需要记录 mac binding。
table=3 (lr_in_ip_input):logical_router 核心表,基础功能。
ipv6 icmp:match=(ip6.dst == {dd:10:16::1, fe80::200:ff:fe63:b3f9} && icmp6.type == 128 && icmp6.code == 0), action=(ip6.dst <-> ip6.src; ip.ttl = 255; icmp6.type = 129; flags.loopback = 1; next; )
table=4(lr_in_unsnat):egress 时做了 snat,那么回包需要取消之前的 snat
table=5(lr_in_defrag):将数据包发送到连接跟踪器进行跟踪,如在 lr 上的 三层,四层 lb
kubectl ko nbctl lb-add lb0 10.96.99.99:8080 10.16.0.10:80,10.16.0.15:80,10.16.0.2:80 tcp
kubectl ko nbctl lr-lb-add ovn-cluster lb0
table=6(lr_in_dnat):数据包 dnat 处理
table=7(lr_in_ecmp_stateful):ECMP对称应答处理
table=8(lr_in_nd_ra_options):IPv6 ND RA 配置处理
table=9(lr_in_nd_ra_response) :IPv6 RA 回复
table=10(lr_in_ip_routing_pre):路由转发前配置,通常为 logical_route_port 带 options:route_table 配置的使用,未使用
table=11(lr_in_ip_routing):路由转发
table=12(lr_in_ip_routing_ecmp):ECMP 路由
table=13(lr_in_policy):策略路由
由此可以看出先匹配静态路由,再匹配策略路由,静态路由会先设置 outport,但不会发出,然后过策略路由表,策略路由匹配上会更改 outport。
table=14(lr_in_policy_ecmp):ECMP 策略路由
table=15(lr_in_arp_resolve):arp/nd 决定,根据之前路由设置的 outport 和 寄存器 reg0 存的 ip 设置目的 mac
table=16(lr_in_chk_pkt_len):检查包长度,在给 gw_port 设置 options:gateway_mtu 时生效。
table=17(lr_in_larger_pkts):同上,为从 logical_router_port 到 gw_port 的流量使用。
table=18(lr_in_gw_redirect):分布式逻辑路由中,当 logical_router_port 设置 chassises 时使用。
table=19(lr_in_arp_request):如果 目的 mac 未确定,通过 reg0 和 reg1 存的 ip 地址进行 arp 请求;如果确定 mac,则发出。
match=(eth.dst == 00:00:00:00:00:00 && ip4), action=(arp { eth.dst = ff:ff:ff:ff:ff:ff; arp.spa = reg1; arp.tpa = reg0; arp.op = 1; output; }; )
match=(1), action=(output;)
Egress
table=0(lr_out_chk_dnat_local):检查是否需要 DNAT,通常与 ingress 中 SNAT 对应
table=1(lr_out_undnat):已经建立连接后的流是否做 dnat,不如创建 lr-lb,就会多出下面流表
参考文档:https://www.mankier.com/8/ovn-northd