如何使用 Istio 進行多集羣部署管理:多控制平面

頭圖.png

作者 | 王夕寧  阿里雲高級技術專家

導讀:本文摘自於阿里雲高級技術專家王夕寧撰寫的《Istio 服務網格技術解析與實戰》一書,講述瞭如何使用 Istio 進行多集羣部署管理來闡述服務網格對多雲環境、多集羣即混合部署的支持能力。

前文詳情:

在多控制平面拓撲的配置中,每個 Kubernetes 集羣都會安裝相同的 Istio 控制平面,並且每個控制平面只會管理自己集羣內的服務端點。通過使用 Istio 網關、公共根證書頒發機構(CA)以及服務條目 ServiceEntry,可以將多個集羣配置組成一個邏輯上的單一服務網格。這種方法沒有特殊的網絡要求,因此通常被認爲是在 Kubernetes 集羣之間沒有通用網絡連接時的一種最簡單方法。

在這種拓撲配置下,Kubernetes 跨集羣通信需要服務之間的雙向 TLS 連接,要在集羣之間啓用雙向 TLS 通信,每個集羣的 Citadel 將配置由共享的根 CA 生成的中間 CA 證書,如圖所示。

1.png
(多控制平面)

部署控制平面

從共享的根 CA 爲每個集羣的 Citadel 生成中間 CA 證書,共享的根 CA 啓用跨不同集羣的雙向 TLS 通信。爲了便於說明,我們將 samples/certs 目錄下 Istio 安裝中提供的示例根 CA 證書用於兩個集羣。在實際部署中,你可能會爲每個集羣使用不同的 CA 證書,所有 CA 證書都由公共根 CA 簽名。

在每個 Kubernetes 集羣中實施以下步驟,以在所有集羣中部署相同的 Istio 控制平面配置。

  1. 使用以下的命令爲生成的 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
  1. 安裝 Istio 的 CRD 並等待幾秒鐘,以便將它們提交給 Kubernetes API 服務器,如下所示:
for
i in install/kubernetes/helm/istio-init/files/crd*yaml; do kubectl apply -f $i;
done
  1. 部署 Istio 控制平面:如果 helm 依賴項缺失或者不是最新的,可以通過 helm dep update 來更新這些依賴項。注意因爲沒有使用 istio-cni,可以暫時將其從依賴項 requirements.yaml 中去掉再執行更新操作。具體執行命令如下:
helm
template install/kubernetes/helm/istio --name istio --namespace istio-system \
  -f
install/kubernetes/helm/istio/values-istio-multicluster-gateways.yaml >
./istio.yaml
kubectl
apply -f ./istio.yaml

確保上述步驟在每個 Kubernetes 集羣中都執行成功。當然,通過 helm 生成 istio.yaml 的命令執行一次即可。

設置 DNS

爲遠程集羣中的服務提供 DNS 解析,則現有應用程序不需要做修改就可以運行,因爲應用程序通常期望通過其 DNS 名稱來解析服務並訪問所得到的 IP 地址。Istio 本身不使用 DNS 在服務之間路由請求,同一個 Kubernetes 集羣下的服務會共享一個相同的 DNS 後綴(例如 svc.cluster.local)。Kubernetes DNS 爲這些服務提供 DNS 解析能力。爲了給遠程集羣中的服務提供相似的設置,將遠程集羣中的服務以 <name>.<namespace>.global 的格式命名。

Istio 安裝包中附帶了一個 CoreDNS 服務器,該服務器將爲這些服務提供DNS解析能力。爲了利用這個 DNS 解析能力,需要配置 Kubernetes 的 DNS 服務指向該 CoreDNS 服務。該 CoreDNS 服務將作爲 .global DNS 域的 DNS 服務器。

對於使用 kube-dns 的集羣,請創建以下配置項或更新現有的配置項:

kubectl
apply -f - <<EOF
apiVersion:
v1
kind:
ConfigMap
metadata:
  name: kube-dns
  namespace: kube-system
data:
  stubDomains: |
    {"global": ["$(kubectl get
svc -n istio-system istiocoredns -o jsonpath={.spec.clusterIP})"]}
EOF

對於使用 CoreDNS 的集羣,請創建以下配置項或更新現有的配置項:

kubectl
apply -f - <<EOF
apiVersion:
v1
kind:
ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health
        kubernetes cluster.local in-addr.arpa
ip6.arpa {
           pods insecure
           upstream
           fallthrough in-addr.arpa ip6.arpa
        }
        prometheus :9153
        proxy . /etc/resolv.conf
        cache 30
        reload
        loadbalance
    }
    global:53 {
        errors
        cache 30
        proxy . $(kubectl get svc -n
istio-system istiocoredns -o jsonpath={.
          spec.clusterIP})
    }
EOF

部署示例應用

爲了演示跨集羣訪問,在一個 Kubernetes 集羣中部署 sleep 應用服務,在第二個集羣中部署 httpbin 應用服務,然後驗證 sleep 應用是否可以調用遠程集羣的 httpbin 服務。

  1. 部署 sleep 服務到第一個集羣 cluster1 中,執行如下命令:
kubectl
create namespace app1
kubectl
label namespace app1 istio-injection=enabled
kubectl
apply -n app1 -f samples/sleep/sleep.yaml
export
SLEEP_POD=$(kubectl get -n app1 pod -l app=sleep -o
jsonpath={.items..metadata.name})
  1. 部署 httpbin 服務到第二個集羣 cluster2 中,執行如下命令:
kubectl
create namespace app2
kubectl
label namespace app2 istio-injection=enabled
kubectl
apply -n app2 -f samples/httpbin/httpbin.yaml
  1. 獲取集羣 cluster2 的入口網關地址,如下所示:
export
CLUSTER2_GW_ADDR=$(kubectl get svc --selector=app=istio-ingressgateway \
  -n istio-system -o
jsonpath="{.items[0].status.loadBalancer.ingress[0].ip}")
  1. 爲了讓在集羣 cluster1 中的服務 sleep 能夠訪問集羣 cluster2 中的服務 httpbin,我們需要在集羣 cluster1 中爲服務 httpbin 創建一個服務條目 ServiceEntry 資源。服務條目 ServiceEntry 的主機名應該是<name>.<namespace>.globalname,其中 name 和 namespace 分別對應於集羣 cluster2 中的遠程服務的名稱和命名空間。

對於 *.global 域下服務的 DNS 解析,需要爲這些服務分配一個 IP 地址,並且保證 .globalDNS 域中的每個服務在集羣中必須具有唯一的 IP 地址。這些 IP 地址在 pod 之外是不可路由的。在這個例子中,我們將使用網段 127.255.0.0/16 來避免與其他的IP衝突。這些 IP 的應用流量將由 Sidecar 代理捕獲並路由到適當的其他遠程服務。

在集羣 cluster1 中創建該 httpbin 服務對應的 ServiceEntry,執行如下命令:

kubectl
apply -n app1 -f - <<EOF
apiVersion:
networking.istio.io/v1alpha3
kind:
ServiceEntry
metadata:
  name: httpbin-app2
spec:
  hosts:
  # must be of form name.namespace.global
  - httpbin.app2.global
  # Treat remote cluster services as part of
the service mesh
  # as all clusters in the service mesh share
the same root of trust.
  location: MESH_INTERNAL
  ports:
  - name: http1
    number: 8000
    protocol: http
  resolution: DNS
  addresses:
  # the IP address to which httpbin.bar.global
will resolve to
  # must be unique for each remote service,
within a given cluster.
  # This address need not be routable. Traffic
for this IP will be captured
  # by the sidecar and routed appropriately.
  - 127.255.0.2
  endpoints:
  # This is the routable address of the ingress
gateway in cluster2 that
  # sits in front of sleep.bar service. Traffic
from the sidecar will be
  # routed to this address.
  - address: ${CLUSTER2_GW_ADDR}
    ports:
      http1: 15443 # Do not change this port
value
EOF

上面的配置將會使集羣 cluster1 中訪問 httpbin.app2.global 的所有流量,包括訪問它的任何端口的流量,都會被路由到啓用了雙向 TLS 連接的端點 <IPofCluster2IngressGateway>:15443 上。

端口 15443 的網關是一個特殊的 SNI 感知的 Envoy 代理,它是在前面開始部分中作爲多集羣 Istio 安裝步驟的一部分預先配置和安裝的。進入端口 15443 的流量將在目標集羣的適當內部服務的 pod 中進行負載均衡。

在集羣 cluster1 下執行如下命令查看容器 istiocoredns,可以看到上述 ServiceEntry 的域名映射關係已經被加載:

export
ISTIO_COREDNS=$(kubectl get -n istio-system po -l app=istiocoredns -o
jsonpath={.items..metadata.name})
kubectl
logs --tail 2 -n istio-system ${ISTIO_COREDNS} -c istio-coredns-plugin

執行結果如下所示:

2.png

  1. 驗證在集羣 cluster1 中的 sleep 服務是否可以正常調用位於集羣 cluster2 中的 httpbin 服務,在集羣 cluster1 執行如下命令:
kubectl
exec $SLEEP_POD -n app1 -c sleep -- curl httpbin.app2.global:8000/headers

執行結果如下所示:

3.png

至此,集羣 cluster1 與 cluster2 在多控制平面配置下完成了連通。

跨集羣的版本路由

通過前面的文章,我們已經瞭解了 Istio 的很多功能,例如基本版本的路由等,可以在單個 Kubernetes 集羣上很容易地實現。而很多真實的業務場景中,基於微服務的應用程序並非那麼簡單,而是需要在多個位置跨集羣去分配和運行服務。那麼問題就來了,是否 Istio 的這些功能同樣可以很簡單地運行在這些真實的複雜環境中呢?

下面我們將會通過一個示例來了解 Istio 的流量管理功能如何在具有多個控制平面拓撲的多集羣網格中正常運行。

  1. 首先,部署版本 v1 的 helloworld 服務到第一個集羣 cluster1 中,執行如下命令:
kubectl
create namespace hello
kubectl
label namespace hello istio-injection=enabled
kubectl
apply -n hello -f samples/sleep/sleep.yaml
kubectl
apply -n hello -f samples/helloworld/service.yaml
kubectl
apply -n hello -f samples/helloworld/helloworld.yaml -l version=v1
  1. 部署版本 v2 與 v3 的 helloworld 服務到第二個集羣 cluster2 中,執行如下命令:
kubectl
create namespace hello
kubectl
label namespace hello istio-injection=enabled
kubectl
apply -n hello -f samples/helloworld/service.yaml
kubectl
apply -n hello -f samples/helloworld/helloworld.yaml -l version=v2
kubectl
apply -n hello -f samples/helloworld/helloworld.yaml -l version=v3
  1. 如前面章節中所述,多控制平面下,需要使用以 .global 爲後綴的 DNS 名稱訪問遠程服務

在我們的例子中,它是 helloworld.hello.global,所以我們需要在集羣 cluster1 中創建服務條目 ServiceEntry 和目標規則 DestinationRule。服務條目 ServiceEntry 將使用集羣 cluster2 的入口網關作爲端點地址來訪問服務。

通過使用以下命令在集羣 cluster1 中創建 helloworld 服務對應的服務條目 ServiceEntry 和目標規則DestinationRule:

kubectl
apply -n hello -f - <<EOF
apiVersion:
networking.istio.io/v1alpha3
kind:
ServiceEntry
metadata:
  name: helloworld
spec:
  hosts:
  - helloworld.hello.global
  location: MESH_INTERNAL
  ports:
  - name: http1
    number: 5000
    protocol: http
  resolution: DNS
  addresses:
  - 127.255.0.8
  endpoints:
  - address: ${CLUSTER2_GW_ADDR}
    labels:
      cluster: cluster2
    ports:
      http1: 15443 # Do not change this port
value
---
apiVersion:
networking.istio.io/v1alpha3
kind:
DestinationRule
metadata:
  name: helloworld-global
spec:
  host: helloworld.hello.global
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
  subsets:
  - name: v2
    labels:
      cluster: cluster2
  - name: v3
    labels:
      cluster: cluster2
EOF
  1. 在兩個集羣上創建目標規則。在集羣 cluster1 中創建子集 v1 對應的目標規則,執行如下命令:
kubectl
apply -n hello -f - <<EOF
apiVersion:
networking.istio.io/v1alpha3
kind:
DestinationRule
metadata:
  name: helloworld
spec:
  host: helloworld.hello.svc.cluster.local
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
  subsets:
  - name: v1
    labels:
      version: v1
EOF

而在集羣 cluster2 中創建子集 v2 和 v3 對應的目標規則,執行如下命令:

kubectl
apply -n hello -f - <<EOF
apiVersion:
networking.istio.io/v1alpha3
kind:
DestinationRule
metadata:
  name: helloworld
spec:
  host: helloworld.hello.svc.cluster.local
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
  subsets:
  - name: v2
    labels:
      version: v2
  - name: v3
    labels:
      version: v3
EOF
  1. 創建虛擬服務以路由流量。

應用下面的虛擬服務將會使得來自用戶 jason 對 helloworld 的流量請求指向位於集羣 cluster2 中的版本 v2 和 v3,其中 v2 比例爲 70%,v3 比例爲 30%;來自任何其他用戶對 helloworld 的流量請求都將轉到位於集羣 cluster1 中的版本 v1:

kubectl
apply -n hello -f - <<EOF
apiVersion:
networking.istio.io/v1alpha3
kind:
VirtualService
metadata:
  name: helloworld
spec:
  hosts:
    - helloworld.hello.svc.cluster.local
    - helloworld.hello.global
  http:
  - match:
    - headers:
        end-user:
          exact: jason
    route:
    - destination:
        host: helloworld.hello.global
        subset: v2
      weight: 70
    - destination:
        host: helloworld.hello.global
        subset: v3
      weight: 30
  - route:
    - destination:
        host:
helloworld.hello.svc.cluster.local
        subset: v1
EOF

執行多次調用,可以從下面的執行結果中看出,上述流量路由的規則生效,這也說明了在多控制平面拓撲下,用於路由的規則定義與在本地集羣的使用方式是一樣的:

4.png

設置多集羣網格的最簡單方法是使用多控制平面拓撲,因爲它沒有特殊的網絡要求。通過上述示例可以看出,在單個 Kubernetes 集羣上運行的路由功能同樣很容易地在多個集羣中使用運行。

《Istio服務網格技術解析與實戰》讀者可免費體驗 ASM 產品進行學習!點擊瞭解阿里雲服務網格產品 ASM:www.aliyun.com/product/servicemesh

作者簡介

王夕寧 阿里雲高級技術專家,阿里雲服務網格產品 ASM 及 Istio on Kubernetes 技術負責人,專注於 Kubernetes、雲原生、服務網格等領域。曾在 IBM 中國開發中心工作,擔任過專利技術評審委員會主席,擁有 40 多項相關領域的國際技術專利。《Istio 服務網格解析與實戰》一書由其撰寫,詳細介紹了 Istio 的基本原理與開發實戰,包含大量精選案例和參考代碼可以下載,可快速入門 Istio 開發。Gartner 認爲,2020 年服務網格將成爲所有領先的容器管理系統的標配技術。本書適合所有對微服務和雲原生感興趣的讀者,推薦大家對本書進行深入的閱讀。

課程推薦

爲了更多開發者能夠享受到 Serverless 帶來的紅利,這一次,我們集結了 10+ 位阿里巴巴 Serverless 領域技術專家,打造出最適合開發者入門的 Serverless 公開課,讓你即學即用,輕鬆擁抱雲計算的新範式——Serverless。

點擊即可免費觀看課程:https://developer.aliyun.com/learning/roadmap/serverless

阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的公衆號。”

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章