**
**
当前公有云厂商众多,如:阿里云、华为云、腾讯云、AWS 等,但当前开源社区的主流 CNI 插件难以通过 Underlay 的网络方式运行其上,只能使用每个公有云厂商的专有 CNI 插件,也没有统一的公有云 Underlay 解决方案。尤其是在混合云场景下,统一的 CNI 方案能够便于多云管理。 本文将介绍一种适用于任意的公有云环境中的云原生 Underlay 网络解决方案:Spiderpool (github.com/spidernet-i…
01
统一的云原生 Underlay 网络方案
Spiderpool 能基于 IPVlan Underlay CNI 运行在公有云环境上,它的实现原理如下:
-
公有云下使用 Underlay 网络,但公有云的每个云服务器的每张网卡只能分配有限的 IP 地址,当应用运行在某个云服务器上时,需要同步获取到 VPC 网络中分配给该云服务器不同网卡的合法 IP 地址,才能实现通信。根据上述分配 IP 的特点,Spiderpool 的 CR:SpiderIPPool 可以设置 nodeName,multusName 实现节点拓扑的功能,通过 IP 池与节点、IPvlan Multus 配置的亲和性,能最大化的利用与管理节点可用的 IP 地址,给应用分配到合法的 IP 地址,让应用在 VPC 网络内自由通信,包括 Pod 与 Pod 通信,Pod 与云服务器通信等。
-
公有云的 VPC 网络中,基于网络安全管控和数据包转发的原理,当网络数据报文中出现 VPC 网络未知的 MAC 和 IP 地址时,它无法得到正确的转发。例如,基于 Macvlan 和 OVS 原理的 Underlay CNI 插件,Pod 网卡中的 MAC 地址是新生成的,会导致 Pod 无法通信。针对该问题,Spiderpool 可搭配 IPVlan CNI 进行解决。IPVlan 基于三层网络,无需依赖二层广播,并且不会重新生成 Mac 地址,与父接口保持一致,因此通过 IPvlan 可以解决公有云中关于 MAC 地址合法性的问题。
02
实施要求
-
使用 IPVlan 做集群 CNI 时,系统内核版本必须大于 4.2。
-
已安装 Helm (helm.sh/docs/intro/…
03
步骤
公有云环境
准备一个公有云环境,例如:以阿里云为例,给虚拟机分配 2 个网卡,每张网卡均分配一些辅助私网 IP,如图:
图源:系统界面截图
使用上述配置的虚拟机,搭建一套 Kubernetes 集群,集群的网络拓扑图如下:
安装 Spiderpool
通过 Helm 安装 Spiderpool。
helm repo add spiderpool https://spidernet-io.github.io/spiderpool
helm repo update spiderpool
helm install spiderpool spiderpool/spiderpool --namespace kube-system --set ipam.enableStatefulSet=false --set multus.multusCNI.defaultCniCRName="ipvlan-eth0"
如果您使用的是中国大陆的云厂商服务器,可以指定参数 --set global.imageRegistryOverride=ghcr.m.daocloud.io,以帮助您更快的拉取镜像。
Spiderpool 可以为控制器: Statefulset 类型的应用副本固定 IP 地址。在公有云的 Underlay 网络场景中,云主机只能使用限定的 IP 地址,当 StatefulSet 类型的应用副本漂移到其他节点,但由于原固定的 IP 在其他节点是非法不可用的,漂移后的 Pod 在新节点上运行将出现网络不可用的问题。对此场景,将 ipam.enableStatefulSet 设置为 false,禁用该功能。
通过 multus.multusCNI.defaultCniCRName 指定集群的 Multus clusterNetwork,clusterNetwork 是 Multus 插件的一个特定字段,用于指定 Pod 的默认网络接口。
安装 CNI 配置
Spiderpool 为简化书写 JSON 格式的 Multus CNI 配置,它提供了 SpiderMultusConfig CR 来自动管理 Multus NetworkAttachmentDefinition CR。如下是创建 IPvlan SpiderMultusConfig 配置的示例:
IPVLAN_MASTER_INTERFACE0="eth0"
IPVLAN_MULTUS_NAME0="ipvlan-$IPVLAN_MASTER_INTERFACE0"
IPVLAN_MASTER_INTERFACE1="eth1"
IPVLAN_MULTUS_NAME1="ipvlan-$IPVLAN_MASTER_INTERFACE1"
cat <<EOF | kubectl apply -f -
apiVersion: spiderpool.spidernet.io/v2beta1
kind: SpiderMultusConfig
metadata:
name: ${IPVLAN_MULTUS_NAME0}
namespace: kube-system
spec:
cniType: ipvlan
coordinator:
mode: underlay
tunePodRoutes: true
podCIDRType: cluster
enableCoordinator: true
ipvlan:
master:
- ${IPVLAN_MASTER_INTERFACE0}
---
apiVersion: spiderpool.spidernet.io/v2beta1
kind: SpiderMultusConfig
metadata:
name: ${IPVLAN_MULTUS_NAME1}
namespace: kube-system
spec:
cniType: ipvlan
coordinator:
mode: underlay
tunePodRoutes: true
podCIDRType: cluster
enableCoordinator: true
ipvlan:
master:
- ${IPVLAN_MASTER_INTERFACE1}
EOF
在本文示例中,使用如上配置,创建如下的两个 IPvlan SpiderMultusConfig,将基于它们自动生成的 Multus NetworkAttachmentDefinition CR,它们分别对应了宿主机的 eth0 与 eth1 网卡。
~# kubectl get spidermultusconfigs.spiderpool.spidernet.io -n kube-system
NAME AGE
ipvlan-eth0 10m
ipvlan-eth1 10m
~# kubectl get network-attachment-definitions.k8s.cni.cncf.io -n kube-system
NAME AGE
ipvlan-eth0 10m
ipvlan-eth1 10m
创建 IPPools
Spiderpool 的 CR:SpiderIPPool 提供了 nodeName、multusName 与 ips 字段:
- nodeName: 当 nodeName 不为空时,Pod 在某个节点上启动,并尝试从 SpiderIPPool 分配 IP 地址, 若 Pod 所在节点符合该 nodeName ,则能从该 SpiderIPPool 中成功分配出 IP,若 Pod 所在节点不符合 nodeName,则无法从该 SpiderIPPool 中分配出 IP。当 nodeName 为空时,Spiderpool 对 Pod 不实施任何分配限制。
- multusName: Spiderpool 通过该字段与 Multus CNI 深度结合以应对多网卡场景。当 multusName 不为空时,SpiderIPPool 会使用对应的 Multus CR 实例为 Pod 配置网络,若 multusName 对应的 Multus CR 不存在,那么 Spiderpool 将无法为 Pod 指定 Multus CR。当 multusName 为空时,Spiderpool 对 Pod 所使用的 Multus CR 不作限制。
- spec.ips: 该字段的值必须设置。由于公有云限制了节点可使用的 IP 地址,故该值的范围必须在 nodeName 对应主机的辅助私网 IP 范围内,您可以从阿里云的弹性网卡界面获取。
依据如上所述,使用如下的 Yaml,为每个节点的每张网卡 ( eth0、eth1 ) 分别创建了一个 SpiderIPPool,它们将为不同节点上的 Pod 提供 IP 地址。
~# cat <<EOF | kubectl apply -f -
apiVersion: spiderpool.spidernet.io/v2beta1
kind: SpiderIPPool
metadata:
name: master-172
spec:
default: true
ips:
- 172.31.199.185-172.31.199.189
subnet: 172.31.192.0/20
gateway: 172.31.207.253
nodeName:
- master
multusName:
- kube-system/ipvlan-eth0
---
apiVersion: spiderpool.spidernet.io/v2beta1
kind: SpiderIPPool
metadata:
name: master-192
spec:
default: true
ips:
- 192.168.0.156-192.168.0.160
subnet: 192.168.0.0/24
gateway: 192.168.0.253
nodeName:
- master
multusName:
- kube-system/ipvlan-eth1
---
apiVersion: spiderpool.spidernet.io/v2beta1
kind: SpiderIPPool
metadata:
name: worker-172
spec:
default: true
ips:
- 172.31.199.190-172.31.199.194
subnet: 172.31.192.0/20
gateway: 172.31.207.253
nodeName:
- worker
multusName:
- kube-system/ipvlan-eth0
---
apiVersion: spiderpool.spidernet.io/v2beta1
kind: SpiderIPPool
metadata:
name: worker-192
spec:
default: true
ips:
- 192.168.0.161-192.168.0.165
subnet: 192.168.0.0/24
gateway: 192.168.0.253
nodeName:
- worker
multusName:
- kube-system/ipvlan-eth1
EOF
创建应用
以下的示例 Yaml 中,会创建 2 组 daemonSet 应用和 1 个 service ,其中:
- v1.multus-cni.io/default-network:用于指定应用所使用的子网,示例中的应用分别使用了不同的子网。
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app: test-app-1
name: test-app-1
namespace: default
spec:
selector:
matchLabels:
app: test-app-1
template:
metadata:
labels:
app: test-app-1
annotations:
v1.multus-cni.io/default-network: kube-system/ipvlan-eth0
spec:
containers:
- image: busybox
command: ["sleep", "3600"]
imagePullPolicy: IfNotPresent
name: test-app-1
ports:
- name: http
containerPort: 80
protocol: TCP
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app: test-app-2
name: test-app-2
namespace: default
spec:
selector:
matchLabels:
app: test-app-2
template:
metadata:
labels:
app: test-app-2
annotations:
v1.multus-cni.io/default-network: kube-system/ipvlan-eth1
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: test-app-2
ports:
- name: http
containerPort: 80
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: test-svc
labels:
app: test-app-2
spec:
type: ClusterIP
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: test-app-2
EOF
Spiderpool 自动为应用分配 IP 地址,应用的 IP 均在期望的 IP 池内:
~# kubectl get spiderippool
NAME VERSION SUBNET ALLOCATED-IP-COUNT TOTAL-IP-COUNT DEFAULT
master-172 4 172.31.192.0/20 1 5 true
master-192 4 192.168.0.0/24 1 5 true
worker-172 4 172.31.192.0/20 1 5 true
worker-192 4 192.168.0.0/24 1 5 true
测试连通性
测试 Pod 与宿主机的通讯情况:
~# kubectl get nodes -owide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
master Ready control-plane 2d12h v1.27.3 172.31.199.183 <none> CentOS Linux 7 (Core) 6.4.0-1.el7.elrepo.x86_64 containerd://1.7.1
worker Ready <none> 2d12h v1.27.3 172.31.199.184 <none> CentOS Linux 7 (Core) 6.4.0-1.el7.elrepo.x86_64 containerd://1.7.1
~# kubectl exec -ti test-app-1-b7765b8d8-422sb -- ping 172.31.199.183 -c 2
PING 172.31.199.183 (172.31.199.183): 56 data bytes
64 bytes from 172.31.199.183: seq=0 ttl=64 time=0.088 ms
64 bytes from 172.31.199.183: seq=1 ttl=64 time=0.054 ms
--- 172.31.199.183 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.054/0.071/0.088 ms
测试 Pod 与跨节点、跨子网 Pod 的通讯情况:
~# kubectl exec -ti test-app-1-b7765b8d8-422sb -- ping 172.31.199.193 -c 2
PING 172.31.199.193 (172.31.199.193): 56 data bytes
64 bytes from 172.31.199.193: seq=0 ttl=64 time=0.460 ms
64 bytes from 172.31.199.193: seq=1 ttl=64 time=0.210 ms
--- 172.31.199.193 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.210/0.335/0.460 ms
~# kubectl exec -ti test-app-1-b7765b8d8-422sb -- ping 192.168.0.161 -c 2
PING 192.168.0.161 (192.168.0.161): 56 data bytes
64 bytes from 192.168.0.161: seq=0 ttl=64 time=0.408 ms
64 bytes from 192.168.0.161: seq=1 ttl=64 time=0.194 ms
--- 192.168.0.161 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.194/0.301/0.408 ms
测试 Pod 与 ClusterIP 的通讯情况:
~# kubectl get svc test-svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
test-svc ClusterIP 10.233.23.194 <none> 80/TCP 26s
~# kubectl exec -ti test-app-2-7c56876fc6-7brhf -- curl 10.233.23.194 -I
HTTP/1.1 200 OK
Server: nginx/1.10.1
Date: Fri, 21 Jul 2023 06:45:56 GMT
Content-Type: text/html
Content-Length: 4086
Last-Modified: Fri, 21 Jul 2023 06:38:41 GMT
Connection: keep-alive
ETag: "64ba27f1-ff6"
Accept-Ranges: bytes
04
Spiderpool v0.6.0 新增功能
Spiderpool 在 v0.6.0 (github.com/spidernet-i…) 中新增如下功能:
-
Spiderpool CR 中新增 nodeName、multusName 字段,用于支持节点拓扑,能按需配置网络。
-
Spiderpool 提供了 SpiderMultusConfig CR ,它能简化书写 JSON 格式的 Multus CNI 配置,自动管理 Multus NetworkAttachmentDefinition CR。
-
Spiderpool 提供了 coordinator 插件,解决 Underlay Pod 无法访问 ClusterIP 、调谐 Pod 的路由、检测 Pod 的 IP 是否冲突、Pod 的网关是否可达等。参考 coordinator 文档 (github.com/spidernet-i…)
-
IPVlan 的深度支持,适用于任何公有云环境。
-
支持多个默认 IP 池,简化使用成本。
-
增加 CNI 插件 ifacer,用于自动创建子接口,参考 ifacer 文档(github.com/spidernet-i…
-
支持通过 Pod 注解指定默认网卡。
-
支持自动池的回收开关支持,可以自定义自动池是否删除。
-
集群子网弹性 IP 的支持,可以很好地解决应用在滚动更新时,旧 Pod 未删除,新 Pod 没有可用 IP 的问题。
此外,Spiderpool Underlay 网络解决方案也完美支持网络出入口流量访问。您可以参考 Spiderpool 出入口流量示例 (github.com/spidernet-i…
05
总结
Spiderpool 能够运行在任意的公有云环境中,在多云、混合云的场景下提供了一种统一的 Underlay 网络解决方案。
本文作者
杨涛
现任「DaoCloud 道客」云原生网络测试工程师