本文分享自華為雲社群《基於開源istio治理CCE多叢集--非扁平網路場景》,作者:可以交個朋友。
一 背景
鑑於扁平網路模式下對網路存在嚴格的要求,需要所有叢集處於同一個扁平網路,Pod IP地址互通且不重疊,Service 網段也不能衝突。這些網路需求在實際環境中可能難以滿足,限制了該模式的應用場景。istio支援了更加靈活的多叢集網路方案,即非扁平網路模型。
二 方案簡介
採用多網路多控制面方案,除了扁平網路多控制面網格的方案中使用的技術外(dns解析,跨叢集apiserver的訪問),該方案最為關鍵的技術是istio-eastwestgateway 東西向閘道器。東西向閘道器解決了網格跨網路訪問的難題,在客戶端的工作負載發起請求時,針對其他叢集的工作負載,資料流量首先會被轉發到目標所在叢集的入口閘道器,然後由閘道器將請求轉發到工作負載容器。
若需實現該方案,需要在扁平網路的基礎上再回答如下關鍵問題:
-
問題一、同一個負載多叢集部署,如何判斷各個endpoint在哪個network內?
在安裝istio時可以在operator配置中指定網路環境,比如A叢集istio的network=networkA,B叢集istio的network=networkB,那麼單網格多控制面配置後,各自叢集的pod會預設被注入對應的env。A、B叢集的istiod在聚合多叢集負載例項的endpoint資訊時,也會基於pod的特定env或者lable欄位中的network標識獲取網路資訊。也可以透過為istiod系統namespace設定topology.istio.io/network
為該網格標識network,則該叢集注入的pod預設增加topology.istio.io/network
label。
或 -
問題二、跨叢集的流量是如何被識別併傳送到東西向閘道器?
在為所有sidecar生成EDS配置時,根據客戶端代理和各個服務端例項的network標識(如問題一中的topology.istio.io/network
label或ISTIO_MATE_NETWORK
env),如果標識不同則認為客戶端和服務端跨網路,自動將對應後端的Endpoint地址轉換成其所在網路入口的東西向閘道器地址。大致的過程大致為:
1) istiod 將服務的所有Endpoint都聚合起來,然後對所有的Endpoint都做一次遍歷,根據呼叫方及Endpoint的網路標籤topology.istio.io/network
,決定是否進行Endpoint地址轉換。
2) 如果Endpoint與呼叫者在同一Network內,則其地址保持不變;如果Endpoint與呼叫者不在同一Network內,則將其地址轉換為所在叢集的入口閘道器地址。這種在非扁平網路多叢集模型中核心技術被稱為Split Horizon EDS
上圖中1.94.58.222為istio-eastwestgateway 東西向閘道器的service externalIP地址 -
問題三、東西向閘道器又是如何將資料包文轉發給目標服務的?
Istio東西向閘道器在工作時使用基於SNI的路由,指定了在TLS握手時要連線的主機名(對應後端的服務名)。SNI協議是為了支援同一個IP地址的多個域名。東西向閘道器埠15443預設了SNI感知的Listener。當cluster1中客戶端發出的流量被攔截到Sidecar後,Sidecar會將其轉換為mTLS流量,並帶上SNI資訊轉發出去,在流量到達cluster2的 istio-eastwest-gateway 15443埠後,gateway會提取SNI資訊,分析出實際的目的服務,最終轉發給cluster2中的相關pod。
三 跨網路多控制面網格環境搭建
3.1 CCE叢集準備
-
cluster1位於北京四region,cluster2 位於上海一region。不在一個網路平面,所以不用考慮Pod網段 Service網段是否衝突等問題,組網複雜性降低。
-
需要注意: istio控制面元件istioD會ist-watch 每個kubernetes叢集的pod 、endpoint、service等資源資訊,因此每個叢集需要給apiserver繫結公網地址,以便各自叢集中的istioD元件能夠訪問remote 叢集的apiserver。
-
將每個叢集的kubeconfig檔案複製儲存到相關檔案中,istiod訪問remote 叢集需要藉助該kubeconfig檔案生成相關secret。istiod會根據相關標籤(istio/multiCluster: ‘true’)探測到該secret,然後使用該secret訪問到remote叢集的kube-apiserver
-
為了方便使用kubectl,設定alias
alias k1='kubectl --kubeconfig=/root/.kube/cluster1.yaml'
alias k2='kubectl --kubeconfig=/root/.kube/cluster2.yaml'
3.2 CA根證書準備
在istio多叢集通訊中,每個叢集都會有一個istio控制面,其中包含一個CA服務,該CA服務會自動為每個叢集中的Istio Sidecar 生成證書和金鑰,並用於為Pod之間的mTLS加密認證提供證書籤名。這些證書和金鑰是由Istio CA服務簽發的,以確保通訊的安全性和可靠性。跨叢集的mTL要求共享一個root CA,各叢集本地的CA(citadel)間的CA證書,需要由root CA簽發
-
下載istio安裝包
wget https://github.com/istio/istio/releases/download/1.22.3/istio-1.22.3-linux-amd64.tar.gz
CCE 叢集的kubernetes 版本為1.28,可使用1.22版本的istio,kubernetes 和istio的對應關係 可參照:https://istio.io/latest/zh/docs/releases/supported-releases/#support-status-of-istio-releases -
在 Istio 安裝包的頂層目錄下,建立一個目錄來存放證書和金鑰
mkdir -p certs
pushd certs
-
生成根證書和金鑰:
make -f ../tools/certs/Makefile.selfsigned.mk root-ca
將會生成以下檔案:
○ root-cert.pem:生成的根證書
○ root-key.pem:生成的根金鑰
○ root-ca.conf:生成根證書的 openssl 配置
○ root-cert.csr:為根證書生成的 CSR -
對於每個叢集,為 Istio CA 生成一箇中間證書和金鑰
make -f ../tools/certs/Makefile.selfsigned.mk cluster1-cacerts
make -f ../tools/certs/Makefile.selfsigned.mk cluster2-cacerts
執行以上命令,將會在名為 cluster1、cluster2 的目錄下生成以下檔案:
○ ca-cert.pem:生成的中間證書
○ ca-key.pem:生成的中間金鑰
○ cert-chain.pem:istiod 使用的生成的證書鏈
○ root-cert.pem:根證書 -
在每個叢集中,建立一個私密 cacerts,包括所有輸入檔案 ca-cert.pem, ca-key.pem,root-cert.pem 和 cert-chain.pem
k1 create namespace istio-system k1 create secret generic cacerts -n istio-system \ --from-file=cluster1/ca-cert.pem \ --from-file=cluster1/ca-key.pem \ --from-file=cluster1/root-cert.pem \ --from-file=cluster1/cert-chain.pem
k2 create namespace istio-system k2 create secret generic cacerts -n istio-system \ --from-file=cluster1/ca-cert.pem \ --from-file=cluster1/ca-key.pem \ --from-file=cluster1/root-cert.pem \ --from-file=cluster1/cert-chain.pem
-
證書建立完畢,返回istio 安裝目錄
3.3 為各個叢集設定預設網路
為了向Istio提供叢集或者網路上下文,每個叢集都有自己的ClusterID(叢集標籤)及對應的Network(網路標籤),可以透過給Istio系統名稱空間新增topology.istio.io/network
標籤,標識Network
建立名稱空間 istio-system 之後,我們需要設定叢集的網路
kubectl --kubeconfig=/root/.kube/cluster1.yaml label ns istio-system topology.istio.io/network=network1
kubectl --kubeconfig=/root/.kube/cluster2.yaml label ns istio-system topology.istio.io/network=network2
3.4 註冊遠端叢集的服務發現
istiod 需要負責連線所有叢集的kube-apiserver,並且List-Watch 獲取每個叢集的Service、Endpoint、Pod、等。
對於叢集內 istiod 透過Pod內建的token連線所在叢集的Kube-apiserver,自動將該叢集的服務發現資料(如 service、Endpoint等)接入istio控制面
至於如何獲取remote叢集中的k8s資源,istio約定的方式是 : 需要使用者主動提供遠端叢集的訪問憑證kubconfig檔案,然後將該訪問憑證存於Secret,該Secret的標籤需設定為istio/multiCluster: true
,istiod可以透過該secret提供的訪問憑據與remote叢集建立連線,監聽remote叢集的所有服務和相關資源的變化。
-
在 cluster1 中安裝remote叢集的 secret,該 secret 提供 cluster2 的 kube-apiserver的訪問許可權。
istioctl create-remote-secret \ --kubeconfig=/root/.kube/cluster2.yaml \ --name=cluster2 | \ kubectl apply -f - --kubeconfig=/root/.kube/cluster1.yaml
-
在 cluster2 中安裝remote叢集的 secret,該 secret 提供 cluster1 的 kube-apiserver的訪問許可權。
istioctl create-remote-secret \ --kubeconfig=/root/.kube/cluster1.yaml \ --name=cluster1 | \ kubectl apply -f - --kubeconfig=/root/.kube/cluster2.yaml
-
檢視secret
3.5 將istio分別安裝在各個叢集中
因為是多istio控制面部署,所以每個叢集都需要安裝istio。本文件採用istioctl方式進行安裝,安裝API引數配置參考: https://istio.io/latest/zh/docs/reference/config/istio.operator.v1alpha1/
-
cluster1叢集istio安裝配置,將該配置檔案儲存在當前目錄下 命名為xxx.yaml
apiVersion: install.istio.io/v1alpha1 kind: IstioOperator spec: hub: swr.cn-north-4.myhuaweicloud.com/hjmtest #配置istio安裝的映象倉庫 meshConfig: accessLogFile: /dev/stdout #開啟訪問日誌 values: global: meshID: mesh1 multiCluster: clusterName: cluster1 network: network1 # 叢集的網路標識
istioctl install --kubeconfig=/root/.kube/cluster1.yaml -f cluster1.yaml
-
cluster2 叢集istio 安裝配置
apiVersion: install.istio.io/v1alpha1 kind: IstioOperator spec: hub: swr.cn-north-4.myhuaweicloud.com/hjmtest #配置istio安裝的映象倉庫 meshConfig: accessLogFile: /dev/stdout #開啟訪問日誌 values: global: meshID: mesh1 #和cluster1 同屬於一個mesh multiCluster: clusterName: cluster2 #需要區別cluster1 network: network2 # 叢集的網路標識,非扁平網路,區別cluster1中的network1
istioctl install --kubeconfig=/root/.kube/cluster2.yaml -f cluster2.yaml
-
檢視istio控制面和南北向閘道器資料面例項是否就緒
3.6 為各個叢集安裝istio東西向閘道器
為了解決非扁平網路的訪問限制,可以透過配置東西向閘道器來轉發跨叢集的訪問流量。這種方案依賴Split Horizon EDS ,自動重寫Remote叢集的Endpoint地址為閘道器地址
-
在 cluster1 、cluster2叢集中安裝專用的 東西向閘道器。
samples/multicluster/gen-eastwest-gateway.sh --network network1 | istioctl --kubeconfig=/root/.kube/cluster1.yaml install -y -f -
samples/multicluster/gen-eastwest-gateway.sh --network network2 | istioctl --kubeconfig=/root/.kube/cluster2.yaml install -y -f -
可以看到東西向閘道器例項已就緒
備註: gen-eastwest-gateway.sh東西向閘道器安裝指令碼位於istio安裝目錄下,注意安裝位置。 -
由於東西向閘道器建立的是LoadBalancer型別的service,需要雲廠商提供ELB。IstioOperatorAPI配置檔案在進行閘道器資料面定製時並未提供service annotation api的宣告。使用的預設的配置檔案安裝時,存在ELB無法自動建立的情況,可以前往CCE控制檯手動進行設定。
由於繫結了公網EIP,生產環境可能需要新增額外的訪問限制(即:透過防火牆規則)來防止外部攻擊。 -
開放 cluster1 、cluster2中的服務
因為叢集位於不同的網路中,所以我們需要在兩個叢集東西向閘道器上開放所有服務(*.local)。 雖然此閘道器在網際網路上是公開的,但它背後的服務只能被擁有可信 mTLS 證書、工作負載 ID 的服務訪問, 就像它們處於同一網路一樣。kubectl --kubeconfig=/root/.kube/cluster1.yaml apply -n istio-system -f samples/multicluster/expose-services.yaml kubectl --kubeconfig=/root/.kube/cluster2.yaml apply -n istio-system -f samples/multicluster/expose-services.yaml
expose-services.yaml詳細內容如下:
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: cross-network-gateway spec: selector: istio: eastwestgateway servers: - port: number: 15443 name: tls protocol: TLS tls: mode: AUTO_PASSTHROUGH hosts: - "*.local"
-
關於mTLS和AUTO_PASSTHROUGH說明
大部分場景下 Istio Ingress Gateway需要配套指定服務的VIrtualService,用來指定Ingress流量的後端服務。但在多網路模式下,該入口閘道器需要作為本資料平面所有服務的流量入口,也就是說所有服務共享單個Ingress Gateway(單個IP地址),這裡其實是利用了TLS中的SNI(Server Name Indication).
傳統的入口閘道器承載的是南北向流量,這裡的入口閘道器屬於網格內部流量,承載的是東西向流量。設定AUTO_PASSTHROUGH
,可以允許服務無需配置VirtualService,直接使用TLS中的SNI值來表示Upstream,服務相關的service/subset/port都可以編碼到SNI內容中
四 驗證多叢集跨網路流量治理
驗證內容主要包括跨叢集的服務訪問,跨叢集的灰度釋出。
4.1 跨叢集服務訪問
-
部署客戶端和服務端
####客戶端 k1 apply -f samples/sleep/sleep.yaml -n sample k2 apply -f samples/sleep/sleep.yaml -n sample ####服務端兩個版本,cluster1 部署v1 cluster2 部署v2 k1 apply -f samples/helloworld/helloworld.yaml -l version=v1 -n sample k1 apply -f samples/helloworld/helloworld.yaml -l service=helloworld -n sample ### k2 apply -f samples/helloworld/helloworld.yaml -l version=v2 -n sample k2 apply -f samples/helloworld/helloworld.yaml -l service=helloworld -n sample
-
分別從cluster1、cluster2 的客戶端發起請求進行訪問測試
-
檢視客戶端、服務端、東西向閘道器的訪問日誌,發現資料流路徑
cluster1中客戶端邊車容器日誌
cluster2中的東西向閘道器日誌:
cluster2中服務端邊車容器日誌:
4.2 跨叢集灰度釋出
在多控制面模型中,Istio控制面目前只監聽主叢集(也就是istio所在叢集)的 VirtualService、DestinationRule、Gateway等Istio API物件,因此 對於多控制面模型來說,相同的Istio配置需要被複制下發到多個叢集中,否則不同叢集的Sidecar訂閱到的xDS配置可能會存在嚴重的不一致,導致不同叢集的服務訪問行為不一致。
-
針對服務端配置virtualservice路由規則
apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: helloworld namespace: sample spec: hosts: - helloworld http: - route: - destination: host: helloworld subset: v2 weight: 20 - destination: host: helloworld subset: v1 weight: 80
以上規則表示,當有流量訪問域名為helloworld的服務時,客戶端服務的sidecar容器istio-proxy會將20%的流量路由到v2版本的helloworld負載上,將80%的流量路由到v1版本的helloworld負載上。
-
針對服務端配置destinationrule目標規則
apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: helloworld namespace: sample spec: host: helloworld subsets: - name: v2 labels: version: v2 - name: v1 labels: version: v1
以上規則表示,將域名為helloworld的服務建立兩個subset子集。負載例項滿足標籤為version: v2 將被定義到subset v2中,負載例項滿足標籤為version: v1將被定義到subset v1中,一般和virtualservice結合使用。
-
將上述istio CRD規則應用於cluster1、cluster2中
-
訪問測試,以cluster2叢集中的sleep服務作為客戶端訪問helloworld服務端
4.3 地域負載均衡
-
istio的流量治理一般都是透過virtualservice、destinationrule 、envoyfilter等來實現,其中地域故障轉移是透過destinationrule配置實現的。因為在destinationrule中可以配置outerlineDecetion進行異常點檢測,只有檢測到異常後,才會進行故障轉移
-
詳細配置如下
apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: helloworld namespace: sample spec: host: helloworld.sample.svc.cluster.local subsets: - name: v2 labels: version: v2 - name: v1 labels: version: v1 trafficPolicy: connectionPool: http: maxRequestsPerConnection: 1 loadBalancer: simple: ROUND_ROBIN localityLbSetting: #開啟地域負載均衡 enabled: true failover: #配置故障轉移策略,failover主要控制Region等上層位置的切換 - from: cn-north-4 to: cn-east-3 outlierDetection: #異常點檢測 consecutive5xxErrors: 1 interval: 1s baseEjectionTime: 1m
以上治理策略表示:
○ 異常點檢測:當某個客戶端訪問helloworld服務時,客戶端對應的envoy會根據本次訪問HTTP狀態碼對轉發的服務端進行故障檢測,故障檢測條件為當發生1次5xx錯誤時例項就會被隔離1m。
○ 故障隔離:當指定region的所有後端例項均不正常,觸發故障轉移到下一個地域,確保了超出地區邊界的故障轉移將具有可預測的行為。如果位於cn-north-4 region的例項異常,流量就會發往cn-east-3 region 的例項 -
訪問測試,分別從cluster1、cluster2 進行訪問,檢視流量分發結果
五 注意事項
在跨網路多控制面的網格實踐中,發現一些有趣的現象,做下記錄宣告。
-
跨叢集訪問時,只有注入了istio-proxy的例項才能接受到跨網路的流量,同時訪問方式也僅支援透過serviceName:port的形式,不能透過podIP或者clusterIP進行訪問,即使該pod注入了邊車容器。
-
在做灰度釋出訪問測試時,發現一些問題。如果僅在cluster2中下發virtualservice、destinationrule流量治理規則,在進行跨叢集灰度釋出時,訪問會報錯,報錯內容為TLS_error:|33554536:system library:OPENSSL_internal:Connection reset by peer,如果在遠端叢集重複下發virtualservice、destinationrule流量治理規則,則不會報錯
客戶端訪問報錯資訊:
客戶端sidecar容器訪問日誌: 其中119.3.173.42
為東西向閘道器的externalIP
東西向閘道器訪問日誌: 其中192.168.5.21
為東西向閘道器pod例項ip地址,119.3.122.8
為客戶端叢集容器出網的ip -
如果存在灰度釋出和地域負載均衡的使用情況,virtualservice一般作用在不同的cluster上,destinationrule作用在同個cluster的不同endpoint上,所以流量會先按照那個灰度釋出的規則派發流量到不同版本的相同服務上,然後再考慮地域負載均衡。這樣很容易引起誤解。如果不同region的cluster上部署了不同版本的服務,從其中一個region的客戶端進行訪問,流量會安裝灰度規則分發到不同region的叢集,從而忽略了地域負載均衡。