共计 14600 个字符,预计需要花费 37 分钟才能阅读完成。
Networkpolicy 的含意与现状
networkpolicy 是 k8s 在很早就提出的一个抽象概念。它用一个对象来形容一类 pod 的网络出入站规定。对于 networkpolicy 的语义能够参考我之前的文章
networkpolicy 的作用对象是 pod,作用成果包含出站、入站,作用成果拓扑包含 IP 段、namespace、pod、端口、协定。
与以往 IaaS 服务场景下,针对虚拟机、网卡对象的平安组规定不同,networkpolicy 是 k8s 原语 。因而,在 k8s 场景下,进行网络安全规定的布局时, 用 networkpolicy 能做到更加的灵便和自动化。举个例子:
有一套工作负载 A 是做相似数据库代理一类的工作,它只容许代理服务 B 拜访,不容许其余业务拜访。
- 在 k8s 场景下,如果不应用 networkpolicy,咱们须要布局好 A 类 pod 的部署节点,配置相应的 ACL 规定,将 B 类 pod 的 IP 予以放行,一旦 A / B 类 pod 做了扩缩容,可能要在重新配置一份甚至多份 ACL 规定。
- 在 k8s 场景下,咱们会给 A 和 B 类别离配置 label,创立好 networkpolicy 后限度 A 只放行 B 类 pod,每当 A 或 B 扩缩容时,无需做任何额定操作。
网易数帆的 networkpolicy 反对
网易数帆的云计算业务的内部用户中,基于交付的 openshift-sdn 计划或 calico 计划,这些计划都能够原生地反对 networkpolicy(下文会介绍)。
但云计算业务的外部用户(应用 vpc 或 bgp CNI)大多是由 PE 布局治理 IP 白名单,限度某些网络拜访,除此之外没有做任何跨业务的网络限度(比如说:离线转码业务与领取业务是互不相干的,然而两种业务的 pod 彼此网络是可通信的)。因而始终没有 networkpolicy 的需要,而 vpc、bgp 等外部应用的 CNI 也还始终没有实现相干性能。
将来随着业务规模的扩充,相似的网络安全策略是必不可少的,因而咱们会在接下来逐渐将 networkpolicy enable。
业界的 networkpolicy 实现
以后社区对于 k8s 的 networkpolicy 的实现,不外乎三种计划:
计划 | 依赖 | 案例 | 反对的 CNI |
---|---|---|---|
基于 iptables+ipset 实现规定 | 容器流量须要通过宿主机的协定栈 | calico felix | calico、flannel、terway |
基于 ovs 流表实现规定 | 应用 openvswitch | openshift-sdn | openshift-sdn |
基于 ebpf hook 实现规定 | 须要较高版本内核 | cilium | cilium、flannel、terway |
从下面的表格能够看出:
-
基于 ovs 流表实现的计划, 典型的就是 openshift-sdn,此前咱们分享过一篇 openshift-sdn 的详解, 介绍了外面对 ovs table 的设计,其中有一个专门的 table(tableid=21)就是用来实现 networkpolicy 的规定。该计划是间接内建于 openshift-sdn 我的项目,根本无奈移植。而 openshift-sdn 尽管代码开源,但设计上、代码逻辑上与 openshift 平台耦合还是比拟严密的。比如说:
- 公开的 openshift-sdn 部署计划须要依赖 openshift-network-operator
- openshift-sdn 代码中硬编码了要拜访的容器运行时为 crio,不反对 dockershim
- cilium 是最先应用 ebpf 技术实现网络数据面的 CNI,它力求实现大而全的容器网络,封装、加密、全面、高性能等特点包罗万象,它对于 networkpolicy 的反对也曾经非常欠缺。但 ebpf hook 的实现形式,依赖较高的内核版本,且在数据面排障时比拟吃力。ebpf 技术对于网络性能的晋升很大,将来势必会越来越风行,所以值得关注。
- 基于 iptables+ipset 技术实现的计划,其实在几年前就比拟成熟了 calico-felix、romana、kube-router 等开源的网络计划都是基于此实现了反对 networkpolicy。其中,felix 是 calico 网络计划中的一个组件,官网反对在 calico 中 enable networkpolicy,且可能与 flannel 配合应用。阿里云的 terway 便是间接套用 felix 实现了对 networkpolicy 的反对(最近还套用了 cilium)。这套计划要求容器流量要进过宿主机协定栈,否则包就不会进入内核的 netfilter 模块,iptables 规定就无奈失效。
指标
基于上述现状,咱们心愿基于现有的开源实现计划,进行兼容性调研或革新,适配网易数帆的各种网络计划,如:
- netease-vpc
- netease-bgp
- flannel
- …
因为这些网络计划都满足 felix 的要求,同时 felix 有较为沉闷的社区和较多的适配案例,因而咱们决定基于 felix,实现一套即插即用的 networkpolicy addon。本文接下来将会着重介绍该计划的实现。
calico/felix 的设计实现
架构
calico 在部署架构上做了屡次演进,咱们以最新版本 v3.17.1 为准。calico 的残缺架构包含了若干组件:
- calico/kube-controllers:calico 控制器,用于监听一些 k8s 资源的变更,从而进行相应的 calico 资源的变更。例如依据 networkpolicy 对象的变更,变更相应的 calicopolicy 对象
- pod2daemon:一个 initcontainer,用于构建一个 Unix Domain Socket,来让 Felix 程序与
Dikastes
(calico 中反对 istio 的一种 sidecar,详见 calico 的 istio 集成) 进行加密通信. - cni plugin / ipam plugin:规范的 CNI 插件,用于配置 / 解除网络;调配 / 回收网络配置
-
calico-node calico-node 其实是一个数据面工具总成,包含了:
- felix:治理节点上的容器网卡、路由、ACL 规定;并上报节点状态
- bird/bird6:用来建设 bgp 连贯,并依据 felix 配置的路由,在不同节点间散发
- confd:依据以后集群数据生成本地 brid 的配置
- calicoctl:calico 的 CLI 工具。
- datastore plugin:即 calico 的数据库,能够是独立的 etcd,也能够以 crd 形式记录于所在集群的 k8s 中
- typha:相似于数据库代理,能够尽量少防止有大量的连贯建设到 apiserver。实用于超过 100 个 node 的集群。
官网给出了 calico 整体的组件架构图:
原理
在网络连通性(Networking)方面:calico 的数据面是非常简单的三层路由转发。路由的学习和散发由 bgp 协定实现。如果 k8s 的上层是 VPC 之类的三层网络环境,则须要进行 overlay,calico 反对 ipip 封装实现 overlay。
在网络安全性方面:calico 思考到其 Networking 是依赖宿主机协定栈进行路由转发实现的,因而能够基于 iptables+ipset 进行流量标记、地址集布局、流量解决(放行或 DROP),并且基于这些操作能够实现:
- networkpolicy 的抽象概念
- calico 自定义的 networkpolicy,为了在 openstack 场景下利用而设计
- calico 自定义的 profile,已废除。
这里所有的 iptables 规定都作用在:
- pod 在宿主机 namespace 中的 veth 网卡(calico 中将之称为 workload)
- 宿主机 nodeIP 所在网卡(calico 中将之称为 host-endpoint,实际上这部分规定不属于 k8s 的 networkpolicy 领域)。
次要包含如下几类规定:
-
iptables 的 INPUT 链规定中,会先跳入
cali-INPUT
链,在cali-INPUT
链中,会判断和解决两种方向的流量:- pod 拜访 node(
cali-wl-to-host
)实际上这个链中只走了cali-from-wl-dispatch
链,如果是利用在 openstack 中,该链还会容许拜访 metaserver;如果应用 ipv6,该链中还会容许收回 icmpv6 的一系列包 - 来自 node 的流量(
cali-from-host-endpoint
)
- pod 拜访 node(
-
iptables 的 OUTPUT 链中,会首先跳入
cali-OUTPUT
链,在cali-OUTPUT
链中,次要会解决:- 拜访 node 的流量(
cali-to-host-endpoint
)的流量
- 拜访 node 的流量(
-
iptables 的 FORWARD 链中,会首先跳入
cali-FORWARD
链,在cali-FORWARD
链中会解决如下几种流量:- 来自 node 转发的流量
cali-from-hep-forward
- 从 pod 中收回的流量
cali-from-wl-dispatch
- 达到 pod 的流量
cali-to-wl-dispatch
- 达到 node 的转发流量
cali-to-hep-forward
- 纯正的 IP 段到 IP 段的转发流量
cali-cidr-block
- 来自 node 转发的流量
k8s 的 networkpolicy 只须要关注上述流量中与 pod 相干的流量,因而只须要关怀:
cali-from-wl-dispatch
cali-to-wl-dispatch
这两个链的规定,对应到 pod 的 egress 和 ingress networkpolicy。
1. 除了 nat 表,在 raw 和 mangle 表中还有对 calico 关注的网卡上的收发包进行初始标记的规定,和最终的判断规定。2. 在 https://github.com/projectcalico/felix/blob/master/rules/static.go 中能够看到残缺的动态 iptables 表项的设计
接着,iptables 规定中还会在 cali-from-wl-dispatch
和cali-to-wl-dispatch
两个链中依据收包 / 发包的网卡判断这是哪个 pod,走到该 pod 的 egress 或 ingress 链中。每个 pod 的链中则又设置了对应 networkpolicy 实例规定的链,以此递归调用。
这样,pod 的流量通过 INPUT/OUTPUT/FORWARD 等链后,递归地走了多个链,每个链都会 Drop 或者 Return,如果把链表走一遍下来始终 Return,会 Return 到 INPUT/OUTPUT/FORWARD,而后执行 ACCEPT,也就是说这个流量满足了某个 networkpolicy 的规定限度。如果过程中被 Drop 了,就示意受某些规定限度,这个链路不通。
咱们通过一个简略的例子来形容 iptables 这块的链路程序。
felix 实现 networkpolicy 的案例
假如有如下一个 networkpolicy:
spec:
egress:
- {}
ingress:
- from:
- podSelector:
matchLabels:
hyapp: client1
- from:
- ipBlock:
cidr: 10.16.2.0/24
except:
- 10.16.2.122/32
ports:
- port: 3456
protocol: TCP
podSelector:
matchLabels:
hyapp: server
- 他作用于有
hyapp=server
的 label 的 pod - 这类 pod 出方向不限度
-
这类 pod 的入站规定中只容许如下几种流量:
- 来自于有
hyapp=client1
的 label 的 pod - 10.16.2.0/24 网段中除了 10.16.2.122/32 以外的 IP 能够拜访该类 pod 的 3456 TCP 端口。
- 来自于有
咱们应用 iptables -L
或iptables-save
命令来剖析机器上的 iptables 规定。
因为是入站规定,所以咱们能够察看 iptables 表中的 cali-to-wl-dispatch
链。另外,该 networkpolicy 的作用 pod 只有一个,它的 host 侧网卡是veth-13dd25c5cb
。咱们能够看到如下的几条规定:
Chain cali-to-wl-dispatch (1 references)
target prot opt source destination
cali-to-wl-dispatch-0 all -- anywhere anywhere [goto] /* cali:Ok_j0t6AwtLyoFYU */
cali-tw-veth-13dd25c5cb all -- anywhere anywhere [goto] /* cali:909gC5dwdBI3E96S */
DROP all -- anywhere anywhere /* cali:4M4uUxEEGrRKj1PR */ /* Unknown interface */
留神,这里有一个 cali-to-wl-dispatch-0
的链,是用来做前缀映射的,该链的规定下蕴含所有cali-tw-veth-0
这个前缀的链:
Chain cali-to-wl-dispatch-0 (1 references)
target prot opt source destination
cali-tw-veth-086099497f all -- anywhere anywhere [goto] /* cali:Vt4xxuTYlCRFq62M */
cali-tw-veth-0ddbc02656 all -- anywhere anywhere [goto] /* cali:7FDgBEq4y7PN7kMf */
DROP all -- anywhere anywhere /* cali:up42FFMQCctN8FcW */ /* Unknown interface */
这是 felix 设计上用于缩小 iptables 规定遍历次数的一个优化伎俩。
咱们通过 iptables-save |grep cali-to-wl-dispatch
命令,能够发现如下的规定:
cali-to-wl-dispatch -o veth-13dd25c5cb -m comment --comment "cali:909gC5dwdBI3E96S" -g cali-tw-veth-13dd25c5cb
意思就是:在 cali-to-wl-dispatch
链中,依据 pod 在 host 侧网卡的名字,会执行 cali-tw-veth-13dd25c5cb
链,咱们再看这条链:
Chain cali-tw-veth-13dd25c5cb (1 references)
target prot opt source destination
1 ACCEPT all -- anywhere anywhere /* cali:RvljGbJwZ8z9q-Ee */ ctstate RELATED,ESTABLISHED
2 DROP all -- anywhere anywhere /* cali:krH_zVU1BetG5Q5_ */ ctstate INVALID
3 MARK all -- anywhere anywhere /* cali:Zr20J0-I__oX_Y2w */ MARK and 0xfffeffff
4 MARK all -- anywhere anywhere /* cali:lxQlOdcUUS4hyf-h */ /* Start of policies */ MARK and 0xfffdffff
5 cali-pi-_QW8Cu1Tr3dYs2pTUY0- all -- anywhere anywhere /* cali:d2UTZGk8zG6ol0ME */ mark match 0x0/0x20000
6 RETURN all -- anywhere anywhere /* cali:zyuuqgEt28kbSlc_ */ /* Return if policy accepted */ mark match 0x10000/0x10000
7 DROP all -- anywhere anywhere /* cali:DTh9dO0o6NsmIQSx */ /* Drop if no policies passed packet */ mark match 0x0/0x20000
8 cali-pri-kns.default all -- anywhere anywhere /* cali:krKqEtFijSLu5oTz */
9 RETURN all -- anywhere anywhere /* cali:dgRtRf38hD2ZVmC7 */ /* Return if profile accepted */ mark match 0x10000/0x10000
10 cali-pri-ksa.default.default all -- anywhere anywhere /* cali:NxmrZYbhCNLKgL6O */
11 RETURN all -- anywhere anywhere /* cali:zDbjbrN6JPMZx9S1 */ /* Return if profile accepted */ mark match 0x10000/0x10000
12 DROP all -- anywhere anywhere /* cali:d-mHGbHkL0VRl6I6 */ /* Drop if no profiles matched */
- 第 1、2 条:如果 ct 表中能检索到该连贯的状态,咱们间接依据状态来确定这个流量的解决形式,这样能够省略很大一部分工作。
- 第 3 条:先对包进行标记(将第 17 地位 0),在本链的规定执行结束后,会判断标记是否 match(判断第 17 位是否有被置 1),不匹配(没有被置 1)就 DROP;
- 第 4 条:如果该网卡对应的 pod 有相干的 networkpolicy,要再打一次 mark,与之前的 mark 做与计算后目前 mark 应该是 0xfffcffff(17、18 位为 0);
- 第 5 条:如果包 mark match 0x0/0x20000(第 18 位为 0),执行
cali-pi-_QW8Cu1Tr3dYs2pTUY0-
链进入 networkpolicy 的判断。 - 第 6、7 条:如果 networkpolicy 查看通过,会对包进行 mark 批改,所以查看是否 mark match 0x10000/0x10000, 匹配阐明通过,间接 RETURN,不再查看其余的规定;如果 mark 没有批改,与原先统一,视为没有任何一个 networkpolicy 容许该包通过,间接 DROP
- 第 8、9、10、11 条:当没有任何相干的 networkpolicy 时(即第 4~7 条不存在)才会被执行,执行 calico 的 profile 策略,分成 namespace 维度和 serviceaccount 维度,如果在这两个策略里没有对包的 mark 做任何批改,就示意通过。这两个策略是 calico 的概念,且为了不与 networkpolicy 混同,曾经被弃用了。因而此处都是空的。
- 第 12 条:如果包没有进入 上述两个 profile 链,DROP。
接着看 networkpolicy 的链cali-pi-_QW8Cu1Tr3dYs2pTUY0-
,只有在这个链里执行 Return 前有将包打上 mark 使其 match 0x10000/0x10000,就示意匹配了某个 networkpolicy 规定,包容许放行:
Chain cali-pi-_QW8Cu1Tr3dYs2pTUY0- (1 references)
target prot opt source destination
MARK all -- anywhere anywhere /* cali:fdm8p72wShIcZesY */ match-set cali40s:9WLohU2k-3hMTr5j-HlIcA0 src MARK or 0x10000
RETURN all -- anywhere anywhere /* cali:63L9N_r1RGeYN8er */ mark match 0x10000/0x10000
MARK all -- anywhere anywhere /* cali:xLfB_tIU4esDK000 */ MARK xset 0x40000/0xc0000
MARK all -- 10.16.2.122 anywhere /* cali:lUSV425ikXY6zWDE */ MARK and 0xfffbffff
MARK tcp -- 10.16.2.0/24 anywhere /* cali:8-qnPNq_KdC2jrNT */ multiport dports 3456 mark match 0x40000/0x40000 MARK or 0x10000
RETURN all -- anywhere anywhere /* cali:dr-rzJrx0I6Vqfkl */ mark match 0x10000/0x10000
- 第 1、2 条:如果 src ip match ipset:
cali40s:9WLohU2k-3hMTr5j-HlIcA0
, 将包 mark or 0x10000,并查看是否 match,match 就 RETUR。咱们能够在机器上执行ipset list cali40s:9WLohU2k-3hMTr5j-HlIcA0
,能够看到这个 ipset 里蕴含的就是 networkpolicy 中指明的、带有hyapp=client1
这个 label 的两个 pod 的 ip。 - 第 3、4、5、6 条则是针对 networkpolicy 中的第二局部规定,先对包设置正向标记,而后将要隔离的 src IP/IP 段进行判断并做反向标记,接着判断 src 段是否在准入范畴,如果在,并且目标端口匹配,并且标记为正向,就再对包进行 MARK or 0x10000,这样,最终判断 match 了就会 Return。
- 实际上咱们能够看到,这里就算不 match,这个链执行完了也还是会 RETURN 的,所以这个链执行的后果是通过 mark 返回给上一级的,这就是为什么调用该链的上一级,会在调用结束后要判断 mark 并确认是否 ACCEPT。
至此,一个残缺的 networkpolicy 的实现链路就实现了。
egress 规定与上述 ingress 规定相似。能够参考下图:
通用的 felix 插件设计
如果你看了上文 calico/felix 的设计实现,你就会发现原理其实非常简单,这个设计齐全能够利用到任何一个“基于三层路由转发”的网络计划中。但理论利用过程中咱们还是遇到了一些问题。
问题 1:networkpolicy-only
咱们晓得,较新版本的 felix 都是集成到 calico-node 组件中运行。calico-node 默认状况下会实现容器网络和 networkpolicy 两块工作,如何部署一个只负责实现 networkpolicy 规定的 calico-node 呢?
能够参考 calico 官网提供的 canal 计划是如何适配 flannel 的。从 canal 的部署模板中咱们能够根本确认,只有部署好 kube-controllers
,pod2daemon
,calico-node
并且通过环境变量管制 calico-node
的CALICO_NETWORKING
环境变量为"false"
(禁止配置容器网络)即可。
问题 2: 网卡名映射
咱们尝试在轻舟 k8s 集群(应用网易云 VPC 作为容器网络)中尝试以这样的形式部署一套 calico 套件,部署后,咱们会发现 calico-node 的日志里定期报错,提醒:找不到 cali****
的网卡,无奈配置 iptables 规定 。有用过 calico 的同学应该看得明确,cali
是 calico 计划在宿主机侧生成的网卡名前缀。而咱们基于网易云 VPC 设计的容器网络计划,会以 veth-
为容器 hostveth 前缀。如果 calico 是基于前缀来找到容器网卡的,那么是否有参数能够指定前缀呢?
官网的 felix 配置文档中提到:能够应用 InterfacePrefix
参数或 FELIX_INTERFACEPREFIX
环境变量,决定 felix 要检索的 host 侧网卡前缀。一开始看到这个阐明令人欣喜万分。然而当咱们理论配置了之后,会发现,calico-node 还是会报错,提醒: 找不到 veth-*****
的网卡,这个网卡名超过了 linux 内核的常数限度(15 个字符)。
咱们依照日志里打印的网卡名去找,的确找不到这个网卡,看来必须要搞清楚 calico 是如何给 host 侧的网卡进行命名的。
calico 为 pod 的 veth 命名的规定实现在 libcalico-go
我的项目中,存在如下的一个接口
type WorkloadEndpointConverter interface {VethNameForWorkload(namespace, podName string) string
PodToWorkloadEndpoints(pod *kapiv1.Pod) ([]*model.KVPair, error)
}
这个接口用用来实现 pod 映射到 workload 的,同时还能依据 pod 的信息,推导 pod 的 hostveth 网卡名是啥,该接口只有一种实现:defaultWorkloadEndpointConverter
, 其中 VethNameForWorkload
的实现如下:
// VethNameForWorkload returns a deterministic veth name
// for the given Kubernetes workload (WEP) name and namespace.
func (wc defaultWorkloadEndpointConverter) VethNameForWorkload(namespace, podname string) string {
// A SHA1 is always 20 bytes long, and so is sufficient for generating the
// veth name and mac addr.
h := sha1.New()
h.Write([]byte(fmt.Sprintf("%s.%s", namespace, podname)))
prefix := os.Getenv("FELIX_INTERFACEPREFIX")
if prefix == "" {
// Prefix is not set. Default to "cali"
prefix = "cali"
} else {
// Prefix is set - use the first value in the list.
splits := strings.Split(prefix, ",")
prefix = splits[0]
}
log.WithField("prefix", prefix).Debugf("Using prefix to create a WorkloadEndpoint veth name")
return fmt.Sprintf("%s%s", prefix, hex.EncodeToString(h.Sum(nil))[:11])
}
能够看到,calico 依据 pod 的 namespace 和 name 进行 hash,而后依据 FELIX_INTERFACEPREFIX
环境变量的值决定网卡名前缀,将前缀与 hash 的前 11 个字符拼凑起来。libcalico-go
是所有 calico 组件的 lib 库,也就是说,不论是 calico-cni 去创立 veth,还是 felix 去依据 pod 查找对应的网卡,都是基于这个逻辑去匹配的。
显然这个代码破绽很大!calico 没有对前缀做长度查看,这里要填充 hash 的前 11 位,齐全是因为默认的前缀是四个字符的cali
!
问题十分明确了,要想在本人的网络计划下无痛享受 felix,就得本人实现一个 WorkloadEndpointConverter
接口,并编译出定制化的 calico-node 镜像。
案例 1: canal 如何接入
从 canal 的部署模板中咱们就能够看得出来,canal 计划中应用的 CNI plugin 实际上也是 calico,只不过 calico 只负责创立 veth 对,配置 IP 和路由等工作,veth 的命名交给 calico 来做,天然就依照 calico 的官网配置来命名了,理论应用过程中就能够看到,canal 计划下容器在宿主机上的 veth 名称也是 cali
前缀。
案例 2: 阿里云 terway 如何接入
阿里云的 ACK 应用其自研的 terway 来作为容器网络计划。terway 中反对两种容器网卡虚拟化计划:
- veth
- ipvlan L2
veth 计划下会应用 felix 来实现 networkpolicy,而 ipvlan 下则应用 cilium。咱们此处次要关注 veth 计划。
veth 计划下 felix 是如何应用的呢?terway 在部署时,间接基于社区 v3.5.8 版本的 felix 代码进行编译(编译前还往代码中退出了一个 terway 自定义的 patch),将编译进去的 felix 二进制文件丢到 terway 的 docker 镜像中,daemonset 里启动三个 terway 镜像容器,别离用于装置 cni 插件;运行 agent;运行 felix。
terway 是如何实现兼容 felix 的呢?上文提到的网卡名的问题,它如何解决呢?
通过浏览 terway 的源码,咱们发现 terway 做得比拟暴力——间接复用了 calico 代码中的网卡命名形式,对 host 侧的 veth 进行命名,网卡前缀为硬编码的cali
。
案例 3: 网易云 k8s 如何接入
网易云的场景中,host 侧网卡命名是以某个前缀加上 pod 的 sandbox 容器 id 来命名的(起因见下文)。因而咱们即使把前缀改成 cali
或者其余长度 4 以内的字符串,felix 也无奈基于 calico 的那套逻辑找到网卡。
因而咱们改写了该接口。实现了一个 sandboxWorkloadEndpointConverter
,将VethNameForWorkload
做了另一种实现:
- 依据 felix 参数感知自定义的网卡名前缀,这里为了防止 prefix 太长,导致网卡名抵触,对 prefix 长度进行限度,倡议不超过 5 个字符,至多给后缀保留 10 个字符(咱们已经在线上环境呈现过同一个节点的两个 podsandbox 容器 id 前 9 位完全相同的状况)
- 依据 pod 信息获取到他对应的 sandbox 容器 ID,取其
15-len(prefix)
位作为后缀。 - 通过前缀与 sandboxID 后缀形成 workload 的网卡名。
将来咱们会尝试对这部分代码做更通用化的革新,反对多种前缀,并反对主动选用网卡命名办法。
为什么咱们要以 podsandbox 容器 id 来命名网卡?
因为理论应用过程中咱们发现,kubelet 对于 sandbox 容器的解决并不一定是有序的,可能呈现如下场景:
- 为 poda 创立出 sandbox1,调用 CNI ADD 失败,但 veth 曾经创立;
- 为 poda 创立出 sandbox2,调用 CNI ADD 胜利,间接应用了上一次创立的 veth;
- 删除此前曾经失败的 sandbox1,调用 CNI DEL。将第 2 步创立的 veth 删除,导致 poda 的网络异样。
因而,如果 kubelet 调用 CNI 是以 sandbox 为粒度,那么咱们创立的资源就理当也以 sandbox 为粒度。
编译与构建
目前咱们将通用的 felix 基于 calico/node 的 v3.17 分支构建,并将它援用的libcalico-go
fork 到网易云的 github organization 我的项目:163yun/libcalico-go,并建设分支 tag:v1.7.2-nks.1
。
这样,咱们能够间接拉取社区代码,进行编译:
cd $GOPATH/src
mkdir -p github.com/projectcalico
cd github.com/projectcalico
git clone https://github.com/projectcalico/node
cd node
# 批改依赖包,改为援用咱们批改过后的 libcalico-go
go mod edit -replace=github.com/projectcalico/libcalico-go=github.com/163yun/libcalico-go@v1.7.2-nks.1
# 编译出 calico/node 的 docker image
make calico/node
docker tag calico/node:latest-amd64 $EXPECTED_IMAGE_PATH:$EXPECTED_IMAGE_TAG
编译构建过程中可能呈现一些网络起因导致编译阻塞:
- 编译过程中会在容器里进行
go build
,为了不便执行 go module,所以倡议在 Makefile、以及执行 make 时下载的长期版本Makefile.common.v***
文件中的局部地位注入环境变量:GOPROXY=https://goproxy.cn
- 编译结束后构建 docker image 时,会在根底镜像中下载安装多个依赖工具,可能呈现 yum 源无奈解析等问题,倡议在
Makefile
文件中调用docker build
的语句里追加参数--network=host
测试方法
首先咱们要筹备好网易轻舟 k8s 集群,并应用 vpc 或 bgp 网络计划,并额定部署 felix 套件(参考上图)。
咱们应用 sonobuoy 工具进行测试工作,该工具也能够素来进行 k8s 集群的 conformance 认证。下载该工具的二进制文件,而后执行:
sonobuoy run --e2e-focus="\[Feature:NetworkPolicy\]" --e2e-skip="" --image-pull-policy IfNotPresent
即可在以后集群里进行 networkpolicy 相干的 e2e 测试(并且测试过程创立的 pod 的 imagePullPolicy 都是IfNotPresent
)。
执行命令后能够查看集群中的 pods,会看到 sonobuoy 的 pod 以及它创立进去的一些 e2e 相干的 pod,如果有 pod 阻塞于 ImagePullBackoff,能够尝试在 pod 所在节点上拉取备用镜像并批改成所需镜像:
docker pull hub.c.163.com/combk8s/conformance:v1.19.3
docker tag hub.c.163.com/combk8s/conformance:v1.19.3 k8s.gcr.io/conformance:v1.19.3
docker pull hub.c.163.com/combk8s/e2e-test-images/agnhost:2.20
docker tag hub.c.163.com/combk8s/e2e-test-images/agnhost:2.20 k8s.gcr.io/e2e-test-images/agnhost:2.20
测试结束后,实践上所有 e2e 前缀的 pod 都会被删除,此时执行 sonobuoy retrieve
命令,会在当前目录生成一个 tar.gz
文件,解压该文件,并读取 plugins/e2e/results/global/e2e.log
,就能够看到整个 e2e 测试的执行后果。也能够通过plugins/e2e/sonobuoy_results.yaml
文件查看,但这个文件内容包含了未执行的用例,可读性可能不太好。
简要的 e2e 测试后果如下:
root@pubt2-nks-for-dev6:/home/hzhuangyang1/plugins/e2e/results/global# tail -n 10 e2e.log
JUnit report was created: /tmp/results/junit_01.xml
{"msg":"Test Suite completed","total":29,"completed":29,"skipped":5204,"failed":0}
Ran 29 of 5233 Specs in 4847.335 seconds
SUCCESS! -- 29 Passed | 0 Failed | 0 Pending | 5204 Skipped
PASS
Ginkgo ran 1 suite in 1h20m48.75547671s
Test Suite Passed
总结
本文介绍了 networkpolicy 的特点和劣势,并剖析了当下支流的 networkpolicy 实现计划——calico-felix,摸索了 calico-felix 通用化革新的计划和落地。随着 calico-felix 的引入,用户能同时享受到 vpc 的易扩展性和 networkpolicy 的灵活性。
将来咱们还将着力引入 cilium 实现高内核版本下、基于 ebpf 实现的 networkpolicy,并实现 networkpolicy 拓扑可视化。