当前位置:   article > 正文

Kubernetes 多集群网络解决方案 Submariner 中文入门指南

k8s ceipsecpsk怎么填

公众号关注 「奇妙的 Linux 世界」

设为「星标」,每天带你玩转 Linux !

5e62de61fe6d02e9fd80df3142bda1d7.png

Submariner 是一个完全开源的项目,可以帮助我们在不同的 Kubernetes 集群之间(无论是在本地还是云端)实现网络通信。Submariner 有以下功能:

  • 跨集群的 L3 连接

  • 跨集群的服务发现

  • Globalnet 支持 CIDR 重叠

  • 提供命令行工具 subctl 简化部署和管理

  • 兼容各种 CNI

1 Submariner 架构

6cb4cbd21329070f1005182baf1d5fcb.png

Submariner 由几个主要部分组成:

  • Broker: 本质上是两个用于交换集群信息的 CRD(Endpoint 和 Cluster),我们需要选择一个集群作为 Broker 集群,其他集群连接到 Broker 集群的 API Server 来交换集群信息:

    • Endpoint:包含了 Gateway Engine 建立集群间连接需要的信息,例如 Private IP 和 Public IP,NAT 端口等等。

    • Cluster:包含原始集群的静态信息,例如其 Service 和 Pod CIDR。

  • Gateway Engine:管理连接到其他集群的隧道。

  • Route Agent:负责将跨集群的流量路由到 active Gateway Node。

  • Service Discovery: 提供跨集群的 DNS 服务发现。

  • Globalnet(可选):处理具有重叠 CIDR 的集群互连。

  • Submariner Operator:负责在 Kubernetes 集群中安装 Submariner 组件,例如 Broker, Gateway Engine, Route Agent 等等。

1.1 Service Discovery

afc6b9fb08e2efedc0f38d7d8bcf7e30.pngSubmariner 中跨集群的 DNS 服务发现由以下两个组件基于 Kubernetes Multi-Cluster Service APIs 的规范来实现:

  • Lighthouse Agent:访问 Broker 集群的 API Server 与其他集群交换 ServiceImport 元数据信息。

    • 对于本地集群中已创建 ServiceExport 的每个 Service,Agent 创建相应的 ServiceImport 资源并将其导出到 Broker 以供其他集群使用。

    • 对于从其他集群导出到 Broker 中的 ServiceImport 资源,它会在本集群中创建它的副本。

  • Lighthouse DNS Server

    • Lighthouse DNS Server 根据 ServiceImport 资源进行 DNS 解析。

    • CoreDNS 配置为将 clusterset.local 域名的解析请求发往 Lighthouse DNS server。

MCS API 是 Kubernetes 社区定义的用于跨集群服务发现的规范,主要包含了 ServiceExport 和 ServiceImport 两个 CRD。

  • ServiceExport 定义了暴露(导出)到其他集群的 Service,由用户在要导出的 Service 所在的集群中创建,与 Service 的名字和 Namespace 一致。

  1. apiVersion: multicluster.k8s.io/v1alpha1
  2. kind: ServiceExport
  3. metadata:
  4.   name: my-svc
  5.   namespace: my-ns
  • ServiceImport:当一个服务被导出后,实现 MCS API 的控制器会在所有集群(包括导出服务的集群)中自动生成一个与之对应的 ServiceImport 资源。

  1. apiVersion: multicluster.k8s.io/v1alpha1
  2. kind: ServiceImport
  3. metadata:
  4.   name: my-svc
  5.   namespace: my-ns
  6. spec:
  7.   ips:
  8.   - 42.42.42.42 # 跨集群访问的 IP 地址
  9.   type"ClusterSetIP"
  10.   ports:
  11.   - name: http
  12.     protocol: TCP
  13.     port: 80
  14.   sessionAffinity: None

1.2 Gateway Engine

Gateway Engine 部署在每个集群中,负责建立到其他集群的隧道。隧道可以由以下方式实现:

  • IPsec,使用 Libreswan 实现。这是当前的默认设置。

  • WireGuard,使用 wgctrl 库实现。

  • VXLAN,不加密。

可以在使用 subctl join 命令加入集群的时候使用 --cable-driver 参数设置隧道的类型。

Gateway Engine 部署为 DaemonSet,只在有 submariner.io/gateway=true Label 的 Node 上运行,当我们使用 subctl join 命令加入集群的时候,如果没有 Node 有 Label,会提示我们选择一个 Node 作为 Gateway Node。

Submariner 也支持 active/passive 高可用模式的 Gateway Engine,我们可以在多个节点上部署 Gateway Engine。在同一时间内,只能有一个 Gateway Engine 处于 active 状态来处理跨集的流量,Gateway Engine 通过领导者选举的方式确定 active 的实例,其他实例在 passive 模式下等待,准备在 active 实例发生故障时接管。

f10c5e8aa43e34a44472e56b25a2df55.png

1.3 Globalnet

Submariner 的一大亮点是支持在不同集群间存在 CIDR 重叠的情况,这有助于减少网络重构的成本。例如,在部署过程中,某些集群可能使用了默认的网段,导致了 CIDR 重叠。在这种情况下,如果后续需要更改集群网段,可能会对集群的运行产生影响。

为了支持集群间重叠 CIDR 的情况,Submariner 通过一个 GlobalCIDR 网段(默认是 242.0.0.0/8)在流量进出集群时进行 NAT 转换,所有的地址转换都发生在 active Gateway Node 上。在 subctl deploy 部署 Broker 的时候可以通过 --globalnet-cidr-range 参数指定所有集群的全局 GlobalCIDR。在 subctl join 加入集群的时候还可以通过 --globalnet-cidr 参数指定该集群的 GlobalCIDR。

导出的 ClusterIP 类型的 Service 会从 GlobalCIDR 分配一个 Global IP 用于入向流量,对于 Headless 类型的 Service,会为每个关联的 Pod 分配一个 Global IP 用于入向和出向流量。

e3587f3cc801c86ebddd9bf2e98193b2.png

2 环境准备

在本次实验中,我们使用一台运行 Ubuntu 20.04 的虚拟机,并通过 Kind 启动多个 Kubernetes 集群来进行测试。

  1. root@seven-demo:~# seven-demo
  2.    Static hostname: seven-demo
  3.          Icon name: computer-vm
  4.            Chassis: vm
  5.         Machine ID: f780bfec3c409135b11d1ceac73e2293
  6.            Boot ID: e83e9a883800480f86d37189bdb09628
  7.     Virtualization: kvm
  8.   Operating System: Ubuntu 20.04.5 LTS
  9.             Kernel: Linux 5.15.0-1030-gcp
  10.       Architecture: x86-64

安装相关软件和命令行工具。

  1. # 安装 Docker,根据操作系统安装 https://docs.docker.com/engine/install/ 
  2. sudo apt-get update
  3. sudo apt-get install -y \
  4.     ca-certificates \
  5.     curl \
  6.     gnupg
  7. sudo mkdir -m 0755 -p /etc/apt/keyrings
  8. curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
  9. echo \
  10.   "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  11.   "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  12.   sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
  13. sudo apt-get update
  14. sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  15. # 安装 Kind
  16. curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.18.0/kind-linux-amd64
  17. chmod +x ./kind
  18. sudo mv ./kind /usr/local/bin/kind
  19. # 安装 kubectl
  20. curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
  21. chmod +x kubectl
  22. mv kubectl /usr/local/bin
  23. apt install -y bash-completion
  24. echo 'source <(kubectl completion bash)' >>~/.bashrc
  25. # 安装 subctl
  26. curl -Lo subctl-release-0.14-linux-amd64.tar.xz https://github.com/submariner-io/subctl/releases/download/subctl-release-0.14/subctl-release-0.14-linux-amd64.tar.xz
  27. tar -xf subctl-release-0.14-linux-amd64.tar.xz
  28. mv subctl-release-0.14/subctl-release-0.14-linux-amd64 /usr/local/bin/subctl
  29. chmod +x /usr/local/bin/subctl

3 快速开始

3.1 创建集群

使用 Kind 创建一个 3 节点的集群,这里读者需要将 SERVER_IP 替换成自己服务器的 IP。默认情况下,Kind 将 Kubernetes API 服务器 IP:Port 设置为本地环回地址 ( 127.0.0.1): 随机端口,这对于从本机与集群交互来说很好,但是在本实验中多个 Kind 集群之间需要通信,因此我们需要把 Kind 的 apiServerAddress 改成本机 IP。

  1. # 替换成服务器 IP
  2. export SERVER_IP="10.138.0.11"
  3. kind create cluster --config - <<EOF
  4. kind: Cluster
  5. name: broker
  6. apiVersion: kind.x-k8s.io/v1alpha4
  7. nodes:
  8. - role: control-plane
  9. networking:
  10.   apiServerAddress: $SERVER_IP
  11.   podSubnet: "10.7.0.0/16"
  12.   serviceSubnet: "10.77.0.0/16"
  13. EOF
  14. kind create cluster --config - <<EOF
  15. kind: Cluster
  16. name: c1
  17. apiVersion: kind.x-k8s.io/v1alpha4
  18. nodes:
  19. - role: control-plane
  20. - role: worker
  21. networking:
  22.   apiServerAddress: $SERVER_IP
  23.   podSubnet: "10.8.0.0/16"
  24.   serviceSubnet: "10.88.0.0/16"
  25. EOF
  26.  
  27. kind create cluster --config - <<EOF
  28. kind: Cluster
  29. name: c2
  30. apiVersion: kind.x-k8s.io/v1alpha4
  31. nodes:
  32. - role: control-plane
  33. - role: worker
  34. networking:
  35.   apiServerAddress: $SERVER_IP
  36.   podSubnet: "10.9.0.0/16"
  37.   serviceSubnet: "10.99.0.0/16"
  38. EOF

3.2 部署 Broker

在本次实验中,我们专门将一个集群配置为 Broker 集群。Broker 集群可以是专用集群,也可以是连接的集群之一。执行 subctl deploy-broker 命令部署 Broker,Broker 只包含了一组 CRD,并没有部署 Pod 或者 Service。

subctl --context kind-broker deploy-broker
0c3f88684e7e72a373336de996c4d0e3.png

部署完成后,会生成 broker-info.subm 文件,文件以 Base64 加密,其中包含了连接 Broker 集群 API Server 的地址以及证书信息,还有 IPsec 的密钥信息。

  1. {
  2.   "brokerURL""https://10.138.0.11:45681",
  3.   "ClientToken": {
  4.     "metadata": {
  5.       "name""submariner-k8s-broker-admin-token-f7b62",
  6.       "namespace""submariner-k8s-broker",
  7.       "uid""3f949d19-4f42-43d6-af1c-382b53f83d8a",
  8.       "resourceVersion""688",
  9.       "creationTimestamp""2023-04-05T02:50:02Z",
  10.       "annotations": {
  11.         "kubernetes.io/created-by""subctl",
  12.         "kubernetes.io/service-account.name""submariner-k8s-broker-admin",
  13.         "kubernetes.io/service-account.uid""da6eeba1-b707-4d30-8e1e-e414e9eae817"
  14.       },
  15.       "managedFields": [
  16.         {
  17.           "manager""kube-controller-manager",
  18.           "operation""Update",
  19.           "apiVersion""v1",
  20.           "time""2023-04-05T02:50:02Z",
  21.           "fieldsType""FieldsV1",
  22.           "fieldsV1": {
  23.             "f:data": {
  24.               ".": {},
  25.               "f:ca.crt": {},
  26.               "f:namespace": {},
  27.               "f:token": {}
  28.             },
  29.             "f:metadata": {
  30.               "f:annotations": {
  31.                 "f:kubernetes.io/service-account.uid": {}
  32.               }
  33.             }
  34.           }
  35.         },
  36.         {
  37.           "manager""subctl",
  38.           "operation""Update",
  39.           "apiVersion""v1",
  40.           "time""2023-04-05T02:50:02Z",
  41.           "fieldsType""FieldsV1",
  42.           "fieldsV1": {
  43.             "f:metadata": {
  44.               "f:annotations": {
  45.                 ".": {},
  46.                 "f:kubernetes.io/created-by": {},
  47.                 "f:kubernetes.io/service-account.name": {}
  48.               }
  49.             },
  50.             "f:type": {}
  51.           }
  52.         }
  53.       ]
  54.     },
  55.     "data": {
  56.       "ca.crt""LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJek1EUXdOVEF5TkRVMU1Wb1hEVE16TURRd01qQXlORFUxTVZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBSytXCmIzb0h1ZEJlbU5tSWFBNXQrWmI3TFhKNXRLWDB6QVc5a0tudjQzaGpoTE84NHlSaEpyY3ZSK29QVnNaUUJIclkKc01tRmx3aVltbU5ORzA4c2NLMTlyLzV0VkdFR2hCckdML3VKcTIybXZtYi80aHdwdmRTQjN0UDlkU2RzYUFyRwpYYllwOE4vUmlheUJvbTBJVy9aQjNvZ0MwK0tNcWM0NE1MYnBkZXViWnNSckErN2pwTElYczE3OGgxb25kdGNrClIrYlRnNGpjeS92NTkrbGJjamZSeTczbUllMm9DbVFIbE1XUFpSTkMveDhaTktGekl6UHc4SmZSOERjWk5Xc1YKa1NBUVNVUkpnTEhBbjY5MlhDSEsybmJuN21pcjYvYVZzVVpyTGdVNC9zcWg3QVFBdDFGQkk3NDRpcithTjVxSwpJRnRJenkxU3p2ZEpwMThza3EwQ0F3RUFBYU5aTUZjd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZFQUhFbndHditwTXNVcHVQRXNqbkQwTEgvSFpNQlVHQTFVZEVRUU8KTUF5Q0NtdDFZbVZ5Ym1WMFpYTXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQTFGckk1cGR1VTFsQzluVldNNwowYlc2VFRXdzYwUTlFVWdsRzc4bkRFZkNKb3ovY2xWclFNWGZrc2Zjc1VvcHZsaE5yWFlpbmd0UEE4aEMrTnRJCmdPZElDZDJGaWFOTjRCYkt3a1NmRkQvbmhjWDU1WmQ0UzN1SzZqb2JWVHIzaXVJRVhIdHg0WVIyS1ZuZitTMDUKQTFtbXdzSG1ZbkhtWEllOUEyL3hKdVhtSnNybWljWTlhMXhtSXVyYzhNalBsa1pZWVU1OFBvZHJFNi9XcnBaawpBbW9qcERIWWIrbnZxa0FuaG9hYUV3b2FEVGxYRjY0M3lVLy9MZE4wTmw5MWkvSHNwQ2tZdVFrQjJmQXNkSGNaCkMrdzQ4WVhYT21pSzZXcmJGYVJnaEVKdjB6UjdsZk50UEVZVWJHWEFxV0ZlSnFTdnM5aUYwbFV1NzZDNkt3YWIKbmdnPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==",
  57.       "namespace""c3VibWFyaW5lci1rOHMtYnJva2Vy",
  58.       "token""ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklqaHZWVnBuZVVoZk1uVTFjSEJxU1hOdE1UTk1NbUY0TFRaSlIyVlZVRGd4VjI1dmMyNXBNMjFYZFhjaWZRLmV5SnBjM01pT2lKcmRXSmxjbTVsZEdWekwzTmxjblpwWTJWaFkyTnZkVzUwSWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXVZVzFsYzNCaFkyVWlPaUp6ZFdKdFlYSnBibVZ5TFdzNGN5MWljbTlyWlhJaUxDSnJkV0psY201bGRHVnpMbWx2TDNObGNuWnBZMlZoWTJOdmRXNTBMM05sWTNKbGRDNXVZVzFsSWpvaWMzVmliV0Z5YVc1bGNpMXJPSE10WW5KdmEyVnlMV0ZrYldsdUxYUnZhMlZ1TFdZM1lqWXlJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5elpYSjJhV05sTFdGalkyOTFiblF1Ym1GdFpTSTZJbk4xWW0xaGNtbHVaWEl0YXpoekxXSnliMnRsY2kxaFpHMXBiaUlzSW10MVltVnlibVYwWlhNdWFXOHZjMlZ5ZG1salpXRmpZMjkxYm5RdmMyVnlkbWxqWlMxaFkyTnZkVzUwTG5WcFpDSTZJbVJoTm1WbFltRXhMV0kzTURjdE5HUXpNQzA0WlRGbExXVTBNVFJsT1dWaFpUZ3hOeUlzSW5OMVlpSTZJbk41YzNSbGJUcHpaWEoyYVdObFlXTmpiM1Z1ZERwemRXSnRZWEpwYm1WeUxXczRjeTFpY205clpYSTZjM1ZpYldGeWFXNWxjaTFyT0hNdFluSnZhMlZ5TFdGa2JXbHVJbjAub1JHM2d6Wno4MGVYQXk5YlZ5b1V2NmoyTERvdFJiNlJyOTF4d0ZiTDMwdFNJY3dnS3FYd3NZbVV1THhtcFdBb2M5LWRSMldHY0ZLYklORlZmUUttdVJMY2JsenlTUFFVMlB3WVVwN1oyNnlxYXFOMG1UQ3ZNWWxSeHp6cWY3LXlXUm8yNE9pWS1nMnNmNmNrRzRPMkdwa2MwTlNoOWRTUGY4dXJTbjZSVGJwbjFtcFZjTy1IQjJWeU5hTE9EdmtWS3RLVFJfVS1ZRGc1NzVtczM0OXM0X2xMZjljZjlvcjFaQXVvXzcyN0E5U0VvZ0JkN3BaSndwb0FEUHZRb1NGR0VLQWZYYTFXXzJWVE5PYXE4cUQxOENVbXVFRUFxMmtoNElBN0d5LVRGdUV2Q0JYUVlzRHYzUFJQTjZpOGlKSFBLVUN1WVNONS1NT3lGX19aNS1WdlhR"
  59.     },
  60.     "type""kubernetes.io/service-account-token"
  61.   },
  62.   "IPSecPSK": {
  63.     "metadata": {
  64.       "name""submariner-ipsec-psk",
  65.       "creationTimestamp": null
  66.     },
  67.     "data": {
  68.       "psk""NL7dUK+RagDKPQZZj+7Q7wComj0/wLFbfvnHe12hHxR8+d/FnkEqXfmh8JMzLo6h"
  69.     }
  70.   },
  71.   "ServiceDiscovery"true,
  72.   "Components": [
  73.     "service-discovery",
  74.     "connectivity"
  75.   ],
  76.   "CustomDomains": null
  77. }

3.3 c1, c2 加入集群

执行 subctl join 命令将 c1 和 c2 两个集群加入 Broker 集群。使用 --clusterid 参数指定集群 ID,每个集群 ID 需要唯一。提供上一步生成的 broker-info.subm 文件用于集群注册。

  1. subctl --context kind-c1 join broker-info.subm --clusterid c1
  2. subctl --context kind-c2 join broker-info.subm --clusterid c2

会提示我们选择一个节点作为 Gateway Node,c1 集群选择 c1-worker 节点作为 Gateway,c2 集群选择 c2-worker 节点作为 Gateway。

df8048f81927e9388c54e6a7af8a4d28.png 78298f9a249d51e0f8a6fd77ab60c272.png

两个 Gateway Node 的 IP 地址如下,之后会分别使用这两个地址在两个集群间建立隧道连接。

b2b5ebfe283e7939991299ceea6ee449.png

3.4 查看集群连接

等待 c1 和 c2 集群中 Submariner 的相关组件都运行成功后,执行以下命令查看集群间连接情况。

  1. subctl show connections --context kind-c1
  2. subctl show connections --context kind-c2

可以看到 c1 和 c2 集群分别和对方建立的连接。

5e71ab72bea37cf9a090ec99978cb530.png

查看 c1 gateway 日志,可以看到成功与 c2 集群建立了 IPsec 隧道。

8eedc2ec8e5a2ca177ec101b462c99d9.png

3.5 测试跨集群通信

至此,我们已经成功在 c1 和 c2 集群间建立了跨集群的连接,接下来我们将创建服务并演示如何将其导出给其他集群进行访问。

在下面的示例中,我们在 sample Namespace 中创建相关资源。请注意,必须在两个集群中都创建 sample 命名空间,服务发现才能正常工作。

  1. kubectl --context kind-c2 create namespace sample
  2. # 需要确保 c1 集群上也有 sample Namespace,否则 Lighthouse agent 创建 Endpointslices 会失败
  3. kubectl --context kind-c1 create namespace sample
3.5.1 ClusterIP Service

首先测试 ClusterIP 类型的 Service。执行以下命令在 c2 集群创建服务。本实验中 whereami 是一个用 Golang 编写的 HTTP Server,它通过 Downward API 将 Kubernetes 的相关信息(Pod 名字,Pod 所在的 Namespace,Node)注入到容器的环境变量中,当接收到请求时进行输出。另外 whereami 还会打印请求方的 IP 地址和端口信息。

  1. kubectl --context kind-c2 apply -f - <<EOF
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5.   name: whereami
  6.   namespace: sample
  7. spec:
  8.   replicas: 3
  9.   selector:
  10.     matchLabels:
  11.       app: whereami
  12.   template:
  13.     metadata:
  14.       labels:
  15.         app: whereami
  16.     spec:
  17.       containers:
  18.       - name: whereami
  19.         image: cr7258/whereami:v1
  20.         imagePullPolicy: Always
  21.         ports:
  22.         - containerPort: 80
  23.         env:
  24.         - name: NAMESPACE
  25.           valueFrom:
  26.             fieldRef:
  27.               fieldPath: metadata.namespace
  28.         - name: NODE_NAME
  29.           valueFrom:
  30.             fieldRef:
  31.               fieldPath: spec.nodeName
  32.         - name: POD_NAME
  33.           valueFrom:
  34.             fieldRef:
  35.               fieldPath: metadata.name
  36.         - name: POD_IP
  37.           valueFrom:
  38.             fieldRef:
  39.               fieldPath: status.podIP
  40. ---
  41. apiVersion: v1
  42. kind: Service
  43. metadata:
  44.   name: whereami-cs
  45.   namespace: sample
  46. spec:
  47.   selector:
  48.     app: whereami
  49.   ports:
  50.     - protocol: TCP
  51.       port: 80
  52.       targetPort: 80
  53. EOF

在 c2 集群查看服务。

  1. root@seven-demo:~# kubectl --context kind-c2 get pod -n sample -o wide
  2. NAME                        READY   STATUS    RESTARTS   AGE   IP          NODE        NOMINATED NODE   READINESS GATES
  3. whereami-754776cdc9-28kgd   1/1     Running   0          19h   10.9.1.18   c2-control-plane   <none>           <none>
  4. whereami-754776cdc9-8ccmc   1/1     Running   0          19h   10.9.1.17   c2-control-plane   <none>           <none>
  5. whereami-754776cdc9-dlp55   1/1     Running   0          19h   10.9.1.16   c2-control-plane   <none>           <none>
  6. root@seven-demo:~# kubectl --context kind-c2 get svc -n sample -o wide
  7. NAME          TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE   SELECTOR
  8. whereami-cs   ClusterIP   10.99.2.201   <none>        80/TCP    19h   app=whereami

在 c2 集群中使用 subctl export 命令将服务导出。

subctl --context kind-c2 export service --namespace sample whereami-cs

该命令会在创建一个和 Service 相同名字和 Namespace 的 ServiceExport 资源。

  1. root@seven-demo:~# kubectl  get serviceexports --context kind-c2 -n sample whereami-cs -o yaml
  2. apiVersion: multicluster.x-k8s.io/v1alpha1
  3. kind: ServiceExport
  4. metadata:
  5.   creationTimestamp: "2023-04-06T13:04:15Z"
  6.   generation: 1
  7.   name: whereami-cs
  8.   namespace: sample
  9.   resourceVersion: "327707"
  10.   uid: d1da8953-3fa5-4635-a8bb-6de4cd3c45a9
  11. status:
  12.   conditions:
  13.   - lastTransitionTime: "2023-04-06T13:04:15Z"
  14.     message: ""
  15.     reason: ""
  16.     status: "True"
  17.     type: Valid
  18.   - lastTransitionTime: "2023-04-06T13:04:15Z"
  19.     message: ServiceImport was successfully synced to the broker
  20.     reason: ""
  21.     status: "True"
  22.     type: Synced

ServiceImport 资源会由 Submariner 自动在 c1,c2 集群中创建,IP 地址是 Service 的 ClusterIP 地址。

  1. kubectl --context kind-c1 get -n submariner-operator serviceimport
  2. kubectl --context kind-c2 get -n submariner-operator serviceimport
45393fdaa8bfb2cecf0f7e20cf6cc4ae.png

在 c1 集群创建一个 client Pod 来访问 c2 集群的 whereami 服务。

  1. kubectl --context kind-c1 run client --image=cr7258/nettool:v1
  2. kubectl --context kind-c1 exec -it client -- bash

先尝试下 DNS 解析,ClusterIP Service 类型的 Service 可以通过以下格式进行访问 <svc-name>.<namespace>.svc.clusterset.local

nslookup whereami-cs.sample.svc.clusterset.local

返回的 IP 是在 c2 集群 Service 的 ClusterIP 的地址。

e3b32b12bd7a76659c0f0589b1038160.png

我们查看一下 CoreDNS 的配置文件,这个 Configmap 会被 Submariner Operator 修改,将 clusterset.local 用于跨集群通信的域名交给 Lighthouse DNS 来解析。

  1. root@seven-demo:~# kubectl get cm -n kube-system coredns -oyaml
  2. apiVersion: v1
  3. data:
  4.   Corefile: |+
  5.     #lighthouse-start AUTO-GENERATED SECTION. DO NOT EDIT
  6.     clusterset.local:53 {
  7.         forward . 10.88.78.89
  8.     }
  9.     #lighthouse-end
  10.     .:53 {
  11.         errors
  12.         health {
  13.            lameduck 5s
  14.         }
  15.         ready
  16.         kubernetes cluster.local in-addr.arpa ip6.arpa {
  17.            pods insecure
  18.            fallthrough in-addr.arpa ip6.arpa
  19.            ttl 30
  20.         }
  21.         prometheus :9153
  22.         forward . /etc/resolv.conf {
  23.            max_concurrent 1000
  24.         }
  25.         cache 30
  26.         loop
  27.         reload
  28.         loadbalance
  29.     }
  30. kind: ConfigMap
  31. metadata:
  32.   creationTimestamp: "2023-04-05T02:47:34Z"
  33.   name: coredns
  34.   namespace: kube-system
  35.   resourceVersion: "1211"
  36.   uid: 698f20a5-83ea-4a3e-8a1e-8b9438a6b3f8

Submariner 遵循以下逻辑来进行跨集群集的服务发现:

  • 如果导出的服务在本地集群中不可用,Lighthouse DNS 从服务导出的远程集群之一返回 ClusterIP 服务的 IP 地址。

  • 如果导出的服务在本地集群中可用,Lighthouse DNS 总是返回本地 ClusterIP 服务的 IP 地址。

  • 如果多个集群从同一个命名空间导出具有相同名称的服务,Lighthouse DNS 会以轮询的方式在集群之间进行负载均衡。

  • 可以在 DNS 查询前加上 cluster-id 前缀来访问特定集群的服务,<cluster-id>.<svc-name>.<namespace>.svc.clusterset.local

027e2fedd865468d52e89b92d2bc852c.png

通过 curl 命令发起 HTTP 请求。

curl whereami-cs.sample.svc.clusterset.local

返回结果如下,我们根据输出的 node_name 字段可以确认该 Pod 是在 c2 集群。

0bd2b544239776f9b2880f07694f2158.png

这里结合下图对流量进行简单的说明:流量从 c1 集群的 client Pod 发出,首先经过 veth-pair 到达 Node 的 Root Network Namespace,然后经过 Submariner Route Agent 设置的 vx-submariner 这个 VXLAN 隧道将流量发往 Gateway Node 上(c1-worker)。接着经过连接 c1 和 c2 集群的 IPsec 隧道到达对端,c2 集群的 Gateway Node(c2-worker)接收到流量后将,经过 iptables 的反向代理规则(在这过程中根据 ClusterIP 进行了 DNAT)最终发送到后端的 whereami Pod 上。

2a5b3865f2d2a313aff3e57adeefb663.png

接下来我们在 c1 集群也创建相同的服务。

  1. kubectl --context kind-c1 apply -f - <<EOF
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5.   name: whereami
  6.   namespace: sample
  7. spec:
  8.   replicas: 3
  9.   selector:
  10.     matchLabels:
  11.       app: whereami
  12.   template:
  13.     metadata:
  14.       labels:
  15.         app: whereami
  16.     spec:
  17.       containers:
  18.       - name: whereami
  19.         image: cr7258/whereami:v1
  20.         imagePullPolicy: Always
  21.         ports:
  22.         - containerPort: 80
  23.         env:
  24.         - name: NAMESPACE
  25.           valueFrom:
  26.             fieldRef:
  27.               fieldPath: metadata.namespace
  28.         - name: NODE_NAME
  29.           valueFrom:
  30.             fieldRef:
  31.               fieldPath: spec.nodeName
  32.         - name: POD_NAME
  33.           valueFrom:
  34.             fieldRef:
  35.               fieldPath: metadata.name
  36.         - name: POD_IP
  37.           valueFrom:
  38.             fieldRef:
  39.               fieldPath: status.podIP
  40. ---
  41. apiVersion: v1
  42. kind: Service
  43. metadata:
  44.   name: whereami-cs
  45.   namespace: sample
  46. spec:
  47.   selector:
  48.     app: whereami
  49.   ports:
  50.     - protocol: TCP
  51.       port: 80
  52.       targetPort: 80
  53. EOF

在 c1 集群上查看服务。

  1. root@seven-demo:~# kubectl --context kind-c1 get pod -n sample -o wide
  2. NAME                        READY   STATUS    RESTARTS   AGE   IP          NODE        NOMINATED NODE   READINESS GATES
  3. whereami-754776cdc9-hq4m2   1/1     Running   0          45s   10.8.1.25   c1-worker   <none>           <none>
  4. whereami-754776cdc9-rt84w   1/1     Running   0          45s   10.8.1.23   c1-worker   <none>           <none>
  5. whereami-754776cdc9-v5zrk   1/1     Running   0          45s   10.8.1.24   c1-worker   <none>           <none>
  6. root@seven-demo:~# kubectl --context kind-c1 get svc -n sample
  7. NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
  8. whereami-cs   ClusterIP   10.88.132.102   <none>        80/TCP    50s

在 c1 集群导出服务。

subctl --context kind-c1 export service --namespace sample whereami-cs

查看 ServiceImport。

  1. kubectl --context kind-c1 get -n submariner-operator serviceimport
  2. kubectl --context kind-c2 get -n submariner-operator serviceimport
262fa946f26a0c6e1e1115a3f8769225.png

由于在 c1 集群本地也有相同的服务,因此这次请求将会发给 c1 集群的服务。

1c561f231d3acd9ad7220747e013bf99.png
  1. kubectl --context kind-c1 exec -it client -- bash
  2. nslookup whereami-cs.sample.svc.clusterset.local
346ea10f6bbc433f5e99c3445a01692c.png
curl whereami-cs.sample.svc.clusterset.local
3237e12e8b7a7e199e31b308a87c825c.png

我们也可以通过 <cluster-id>.<svc-name>.<namespace>.svc.clusterset.local 来指定访问访问特定集群的 ClusterIP Service。例如我们指定访问 c2 集群的 Service。

curl c2.whereami-cs.sample.svc.clusterset.local
d246bded1975833da6702b6f06a68662.png
3.5.2 Headless Service + StatefulSet

Submariner 还支持带有 StatefulSets 的 Headless Services,从而可以通过稳定的 DNS 名称访问各个 Pod。在单个集群中,Kubernetes 通过引入稳定的 Pod ID 来支持这一点,在单个集群中可以通过<pod-name>.<svc-name>.<ns>.svc.cluster.local格式来解析域名。跨集群场景下,Submariner 通过 <pod-name>.<cluster-id>.<svc-name>.<ns>.svc.clusterset.local 的格式来解析域名。

在 c2 集群创建 Headless Service 和 StatefulSet。

  1. kubectl --context kind-c2 apply -f - <<EOF
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5.  name: whereami-ss
  6.  namespace: sample
  7.  labels:
  8.    app: whereami-ss
  9. spec:
  10.  ports:
  11.  - port: 80
  12.    name: whereami
  13.  clusterIP: None
  14.  selector:
  15.    app: whereami-ss
  16. ---
  17. apiVersion: apps/v1
  18. kind: StatefulSet
  19. metadata:
  20.  name: whereami
  21.  namespace: sample
  22. spec:
  23.  serviceName: "whereami-ss"
  24.  replicas: 3
  25.  selector:
  26.    matchLabels:
  27.        app: whereami-ss
  28.  template:
  29.    metadata:
  30.      labels:
  31.        app: whereami-ss
  32.    spec:
  33.      containers:
  34.      - name: whereami-ss
  35.        image: cr7258/whereami:v1
  36.        ports:
  37.        - containerPort: 80
  38.          name: whereami
  39.        env:
  40.        - name: NAMESPACE
  41.          valueFrom:
  42.            fieldRef:
  43.              fieldPath: metadata.namespace
  44.        - name: NODE_NAME
  45.          valueFrom:
  46.            fieldRef:
  47.              fieldPath: spec.nodeName
  48.        - name: POD_NAME
  49.          valueFrom:
  50.            fieldRef:
  51.              fieldPath: metadata.name
  52.        - name: POD_IP
  53.          valueFrom:
  54.           fieldRef:
  55.              fieldPath: status.podIP
  56. EOF

在 c2 集群查看服务。

  1. root@seven-demo:~# kubectl get pod -n sample --context kind-c2 -o wide -l app=whereami-ss
  2. NAME                        READY   STATUS    RESTARTS   AGE   IP          NODE        NOMINATED NODE   READINESS GATES
  3. whereami-0                  1/1     Running   0          38s   10.9.1.20   c2-control-plane   <none>           <none>
  4. whereami-1                  1/1     Running   0          36s   10.9.1.21   c2-control-plane    <none>           <none>
  5. whereami-2                  1/1     Running   0          31s   10.9.1.22   c2-control-plane    <none>           <none>
  6. root@seven-demo:~# kubectl get svc -n sample --context kind-c2 -l app=whereami-ss
  7. NAME          TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
  8. whereami-ss   ClusterIP   None          <none>        80/TCP    4m58s

在 c2 集群导出服务。

subctl --context kind-c2 export service whereami-ss --namespace sample

解析 Headless Service 的域名可以得到所有 Pod 的 IP。

  1. kubectl --context kind-c1 exec -it client -- bash
  2. nslookup whereami-ss.sample.svc.clusterset.local
3020638c1efaa1daf3a4ae1358b1c401.png

也可以指定单个 Pod 进行解析。

nslookup whereami-0.c2.whereami-ss.sample.svc.clusterset.local
f8e92454fcb013c5fc9643c7883050bd.png

通过域名访问指定的 Pod。

curl whereami-0.c2.whereami-ss.sample.svc.clusterset.local
11425698f8b58490e8a458105ae87473.png

查看 ServiceImport,在 IP 地址的一栏是空的,因为导出的服务类型是 Headless。

  1. kubectl --context kind-c1 get -n submariner-operator serviceimport
  2. kubectl --context kind-c2 get -n submariner-operator serviceimport
2e7f0f2c0f95d64c0e3b0ca49e1d7bcf.png

对于 Headless Service,Pod IP 是根据 Endpointslice 来解析的。

  1. kubectl --context kind-c1 get endpointslices -n sample
  2. kubectl --context kind-c2 get endpointslices -n sample
2d8b43b4ddec1b3ce955a9eecdd256f5.png

4 使用 Globalnet 解决 CIDR 重叠问题

接下来将演示如何通过 Submariner 的 Globalnet 功能来解决多集群间 CIDR 重叠的问题,在本实验中,我们将会创建 3 个集群,并且将每个集群的 Service 和 Pod CIDR 都设置成相同的。

4.1 创建集群

  1. # 替换成服务器 IP
  2. export SERVER_IP="10.138.0.11"
  3. kind create cluster --config - <<EOF
  4. kind: Cluster
  5. name: broker-globalnet
  6. apiVersion: kind.x-k8s.io/v1alpha4
  7. nodes:
  8. - role: control-plane
  9. networking:
  10.   apiServerAddress: $SERVER_IP
  11.   podSubnet: "10.7.0.0/16"
  12.   serviceSubnet: "10.77.0.0/16"
  13. EOF
  14. kind create cluster --config - <<EOF
  15. kind: Cluster
  16. name: g1
  17. apiVersion: kind.x-k8s.io/v1alpha4
  18. nodes:
  19. - role: control-plane
  20. - role: worker
  21. networking:
  22.   apiServerAddress: $SERVER_IP
  23.   podSubnet: "10.7.0.0/16"
  24.   serviceSubnet: "10.77.0.0/16"
  25. EOF
  26.  
  27. kind create cluster --config - <<EOF
  28. kind: Cluster
  29. name: g2
  30. apiVersion: kind.x-k8s.io/v1alpha4
  31. nodes:
  32. - role: control-plane
  33. - role: worker
  34. networking:
  35.   apiServerAddress: $SERVER_IP
  36.   podSubnet: "10.7.0.0/16"
  37.   serviceSubnet: "10.77.0.0/16"
  38. EOF

4.2 部署 Broker

使用 --globalnet=true 参数启用 Globalnet 功能,使用 --globalnet-cidr-range 参数指定所有集群的全局 GlobalCIDR(默认 242.0.0.0/8)。

subctl --context kind-broker-globalnet deploy-broker --globalnet=true --globalnet-cidr-range 120.0.0.0/8

4.3 g1, g2 加入集群

使用 --globalnet-cidr 参数指定本集群的 GlobalCIDR。

  1. subctl --context kind-g1 join broker-info.subm --clusterid g1 --globalnet-cidr 120.1.0.0/24
  2. subctl --context kind-g2 join broker-info.subm --clusterid g2 --globalnet-cidr 120.2.0.0/24

4.4 查看集群连接

  1. subctl show connections --context kind-g1
  2. subctl show connections --context kind-g2
a1bc7ac0a30a669094c8f655b8b53430.png

4.5 测试跨集群通信

在两个集群中都创建 sample 命名空间。

  1. kubectl --context kind-g2 create namespace sample
  2. kubectl --context kind-g1 create namespace sample
4.5.1 ClusterIP Service

在 g2 集群创建服务。

  1. kubectl --context kind-g2 apply -f - <<EOF
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5.   name: whereami
  6.   namespace: sample
  7. spec:
  8.   replicas: 3
  9.   selector:
  10.     matchLabels:
  11.       app: whereami
  12.   template:
  13.     metadata:
  14.       labels:
  15.         app: whereami
  16.     spec:
  17.       containers:
  18.       - name: whereami
  19.         image: cr7258/whereami:v1
  20.         imagePullPolicy: Always
  21.         ports:
  22.         - containerPort: 80
  23.         env:
  24.         - name: NAMESPACE
  25.           valueFrom:
  26.             fieldRef:
  27.               fieldPath: metadata.namespace
  28.         - name: NODE_NAME
  29.           valueFrom:
  30.             fieldRef:
  31.               fieldPath: spec.nodeName
  32.         - name: POD_NAME
  33.           valueFrom:
  34.             fieldRef:
  35.               fieldPath: metadata.name
  36.         - name: POD_IP
  37.           valueFrom:
  38.             fieldRef:
  39.               fieldPath: status.podIP
  40. ---
  41. apiVersion: v1
  42. kind: Service
  43. metadata:
  44.   name: whereami-cs
  45.   namespace: sample
  46. spec:
  47.   selector:
  48.     app: whereami
  49.   ports:
  50.     - protocol: TCP
  51.       port: 80
  52.       targetPort: 80
  53. EOF

在 g2 集群查看服务。

  1. root@seven-demo:~/globalnet# kubectl --context kind-g2 get pod -n sample -o wide
  2. NAME                        READY   STATUS    RESTARTS   AGE   IP         NODE        NOMINATED NODE   READINESS GATES
  3. whereami-754776cdc9-72qd4   1/1     Running   0          19s   10.7.1.8   g2-control-plane    <none>           <none>
  4. whereami-754776cdc9-jsnhk   1/1     Running   0          20s   10.7.1.7   g2-control-plane    <none>           <none>
  5. whereami-754776cdc9-n4mm6   1/1     Running   0          19s   10.7.1.9   g2-control-plane    <none>           <none>
  6. root@seven-demo:~/globalnet# kubectl --context kind-g2 get svc -n sample -o wide
  7. NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE   SELECTOR
  8. whereami-cs   ClusterIP   10.77.153.172   <none>        80/TCP    26s   app=whereami

在 g2 集群导出服务。

subctl --context kind-g2 export service --namespace sample whereami-cs

导出服务后,我们再查看一下 g2 集群的 Service,会发现 Submariner 自动在与导出的服务相同的命名空间中创建了一个额外的服务,并且设置 externalIPs 为分配给相应服务的 Global IP。

kubectl --context kind-g2 get svc -n sample
2cd8f7a66dd05cf7c65488c0d0eeb794.png

在g1 集群访问 g2 集群的 whereami 服务。

  1. kubectl --context kind-g1 run client --image=cr7258/nettool:v1
  2. kubectl --context kind-g1 exec -it client -- bash

DNS 将会解析到分配给 c2 集群 whereami 服务的 Global IP 地址,而不是服务的 ClusterIP IP 地址。

nslookup whereami-cs.sample.svc.clusterset.local
4f338ff335a755c70019fd0e7956e7ce.png

用 curl 命令发起 HTTP 请求,从输出的结果可以发现,在 g2 集群的 whereami 看来,请求的源 IP 是 120.1.0.5,也就是说当流量从 g1 发往 g2 集群时,在 g1 集群的 Gateway Node 上对流量进行了 SNAT 源地址转换。

curl whereami-cs.sample.svc.clusterset.local
a9bb9505482339829effe12dedc868f5.png

这里结合下图对流量进行简单的说明:流量从 c1 集群的 client Pod 发出,经过 DNS 解析后应该请求 IP 120.2.0.253。首先经过 veth-pair 到达 Node 的 Root Network Namespace,然后经过 Submariner Route Agent 设置的 vx-submariner 这个 VXLAN 隧道将流量发往 Gateway Node 上(c1-worker)。在 Gateway Node 上将源 IP 10.7.1.7 转换成了 120.1.0.5, 然后通过 c1 和 c2 集群的 IPsec 隧道发送到对端,c2 集群的 Gateway Node(c2-worker)接收到流量后,经过 iptables 的反向代理规则(在这过程中根据 Global IP 进行了 DNAT)最终发送到后端的 whereami Pod 上。

8f538c7e2cf008fa48e5b57a8bc58190.png

我们可以分别查看 g1 和 g2 集群上 Gateway Node 的 Iptables 来验证 NAT 规则,首先执行 docker exec -it g1-worker bash 和 docker exec -it g2-worker bash 进入这两个节点,然后执行 iptables-save 命令可以看到 iptables 配置,以下我筛选了相关的 iptables 配置。

g1-worker 节点:

  1. # 在出访的时候将源 IP 转换为 120.1.0.1-120.1.0.8 中的一个
  2. -A SM-GN-EGRESS-CLUSTER -s 10.7.0.0/16 -m mark --mark 0xc0000/0xc0000 -j SNAT --to-source 120.1.0.1-120.1.0.8

g2-worker 节点:

  1. # 访问 120.2.0.253:80 的流量跳转到 KUBE-EXT-ZTP7SBVPSRVMWSUN 链
  2. -A KUBE-SERVICES -d 120.2.0.253/32 -p tcp -m comment --comment "sample/submariner-fzpkhsc5wssywpk5x3par6ceb6b2jinr external IP" -m tcp --dport 80 -j KUBE-EXT-ZTP7SBVPSRVMWSUN
  3. # 跳转到 KUBE-SVC-ZTP7SBVPSRVMWSUN 链
  4. -A KUBE-EXT-ZTP7SBVPSRVMWSUN -j KUBE-SVC-ZTP7SBVPSRVMWSUN
  5. # 随机选择 whereami 后端的一个 Pod
  6. -A KUBE-SVC-ZTP7SBVPSRVMWSUN -m comment --comment "sample/submariner-fzpkhsc5wssywpk5x3par6ceb6b2jinr -> 10.7.1.7:80" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-BB74OZOLBDYS7GHU
  7. -A KUBE-SVC-ZTP7SBVPSRVMWSUN -m comment --comment "sample/submariner-fzpkhsc5wssywpk5x3par6ceb6b2jinr -> 10.7.1.8:80" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-MTZHPN36KRSHGEO6
  8. -A KUBE-SVC-ZTP7SBVPSRVMWSUN -m comment --comment "sample/submariner-fzpkhsc5wssywpk5x3par6ceb6b2jinr -> 10.7.1.9:80" -j KUBE-SEP-UYVYXWJKZN2VHFJW
  9. # DNAT 地址转换
  10. -A KUBE-SEP-BB74OZOLBDYS7GHU -p tcp -m comment --comment "sample/submariner-fzpkhsc5wssywpk5x3par6ceb6b2jinr" -m tcp -j DNAT --to-destination 10.7.1.7:80
  11. -A KUBE-SEP-MTZHPN36KRSHGEO6 -p tcp -m comment --comment "sample/submariner-fzpkhsc5wssywpk5x3par6ceb6b2jinr" -m tcp -j DNAT --to-destination 10.7.1.8:80
  12. -A KUBE-SEP-UYVYXWJKZN2VHFJW -p tcp -m comment --comment "sample/submariner-fzpkhsc5wssywpk5x3par6ceb6b2jinr" -m tcp -j DNAT --to-destination 10.7.1.9:80
4.5.2 Headless Service + StatefulSet

接下来测试 Globalnet 在 Headless Service + StatefulSet 场景下的应用。在 g2 集群创建服务。

  1. kubectl --context kind-g2 apply -f - <<EOF
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5.  name: whereami-ss
  6.  namespace: sample
  7.  labels:
  8.    app: whereami-ss
  9. spec:
  10.  ports:
  11.  - port: 80
  12.    name: whereami
  13.  clusterIP: None
  14.  selector:
  15.    app: whereami-ss
  16. ---
  17. apiVersion: apps/v1
  18. kind: StatefulSet
  19. metadata:
  20.  name: whereami
  21.  namespace: sample
  22. spec:
  23.  serviceName: "whereami-ss"
  24.  replicas: 3
  25.  selector:
  26.    matchLabels:
  27.        app: whereami-ss
  28.  template:
  29.    metadata:
  30.      labels:
  31.        app: whereami-ss
  32.    spec:
  33.      containers:
  34.      - name: whereami-ss
  35.        image: cr7258/whereami:v1
  36.        ports:
  37.        - containerPort: 80
  38.          name: whereami
  39.        env:
  40.        - name: NAMESPACE
  41.          valueFrom:
  42.            fieldRef:
  43.              fieldPath: metadata.namespace
  44.        - name: NODE_NAME
  45.          valueFrom:
  46.            fieldRef:
  47.              fieldPath: spec.nodeName
  48.        - name: POD_NAME
  49.          valueFrom:
  50.            fieldRef:
  51.              fieldPath: metadata.name
  52.        - name: POD_IP
  53.          valueFrom:
  54.           fieldRef:
  55.              fieldPath: status.podIP
  56. EOF

在 g2 集群查看服务。

  1. root@seven-demo:~# kubectl get pod -n sample --context kind-g2 -o wide -l app=whereami-ss
  2. NAME         READY   STATUS    RESTARTS   AGE   IP          NODE        NOMINATED NODE   READINESS GATES
  3. whereami-0   1/1     Running   0          62s   10.7.1.10   g2-worker   <none>           <none>
  4. whereami-1   1/1     Running   0          56s   10.7.1.11   g2-worker   <none>           <none>
  5. whereami-2   1/1     Running   0          51s   10.7.1.12   g2-worker   <none>           <none>
  6. root@seven-demo:~# kubectl get svc -n sample --context kind-c2 -l app=whereami-ss
  7. NAME          TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
  8. whereami-ss   ClusterIP   None         <none>        80/TCP    42h

在 g2 集群导出服务。

subctl --context kind-g2 export service whereami-ss --namespace sample

在 g1 集群访问 g2 集群服务,Globalnet 会为每一个 Headless Service 关联的 Pod 分配一个 Global IP,用于出向和入向的流量。

dbd416cd4c3089c8ef01835cac5157a1.png
  1. kubectl --context kind-g1 exec -it client -- bash
  2. nslookup whereami-ss.sample.svc.clusterset.local
8f04ba49a2405357fe036abf0a970643.png

指定解析某个 Pod。

nslookup whereami-0.g2.whereami-ss.sample.svc.clusterset.local
8afaa900cdfeb8eab3a8519eaed7771c.png

指定访问某个 Pod。

curl whereami-0.g2.whereami-ss.sample.svc.clusterset.local
dcccc652a069e579985b42952e011f28.png

查看 ServiceImport,在 IP 地址的一栏是空的,因为导出的服务类型是 Headless。

  1. kubectl --context kind-g1 get -n submariner-operator serviceimport
  2. kubectl --context kind-g2 get -n submariner-operator serviceimport
4b662c6e3c93859f916e2c20aa114214.png

对于 Headless Service,Pod IP 是根据 Endpointslice 来解析的。

  1. kubectl --context kind-g1 get endpointslices -n sample
  2. kubectl --context kind-g2 get endpointslices -n sample
cbff2869164f62ec84fea9ee19b97a9b.png

执行 docker exec -it g2-worker bash 命令进入 g2-worker 节点,然后执行 iptables-save 命令寻找相关的 Iptables 规则。

  1. # SNAT
  2. -A SM-GN-EGRESS-HDLS-PODS -s 10.7.1.12/32 -m mark --mark 0xc0000/0xc0000 -j SNAT --to-source 120.2.0.252
  3. -A SM-GN-EGRESS-HDLS-PODS -s 10.7.1.11/32 -m mark --mark 0xc0000/0xc0000 -j SNAT --to-source 120.2.0.251
  4. -A SM-GN-EGRESS-HDLS-PODS -s 10.7.1.10/32 -m mark --mark 0xc0000/0xc0000 -j SNAT --to-source 120.2.0.250
  5. # DNAT
  6. -A SUBMARINER-GN-INGRESS -d 120.2.0.252/32 -j DNAT --to-destination 10.7.1.12
  7. -A SUBMARINER-GN-INGRESS -d 120.2.0.251/32 -j DNAT --to-destination 10.7.1.11
  8. -A SUBMARINER-GN-INGRESS -d 120.2.0.250/32 -j DNAT --to-destination 10.7.1.10

5 清理环境

执行以下命令删除本次实验创建的 Kind 集群。

kind delete clusters broker c1 c2 g1 g2

6 总结

本文首先介绍了 Submariner 的架构,包括 Broker、Gateway Engine、Route Agent、Service Discovery、Globalnet 和 Submariner Operator。接着,通过实验向读者展示了 Submariner 在跨集群场景中如何处理 ClusterIP 和 Headless 类型的流量。最后,演示了 Submariner 的 Globalnet 是如何通过 GlobalCIDR 支持不同集群间存在 CIDR 重叠的情况。划线


本文转载自:「Se7en的架构笔记」,原文:https://tinyurl.com/378mr5r2,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。

0a73a761c6ab83ed0a2b1712590245ff.gif

最近,我们建立了一个技术交流微信群。目前群里已加入了不少行业内的大神,有兴趣的同学可以加入和我们一起交流技术,在 「奇妙的 Linux 世界」 公众号直接回复 「加群」 邀请你入群。

37099cf352fc9687a849e3cf81360ffd.png

你可能还喜欢

点击下方链接即可阅读

054860b3811696332da36367f4c8e1f6.png

如何在 K3s 上快速体验 Kube-OVN

3186271f05fe00d697dc8a903e98ff72.png
点击上方图片,『美团|饿了么』外卖红包天天免费领

d2630b0fc6d63b21ae66b7614852b13f.png

更多有趣的互联网新鲜事,关注「奇妙的互联网」视频号全了解!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/128303
推荐阅读
相关标签
  

闽ICP备14008679号