假如你正在一家典型的企業裡工作,需要與多個團隊一起工作,併為客戶提供一個獨立的軟體,組成一個應用程式。你的團隊遵循微服務架構,並擁有由多個Kubernetes叢集組成的廣泛基礎設施。
由於微服務分佈在多個叢集中,你需要設計一個解決方案來集中管理所有微服務。幸運的是,你正在使用Istio,提供這個解決方案只不過是另一個配置的變化。
像Istio這樣的服務網格技術可以幫助你安全地發現和連線分佈在多個叢集和環境中的微服務。今天我們來討論一下使用Istio管理託管在多個Kubernetes叢集中的微服務。
架構說明
Istio使用以下元件提供跨叢集服務發現:
-
Istio CoreDNS:每個Istio控制平面都有一個CoreDNS。Istio使用它來發現全域性範圍上定義的服務。例如,如果一個託管在叢集1上的微服務需要連線到另一個託管在叢集2上的微服務,你需要為執行在叢集2上的微服務在Istio CoreDNS上做一個全域性條目。
-
Root CA:由於Istio需要在不同叢集上執行的服務之間建立mTLS連線,因此需要使用共享的Root CA為兩個叢集生成中間CA證書。這就在不同叢集上執行的微服務之間建立了信任,因為中間CA共享同一個Root CA。
-
Istio Ingress閘道器:叢集間的通訊通過Ingress閘道器進行,服務之間沒有直接連線。因此,你要確保Ingress閘道器是可發現的,並且所有叢集都可以連線到它。
服務發現
Istio使用以下步驟來促進服務發現:
-
叢集上都有相同的控制平面,以促進高可用性。
-
Kube DNS與Istio CoreDNS為支點,以提供全域性服務發現。
-
使用者通過Istio CoreDNS中的ServiceEntries定義遠端服務的路由,格式為name.namespace.global。
-
源sidecar使用全域性CoreDNS條目將流量路由到目標Istio Ingress閘道器。
-
目標 Istio Ingress 閘道器將流量路由到正確的微服務 pod。
前期準備
本文假設你已經對Kubernetes以及Istio的工作原理有一個基本的瞭解。如果你想了解Istio 1.5和1.6的詳細內容,點選此處即可檢視相關視訊。為了能夠跟上我們接下來的演示,請確保:
-
你有至少兩個Kubernetes叢集,Kubernetes的版本為1.14、1.15或1.16
-
你擁有在叢集內安裝和配置Istio的許可權
-
你在兩個Kubernetes叢集上都有叢集管理許可權。
-
Ingress閘道器可通過網路負載均衡器或類似配置連線到其他叢集。扁平網路是不必要的。
安裝Istio
在兩個叢集上,使用以下命令安裝Istio 1.6.1:
curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.6.1 sh -
cd istio-1.6.1
export PATH=$PWD/bin:$PATH
由於我們需要用共享的根證書生成的中間證書來啟動我們的Istio服務網格,所以使用中間證書建立一個secret。
在這個例子中,我們使用提供的樣本證書。但是,我不建議你在生產中使用這些證書,因為它們一般都是可輕鬆獲取的,而且是眾所周知的。最好是使用你的組織的Root CA來生成中間CA證書。
在兩個叢集上執行以下命令來使用樣本證書。如果你使用的是你的證書,請替換適用的檔案路徑。
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
secret/cacerts created
由於我們需要安裝Istio進行多叢集設定,所以在兩個叢集上使用提供的Istio多叢集閘道器manifest檔案。
$ istioctl manifest apply -f install/kubernetes/operator/examples/multicluster/values-istio-multicluster-gateways.yaml
- Applying manifest for component Base...
✔ Finished applying manifest for component Base.
- Applying manifest for component Pilot...
✔ Finished applying manifest for component Pilot.
Waiting for resources to become ready...
- Applying manifest for component AddonComponents...
- Applying manifest for component IngressGateways...
- Applying manifest for component EgressGateways...
✔ Finished applying manifest for component EgressGateways.
✔ Finished applying manifest for component IngressGateways.
✔ Finished applying manifest for component AddonComponents.
✔ Installation complete
配置KubeDNS
下一步是將DNS解析從Kube DNS聯邦到Istio CoreDNS。讓我們通過為kube-dns
定義一個ConfigMap
來配置一個存根域。在兩個叢集上應用以下manifest:
$ 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
configmap/kube-dns configured
設定上下文(context)
由於我們需要為不同得活動連線兩個叢集,因此獲取上下文並將其儲存在環境變數中會很有意義。有了這些,我們只要在kubectl
命令中加入上下文,就可以在我們選擇得叢集中執行kubectl
命令。
獲取上下文:
$ kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
cluster-1 cluster-1 cluster-1
* cluster-2 cluster-2 cluster-2
設定環境變數以使用上下文:
$ export CTX_CLUSTER1=$(kubectl config view -o jsonpath='{.contexts[0].name}')
$ export CTX_CLUSTER2=$(kubectl config view -o jsonpath='{.contexts[1].name}')
$ echo CTX_CLUSTER1 = ${CTX_CLUSTER1}, CTX_CLUSTER2 = ${CTX_CLUSTER2}
CTX_CLUSTER1 = cluster-1, CTX_CLUSTER2 = cluster-2
部署示例微服務
我們先在叢集1的foo名稱空間上部署sleep微服務。
$ kubectl create --context=$CTX_CLUSTER1 namespace foo
namespace/foo created
$ kubectl label --context=$CTX_CLUSTER1 namespace foo istio-injection=enabled
namespace/foo labeled
$ kubectl apply --context=$CTX_CLUSTER1 -n foo -f samples/sleep/sleep.yaml
serviceaccount/sleep created
service/sleep created
deployment.apps/sleep created
$ export SLEEP_POD=$(kubectl get --context=$CTX_CLUSTER1 -n foo pod -l app=sleep -o jsonpath={.items..metadata.name})
現在我們在叢集2的bar名稱空間上部署httpbin
微服務:
$ kubectl create --context=$CTX_CLUSTER2 namespace bar
namespace/bar created
$ kubectl label --context=$CTX_CLUSTER2 namespace bar istio-injection=enabled
namespace/bar labeled
$ kubectl apply --context=$CTX_CLUSTER2 -n bar -f samples/httpbin/httpbin.yaml
serviceaccount/httpbin created
service/httpbin created
deployment.apps/httpbin created
建立服務條目
現在我們需要在Istio CoreDNS上建立一個服務條目以便於我們可以從叢集1中發現叢集2上的服務。由於所有的通訊都會通過Ingress 閘道器,匯出叢集2 Ingress閘道器地址。
export CLUSTER2_GW_ADDR=$(kubectl get --context=$CTX_CLUSTER2 svc --selector=app=istio-ingressgateway \
-n istio-system -o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}')
為了讓叢集1上的服務能夠發現叢集2上的httpbin
,我們需要在叢集1上為httpbin.bar.global
建立一個ServiceEntry
。這樣可以保證叢集1上的Istio Core DNS在叢集1上的服務到達httpbin.bar.global
這個端點時,可以到達叢集2的Ingress閘道器。下面的yaml:
-
在hosts部分定義服務域名
-
位置是
mesh_INTERNAL
,因為我們需要把其他服務當作同一個服務網格的一部分 -
將服務暴露在8000埠上
-
我們需要給該服務提供一個獨特的IP地址。該IP地址不需要可路由,並且你可以使用240.0.0.0/16範圍內的任意地址
-
我們在端點地址部分上定義叢集2 ingress閘道器地址,以便於請求可以路由給它。埠15443是Ingress閘道器的SNI識別的Envoy代理埠,用於在目標群集服務的Pod之間路由流量。
應用yaml
檔案:
$ kubectl apply --context=$CTX_CLUSTER1 -n foo -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: httpbin-bar
spec:
hosts:
- httpbin.bar.global
location: MESH_INTERNAL
ports:
- name: http1
number: 8000
protocol: http
resolution: DNS
addresses:
- 240.0.0.2
endpoints:
- address: ${CLUSTER2_GW_ADDR}
ports:
http1: 15443 # Do not change this port value
EOF
serviceentry.networking.istio.io/httpbin-bar created
測試服務
現在讓我們從sleep微服務中產生一些流量,看看它是否能到達叢集2上執行的httpbin
微服務。
$ kubectl exec --context=$CTX_CLUSTER1 $SLEEP_POD -n foo -c sleep -- curl -I httpbin.bar.global:8000/headers
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0HTTP/1.1 200 OK
0 519 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
server: envoy
date: Sat, 16 May 2020 23:03:22 GMT
content-type: application/json
content-length: 519
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 37
我們得到一個成功的響應!恭喜你,我們已經成功地使用Istio在多個Kubernetes叢集之間配置了服務發現。
結 論
感謝你的閱讀,希望你能喜歡這篇文章。
這是一個在多個叢集上執行的高可用Istio服務網格配置的演示。你也可以有一個共享的控制平面配置,但這並不推薦用於生產——如果你因為中斷而失去一個叢集,你也會失去對正在執行的叢集的控制。