天天看點

如何使用 Istio 進行多叢集部署管理(1): 單控制平面 VPN 連接配接拓撲

服務網格作為一個改善服務到服務通信的專用基礎設施層,是雲原生範疇中最熱門的話題。随着容器愈加流行,服務拓撲也頻繁變動,這就需要更好的網絡性能。服務網格能夠通過服務發現、路由、負載均衡、心跳檢測和支援可觀測性,幫助我們管理網絡流量。服務網格試圖為無規則的複雜的容器問題提供規範化的解決方案。

服務網格也可以用于混沌工程 —— “一門在分布式系統上進行實驗的學科,目的是建構能夠應對極端條件的可靠系統”。服務網格能夠将延遲和錯誤注入到環境中,而不需要在每個主機上安裝一個守護程序。

容器是雲原生應用的基石,通過應用容器化,使得應用開發部署更加靈活、遷移更加靈活,并且這些實作都是基于标準化的。而容器編排則是更近一步,能夠更加有效地編排資源、更加高效地排程利用這些資源。而到了雲原生時代,在 Kubernetes 基礎架構之上,結合 Istio 服務網格,提供了多雲、混合雲的支援能力,針對微服務提供了有效的治理能力,并以 Kubernetes 和 Istio 為基礎,提供了針對特定應用負載的不同支援,例如針對 Kubeflow 服務的流量治理、為 Knative 提供負載的路由管理能力等。

盡管 Service Mesh 在雲原生系統方面的應用已經有了快速的增長,但仍然存在巨大的提升空間。無伺服器(Serverless)計算正好需要 Service Mesh 的命名和連結模型,這讓 Service Mesh 在雲原生生态系統中的角色得到了彰顯。服務識别和通路政策在雲原生環境中仍顯初級,而 Service Mesh 毫無疑問将成為這方面不可或缺的基礎。就像 TCP/IP 一樣,Service Mesh 将在底層基礎設施這條道路上更進一步。

混合雲可以采用多種形式。通常,混合雲指的是跨公有雲和私有(内部部署)雲運作,而多雲意味着跨多個公有雲平台運作。

采用混合雲或多雲架構可以為你的組織帶來諸多好處。例如,使用多個雲提供商可以幫助你避免供應商鎖定,能夠讓你為實作目标選擇最佳的雲服務。使用雲和本地環境,你可以同時享受雲的優勢(靈活性、可擴充性、成本降低)和本地的好處(安全性、低延遲、硬體複用)。如果你是首次遷移到雲端,采用混合雲步驟可以讓你按照自己的節奏,以最适合你業務的方式進行。

根據我們在公有雲上的實踐經驗及從客戶那裡得到的資訊,我們認為采用混合服務網絡是簡化雲和本地環境中應用程式管理、安全性和可靠性的關鍵,無論你的應用程式是在容器中運作,或是在虛拟機中運作。

Istio 的一個關鍵特性是它為你的工作負載(例如 pod、job、基于 VM 的應用程式)提供服務抽象。當你轉向混合拓撲時,這種服務抽象變得更加重要,因為現在你不隻需要關注一個環境,而是需要關注若幹個環境。

當你在一個 Kubernetes 叢集上使用 Istio 時,可以獲得包括可見性、細粒度流量政策、統一遙測和安全性在内的微服務的所有管理優勢。但是當你在多個環境中使用 Istio 時,實際上是為應用程式提供了一個新的超級能力。因為 Istio 不僅僅是 Kubernetes 的服務抽象,也是一種在整個環境中标準化網絡的方法。它是一種集中 API 管理并将 JWT 驗證與代碼分離的方法。它是跨雲提供商的安全、零信任網絡的快速通道。

那麼所有這些魔法是如何發生的呢?混合 Istio 是指一組 Istio Sidecar 代理,每一個 Envoy 代理位于所有服務的旁邊,而這些服務可能運作在不同環境中的每一個虛拟機、每一個容器中,而且這些 Sidecar 代理之前互相知道如何跨邊界互動。這些 Envoy Sidecar 代理可能由一個中央 Istio 控制平面管理,或由每個環境中運作的多個控制平面管理。

多叢集部署管理

服務網格本質上是将一組單獨的微服務組合成單個可控的複合應用程式,Istio 作為一種服務網格,也是旨在單一管理域下監視和管理協作微服務網絡。對于特定大小的應用程式,所有微服務是可以在單個編排平台如一個 Kubernetes 叢集上運作的。然而,由于規模不斷增大或者備援等原因,大多數應用程式最終将需要分發一些服務在其他地方運作。

社群越來越關注在多個叢集上運作工作負載,以實作更好的擴充,故障可以更好地隔離,進而提升應用程式的靈活性。Istio v1.0 開始支援一些多叢集功能,并在之後的版本中添加了新功能。

Istio 服務網格支援許多可能的拓撲結構,用于在單個叢集之外分發應用程式的服務,有兩種常見的模式或用例:單網格和網格聯合。顧名思義,單個網格将多個叢集組合成一個單元,由一個 Istio 控制平面管理;它可以實作為一個實體控制平面,也可以實作為一組控制平面,同時所有控制平面都能通過複制配置保持同步。而網格聯合則會将多個叢集分離作為單獨的管理域,有選擇地完成叢集之間的連接配接,僅将服務的子集暴露給其他叢集;自然它的實作會包含多個控制平面。

具體來說,這些不同的拓撲結構包括以下幾個方面:

  • 網格中的服務可以使用服務條目(Service Entry)來通路獨立的外部服務或通路由另一個松散耦合的服務網格公開的服務,通常稱為網格聯邦(Mesh Federation)。這種拓撲适合于互相獨立并且網絡隔離、隻能通過公網互動的多叢集的場景;
  • 支援在虛拟機或實體裸機上運作的服務進行服務網格擴充,通常稱為網格聯合(Mesh Expansion)。在前面章節中,我們已經講述了這種 Kubernetes 叢集與虛拟機、實體裸機之間混合部署的場景;
  • 把來自多個叢集的服務組合到單個服務網格中,通常稱為多叢集網格(Multicluster Mesh)。根據網絡拓撲結構的不同,多叢集網格通常分為單控制平面 VPN 連接配接、單控制平面網關連接配接以及多控制平面拓撲。

單控制平面 VPN 連接配接拓撲

作為基準,在 Istio 的 1.1 版本之前,Istio 1.0 多叢集僅支援使用單網格設計。它允許多個叢集連接配接到網格中,但所有叢集都在一個共享網絡上。也就是說,所有叢集中所有 pod 和服務的 IP 位址都是可直接路由的,不會發生沖突,同時保證在一個叢集中配置設定的IP位址不會在另一個叢集中同時重用。

在這種拓撲配置下,在其中一個叢集上運作單個 Istio 控制平面。該控制平面的 Pilot 管理本地和遠端叢集上的服務,并為所有叢集配置 Envoy 代理。這種方法在所有參與叢集都具有 VPN 連接配接的環境中效果最佳,是以可以使用相同的 IP 位址從其他任何地方通路網格中的每個 pod。

在此配置中,Istio 控制平面部署在其中一個叢集上,而所有其他叢集運作更簡單的遠端 Istio 配置,該配置将它們連接配接到單個 Istio 控制平面,該平面将所有 Envoy 管理為單個網格。各個叢集上的 IP 位址不允許重疊,并且遠端叢集上的服務的 DNS 解析不是自動的。使用者需要在每個參與叢集上複制服務,這樣每個叢集中的 Kubernetes 叢集服務和應用程式都能夠将其内部 Kubernetes 網絡暴露給其他叢集。一旦一個或多個遠端 Kubernetes 叢集連接配接到 Istio 控制平面,Envoy 就可以與單個控制平面通信并形成跨多個叢集的網狀網絡。

前提限制

事實上,我們已經了解到網格、叢集和網絡之間的存在各種限制,例如,在某些環境中,網絡和叢集直接相關。Istio單網格設計下的單控制平面VPN連接配接拓撲需要滿足以下幾個條件:

  • 運作 Kubernetes 1.9 或更高版本的兩個或更多叢集;
  • 能夠在其中一個叢集上部署 Istio 控制平面;
  • RFC1918 網絡、VPN 或滿足以下要求的更進階網絡技術:
    • 單個叢集 pod CIDR 範圍和服務 CIDR 範圍在多叢集環境中必須是唯一的,并且應當不重疊;
    • 每個叢集中的所有 pod CIDR 必須可以互相路由
    • 所有 Kubernetes 控制平面 API 伺服器必須可以互相路由。

此外,為了跨叢集支援 DNS 名稱解析,必須確定在所有需要跨叢集服務調用的叢集中定義對應的命名空間、服務和服務賬戶;例如,叢集 cluster1 中命名空間 ns1 的服務 service1 需要調用叢集 cluster2 中命名空間 ns2 的服務 service2,那麼在叢集 cluster1 中為了支援服務名的 DNS 解析,需要在叢集 cluster1 中建立一個命名空間 ns2 以及該命名空間下的服務 service2。

以下示例中的兩個 Kubernetes 叢集的網絡假定已經滿足上述要求,每個叢集中的 pod 都能夠互相路由,也就是說網絡可通并且端口是可通路的(如果采用的是類似于阿裡雲的公有雲服務,請確定這些端口在安全組規則下是可以通路的;否則服務間的調用會受到影響)。

兩個 Kubernetes 叢集的 pod CIDR 範圍和服務 CIDR 範圍定義如下表所示:

如何使用 Istio 進行多叢集部署管理(1): 單控制平面 VPN 連接配接拓撲

拓撲架構

如何使用 Istio 進行多叢集部署管理(1): 單控制平面 VPN 連接配接拓撲

從圖中可以看到整個多叢集拓撲中隻會在一個 Kubernetes 叢集上安裝 Istio 控制平面。這個安裝 Istio 控制平面的叢集通常被稱為本地叢集,所有其它叢集稱為遠端叢集。

這些遠端叢集隻需要安裝 Istio 的 Citadel 和 Sidecar Injector 準入控制器,具有較小的 Istio 占用空間,Citadel 用于這些遠端叢集的安全管理,Sidecar Injector 準入控制器用于控制平面中的自動注入和資料平面中工作負載的 Sidecar 代理功能。

在這個架構中,Pilot 可以通路所有叢集中的所有 Kubernetes API 伺服器,是以它具有全局網絡通路視圖。Citadel 和 Sidecar Injector 準入控制器則隻會在叢集本地範圍内運作。每個叢集都有唯一的 pod 和服務 CIDR,除此之外,叢集之間還有一個共享的扁平網絡,以保證能直接路由到任何工作負載,包括到 Istio 的控制平面。例如,遠端叢集上的 Envoy 代理需要從 Pilot 獲得配置,檢查并報告給 Mixer 等。

啟用雙向 TLS 通信

如果在多個叢集中啟用跨叢集的雙向 TLS 通信,就需要按照如下方式在各個叢集中進行部署配置。首先,從共享的根 CA 為每個叢集的 Citadel 生成中間 CA 證書,共享的根 CA 啟用跨不同叢集的雙向 TLS 通信。為了便于說明,我們将 samples/certs 目錄下 Istio 安裝中提供的示例根 CA 證書用于兩個叢集。在實際部署中,你可能會為每個叢集使用不同的 CA 證書,所有 CA 證書都由公共根 CA 簽名。

在每個 Kubernetes 叢集中(包括示例中的叢集 cluster1 與 cluster2)建立密鑰。使用以下的指令為生成的 CA 證書建立 Kubernetes 密鑰:

kubectl create namespace istio-system
kubectl create secret generic cacerts -n istio-system \
  --from-file=samples/certs/ca-cert.pem \
  --from-file=samples/certs/ca-key.pem \
  --from-file=samples/certs/root-cert.pem \
  --from-file=samples/certs/cert-chain.pem           

當然,如果你的環境隻是開發測試或者不需要啟用雙向 TLS 通信,上述步驟完全可以跳過。

部署本地控制平面

在所謂的本地叢集上安裝一個 Istio 控制平面的過程,與在單叢集上安裝 Istio 并沒有太多差别,需要注意的一點是如何配置 Envoy 代理用于管理直接通路某個 IP 範圍内的外部服務的參數。如果是使用 Helm 安裝 Istio,那麼在 Helm 中有一個名為 global.proxy.includeIPRanges 的變量,確定該變量為“*”或者包括本地叢集、所有遠端叢集的 pod CIDR 範圍和服務 CIDR。

可以通過檢視命名空間 istio-system 下的配置項 istio-sidecar-injector 中的 traffic.sidecar.istio.io/includeOutboundIPRanges 來确認 global.proxy.includeIPRanges 參數設定,如下所示:

kubectl get configmap istio-sidecar-injector -n istio-system -o yaml| grep includeOutboundIPRanges           

在部署 Istio 控制平面元件的叢集 cluster1 中,按照以下步驟執行。

如果啟用了雙向 TLS 通信,則需要如下配置參數:

helm template --namespace=istio-system \
  --values
install/kubernetes/helm/istio/values.yaml \
  --set global.mtls.enabled=true \
  --set security.selfSigned=false \
  --set global.controlPlaneSecurityEnabled=true
\
  install/kubernetes/helm/istio > istio-auth.yaml
kubectl
apply -f istio-auth.yaml           

如果不需要啟用雙向 TLS 通信,配置參數則需要做出如下修改:

helm template --namespace=istio-system \
  --values
install/kubernetes/helm/istio/values.yaml \
  --set global.mtls.enabled=false \
  --set security.selfSigned=true \
  --set global.controlPlaneSecurityEnabled=false
\
  install/kubernetes/helm/istio >
istio-noauth.yaml
kubectl
apply -f istio-noauth.yaml           

修改 Istio 服務 istio-pilot、istio-telemetry、istio-policy 及 zipkin 的類型為内網負載均衡,将這些服務以内網方式暴露給遠端叢集使用。不同的雲廠商實作機制不盡相同,但大都是通過修改 annotation 的方式實作。針對阿裡雲容器服務來說,設定為内網負載均衡的方式非常簡單,隻需要添加如下 annotation 到服務的 YAML 定義中即可:

service.beta.kubernetes.io/alicloud-loadbalancer-address-type: intranet。

此外,需要按照圖中的端口定義為每一個服務進行設定。

如何使用 Istio 進行多叢集部署管理(1): 單控制平面 VPN 連接配接拓撲

istio-pilot 服務端口如表 1 所示。

如何使用 Istio 進行多叢集部署管理(1): 單控制平面 VPN 連接配接拓撲

istio-telemetry 服務端口如表 2 所示

如何使用 Istio 進行多叢集部署管理(1): 單控制平面 VPN 連接配接拓撲

istio-policy 服務端口如表 3 所示。

如何使用 Istio 進行多叢集部署管理(1): 單控制平面 VPN 連接配接拓撲

zipkin 服務端口如表 4 所示。

如何使用 Istio 進行多叢集部署管理(1): 單控制平面 VPN 連接配接拓撲

安裝 istio-remote

在本地叢集中安裝完控制平面之後,必須将 istio-remote 元件部署到每個遠端 Kubernetes 叢集。等待 Istio 控制平面完成初始化,然後再執行本節中的步驟。你必須在 Istio 控制平面叢集上運作這些操作以捕獲 Istio 控制平面服務端點,例如上述提到的 Istio 服務 istio-pilot、istio-telemetry、istio-policy 以及 zipkin。

在遠端叢集 cluster2 中部署 Istio-remote 元件,按照以下步驟執行:

1.在本地叢集上使用以下指令設定環境變量:

export
PILOT_IP=$(kubectl -n istio-system get service istio-pilot -o
jsonpath='{.status.loadBalancer.ingress[0].ip}')
export
POLICY_IP=$(kubectl -n istio-system get service istio-policy -o
jsonpath='{.status.loadBalancer.ingress[0].ip}')
export
TELEMETRY_IP=$(kubectl -n istio-system get service istio-telemetry -o
jsonpath='{.status.loadBalancer.ingress[0].ip}')
export
ZIPKIN_IP=$(kubectl -n istio-system get service zipkin -o
jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo
$PILOT_IP $POLICY_IP $TELEMETRY_IP $ZIPKIN_IP           

2.如果在多個叢集中啟用跨叢集的雙向 TLS 通信,就需要在叢集中進行部署配置。

當然,如果你的環境隻是開發測試或者不需要啟用雙向 TLS 通信的話,該步驟完全可以跳過。在遠端 Kubernetes 叢集 cluster2 上運作以下指令,在叢集中為生成的 CA 證書建立 Kubernetes 密鑰:

kubectl
create namespace istio-system
kubectl
create secret generic cacerts -n istio-system \
    --from-file=samples/certs/ca-cert.pem \
    --from-file=samples/certs/ca-key.pem \
    --from-file=samples/certs/root-cert.pem \
--from-file=samples/certs/cert-chain.pem           

3.在遠端 Kubernetes 叢集 cluster2 上,通過執行以下指令,使用 Helm 建立 Istio remote 部署 YAML 檔案。

helm
template install/kubernetes/helm/istio \
  --name istio-remote \
  --namespace istio-system \
  --values install/kubernetes/helm/istio/values-istio-remote.yaml
\
  --set global.mtls.enabled=true \
  --set security.selfSigned=false \
  --set global.controlPlaneSecurityEnabled=true
\
  --set
global.remotePilotCreateSvcEndpoint=true \
  --set global.remotePilotAddress=${PILOT_IP} \
  --set global.remotePolicyAddress=${POLICY_IP}
\
  --set
global.remoteTelemetryAddress=${TELEMETRY_IP}
  --set global.remoteZipkinAddress=${ZIPKIN_IP}
> istio-remote-auth.yaml           

然後将 Istio remote 元件部署到 cluster2,如下所示:

kubectl apply -f ./istio-remote-auth.yaml           
helm
template install/kubernetes/helm/istio \
  --name istio-remote \
  --namespace istio-system \
  --values
install/kubernetes/helm/istio/values-istio-remote.yaml \
  --set global.mtls.enabled=false \
  --set security.selfSigned=true \
  --set
global.controlPlaneSecurityEnabled=false \
  --set
global.remotePilotCreateSvcEndpoint=true \
  --set global.remotePilotAddress=${PILOT_IP} \
  --set global.remotePolicyAddress=${POLICY_IP}
\
  --set global.remoteTelemetryAddress=${TELEMETRY_IP}
  --set global.remoteZipkinAddress=${ZIPKIN_IP}
> istio-remote-noauth.yaml           
kubectl
apply -f ./istio-remote-noauth.yaml           

確定上述步驟在 Kubernetes 叢集中執行成功。

4.建立叢集 cluster2 的 Kubeconfig。

安裝 Istio-remote Helm chart 後,在遠端叢集中建立了一個叫 istio-multi 的 Kubernetes 服務帳号,該服務帳号用于最小化 RBAC 通路請求,對應的叢集角色定義如下:

kind:
ClusterRole
apiVersion:
rbac.authorization.k8s.io/v1
metadata:
  name: istio-reader
rules:
  - apiGroups: ['']
    resources: ['nodes', 'pods', 'services',
'endpoints']
    verbs: ['get', 'watch', 'list']           

下面的過程通過使用先前所述的 istio-multi 服務帳号憑證生成一個遠端叢集的 kubeconfig 配置檔案。通過以下指令,在叢集 cluster2 上建立服務帳号 istio-multi 的 Kubeconfig,并儲存為檔案 n2-k8s-config:

CLUSTER_NAME="cluster2"
SERVER=$(kubectl
config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
SECRET_NAME=$(kubectl
get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
CA_DATA=$(kubectl
get secret ${SECRET_NAME} -n istio-system -o
"jsonpath={.data['ca\.crt']}")
TOKEN=$(kubectl
get secret ${SECRET_NAME} -n istio-system -o
"jsonpath={.data['token']}" | base64 --decode)
cat
<<EOF > n2-k8s-config
apiVersion:
v1
kind:
Config
clusters:
  - cluster:
      certificate-authority-data: ${CA_DATA}
      server: ${SERVER}
    name: ${CLUSTER_NAME}
contexts:
  - context:
      cluster: ${CLUSTER_NAME}
      user: ${CLUSTER_NAME}
    name: ${CLUSTER_NAME}
current-context:
${CLUSTER_NAME}
users:
  - name: ${CLUSTER_NAME}
    user:
      token: ${TOKEN}
EOF           

5.将叢集 cluster2 加入 Istio Pilot 所在叢集中。

在叢集 dusterl 執行以下指令,将上述生成的叢集 cluster2 的 kubeconfig 添加到叢集 cluster1 的 secret 中。執行這些指令後,叢集 cluster1 中的 Istio Pilot 将開始監聽叢集 cluster2 的服務和執行個體,就像監聽叢集 cluster1 中的服務與執行個體一樣:

kubectl
create secret generic n2-k8s-secret --from-file n2-k8s-config -n istio-system
kubectl
label secret n2-k8s-secret istio/multiCluster=true -n istio-system           

部署示例應用

為了示範跨叢集通路,在第一個 Kubernetes 叢集 cluster1 中部署 sleep 應用服務和版本 v1 的 helloworld 服務,在第二個叢集 cluster2 中部署版本 v2 的 helloworld 服務,然後驗證 sleep 應用是否可以調用本地或者遠端叢集的 helloworld 服務。

1.部署 sleep 和版本 v1 的 helloworld 服務到第一個叢集 cluster1 中,執行如下指令:

kubectl
create namespace app1
kubectl
label namespace app1 istio-injection=enabled
kubectl
apply -n app1 -f multicluster/sleep/sleep.yaml
kubectl
apply -n app1 -f multicluster/helloworld/service.yaml
kubectl
apply -n app1 -f multicluster/helloworld/helloworld.yaml -l version=v1
export
SLEEP_POD=$(kubectl get -n app1 pod -l app=sleep -o
jsonpath={.items..metadata.name})           
  1. 部署版本 v2 的 helloworld 服務到第二個叢集 cluster2 中,執行如下指令:
kubectl
create namespace app1
kubectl
label namespace app1 istio-injection=enabled
kubectl
apply -n app1 -f multicluster/helloworld/service.yaml
kubectl
apply -n app1 -f multicluster/helloworld/helloworld.yaml -l version=v2           
  1. 驗證在叢集 cluster1 中的 sleep 服務是否可以正常調用本地或者遠端叢集的 helloworld 服務,在叢集 cluster1 下執行如下指令:
kubectl
exec $SLEEP_POD -n app1 -c sleep -- curl helloworld.app1:5000/hello           

如果設定正确,則在傳回的調用結果中可以看到兩個版本的 helloworld 服務,同時可以通過檢視 sleep 容器組中的 istio-proxy 容器日志來驗證通路的端點 IP 位址,傳回結果如下所示:

如何使用 Istio 進行多叢集部署管理(1): 單控制平面 VPN 連接配接拓撲
  1. 驗證 Istio 路由規則是否生效。

    建立針對上述兩個版本的 helloworld 服務的路由規則,以便驗證 Istio 配置是否可以正常工作。

建立 Istio 虛拟服務 VirtualService,執行如下指令:

kubectl
apply -n app1 -f multicluster/helloworld/virtualservice.yaml           

接着,建立 Istio 目标規則 DestinationRule:

kubectl
apply -n app1 -f multicluster/helloworld/destinationrule.yaml           
  • 如果不需要啟用雙向 TLS 通信,配置參數則需要做出修改,在 YAML 定義中添加trafficPolicy.tls.mode:ISTIO_MUTUAL,定義如下所示:
apiVersion:
networking.istio.io/v1alpha3
kind:
DestinationRule
metadata:
  name: helloworld
spec:
  host: helloworld
  **trafficPolicy:
  tls:
    mode: ISTIO_MUTUAL**
  subsets:
  - name: v1
  labels:
    version: v1
  - name: v2
  labels:
    version: v2           

通過執行指令 kubectl apply 建立啟用雙向 TLS 的 Istio 目标規則,如下所示:

kubectl
apply -n app1 -f multicluster/helloworld/destinationrule-auth.yaml           

多次調用 helloworld 服務,隻會傳回版本 v2 的響應結果,如下所示:

如何使用 Istio 進行多叢集部署管理(1): 單控制平面 VPN 連接配接拓撲

繼續閱讀