Submariner 是一個(gè)完全開源的項(xiàng)目,可以幫助我們?cè)诓煌?Kubernetes 集群之間(無論是在本地還是云端)實(shí)現(xiàn)網(wǎng)絡(luò)通信。Submariner 有以下功能:
- 跨集群的 L3 連接
- 跨集群的服務(wù)發(fā)現(xiàn)
- Globalnet 支持 CIDR 重疊
- 提供命令行工具 subctl 簡(jiǎn)化部署和管理
- 兼容各種 CNI
1 Submariner 架構(gòu)
Submariner 由幾個(gè)主要部分組成:
-
Broker: 本質(zhì)上是兩個(gè)用于交換集群信息的 CRD(Endpoint 和 Cluster),我們需要選擇一個(gè)集群作為 Broker 集群,其他集群連接到 Broker 集群的 API Server 來交換集群信息:
- Endpoint:包含了 Gateway Engine 建立集群間連接需要的信息,例如 Private IP 和 Public IP,NAT 端口等等。
- Cluster:包含原始集群的靜態(tài)信息,例如其 Service 和 Pod CIDR。
- Gateway Engine:管理連接到其他集群的隧道。
- Route Agent:負(fù)責(zé)將跨集群的流量路由到 active Gateway Node。
- Service Discovery: 提供跨集群的 DNS 服務(wù)發(fā)現(xiàn)。
- Globalnet(可選):處理具有重疊 CIDR 的集群互連。
- Submariner Operator:負(fù)責(zé)在 Kubernetes 集群中安裝 Submariner 組件,例如 Broker, Gateway Engine, Route Agent 等等。
1.1 Service Discovery
Submariner 中跨集群的 DNS 服務(wù)發(fā)現(xiàn)由以下兩個(gè)組件基于 Kubernetes?Multi-Cluster Service APIs 的規(guī)范來實(shí)現(xiàn):
-
Lighthouse Agent:訪問 Broker 集群的 API Server 與其他集群交換 ServiceImport 元數(shù)據(jù)信息。
- 對(duì)于本地集群中已創(chuàng)建 ServiceExport 的每個(gè) Service,Agent 創(chuàng)建相應(yīng)的 ServiceImport 資源并將其導(dǎo)出到 Broker 以供其他集群使用。
- 對(duì)于從其他集群導(dǎo)出到 Broker 中的 ServiceImport 資源,它會(huì)在本集群中創(chuàng)建它的副本。
-
Lighthouse DNS Server:
- Lighthouse DNS Server 根據(jù) ServiceImport 資源進(jìn)行 DNS 解析。
- CoreDNS 配置為將?
clusterset.local
?域名的解析請(qǐng)求發(fā)往 Lighthouse DNS server。
MCS API 是 Kubernetes 社區(qū)定義的用于跨集群服務(wù)發(fā)現(xiàn)的規(guī)范,主要包含了 ServiceExport 和 ServiceImport 兩個(gè) CRD。
- ServiceExport?定義了暴露(導(dǎo)出)到其他集群的 Service,由用戶在要導(dǎo)出的 Service 所在的集群中創(chuàng)建,與 Service 的名字和 Namespace 一致。
apiVersion: multicluster.k8s.io/v1alpha1
kind: ServiceExport
metadata:
name: my-svc
namespace: my-ns
- ServiceImport:當(dāng)一個(gè)服務(wù)被導(dǎo)出后,實(shí)現(xiàn) MCS API 的控制器會(huì)在所有集群(包括導(dǎo)出服務(wù)的集群)中自動(dòng)生成一個(gè)與之對(duì)應(yīng)的 ServiceImport 資源。
apiVersion: multicluster.k8s.io/v1alpha1
kind: ServiceImport
metadata:
name: my-svc
namespace: my-ns
spec:
ips:
- 42.42.42.42 # 跨集群訪問的 IP 地址
type: "ClusterSetIP"
ports:
- name: http
protocol: TCP
port: 80
sessionAffinity: None
1.2 Gateway Engine
Gateway Engine 部署在每個(gè)集群中,負(fù)責(zé)建立到其他集群的隧道。隧道可以由以下方式實(shí)現(xiàn):
- IPsec,使用 Libreswan 實(shí)現(xiàn)。這是當(dāng)前的默認(rèn)設(shè)置。
- WireGuard,使用 wgctrl 庫(kù)實(shí)現(xiàn)。
- VXLAN,不加密。
可以在使用 subctl join
命令加入集群的時(shí)候使用 --cable-driver
參數(shù)設(shè)置隧道的類型。
Gateway Engine 部署為 DaemonSet,只在有 submariner.io/gateway=true
Label 的 Node 上運(yùn)行,當(dāng)我們使用 subctl join
命令加入集群的時(shí)候,如果沒有 Node 有該 Label,會(huì)提示我們選擇一個(gè) Node 作為 Gateway Node。
Submariner 也支持 active/passive 高可用模式的 Gateway Engine,我們可以在多個(gè)節(jié)點(diǎn)上部署 Gateway Engine。在同一時(shí)間內(nèi),只能有一個(gè) Gateway Engine 處于 active 狀態(tài)來處理跨集的流量,Gateway Engine 通過領(lǐng)導(dǎo)者選舉的方式確定 active 的實(shí)例,其他實(shí)例在 passive 模式下等待,準(zhǔn)備在 active 實(shí)例發(fā)生故障時(shí)接管。
1.3 Globalnet
Submariner 的一大亮點(diǎn)是支持在不同集群間存在 CIDR 重疊的情況,這有助于減少網(wǎng)絡(luò)重構(gòu)的成本。例如,在部署過程中,某些集群可能使用了默認(rèn)的網(wǎng)段,導(dǎo)致了 CIDR 重疊。在這種情況下,如果后續(xù)需要更改集群網(wǎng)段,可能會(huì)對(duì)集群的運(yùn)行產(chǎn)生影響。
為了支持集群間重疊 CIDR 的情況,Submariner 通過一個(gè) GlobalCIDR 網(wǎng)段(默認(rèn)是 242.0.0.0/8)在流量進(jìn)出集群時(shí)進(jìn)行 NAT 轉(zhuǎn)換,所有的地址轉(zhuǎn)換都發(fā)生在 active Gateway Node 上。在 subctl deploy
部署 Broker 的時(shí)候可以通過 --globalnet-cidr-range
參數(shù)指定所有集群的全局 GlobalCIDR。在 subctl join
加入集群的時(shí)候還可以通過 --globalnet-cidr
參數(shù)指定該集群的 GlobalCIDR。
導(dǎo)出的 ClusterIP 類型的 Service 會(huì)從 GlobalCIDR 分配一個(gè) Global IP 用于入向流量,對(duì)于 Headless 類型的 Service,會(huì)為每個(gè)關(guān)聯(lián)的 Pod 分配一個(gè) Global IP 用于入向和出向流量。
2 環(huán)境準(zhǔn)備
在本次實(shí)驗(yàn)中,我們使用一臺(tái)運(yùn)行 Ubuntu 20.04 的虛擬機(jī),并通過 Kind 啟動(dòng)多個(gè) Kubernetes 集群來進(jìn)行測(cè)試。
root@seven-demo:~# seven-demo
Static hostname: seven-demo
Icon name: computer-vm
Chassis: vm
Machine ID: f780bfec3c409135b11d1ceac73e2293
Boot ID: e83e9a883800480f86d37189bdb09628
Virtualization: kvm
Operating System: Ubuntu 20.04.5 LTS
Kernel: Linux 5.15.0-1030-gcp
Architecture: x86-64
安裝相關(guān)軟件和命令行工具。
# 安裝 Docker,根據(jù)操作系統(tǒng)安裝 https://docs.docker.com/engine/install/
sudo apt-get update
sudo apt-get install -y \
ca-certificates \
curl \
gnupg
sudo mkdir -m 0755 -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 安裝 Kind
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.18.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind
# 安裝 kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin
apt install -y bash-completion
echo 'source <(kubectl completion bash)' >>~/.bashrc
# 安裝 subctl
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
tar -xf subctl-release-0.14-linux-amd64.tar.xz
mv subctl-release-0.14/subctl-release-0.14-linux-amd64 /usr/local/bin/subctl
chmod +x /usr/local/bin/subctl
3 快速開始
3.1 創(chuàng)建集群
使用 Kind 創(chuàng)建一個(gè) 3 節(jié)點(diǎn)的集群,這里讀者需要將 SERVER_IP
替換成自己服務(wù)器的 IP。默認(rèn)情況下,Kind 將 Kubernetes API 服務(wù)器 IP:Port 設(shè)置為本地環(huán)回地址 (?127.0.0.1
): 隨機(jī)端口,這對(duì)于從本機(jī)與集群交互來說很好,但是在本實(shí)驗(yàn)中多個(gè) Kind 集群之間需要通信,因此我們需要把 Kind 的 apiServerAddress 改成本機(jī) IP。
# 替換成服務(wù)器 IP
export SERVER_IP="10.138.0.11"
kind create cluster --config - <<EOF
kind: Cluster
name: broker
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
networking:
apiServerAddress: $SERVER_IP
podSubnet: "10.7.0.0/16"
serviceSubnet: "10.77.0.0/16"
EOF
kind create cluster --config - <<EOF
kind: Cluster
name: c1
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
networking:
apiServerAddress: $SERVER_IP
podSubnet: "10.8.0.0/16"
serviceSubnet: "10.88.0.0/16"
EOF
kind create cluster --config - <<EOF
kind: Cluster
name: c2
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
networking:
apiServerAddress: $SERVER_IP
podSubnet: "10.9.0.0/16"
serviceSubnet: "10.99.0.0/16"
EOF
3.2 部署 Broker
在本次實(shí)驗(yàn)中,我們專門將一個(gè)集群配置為 Broker 集群。Broker 集群可以是專用集群,也可以是連接的集群之一。執(zhí)行 subctl deploy-broker
命令部署 Broker,Broker 只包含了一組 CRD,并沒有部署 Pod 或者 Service。
subctl --context kind-broker deploy-broker
部署完成后,會(huì)生成 broker-info.subm
文件,文件以 Base64 加密,其中包含了連接 Broker 集群 API Server 的地址以及證書信息,還有 IPsec 的密鑰信息。
{
"brokerURL": "https://10.138.0.11:45681",
"ClientToken": {
"metadata": {
"name": "submariner-k8s-broker-admin-token-f7b62",
"namespace": "submariner-k8s-broker",
"uid": "3f949d19-4f42-43d6-af1c-382b53f83d8a",
"resourceVersion": "688",
"creationTimestamp": "2023-04-05T02:50:02Z",
"annotations": {
"kubernetes.io/created-by": "subctl",
"kubernetes.io/service-account.name": "submariner-k8s-broker-admin",
"kubernetes.io/service-account.uid": "da6eeba1-b707-4d30-8e1e-e414e9eae817"
},
"managedFields": [
{
"manager": "kube-controller-manager",
"operation": "Update",
"apiVersion": "v1",
"time": "2023-04-05T02:50:02Z",
"fieldsType": "FieldsV1",
"fieldsV1": {
"f:data": {
".": {},
"f:ca.crt": {},
"f:namespace": {},
"f:token": {}
},
"f:metadata": {
"f:annotations": {
"f:kubernetes.io/service-account.uid": {}
}
}
}
},
{
"manager": "subctl",
"operation": "Update",
"apiVersion": "v1",
"time": "2023-04-05T02:50:02Z",
"fieldsType": "FieldsV1",
"fieldsV1": {
"f:metadata": {
"f:annotations": {
".": {},
"f:kubernetes.io/created-by": {},
"f:kubernetes.io/service-account.name": {}
}
},
"f:type": {}
}
}
]
},
"data": {
"ca.crt": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJek1EUXdOVEF5TkRVMU1Wb1hEVE16TURRd01qQXlORFUxTVZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBSytXCmIzb0h1ZEJlbU5tSWFBNXQrWmI3TFhKNXRLWDB6QVc5a0tudjQzaGpoTE84NHlSaEpyY3ZSK29QVnNaUUJIclkKc01tRmx3aVltbU5ORzA4c2NLMTlyLzV0VkdFR2hCckdML3VKcTIybXZtYi80aHdwdmRTQjN0UDlkU2RzYUFyRwpYYllwOE4vUmlheUJvbTBJVy9aQjNvZ0MwK0tNcWM0NE1MYnBkZXViWnNSckErN2pwTElYczE3OGgxb25kdGNrClIrYlRnNGpjeS92NTkrbGJjamZSeTczbUllMm9DbVFIbE1XUFpSTkMveDhaTktGekl6UHc4SmZSOERjWk5Xc1YKa1NBUVNVUkpnTEhBbjY5MlhDSEsybmJuN21pcjYvYVZzVVpyTGdVNC9zcWg3QVFBdDFGQkk3NDRpcithTjVxSwpJRnRJenkxU3p2ZEpwMThza3EwQ0F3RUFBYU5aTUZjd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZFQUhFbndHditwTXNVcHVQRXNqbkQwTEgvSFpNQlVHQTFVZEVRUU8KTUF5Q0NtdDFZbVZ5Ym1WMFpYTXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQTFGckk1cGR1VTFsQzluVldNNwowYlc2VFRXdzYwUTlFVWdsRzc4bkRFZkNKb3ovY2xWclFNWGZrc2Zjc1VvcHZsaE5yWFlpbmd0UEE4aEMrTnRJCmdPZElDZDJGaWFOTjRCYkt3a1NmRkQvbmhjWDU1WmQ0UzN1SzZqb2JWVHIzaXVJRVhIdHg0WVIyS1ZuZitTMDUKQTFtbXdzSG1ZbkhtWEllOUEyL3hKdVhtSnNybWljWTlhMXhtSXVyYzhNalBsa1pZWVU1OFBvZHJFNi9XcnBaawpBbW9qcERIWWIrbnZxa0FuaG9hYUV3b2FEVGxYRjY0M3lVLy9MZE4wTmw5MWkvSHNwQ2tZdVFrQjJmQXNkSGNaCkMrdzQ4WVhYT21pSzZXcmJGYVJnaEVKdjB6UjdsZk50UEVZVWJHWEFxV0ZlSnFTdnM5aUYwbFV1NzZDNkt3YWIKbmdnPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==",
"namespace": "c3VibWFyaW5lci1rOHMtYnJva2Vy",
"token": "ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklqaHZWVnBuZVVoZk1uVTFjSEJxU1hOdE1UTk1NbUY0TFRaSlIyVlZVRGd4VjI1dmMyNXBNMjFYZFhjaWZRLmV5SnBjM01pT2lKcmRXSmxjbTVsZEdWekwzTmxjblpwWTJWaFkyTnZkVzUwSWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXVZVzFsYzNCaFkyVWlPaUp6ZFdKdFlYSnBibVZ5TFdzNGN5MWljbTlyWlhJaUxDSnJkV0psY201bGRHVnpMbWx2TDNObGNuWnBZMlZoWTJOdmRXNTBMM05sWTNKbGRDNXVZVzFsSWpvaWMzVmliV0Z5YVc1bGNpMXJPSE10WW5KdmEyVnlMV0ZrYldsdUxYUnZhMlZ1TFdZM1lqWXlJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5elpYSjJhV05sTFdGalkyOTFiblF1Ym1GdFpTSTZJbk4xWW0xaGNtbHVaWEl0YXpoekxXSnliMnRsY2kxaFpHMXBiaUlzSW10MVltVnlibVYwWlhNdWFXOHZjMlZ5ZG1salpXRmpZMjkxYm5RdmMyVnlkbWxqWlMxaFkyTnZkVzUwTG5WcFpDSTZJbVJoTm1WbFltRXhMV0kzTURjdE5HUXpNQzA0WlRGbExXVTBNVFJsT1dWaFpUZ3hOeUlzSW5OMVlpSTZJbk41YzNSbGJUcHpaWEoyYVdObFlXTmpiM1Z1ZERwemRXSnRZWEpwYm1WeUxXczRjeTFpY205clpYSTZjM1ZpYldGeWFXNWxjaTFyT0hNdFluSnZhMlZ5TFdGa2JXbHVJbjAub1JHM2d6Wno4MGVYQXk5YlZ5b1V2NmoyTERvdFJiNlJyOTF4d0ZiTDMwdFNJY3dnS3FYd3NZbVV1THhtcFdBb2M5LWRSMldHY0ZLYklORlZmUUttdVJMY2JsenlTUFFVMlB3WVVwN1oyNnlxYXFOMG1UQ3ZNWWxSeHp6cWY3LXlXUm8yNE9pWS1nMnNmNmNrRzRPMkdwa2MwTlNoOWRTUGY4dXJTbjZSVGJwbjFtcFZjTy1IQjJWeU5hTE9EdmtWS3RLVFJfVS1ZRGc1NzVtczM0OXM0X2xMZjljZjlvcjFaQXVvXzcyN0E5U0VvZ0JkN3BaSndwb0FEUHZRb1NGR0VLQWZYYTFXXzJWVE5PYXE4cUQxOENVbXVFRUFxMmtoNElBN0d5LVRGdUV2Q0JYUVlzRHYzUFJQTjZpOGlKSFBLVUN1WVNONS1NT3lGX19aNS1WdlhR"
},
"type": "kubernetes.io/service-account-token"
},
"IPSecPSK": {
"metadata": {
"name": "submariner-ipsec-psk",
"creationTimestamp": null
},
"data": {
"psk": "NL7dUK+RagDKPQZZj+7Q7wComj0/wLFbfvnHe12hHxR8+d/FnkEqXfmh8JMzLo6h"
}
},
"ServiceDiscovery": true,
"Components": [
"service-discovery",
"connectivity"
],
"CustomDomains": null
}
3.3 c1, c2 加入集群
執(zhí)行 subctl join
命令將 c1 和 c2 兩個(gè)集群加入 Broker 集群。使用 --clusterid
參數(shù)指定集群 ID,每個(gè)集群 ID 需要唯一。提供上一步生成的 broker-info.subm
文件用于集群注冊(cè)。
subctl --context kind-c1 join broker-info.subm --clusterid c1
subctl --context kind-c2 join broker-info.subm --clusterid c2
會(huì)提示我們選擇一個(gè)節(jié)點(diǎn)作為 Gateway Node,c1 集群選擇 c1-worker 節(jié)點(diǎn)作為 Gateway,c2 集群選擇 c2-worker 節(jié)點(diǎn)作為 Gateway。
兩個(gè) Gateway Node 的 IP 地址如下,之后會(huì)分別使用這兩個(gè)地址在兩個(gè)集群間建立隧道連接。
3.4 查看集群連接
等待 c1 和 c2 集群中 Submariner 的相關(guān)組件都運(yùn)行成功后,執(zhí)行以下命令查看集群間連接情況。
subctl show connections --context kind-c1
subctl show connections --context kind-c2
可以看到 c1 和 c2 集群分別和對(duì)方建立的連接。
查看 c1 gateway 日志,可以看到成功與 c2 集群建立了 IPsec 隧道。
3.5 測(cè)試跨集群通信
至此,我們已經(jīng)成功在 c1 和 c2 集群間建立了跨集群的連接,接下來我們將創(chuàng)建服務(wù)并演示如何將其導(dǎo)出給其他集群進(jìn)行訪問。
在下面的示例中,我們?cè)?sample Namespace 中創(chuàng)建相關(guān)資源。請(qǐng)注意,必須在兩個(gè)集群中都創(chuàng)建 sample 命名空間,服務(wù)發(fā)現(xiàn)才能正常工作。
kubectl --context kind-c2 create namespace sample
# 需要確保 c1 集群上也有 sample Namespace,否則 Lighthouse agent 創(chuàng)建 Endpointslices 會(huì)失敗
kubectl --context kind-c1 create namespace sample
3.5.1 ClusterIP Service
首先測(cè)試 ClusterIP 類型的 Service。執(zhí)行以下命令在 c2 集群創(chuàng)建服務(wù)。本實(shí)驗(yàn)中 whereami 是一個(gè)用 Golang 編寫的 HTTP Server,它通過 Downward API 將 Kubernetes 的相關(guān)信息(Pod 名字,Pod 所在的 Namespace,Node)注入到容器的環(huán)境變量中,當(dāng)接收到請(qǐng)求時(shí)進(jìn)行輸出。另外 whereami 還會(huì)打印請(qǐng)求方的 IP 地址和端口信息。
kubectl --context kind-c2 apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: whereami
namespace: sample
spec:
replicas: 3
selector:
matchLabels:
app: whereami
template:
metadata:
labels:
app: whereami
spec:
containers:
- name: whereami
image: cr7258/whereami:v1
imagePullPolicy: Always
ports:
- containerPort: 80
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
---
apiVersion: v1
kind: Service
metadata:
name: whereami-cs
namespace: sample
spec:
selector:
app: whereami
ports:
- protocol: TCP
port: 80
targetPort: 80
EOF
在 c2 集群查看服務(wù)。
root@seven-demo:~# kubectl --context kind-c2 get pod -n sample -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
whereami-754776cdc9-28kgd 1/1 Running 0 19h 10.9.1.18 c2-control-plane <none> <none>
whereami-754776cdc9-8ccmc 1/1 Running 0 19h 10.9.1.17 c2-control-plane <none> <none>
whereami-754776cdc9-dlp55 1/1 Running 0 19h 10.9.1.16 c2-control-plane <none> <none>
root@seven-demo:~# kubectl --context kind-c2 get svc -n sample -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
whereami-cs ClusterIP 10.99.2.201 <none> 80/TCP 19h app=whereami
在 c2 集群中使用 subctl export
命令將服務(wù)導(dǎo)出。
subctl --context kind-c2 export service --namespace sample whereami-cs
該命令會(huì)在創(chuàng)建一個(gè)和 Service 相同名字和 Namespace 的 ServiceExport 資源。
root@seven-demo:~# kubectl get serviceexports --context kind-c2 -n sample whereami-cs -o yaml
apiVersion: multicluster.x-k8s.io/v1alpha1
kind: ServiceExport
metadata:
creationTimestamp: "2023-04-06T13:04:15Z"
generation: 1
name: whereami-cs
namespace: sample
resourceVersion: "327707"
uid: d1da8953-3fa5-4635-a8bb-6de4cd3c45a9
status:
conditions:
- lastTransitionTime: "2023-04-06T13:04:15Z"
message: ""
reason: ""
status: "True"
type: Valid
- lastTransitionTime: "2023-04-06T13:04:15Z"
message: ServiceImport was successfully synced to the broker
reason: ""
status: "True"
type: Synced
ServiceImport 資源會(huì)由 Submariner 自動(dòng)在 c1,c2 集群中創(chuàng)建,IP 地址是 Service 的 ClusterIP 地址。
kubectl --context kind-c1 get -n submariner-operator serviceimport
kubectl --context kind-c2 get -n submariner-operator serviceimport
在 c1 集群創(chuàng)建一個(gè) client Pod 來訪問 c2 集群的 whereami 服務(wù)。
kubectl --context kind-c1 run client --image=cr7258/nettool:v1
kubectl --context kind-c1 exec -it client -- bash
先嘗試下 DNS 解析,ClusterIP Service 類型的 Service 可以通過以下格式進(jìn)行訪問 <svc-name>.<namespace>.svc.clusterset.local
。
nslookup whereami-cs.sample.svc.clusterset.local
返回的 IP 是在 c2 集群 Service 的 ClusterIP 的地址。
我們查看一下 CoreDNS 的配置文件,這個(gè) Configmap 會(huì)被 Submariner Operator 修改,將 clusterset.local
用于跨集群通信的域名交給 Lighthouse DNS 來解析。
root@seven-demo:~# kubectl get cm -n kube-system coredns -oyaml
apiVersion: v1
data:
Corefile: |+
#lighthouse-start AUTO-GENERATED SECTION. DO NOT EDIT
clusterset.local:53 {
forward . 10.88.78.89
}
#lighthouse-end
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
kind: ConfigMap
metadata:
creationTimestamp: "2023-04-05T02:47:34Z"
name: coredns
namespace: kube-system
resourceVersion: "1211"
uid: 698f20a5-83ea-4a3e-8a1e-8b9438a6b3f8
Submariner 遵循以下邏輯來進(jìn)行跨集群集的服務(wù)發(fā)現(xiàn):
- 如果導(dǎo)出的服務(wù)在本地集群中不可用,Lighthouse DNS 從服務(wù)導(dǎo)出的遠(yuǎn)程集群之一返回 ClusterIP 服務(wù)的 IP 地址。
- 如果導(dǎo)出的服務(wù)在本地集群中可用,Lighthouse DNS 總是返回本地 ClusterIP 服務(wù)的 IP 地址。
- 如果多個(gè)集群從同一個(gè)命名空間導(dǎo)出具有相同名稱的服務(wù),Lighthouse DNS 會(huì)以輪詢的方式在集群之間進(jìn)行負(fù)載均衡。
- 可以在 DNS 查詢前加上
cluster-id
前綴來訪問特定集群的服務(wù),<cluster-id>.<svc-name>.<namespace>.svc.clusterset.local
。
通過 curl 命令發(fā)起 HTTP 請(qǐng)求。
curl whereami-cs.sample.svc.clusterset.local
返回結(jié)果如下,我們根據(jù)輸出的 node_name 字段可以確認(rèn)該 Pod 是在 c2 集群。
這里結(jié)合下圖對(duì)流量進(jìn)行簡(jiǎn)單的說明:流量從 c1 集群的 client Pod 發(fā)出,首先經(jīng)過 veth-pair 到達(dá) Node 的 Root Network Namespace,然后經(jīng)過 Submariner Route Agent 設(shè)置的 vx-submariner 這個(gè) VXLAN 隧道將流量發(fā)往 Gateway Node 上(c1-worker)。接著經(jīng)過連接 c1 和 c2 集群的 IPsec 隧道到達(dá)對(duì)端,c2 集群的 Gateway Node(c2-worker)接收到流量后將,經(jīng)過 iptables 的反向代理規(guī)則(在這過程中根據(jù) ClusterIP 進(jìn)行了 DNAT)最終發(fā)送到后端的 whereami Pod 上。
接下來我們?cè)?c1 集群也創(chuàng)建相同的服務(wù)。
kubectl --context kind-c1 apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: whereami
namespace: sample
spec:
replicas: 3
selector:
matchLabels:
app: whereami
template:
metadata:
labels:
app: whereami
spec:
containers:
- name: whereami
image: cr7258/whereami:v1
imagePullPolicy: Always
ports:
- containerPort: 80
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
---
apiVersion: v1
kind: Service
metadata:
name: whereami-cs
namespace: sample
spec:
selector:
app: whereami
ports:
- protocol: TCP
port: 80
targetPort: 80
EOF
在 c1 集群上查看服務(wù)。
root@seven-demo:~# kubectl --context kind-c1 get pod -n sample -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
whereami-754776cdc9-hq4m2 1/1 Running 0 45s 10.8.1.25 c1-worker <none> <none>
whereami-754776cdc9-rt84w 1/1 Running 0 45s 10.8.1.23 c1-worker <none> <none>
whereami-754776cdc9-v5zrk 1/1 Running 0 45s 10.8.1.24 c1-worker <none> <none>
root@seven-demo:~# kubectl --context kind-c1 get svc -n sample
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
whereami-cs ClusterIP 10.88.132.102 <none> 80/TCP 50s
在 c1 集群導(dǎo)出服務(wù)。
subctl --context kind-c1 export service --namespace sample whereami-cs
查看 ServiceImport。
kubectl --context kind-c1 get -n submariner-operator serviceimport
kubectl --context kind-c2 get -n submariner-operator serviceimport
由于在 c1 集群本地也有相同的服務(wù),因此這次請(qǐng)求將會(huì)發(fā)給 c1 集群的服務(wù)。
kubectl --context kind-c1 exec -it client -- bash
nslookup whereami-cs.sample.svc.clusterset.local
curl whereami-cs.sample.svc.clusterset.local
我們也可以通過 <cluster-id>.<svc-name>.<namespace>.svc.clusterset.local
來指定訪問訪問特定集群的 ClusterIP Service。例如我們指定訪問 c2 集群的 Service。
curl c2.whereami-cs.sample.svc.clusterset.local
3.5.2 Headless Service + StatefulSet
Submariner 還支持帶有 StatefulSets 的 Headless Services,從而可以通過穩(wěn)定的 DNS 名稱訪問各個(gè) Pod。在單個(gè)集群中,Kubernetes 通過引入穩(wěn)定的 Pod ID 來支持這一點(diǎn),在單個(gè)集群中可以通過 <pod-name>.<svc-name>.<ns>.svc.cluster.local
格式來解析域名。跨集群場(chǎng)景下,Submariner 通過 <pod-name>.<cluster-id>.<svc-name>.<ns>.svc.clusterset.local
的格式來解析域名。
在 c2 集群創(chuàng)建 Headless Service 和 StatefulSet。
kubectl --context kind-c2 apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: whereami-ss
namespace: sample
labels:
app: whereami-ss
spec:
ports:
- port: 80
name: whereami
clusterIP: None
selector:
app: whereami-ss
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: whereami
namespace: sample
spec:
serviceName: "whereami-ss"
replicas: 3
selector:
matchLabels:
app: whereami-ss
template:
metadata:
labels:
app: whereami-ss
spec:
containers:
- name: whereami-ss
image: cr7258/whereami:v1
ports:
- containerPort: 80
name: whereami
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
EOF
在 c2 集群查看服務(wù)。
root@seven-demo:~# kubectl get pod -n sample --context kind-c2 -o wide -l app=whereami-ss
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
whereami-0 1/1 Running 0 38s 10.9.1.20 c2-control-plane <none> <none>
whereami-1 1/1 Running 0 36s 10.9.1.21 c2-control-plane <none> <none>
whereami-2 1/1 Running 0 31s 10.9.1.22 c2-control-plane <none> <none>
root@seven-demo:~# kubectl get svc -n sample --context kind-c2 -l app=whereami-ss
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
whereami-ss ClusterIP None <none> 80/TCP 4m58s
在 c2 集群導(dǎo)出服務(wù)。
subctl --context kind-c2 export service whereami-ss --namespace sample
解析 Headless Service 的域名可以得到所有 Pod 的 IP。
kubectl --context kind-c1 exec -it client -- bash
nslookup whereami-ss.sample.svc.clusterset.local
也可以指定單個(gè) Pod 進(jìn)行解析。
nslookup whereami-0.c2.whereami-ss.sample.svc.clusterset.local
通過域名訪問指定的 Pod。
curl whereami-0.c2.whereami-ss.sample.svc.clusterset.local
查看 ServiceImport,在 IP 地址的一欄是空的,因?yàn)閷?dǎo)出的服務(wù)類型是 Headless。
kubectl --context kind-c1 get -n submariner-operator serviceimport
kubectl --context kind-c2 get -n submariner-operator serviceimport
對(duì)于 Headless Service,Pod IP 是根據(jù) Endpointslice 來解析的。
kubectl --context kind-c1 get endpointslices -n sample
kubectl --context kind-c2 get endpointslices -n sample
4 使用 Globalnet 解決 CIDR 重疊問題
接下來將演示如何通過 Submariner 的 Globalnet 功能來解決多集群間 CIDR 重疊的問題,在本實(shí)驗(yàn)中,我們將會(huì)創(chuàng)建 3 個(gè)集群,并且將每個(gè)集群的 Service 和 Pod CIDR 都設(shè)置成相同的。
4.1 創(chuàng)建集群
# 替換成服務(wù)器 IP
export SERVER_IP="10.138.0.11"
kind create cluster --config - <<EOF
kind: Cluster
name: broker-globalnet
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
networking:
apiServerAddress: $SERVER_IP
podSubnet: "10.7.0.0/16"
serviceSubnet: "10.77.0.0/16"
EOF
kind create cluster --config - <<EOF
kind: Cluster
name: g1
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
networking:
apiServerAddress: $SERVER_IP
podSubnet: "10.7.0.0/16"
serviceSubnet: "10.77.0.0/16"
EOF
kind create cluster --config - <<EOF
kind: Cluster
name: g2
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
networking:
apiServerAddress: $SERVER_IP
podSubnet: "10.7.0.0/16"
serviceSubnet: "10.77.0.0/16"
EOF
4.2 部署 Broker
使用 --globalnet=true
參數(shù)啟用 Globalnet 功能,使用 --globalnet-cidr-range
參數(shù)指定所有集群的全局 GlobalCIDR(默認(rèn) 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
參數(shù)指定本集群的 GlobalCIDR。
subctl --context kind-g1 join broker-info.subm --clusterid g1 --globalnet-cidr 120.1.0.0/24
subctl --context kind-g2 join broker-info.subm --clusterid g2 --globalnet-cidr 120.2.0.0/24
4.4 查看集群連接
subctl show connections --context kind-g1
subctl show connections --context kind-g2
4.5 測(cè)試跨集群通信
在兩個(gè)集群中都創(chuàng)建 sample 命名空間。
kubectl --context kind-g2 create namespace sample
kubectl --context kind-g1 create namespace sample
4.5.1 ClusterIP Service
在 g2 集群創(chuàng)建服務(wù)。
kubectl --context kind-g2 apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: whereami
namespace: sample
spec:
replicas: 3
selector:
matchLabels:
app: whereami
template:
metadata:
labels:
app: whereami
spec:
containers:
- name: whereami
image: cr7258/whereami:v1
imagePullPolicy: Always
ports:
- containerPort: 80
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
---
apiVersion: v1
kind: Service
metadata:
name: whereami-cs
namespace: sample
spec:
selector:
app: whereami
ports:
- protocol: TCP
port: 80
targetPort: 80
EOF
在 g2 集群查看服務(wù)。
root@seven-demo:~/globalnet# kubectl --context kind-g2 get pod -n sample -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
whereami-754776cdc9-72qd4 1/1 Running 0 19s 10.7.1.8 g2-control-plane <none> <none>
whereami-754776cdc9-jsnhk 1/1 Running 0 20s 10.7.1.7 g2-control-plane <none> <none>
whereami-754776cdc9-n4mm6 1/1 Running 0 19s 10.7.1.9 g2-control-plane <none> <none>
root@seven-demo:~/globalnet# kubectl --context kind-g2 get svc -n sample -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
whereami-cs ClusterIP 10.77.153.172 <none> 80/TCP 26s app=whereami
在 g2 集群導(dǎo)出服務(wù)。
subctl --context kind-g2 export service --namespace sample whereami-cs
導(dǎo)出服務(wù)后,我們?cè)俨榭匆幌?g2 集群的 Service,會(huì)發(fā)現(xiàn) Submariner 自動(dòng)在與導(dǎo)出的服務(wù)相同的命名空間中創(chuàng)建了一個(gè)額外的服務(wù),并且設(shè)置 externalIPs
為分配給相應(yīng)服務(wù)的 Global IP。
kubectl --context kind-g2 get svc -n sample
在g1 集群訪問 g2 集群的 whereami 服務(wù)。
kubectl --context kind-g1 run client --image=cr7258/nettool:v1
kubectl --context kind-g1 exec -it client -- bash
DNS 將會(huì)解析到分配給 c2 集群 whereami 服務(wù)的 Global IP 地址,而不是服務(wù)的 ClusterIP IP 地址。
nslookup whereami-cs.sample.svc.clusterset.local
用 curl 命令發(fā)起 HTTP 請(qǐng)求,從輸出的結(jié)果可以發(fā)現(xiàn),在 g2 集群的 whereami 看來,請(qǐng)求的源 IP 是 120.1.0.5,也就是說當(dāng)流量從 g1 發(fā)往 g2 集群時(shí),在 g1 集群的 Gateway Node 上對(duì)流量進(jìn)行了 SNAT 源地址轉(zhuǎn)換。
curl whereami-cs.sample.svc.clusterset.local
這里結(jié)合下圖對(duì)流量進(jìn)行簡(jiǎn)單的說明:流量從 c1 集群的 client Pod 發(fā)出,經(jīng)過 DNS 解析后應(yīng)該請(qǐng)求 IP 120.2.0.253。首先經(jīng)過 veth-pair 到達(dá) Node 的 Root Network Namespace,然后經(jīng)過 Submariner Route Agent 設(shè)置的 vx-submariner 這個(gè) VXLAN 隧道將流量發(fā)往 Gateway Node 上(c1-worker)。在 Gateway Node 上將源 IP 10.7.1.7 轉(zhuǎn)換成了 120.1.0.5, 然后通過 c1 和 c2 集群的 IPsec 隧道發(fā)送到對(duì)端,c2 集群的 Gateway Node(c2-worker)接收到流量后,經(jīng)過 iptables 的反向代理規(guī)則(在這過程中根據(jù) Global IP 進(jìn)行了 DNAT)最終發(fā)送到后端的 whereami Pod 上。
我們可以分別查看 g1 和 g2 集群上 Gateway Node 的 Iptables 來驗(yàn)證 NAT 規(guī)則,首先執(zhí)行 docker exec -it g1-worker bash
和 docker exec -it g2-worker bash
進(jìn)入這兩個(gè)節(jié)點(diǎn),然后執(zhí)行 iptables-save
命令可以看到 iptables 配置,以下我篩選了相關(guān)的 iptables 配置。
g1-worker 節(jié)點(diǎn):
# 在出訪的時(shí)候?qū)⒃?IP 轉(zhuǎn)換為 120.1.0.1-120.1.0.8 中的一個(gè)
-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 節(jié)點(diǎn):
# 訪問 120.2.0.253:80 的流量跳轉(zhuǎn)到 KUBE-EXT-ZTP7SBVPSRVMWSUN 鏈
-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
# 跳轉(zhuǎn)到 KUBE-SVC-ZTP7SBVPSRVMWSUN 鏈
-A KUBE-EXT-ZTP7SBVPSRVMWSUN -j KUBE-SVC-ZTP7SBVPSRVMWSUN
# 隨機(jī)選擇 whereami 后端的一個(gè) Pod
-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
-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
-A KUBE-SVC-ZTP7SBVPSRVMWSUN -m comment --comment "sample/submariner-fzpkhsc5wssywpk5x3par6ceb6b2jinr -> 10.7.1.9:80" -j KUBE-SEP-UYVYXWJKZN2VHFJW
# DNAT 地址轉(zhuǎn)換
-A KUBE-SEP-BB74OZOLBDYS7GHU -p tcp -m comment --comment "sample/submariner-fzpkhsc5wssywpk5x3par6ceb6b2jinr" -m tcp -j DNAT --to-destination 10.7.1.7:80
-A KUBE-SEP-MTZHPN36KRSHGEO6 -p tcp -m comment --comment "sample/submariner-fzpkhsc5wssywpk5x3par6ceb6b2jinr" -m tcp -j DNAT --to-destination 10.7.1.8:80
-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
接下來測(cè)試 Globalnet 在 Headless Service + StatefulSet 場(chǎng)景下的應(yīng)用。在 g2 集群創(chuàng)建服務(wù)。
kubectl --context kind-g2 apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: whereami-ss
namespace: sample
labels:
app: whereami-ss
spec:
ports:
- port: 80
name: whereami
clusterIP: None
selector:
app: whereami-ss
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: whereami
namespace: sample
spec:
serviceName: "whereami-ss"
replicas: 3
selector:
matchLabels:
app: whereami-ss
template:
metadata:
labels:
app: whereami-ss
spec:
containers:
- name: whereami-ss
image: cr7258/whereami:v1
ports:
- containerPort: 80
name: whereami
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
EOF
在 g2 集群查看服務(wù)。
root@seven-demo:~# kubectl get pod -n sample --context kind-g2 -o wide -l app=whereami-ss
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
whereami-0 1/1 Running 0 62s 10.7.1.10 g2-worker <none> <none>
whereami-1 1/1 Running 0 56s 10.7.1.11 g2-worker <none> <none>
whereami-2 1/1 Running 0 51s 10.7.1.12 g2-worker <none> <none>
root@seven-demo:~# kubectl get svc -n sample --context kind-c2 -l app=whereami-ss
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
whereami-ss ClusterIP None <none> 80/TCP 42h
在 g2 集群導(dǎo)出服務(wù)。
subctl --context kind-g2 export service whereami-ss --namespace sample
在 g1 集群訪問 g2 集群服務(wù),Globalnet 會(huì)為每一個(gè) Headless Service 關(guān)聯(lián)的 Pod 分配一個(gè) Global IP,用于出向和入向的流量。
kubectl --context kind-g1 exec -it client -- bash
nslookup whereami-ss.sample.svc.clusterset.local
指定解析某個(gè) Pod。
nslookup whereami-0.g2.whereami-ss.sample.svc.clusterset.local
指定訪問某個(gè) Pod。
curl whereami-0.g2.whereami-ss.sample.svc.clusterset.local
查看 ServiceImport,在 IP 地址的一欄是空的,因?yàn)閷?dǎo)出的服務(wù)類型是 Headless。
kubectl --context kind-g1 get -n submariner-operator serviceimport
kubectl --context kind-g2 get -n submariner-operator serviceimport
對(duì)于 Headless Service,Pod IP 是根據(jù) Endpointslice 來解析的。
kubectl --context kind-g1 get endpointslices -n sample
kubectl --context kind-g2 get endpointslices -n sample
執(zhí)行 docker exec -it g2-worker bash
命令進(jìn)入 g2-worker 節(jié)點(diǎn),然后執(zhí)行 iptables-save
命令尋找相關(guān)的 Iptables 規(guī)則。
# SNAT
-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
-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
-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
# DNAT
-A SUBMARINER-GN-INGRESS -d 120.2.0.252/32 -j DNAT --to-destination 10.7.1.12
-A SUBMARINER-GN-INGRESS -d 120.2.0.251/32 -j DNAT --to-destination 10.7.1.11
-A SUBMARINER-GN-INGRESS -d 120.2.0.250/32 -j DNAT --to-destination 10.7.1.10
5 清理環(huán)境
執(zhí)行以下命令刪除本次實(shí)驗(yàn)創(chuàng)建的 Kind 集群。文章來源:http://www.zghlxwxcb.cn/news/detail-411362.html
kind delete clusters broker c1 c2 g1 g2
6 總結(jié)
本文首先介紹了 Submariner 的架構(gòu),包括 Broker、Gateway Engine、Route Agent、Service Discovery、Globalnet 和 Submariner Operator。接著,通過實(shí)驗(yàn)向讀者展示了 Submariner 在跨集群場(chǎng)景中如何處理 ClusterIP 和 Headless 類型的流量。最后,演示了 Submariner 的 Globalnet 是如何通過 GlobalCIDR 支持不同集群間存在 CIDR 重疊的情況。文章來源地址http://www.zghlxwxcb.cn/news/detail-411362.html
7 歡迎關(guān)注
到了這里,關(guān)于Kubernetes 多集群網(wǎng)絡(luò)方案系列 1 -- Submariner 介紹的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!