0%

【Kubernetes】网络连通性基础测试

本文介绍采用了 kubernetes 官方自带的 e2e 测试框架,对于 kubernetes 集群中的网络连通性进行了全面测试的原理与方法。关于 kubernetes e2e test framework,可以参考 Kubernetes End-to-end Testing for Everyone,此处不再详述。

测试原理

k8s 中网络连通性的 e2e 测试主要包括以下几个方面:

  • Pod 与 Pod 互通
  • Pod 与 Node 互通
  • Pod 与 Service 互通

下面分别对这几个项目详细介绍。

Pod 与 Pod 互通

为了测试网络方案的全面可靠,首先需要进行的测试是 Pod 与 Pod 间的网络互通,包括 HTTP 协议、UDP 协议的测试。

  • 在集群的每个节点创建一个 netserver 的 Pod 作为 webserver 暴露 TCP 8080 端口和 UDP 8081 端口
  • 创建一个 test Pod 作为 client 连接 netserver 的 TCP 或 UDP 端口,检查端口访问连通性
  • testPod 直接通过 netserver Pod 的 Pod IP 来访问

下面是 e2e 测试代码:

1
2
3
4
framework.ConformanceIt("should function for intra-pod communication: http [NodeConformance]", func() {
config := e2enetwork.NewCoreNetworkingTestConfig(f, false)
checkPodToPodConnectivity(config, "http", e2enetwork.EndpointHTTPPort)
})

Pod 与 Node 互通

  • 在集群的每个节点创建一个 netserver 的 Pod 作为 webserver 暴露 TCP 8080 端口和 UDP 8081 端口
  • 创建一个 test Pod 作为 client 连接 netserver 的 TCP 或 UDP 端口,检查端口访问连通性
  • testPod 直接通过 netserver Pod 的 Pod IP 来访问
  • 这里的 testPod 采用 hostNetworking 网络模式
1
2
3
4
5
6
7
8
9
framework.ConformanceIt("should function for node-pod communication: http [LinuxOnly] [NodeConformance]", func() {
config := e2enetwork.NewCoreNetworkingTestConfig(f, true)
for _, endpointPod := range config.EndpointPods {
err := config.DialFromNode("http", endpointPod.Status.PodIP, e2enetwork.EndpointHTTPPort, config.MaxTries, 0, sets.NewString(endpointPod.Name))
if err != nil {
framework.Failf("Error dialing HTTP node to pod %v", err)
}
}
})

Pod 与 Service 互通

pod-Service

  • 在集群的每个节点创建一个 netserver 的 Pod 作为 webserver 暴露 TCP 8080 端口和 UDP 8081 端口
  • 创建一个 test Pod 作为 client 连接 netserver 的 TCP 或 UDP 端口,检查端口访问连通性
  • 创建一个 NodePortService, 暴露 TCP 8080 端口和 UDP 8081 端口
  • testPod 直接通过 NodePort Service 的 ClusterIP 来访问 netserver 检查端口访问连通性
  • testPod 直接通过 NodePort Service 的 NodeIP 来访问 netserver 检查端口访问连通性

node-Service

  • 在集群的每个节点创建一个 netserver 的 Pod 作为 webserver 暴露 TCP 8080 端口和 UDP 8081 端口
  • 创建一个 test Pod 作为 client 连接 netserver 的 TCP 或 UDP 端口,检查端口访问连通性
  • 创建一个 NodePortService, 暴露 TCP 8080 端口和 UDP 8081 端口
  • testPod 直接通过 NodePort Service 的 ClusterIP 来访问 netserver 检查端口访问连通性
  • testPod 直接通过 NodePort Service 的 NodeIP 来访问 netserver 检查端口访问连通性
  • 这里的 testPod 采用 hostNetworking 网络模式

endpoint-Service

  • 在集群的每个节点创建一个 netserver 的 Endpoint Pod 作为 webserver 暴露 TCP 8080 端口和 UDP 8081 端口
  • 创建一个 NodePortService, 暴露 TCP 8080 端口和 UDP 8081 端口
  • 在 Endpoint Pod 直接通过 NodePort Service 的 ClusterIP 来访问 netserver 检查端口访问连通性
  • 在 Endpoint Pod 直接通过 NodePort Service 的 NodeIP 来访问 netserver 检查端口访问连通性

Multi-Endpoint Service with same selector

  • 在集群的每个节点创建一个 netserver 的 Pod 作为 webserver 暴露 TCP 8080 端口和 UDP 8081 端口
  • 创建一个 NodePortService, 暴露 TCP 8080 端口和 UDP 8081 端口
  • 创建第二个 NodePortService,与第一个 NodePortService 采用相同的 selector
  • 在 Endpoint Pod 直接通过 NodePort Service 的 ClusterIP 来访问 netserver 检查端口访问连通性
  • 在 Endpoint Pod 直接通过 NodePort Service 的 NodeIP 来访问 netserver 检查端口访问连通性
  • 测试两个 service 同时存在的时候可以访问连通,只存在一个的时候也都可以访问连通

Update Endpoint

  • 在集群的每个节点创建一个 netserver 的 Pod 作为 webserver 暴露 TCP 8080 端口和 UDP 8081 端口
  • 创建一个 test Pod 作为 client 连接 netserver 的 TCP 或 UDP 端口,检查端口访问连通性
  • 创建一个 NodePortService, 暴露 TCP 8080 端口和 UDP 8081 端口
  • testPod 直接通过 NodePort Service 的 ClusterIP 来访问 netserver 检查端口访问连通性
  • 删除 netserver 的一个 EndPoint 后,等待 kube-proxy 同步
  • testPod 再次通过 NodePort Service 的 ClusterIP 来访问 netserver 检查端口访问连通性

NodePort UDP

  • 在集群的每个节点创建一个 netserver 的 Pod 作为 webserver 暴露 TCP 8080 端口和 UDP 8081 端口
  • 创建一个 test Pod 作为 client 连接 netserver 的 TCP 或 UDP 端口,检查端口访问连通性
  • 这里的 testPod 采用 hostNetworking 网络模式
  • 创建一个 NodePortService, 暴露 TCP 8080 端口和 UDP 8081 端口
  • testPod 直接通过 NodePort Service 的 NodeIP 来访问 netserver 检查端口访问连通性

Update NodePort

  • 在集群的每个节点创建一个 netserver 的 Pod 作为 webserver 暴露 TCP 8080 端口和 UDP 8081 端口
  • 创建一个 test Pod 作为 client 连接 netserver 的 TCP 或 UDP 端口,检查端口访问连通性
  • 这里的 testPod 采用 hostNetworking 网络模式
  • 创建一个 NodePortService, 暴露 TCP 8080 端口和 UDP 8081 端口
  • testPod 直接通过 NodePort Service 的 NodeIP 来访问 netserver 检查端口访问连通性
  • 删除 NodePort Service 后,等待 kube-proxy 同步
  • testPod 再次通过 NodePort Service 的 ClusterIP 来访问 netserver 检查端口访问连通性,此时应当不再连通

SessionAffinity Service

  • 在集群的每个节点创建一个 netserver 的 Pod 作为 webserver 暴露 TCP 8080 端口和 UDP 8081 端口
  • 创建一个 NodePortService, 暴露 TCP 8080 端口和 UDP 8081 端口,设置该 Service 的 SessionAffinity 为 ClusterIP
  • 在 Endpoint Pod 通过 NodePort Service 的 ClusterIP 来访问 netserver 检查端口访问连通性

Large Request

  • 在集群的每个节点创建一个 netserver 的 Pod 作为 webserver 暴露 TCP 8080 端口和 UDP 8081 端口
  • 创建一个 test Pod 作为 client 连接 netserver 的 TCP 或 UDP 端口,检查端口访问连通性
  • 创建一个 NodePortService, 暴露 TCP 8080 端口和 UDP 8081 端口
  • testPod 直接通过 NodePort Service 的 ClusterIP 来访问 netserver 检查端口访问连通性
  • 这里访问连通性的是否会传递大消息,大小为 2 KBytes

HostNetwork

  • 在集群的每个节点创建一个 netserver 的EndPoint Pod 作为 webserver 暴露 TCP 8080 端口和 UDP 8081 端口,这里 EndPoint Pod 采用 HostNetwork 网络模式
  • 创建一个 test Pod 作为 client 连接 netserver 的 TCP 或 UDP 端口,检查端口访问连通性
  • 创建一个 NodePortService, 暴露 TCP 8080 端口和 UDP 8081 端口
  • testPod 直接通过 NodePort Service 的 ClusterIP 来访问 netserver 检查端口访问连通性
  • testPod 直接通过 NodePort Service 的 NodeIP 来访问 netserver 检查端口访问连通性

测试方法

基本方法

下载 k8s 代码,并checkout到指定分支,这里以 release-1.20 为例

1
2
git clone https://github.com/kubernetes/kubernetes.git
git checkout -b release-1.20 origin/release-1.20

build e2e测试 manifest

1
hack/generate-bindata.sh

生成 e2e.test 二进制

1
cd test/e2e && GOOS=linux GOARCH=amd64 go test -c -o e2e.test -v

登陆到待测试k8s集群节点,上传e2e.test到指定节点,配置e2e.test需要的镜像仓库列表

1
2
3
4
export KUBE_TEST_REPO_LIST=/home/ubuntu/e2e/repos.yaml //导出环境变量
cat > repos.yaml <<EOF
promoterE2eRegistry: ccr.ccs.tencentyun.com/e2e-test-images
EOF

执行e2e测试,这里以 Networking 为例

1
./e2e.test -ginkgo.focus=Networking --disable-log-dump --provider="skeleton" --kubeconfig="/root/.kube/config" > >(tee e2e.log)

也可以使用 ginkgo

1
./ginkgo --focus="Networking" ./e2e.test -- --disable-log-dump --provider="skeleton" --kubeconfig="/root/.kube/config" > >(tee e2e.log)

使用技巧

  1. 根据使用的 k8s 版本不同,e2e 可能依赖的部分 image 来自于 gcr,在国内的主机可能因为防火墙拉不下来,要手动拉取。
1
2
3
4
5
ccr.ccs.tencentyun.com/e2e-test-images/agnhost      2.21                              a6487be0b50a        11 months ago       114MB
k8s.gcr.io/sig-storage/csi-provisioner v1.6.0 a2ac6956643e 13 months ago 48.3MB
k8s.gcr.io/sig-storage/mock-driver v3.1.0 2098faf8b810 14 months ago 18.7MB
k8s.gcr.io/pause 3.2 80d28bedfe5d 15 months ago 683kB
gcr.io/kubernetes-e2e-test-images/jessie-dnsutils 1.0 1aeaefdd1a62 2 years ago 196MB
  1. focus=Networking 中主要的测试case来自于 kubernetes/test/e2e/network/networking.go,其中一项测试是测试集群连通外部网络的连通性,代码中通过访问 google.com 来测试。在国内,因为防火墙的因素可能无法访问导致此项测试不通过,可以修改此处代码为 baidu.com 或者其他来修复。

  1. focus 语法实际上是通过正则表达式来匹配要进行测试的case,只是指定 Networking 可能测试的case比较多,可以进行更加细粒度的规则。比如:
1
./e2e.test -ginkgo.focus="Networking Granular Checks: Services should function for node-Service: udp" --disable-log-dump --provider="skeleton" --kubeconfig="/root/.kube/config" > >(tee e2e.networking.log3)

这里的focus中的语法来自于 ginkgo 中 Describe 语法。一般跑完一次测试时间相对较长,在跑完一遍完整测试后,可以通过这种细粒度的语法单独对没通过的case进行再测试与分析。

  1. 基本的连通性只需要 focus 在 Networking 即可,如果想要进行更详细的测试,比如 NetworkPolicy、DNS、双栈或者其他测试,可以再单独测试,下面列出了 k8s 1.18 中所有会测试到的case。对于后续版本可能会有部分更新。
  2. 如果利用 k8s 1.18 跑 Networking 的测试,可能碰到 Requires at least 2 nodes (not 0) 的报错信息一直卡住,这是因为 k8s 的 bug,换成 k8s 1.20 就好了。

这里是在检查可调度的 Node 是否 < minNodeCount(这里设置的是2),但是 framework.TestContext.CloudConfig.NumNodes 为 0

1
2
3
4
5
6
// SkipUnlessNodeCountIsAtLeast skips if the number of nodes is less than the minNodeCount.
func SkipUnlessNodeCountIsAtLeast(minNodeCount int) {
if TestContext.CloudConfig.NumNodes < minNodeCount {
skipInternalf(1, "Requires at least %d nodes (not %d)", minNodeCount, TestContext.CloudConfig.NumNodes)
}
}

但是这个 TestContext.CloudConfig.NumNodes 并没有被真正赋值过,赋值的是 framework.TestContext.CloudConfig.NumNodes

查看 framework.TestContext.CloudConfig.NumNodes 在哪里赋值的,通过 k8s client 查询集群实际可用的Node进行赋值

1
2
3
4
5
6
// If NumNodes is not specified then auto-detect how many are scheduleable and not tainted
if framework.TestContext.CloudConfig.NumNodes == framework.DefaultNumNodes {
nodes, err := e2enode.GetReadySchedulableNodes(c)
framework.ExpectNoError(err)
framework.TestContext.CloudConfig.NumNodes = len(nodes.Items)
}

换成 1.20 的版本:

1
2
3
4
5
6
// SkipUnlessNodeCountIsAtLeast skips if the number of nodes is less than the minNodeCount.
func SkipUnlessNodeCountIsAtLeast(minNodeCount int) {
if framework.TestContext.CloudConfig.NumNodes < minNodeCount {
skipInternalf(1, "Requires at least %d nodes (not %d)", minNodeCount, framework.TestContext.CloudConfig.NumNodes)
}
}

测试结果

这里基于腾讯云 TKE 全局路由网络方案进行了全面的 e2e 测试,测试详细数据如下:

测试功能 全局路由测试结果 tke1.18.4
should provide Internet connection for containers [Feature:Networking-IPv4] 支持
should provide Internet connection for containers [Feature:Networking-IPv6][Experimental][LinuxOnly] 不支持
should provide unchanging, static URL paths for kubernetes api services 支持
should check kube-proxy urls 支持
should function for pod-Service: http 支持
should function for pod-Service: udp 支持
should function for pod-Service: sctp [Feature:SCTPConnectivity][Disruptive] 不支持
should function for node-Service: http 支持
should function for node-Service: udp 支持
should function for endpoint-Service: http 支持
should function for endpoint-Service: udp 支持
should function for endpoint-Service: sctp [Feature:SCTPConnectivity][Disruptive] 不支持
should function for multiple endpoint-Services with same selector 支持
should update endpoints: http 支持
should update endpoints: udp 支持
should update nodePort: http [Slow] 支持
should update nodePort: udp [Slow] 支持
should support basic nodePort: udp functionality 支持
should function for client IP based session affinity: http [LinuxOnly] 支持
should function for client IP based session affinity: udp [LinuxOnly] 支持
should be able to handle large requests: http 支持
should be able to handle large requests: udp 支持
should function for pod-Service(hostNetwork): udp 支持
should function for service endpoints using hostNetwork 不支持
should recreate its iptables rules if they are deleted [Disruptive] 支持
should function for intra-pod communication: http [NodeConformance] 支持
should function for intra-pod communication: udp [NodeConformance] 支持
should function for intra-pod communication: sctp [LinuxOnly][Feature:SCTPConnectivity][Disruptive] 不支持
should function for node-pod communication: http [LinuxOnly] [NodeConformance] 支持
should function for node-pod communication: udp [LinuxOnly] [NodeConformance] 支持
  • 目前 TKE 尚未支持 Pod 间通信 SCTP 协议,所以可以看到关于 SCTP 的相关测试都不通过。
  • 目前 TKE 尚未支持 IPv4/ IPv6 双栈,所以关于 IPv6 的测试没有通过