在企業環境中擴充套件 Istio 服務網格
在前面的章節中,我們已經探討瞭如何設定和應用 Istio 強大的功能來支援服務架構。現在,我們將深入討論如何在企業環境中實際執行 Istio。如何在大規模環境中擴充套件、排除故障和調整 Istio?如何在環境中包含多個叢集、虛擬機器和其他約束條件?最後,我們將探討如何使用 WebAssembly (Wasm) 等技術來自定義和調整服務網格的行為,以滿足特定的使用案例需求。
多叢集服務網格的優勢
在雲端遷移初期,許多企業都面臨著如何調整叢集大小的困境。以虛構的 ACME 公司為例,他們最初使用單一大型叢集,但很快就改變了這個決定。ACME 選擇了多個較小的叢集,因為它們具有以下優勢:
- 改進隔離性 — 確保一個團隊的失誤不會影響到另一個團隊
- 故障邊界 — 為可能影響整個叢集的設定或操作劃定邊界,減少對架構其他部分的影響
- 監管與合規 — 限制存取敏感資料的服務與架構的其他部分隔離
- 提高用性與效能 — 在不同區域執行叢集以提高用性,並將流量路由到最近的叢集以減少延遲
- 多雲和混合雲 — 能夠在不同環境中執行工作負載,無論是不同的雲提供商還是混合雲
在初步評估中,ACME 考慮了跨叢集擴充套件服務網格並啟用跨叢集流量管理、可觀察性和安全性的能力,這是他們選擇服務網格的主要驅動因素。為了支援多叢集工作,公司考慮了兩種方法:
多叢集服務網格 — 一個跨越多個叢集的網格,設定工作負載以路由跨叢集流量,並按照應用的 Istio 設定(如 VirtualService、DestinationRule 和 Sidecar 資源)進行操作。
網格聯邦(多網格) — 暴露並啟用兩個獨立服務網格的工作負載之間的通訊。這個選項自動化程度較低,需要在兩個網格上手動設定服務間流量。然而,當網格由不同團隊操作或有嚴格的安全隔離需求時,這是一個好選擇。
在本文中,我們將重點關注多叢集服務網格。
多叢集服務網格概述
多叢集服務網格以對應用完全透明的方式連線跨叢集的服務,同時保持服務網格的所有功能:精細的流量管理、彈性、可觀察性和跨叢集通訊的安全性。Istio 透過查詢所有叢集中的服務,然後使用這些訊息來設定服務代理,實作了多叢集服務網格。
要將叢集加入到單一網格中,需要滿足以下條件:
跨叢集工作負載發現 — 控制平面必須能夠發現對等叢集中的工作負載(對方叢集的 API 伺服器必須對 Istio 的控制平面可存取)。
跨叢集工作負載連線 — 工作負載之間必須具有連線性。僅知道工作負載端點是不夠的,除非我們能夠啟動與它的連線。
叢集間的共同信任 — 跨叢集工作負載必須能夠相互認證,以啟用 Istio 的安全功能。
滿足這些標準可確保叢集能夠感知到其他叢集中執行的工作負載可以相互連線,並且工作負載可以使用 Istio 策略進行認證和授權。這些都是設定多叢集服務網格的前提條件。
Istio 多叢集佈署模型
在多叢集服務網格中,我們區分兩種型別的叢集:
- 主要叢集(Primary cluster) — 安裝 Istio 控制平面的叢集
- 遠端叢集(Remote cluster) — 相對於控制平面安裝的遠端叢集
根據我們想要實作的可用性,我們有以下佈署模型:
主要-遠端模型(分享控制平面) — 單一控制平面管理網格,資源使用較少,但主要叢集的故障會影響整個網格,因此可用性較低。
主要-主要模型(複製控制平面) — 多個控制平面確保更高的可用性,但需要更多資源。這提高了可用性,因為故障僅限於發生故障的叢集。
外部控制平面模型 — 所有叢集都遠端於控制平面的佈署模型,使雲提供商能夠將 Istio 作為代管服務提供。
多叢集佈署中的工作負載發現
Istio 的控制平面需要與 Kubernetes API 伺服器通訊,以收集設定服務代理所需的相關訊息,如服務和這些服務背後的端點。向 Kubernetes API 伺服器發出請求是一種強大的能力,因為你可以查詢資源詳細訊息、敏感訊息,以及更新或刪除資源。
Kubernetes 使用根據角色的存取控制(RBAC)來保護對 API 伺服器的存取。在跨叢集發現中,Istio 使用服務帳戶令牌來認證遠端叢集,並發現在其中執行的工作負載。
跨叢集工作負載連線
另一個前提條件是工作負載具有跨叢集連線性。當叢集在平坦網路中(如分享單一網路或透過網路對等連線的網路),工作負載可以使用 IP 地址連線,條件已經滿足!然而,當叢集在不同網路中時,我們必須使用特殊的 Istio 入口閘道,它位於網路邊緣並代理跨叢集流量。在多網路網格中橋接叢集的入口閘道被稱為東西向閘道(east-west gateways)。
叢集間的共同信任
最後一個需要解決的因素是多叢集服務網格中的叢集必須具有共同信任。擁有共同信任確保對方叢集的工作負載可以相互認證。有兩種方法可以實作叢集間的共同信任:
插入式 CA 證書 — 使用由共同根 CA 頒發的使用者定義證書。
外部 CA 整合 — 整合兩個叢集都使用的外部 CA 來簽署證書。
在本文中,我們將使用插入式 CA 證書來設定叢集間的共同信任,因為它更簡單,並且能夠保持對多叢集服務網格的關注。
多叢集、多網路、多控制平面服務網格概述
我們將設定一個模擬真實世界企業服務的基礎設施,這些服務在多個叢集中執行,佈署在不同區域,並位於不同網路中。基礎設施包括以下內容:
- west-cluster — 在 us-west 區域中具有私有網路的 Kubernetes 叢集,我們將在這裡執行 webapp 服務。
- east-cluster — 在 us-east 區域中具有私有網路的 Kubernetes 叢集,我們將在這裡執行 catalog 服務。
在兩個不同區域中擁有叢集可以在一個區域發生災難時保護我們免受服務中斷的影響。webapp 和 catalog 工作負載沒有技術上的理由必須在不同的叢集中 — 這只是為了演示目的。只要可能,“聊天"頻繁的工作負載應該保持接近以減少延遲。
選擇多叢集佈署模型
多網路基礎設施要求我們使用東西向閘道來橋接網路以實作跨叢集連線,但留下了是使用複製控制平面佈署模型還是單一控制平面的決定。這個決定由業務需求驅動。在 ACME 的案例中,其線上商店非常受歡迎:每分鐘的停機時間都會讓業務損失數百萬美元!因此,高用性是首要優先事項,我們將使用主要-主要佈署模型,其中 Istio 控制平面佈署在每個叢集中。
總結來説,我們將設定一個多叢集、多網路、多控制平面服務網格,使用東西向閘道來橋接網路,並使用主要-主要佈署模型。
設定多叢集服務網格
在設定多叢集服務網格時,我們需要完成以下步驟:
設定插入式 CA 證書 — 在每個叢集中設定中間 CA 證書,以建立共同信任。
在每個叢集中安裝控制平面 — 使用 IstioOperator 資源定義 Istio 安裝,並為每個叢集增加網路中繼資料。
啟用跨叢集工作負載發現 — 使用 istioctl 建立遠端叢集存取的金鑰,使控制平面能夠查詢對方叢集中的工作負載。
設定跨叢集連線 — 安裝東西向閘道,並設定 SNI 自動透傳模式以路由跨叢集流量。
設定插入式 CA 證書
在每個叢集中,我們需要建立 istio-system 名稱空間,然後將證書應用為名為 cacerts 的金鑰:
# 為 west-cluster 設定證書
kubectl --context="west-cluster" create namespace istio-system
kubectl --context="west-cluster" create secret generic cacerts -n istio-system \
--from-file=ca-cert.pem \
--from-file=ca-key.pem \
--from-file=root-cert.pem \
--from-file=cert-chain.pem
# 為 east-cluster 設定證書
kubectl --context="east-cluster" create namespace istio-system
kubectl --context="east-cluster" create secret generic cacerts -n istio-system \
--from-file=ca-cert.pem \
--from-file=ca-key.pem \
--from-file=root-cert.pem \
--from-file=cert-chain.pem
設定好插入式證書後,我們可以安裝 Istio 控制平面,它會使用這些插入式 CA 證書(使用者定義的中間證書)來簽署工作負載證書。
在每個叢集中安裝控制平面
在安裝 Istio 控制平面之前,我們需要為每個叢集增加網路中繼資料。網路中繼資料使 Istio 能夠利用拓撲訊息並據此設定工作負載。這樣,工作負載可以使用位置訊息並優先將流量路由到接近的工作負載。
# 為 west-cluster 標記網路
kubectl --context="west-cluster" label namespace istio-system \
topology.istio.io/network=west-network
# 為 east-cluster 標記網路
kubectl --context="east-cluster" label namespace istio-system \
topology.istio.io/network=east-network
接下來,我們使用 IstioOperator 資源來定義 Istio 安裝:
# west-cluster 的 IstioOperator 定義
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: istio-controlplane
namespace: istio-system
spec:
profile: demo
components:
egressGateways:
name: istio-egressgateway
enabled: false
values:
global:
meshID: usmesh
multiCluster:
clusterName: west-cluster
network: west-network
# 安裝 west-cluster 的控制平面
istioctl --context="west-cluster" install -y -f cluster-west.yaml
然後,我們為 east-cluster 安裝複製的控制平面:
# east-cluster 的 IstioOperator 定義
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: istio-controlplane
namespace: istio-system
spec:
profile: demo
components:
egressGateways:
name: istio-egressgateway
enabled: false
values:
global:
meshID: usmesh
multiCluster:
clusterName: east-cluster
network: east-network
# 安裝 east-cluster 的控制平面
istioctl --context="east-cluster" install -y -f cluster-east.yaml
安裝控制平面後,我們在每個叢集中執行一些工作負載:
# 在 west-cluster 中佈署 webapp
kubectl --context="west-cluster" create ns istioinaction
kubectl --context="west-cluster" label namespace istioinaction istio-injection=enabled
kubectl --context="west-cluster" -n istioinaction apply -f webapp-deployment-svc.yaml
kubectl --context="west-cluster" -n istioinaction apply -f webapp-gw-vs.yaml
kubectl --context="west-cluster" -n istioinaction apply -f catalog-svc.yaml
# 在 east-cluster 中安裝 catalog 服務
kubectl --context="east-cluster" create ns istioinaction
kubectl --context="east-cluster" label namespace istioinaction istio-injection=enabled
kubectl --context="east-cluster" -n istioinaction apply -f catalog.yaml
啟用跨叢集工作負載發現
為了讓 Istio 能夠認證查詢遠端叢集的訊息,它需要一個定義身份和許可權的服務帳戶。Istio 在安裝時建立了一個名為 istio-reader-service-account 的服務帳戶,具有最小許可權集,可以被另一個控制平面用來認證自己並查詢工作負載相關訊息。
我們使用 istioctl 的 create-remote-secret 命令來建立遠端叢集存取的金鑰:
# 為 west-cluster 建立存取 east-cluster 的金鑰
istioctl --context="east-cluster" x create-remote-secret --name="east-cluster" | \
kubectl --context="west-cluster" apply -f -
# 為 east-cluster 建立存取 west-cluster 的金鑰
istioctl --context="west-cluster" x create-remote-secret --name="west-cluster" | \
kubectl --context="east-cluster" apply -f -
現在,控制平面可以查詢對方叢集上的工作負載。
設定跨叢集連線
在不同網路的叢集之間,我們需要東西向閘道來代理跨叢集流量。東西向閘道是根據 Envoy 代理的 Istio 入口閘道,代表了來自公共網路並指向組織內部網路的流量入口點。
我們首先安裝東西向閘道:
# 安裝 east-cluster 的東西向閘道
istioctl --context="east-cluster" install -y -f cluster-east-eastwest-gateway.yaml
# 暴露服務
kubectl --context="east-cluster" apply -n istio-system -f expose-services.yaml
# 安裝 west-cluster 的東西向閘道並暴露服務
istioctl --context="west-cluster" install -y -f cluster-west-eastwest-gateway.yaml
kubectl --context="west-cluster" apply -n istio-system -f expose-services.yaml
東西向閘道使用 SNI 叢集和 SNI 自動透傳模式來路由跨叢集流量。SNI 叢集與常規 Envoy 叢集類別似,但它們在 SNI 中編碼所有 Envoy 叢集訊息。這使東西向閘道能夠將加密流量代理到客戶端在 SNI 中指定的叢集。
我們可以驗證 SNI 叢集是否已設定:
istioctl --context="east-cluster" pc clusters deploy/istio-eastwestgateway.istio-system | grep catalog
最後,我們可以驗證跨叢集工作負載發現和連線是否正常工作:
# 取得東西向閘道的地址
kubectl --context="east-cluster" -n istio-system get svc istio-eastwestgateway \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}'
# 檢查 west-cluster 中的工作負載是否使用東西向閘道地址路由跨叢集流量
istioctl --context="west-cluster" pc endpoints deploy/webapp.istioinaction | grep catalog
# 觸發請求以驗證跨叢集通訊
EXT_IP=$(kubectl --context="west-cluster" -n istio-system get svc istio-ingressgateway \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://$EXT_IP/api/catalog -H "Host: webapp.istioinaction.io"
跨叢集負載平衡與容錯移轉
在多叢集服務網格中,Istio 提供了跨叢集、位置感知的負載平衡功能。為了演示這一點,我們佈署兩個範例服務,每個服務都設定為回傳工作負載執行的叢集名稱:
# 在 west-cluster 中佈署簡單後端服務
kubectl --context="west-cluster" apply -f simple-backend-deployment.yaml
kubectl --context="west-cluster" apply -f simple-backend-svc.yaml
kubectl --context="west-cluster" apply -f simple-backend-gw.yaml
kubectl --context="west-cluster" apply -f simple-backend-vs.yaml
# 在 east-cluster 中佈署簡單後端服務
kubectl --context="east-cluster" apply -f simple-backend-deployment.yaml
kubectl --context="east-cluster" apply -f simple-backend-svc.yaml
預設情況下,Istio 使用輪詢演算法在工作負載之間進行負載平衡,因此流量均勻分配:
for i in {1..10}; do
curl --max-time 5 -s $EXT_IP -H "Host: simple-backend.istioinaction.io" | jq .body
done
我們可以使用位置感知負載平衡來改進效能,使工作負載優先將流量路由到其位置內的工作負載。雲提供商將位置訊息增加到節點的標籤中,Istio 使用這些訊息來設定工作負載的位置。
為了讓 Istio 使用位置訊息,我們需要被動健康檢查。我們應用一個使用離群檢測來被動檢查端點健康狀況的目標規則:
kubectl --context="west-cluster" apply -f simple-backend-dr.yaml
設定傳播後,我們可以驗證請求是否使用位置訊息並在同一叢集內路由:
for i in {1..10}; do
curl --max-time 5 -s $EXT_IP -H "Host: simple-backend.istioinaction.io" | jq .body
done
所有請求都路由到 west-cluster,這是最接近路由流量的入口閘道的叢集。
我們還可以驗證跨叢集容錯移轉。為了模擬 west-cluster 中的簡單後端佈署失敗,我們將環境變數 ERROR_RATE 設定為 1:
kubectl --context="west-cluster" -n istioinaction set env \
deploy simple-backend-west ERROR_RATE='1'
一段時間後,離群檢測會檢測到主機不健康,並將流量路由到 east-cluster 中的工作負載:
for i in {1..10}; do
curl --max-time 5 -s $EXT_IP -H "Host: simple-backend.istioinaction.io" | jq .body
done
跨叢集存取控制
最後一個我們要驗證的功能是跨叢集存取控制。存取控制要求工作負載之間的流量是相互認證的,產生可靠的中繼資料,可用於決定是否允許或拒絕流量。
假設我們只想在源是 Istio 的入口閘道時允許流量到服務;否則,流量被拒絕:
kubectl --context="east-cluster" apply -f allow-only-ingress-policy.yaml
我們可以透過從 west-cluster 中的工作負載觸發請求來測試策略:
kubectl run -i --rm --restart=Never sleep --image=curlimages/curl \
--command -- curl -s simple-backend.istioinaction.svc.cluster.local
請求被拒絕。同時,觸發到入口閘道的請求並從閘道路由的請求會得到成功回應:
curl --max-time 5 -s $EXT_IP -H "Host: simple-backend.istioinaction.io" | jq .body
這表明工作負載在跨叢集相互認證,並且策略可以使用編碼到身分證書中的認證資料進行存取控制。
將虛擬機器工作負載整合到網格中
到目前為止,我們已經從容器和 Kubernetes 的角度探討了 Istio 服務網格。然而,在現實中,工作負載經常執行在虛擬機器(VM)或物理機上。容器和 Kubernetes 通常用於現代化技術堆積積堆積疊,而本文將展示如何使用 Istio 在應用網路層面橋接這兩個世界。
為什麼需要虛擬機器整合?
你可能會想,為什麼不直接將傳統工作負載現代化並在 Kubernetes 叢集中執行,而不是將 VM 整合到網格中?我們建議在可能的情況下採用這種方法,但在某些情況下這是不可能的,或者至少在考慮成本時是不可行的:
企業可能必須在本地執行工作負載(由於監管合規性),而他們缺乏設定和操作 Kubernetes 叢集的專業知識。
容器化應用程式並不那麼簡單。有些應用可能需要重新架構;其他應用可能有需要更新的依賴關係,但這些依賴關係與其他依賴關係衝突 — 依賴地獄。
有些應用可能對它們執行的 VM 有獨特的依賴關係。
在本文中,我們將展示如何透過安裝和設定 sidecar 代理,使任何工作負載成為網格的一部分。這種方法為擁有傳統工作負載的企業提供了有趣的功能,使他們能夠以彈性、安全和高用性的方式將這些工作負載整合到網格中。
Istio 的 VM 支援
Istio 對 VM 的整合從早期就得到支援,但它需要大量的變通方法和外部於控制平面的自動化。Istio 的 VM 支援在 Istio 1.9.0 中升級為 beta 版,一旦一些關鍵功能得到實作並且 API 確定了合適的方法。這些關鍵功能包括:
- 使用 istioctl 簡化了 VM 中的 sidecar 代理安裝和設定。
- 透過引入兩個新的 Istio 資源:WorkloadGroup 和 WorkloadEntry,實作了 VM 的高用性。
- 使用本地 DNS 代理解析來自 VM 的網格內服務,該代理與 Istio 的 sidecar 一起設定。
VM 中的 sidecar 代理安裝和設定
要使 VM 成為網格的一部分,我們需要:
- 安裝 sidecar 代理來管理網路流量
- 設定代理連線到 istiod 並接收網格設定
- 為 VM 提供身份令牌,用於向 istiod 認證
Istio 使用 Kubernetes 作為信任源來提供 VM 的身份。這透過在 Kubernetes 中生成令牌並將其傳輸到機器來實作。此令牌由安裝在機器上的 istio-agent 拾取,並用於向 istiod 認證。
虛擬機器高用性
為了實作 VM 的高用性,Istio 密切模仿 Kubernetes 對容器化工作負載採取的方法。基本上,Kubernetes 透過以下資源實作高用性:
- Deployments 作為更高階別的資源,包含如何建立副本的設定。
- Pods 是從該設定建立的副本。
Istio 為 VM 引入的資源與 Kubernetes 的概念密切對齊:
WorkloadGroup 資源類別似於 Kubernetes 的 Deployments,它定義瞭如何設定其管理的工作負載的範本。它指定了共同屬性,如應用程式暴露的連線埠、分配給組例項的標籤、代表工作負載在網格中身份的服務帳戶,以及如何探測應用程式的健康狀況。
WorkloadEntry 類別似於 Kubernetes Pods。它代表一個服務最終使用者流量的單個 VM。除了 WorkloadGroup 定義的共同屬性外,WorkloadEntry 還擁有獨特的屬性,如它代表的例項的地址和健康狀態。
WorkloadEntry 可以手動建立;然而,推薦的方法是使用工作負載自動註冊,新設定的工作負載自動加入網格。
工作負載自動註冊
在工作負載自動註冊過程中,工作負載連線到控制平面(使用提供給它的設定)並使用身份令牌作為 WorkloadGroup 的成員進行認證。當這個過程成功完成後,控制平面建立一個 WorkloadEntry 來代表網格中的 VM。
VM 在網格中使用 WorkloadEntry 表示很重要,有多種原因。特別是,它可以被 Kubernetes 服務或 Istio ServiceEntry 資源使用標籤選擇器選擇,並用作路由流量的後端。使用 Kubernetes 服務(即叢集中的完全限定網域名稱 [FQDN])選擇工作負載,而不是它們的實際地址,使得在工作負載不健康時可以處理它們,或者輕鬆啟動新的工作負載以滿足增加的需求,而不會對客戶端產生任何影響。
Istio 執行的健康檢查
成為服務網格的一部分後,工作負載需要準備好接收流量,並由健康檢查進行探測。為了維持服務的高用性,我們需要兩種型別的健康檢查(類別似於 Kubernetes 的健康檢查方式):
- 就緒探針(Readiness probes) 檢查工作負載在啟動後是否準備好接收流量。
- 存活探針(Liveness probes) 檢查應用程式在執行後是否健康;如果不健康,應該重新啟動。
存活探針不是服務網格的關注點!確保工作負載的存活是執行工作負載的平台功能。例如,Kubernetes 也是一個平台,它使用在 Deployment 設定中定義的探針執行存活檢查。同樣,在雲中的 VM 上執行工作負載時,我們需要使用雲的功能來實作存活探針,並採取糾正措施來修復 VM(如果探針失敗),例如設定新的例項。
VM 中的 DNS 解析
由於 VM 外部於 Kubernetes 叢集,它們缺乏對其內部 DNS 伺服器的存取。因此,VM 無法解析叢集服務的主機名。提供解決方案是將 VM 整合到服務網格的最後一個里程碑。
你可能想知道為什麼我們首先需要 DNS 解析。代理不是擁有如何路由流量的設定嗎?你是對的:代理確實有路由流量的設定!然而,問題在於讓流量離開應用程式並到達代理。這樣做的前提條件是主機名被解析。如果沒有解析,流量永遠不會離開應用程式,也不能重定向到 Envoy 代理。
Istio 的後期版本(1.8 及以後)引入了一個本地 DNS 代理到 istio-agent sidecar,它由 istiod 設定所有網格內服務。DNS 代理在 Istio 的 sidecar 中與 Envoy 代理一起執行,並處理來自應用程式的 DNS 查詢,這些查詢使用 Iptable 規則重定向到 DNS 代理。
為了保持 DNS 代理持續更新,Istio 引入了一個名為名稱發現服務(Name Discovery Service,NDS)的新 API。透過 NDS,控制平面在增加 Kubernetes 服務或 Istio ServiceEntry 到網格時,使用新的 DNS 條目同步資料平面。
設定虛擬機器整合
在本文中,我們將設定一個基礎設施來展示網格擴充套件。我們將建立一個 Kubernetes 叢集和一個 VM,它將託管我們的 cool-store 應用程式:
- webapp 和 catalogs 服務佈署在 Kubernetes 叢集中。
- forum 服務佈署在 VM 中。
值得注意的是,叢集和 VM 位於不同的網路中,這需要一個東西向閘道來反向代理從 VM 到叢集服務的流量。
設定服務網格
首先,我們建立一個 Kubernetes 叢集,並標記 Istio 安裝名稱空間的網路訊息:
kubectl create namespace istio-system
kubectl label namespace istio-system \
topology.istio.io/network=west-network
然後安裝控制平面並指定西部網路:
istioctl install -y -f cluster-in-west-network.yaml
安裝控制平面後,我們佈署 cool-store 服務:
kubectl create ns istioinaction
kubectl label namespace istioinaction istio-injection=enabled
kubectl -n istioinaction apply -f webapp-deployment-svc.yaml
kubectl -n istioinaction apply -f webapp-gw-vs.yaml
kubectl -n istioinaction apply -f catalog.yaml
設定 VM 加入網格
為了使 VM 成為網格的一部分,我們需要啟用一些功能,如工作負載自動註冊、健康檢查、捕捉 DNS 查詢並將這些查詢重定向到 DNS 代理。我們使用以下 IstioOperator 定義更新 Istio 安裝:
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: istio-controlplane
namespace: istio-system
spec:
profile: demo
components:
egressGateways:
name: istio-egressgateway
enabled: false
meshConfig:
defaultConfig:
proxyMetadata:
ISTIO_META_DNS_CAPTURE: "true"
values:
pilot:
env:
PILOT_ENABLE_WORKLOAD_ENTRY_AUTOREGISTRATION: true
PILOT_ENABLE_WORKLOAD_ENTRY_HEALTHCHECKS: true
global:
meshID: usmesh
multiCluster:
clusterName: west-cluster
network: west-network
接下來,我們安裝東西向閘道,並暴露 VM 存取叢集服務和 istiod 所需的連線埠:
istioctl install -y -f cluster-east-west-gw.yaml
kubectl apply -f expose-services.yaml
kubectl apply -f expose-istiod.yaml
然後,我們建立一個 WorkloadGroup 來代表 VM 工作負載的共同屬性:
apiVersion: networking.istio.io/v1alpha3
kind: WorkloadGroup
metadata:
name: forum
namespace: forum-services
spec:
metadata:
labels:
app: forum
template:
ports:
http: 8080
serviceAccount: forum-sa
network: vm-network
probe:
periodSeconds: 5
initialDelaySeconds: 1
httpGet:
port: 8080
path: /api/healthz
我們建立名稱空間和服務帳戶,然後應用 WorkloadGroup 設定:
kubectl create namespace forum-services
kubectl create serviceaccount forum-sa -n forum-services
kubectl apply -f workloadgroup.yaml
使用 istioctl,我們為 VM 生成設定:
istioctl x workload entry configure \
--name forum \
--namespace forum-services \
--clusterID "west-cluster" \
--externalIP $VM_IP \
--autoregister \
-o ./workload-files/
生成的設定包含以下內容:
- 透過東西向閘道暴露的 istiod 的 IP 地址。
- 根證書,用於驗證 istiod 提供的證書的真實性。
- 服務帳戶令牌,用於作為 forum WorkloadGroup 的成員向 istiod 認證。
- 關於服務網格、網路和 WorkloadGroup 中定義的共同屬性的設定。
有了這些設定,服務代理可以啟動與控制平面的安全連線,取得其 SVID,並透過 xDS 接收其 Envoy 設定,成為網格的成員。
在 VM 中安裝和設定 istio-agent
我們需要下載並安裝 VM 上的 istio-agent。Istio 以下包格式發布 istio-agent:
- Debian 軟體包(.deb),可用於在任何根據 Debian 的 Linux 發行版上安裝代理。
- Red Hat 套件管理器(.rpm),可用於在根據 Red Hat 的 Linux 發行版上安裝代理。
我們下載並安裝 Debian 格式的 istio-agent:
curl -LO https://storage.googleapis.com/istio-release/releases/1.13.0/deb/istio-sidecar.deb
sudo dpkg -i istio-sidecar.deb
然後,我們移動設定檔案到特定位置:
sudo mkdir -p /etc/certs
sudo cp "${HOME}"/root-cert.pem /etc/certs/root-cert.pem
sudo mkdir -p /var/run/secrets/tokens
sudo cp "${HOME}"/istio-token /var/run/secrets/tokens/istio-token
sudo cp "${HOME}"/cluster.env /var/lib/istio/envoy/cluster.env
sudo cp "${HOME}"/mesh.yaml /etc/istio/config/mesh
我們還需要設定系統主機檔案,靜態解析主機名 istiod.istio-system.svc 到東西向閘道 IP 地址:
cat "${HOME}"/hosts | sudo sh -c 'cat >> /etc/hosts'
最後,我們啟動 istio-agent 作為系統服務:
sudo systemctl start istio
驗證 VM 整合
我們可以檢查代理日誌,確認它已成功連線到控制平面:
cat /var/log/istio/istio.log | grep xdsproxy
我們還可以驗證工作負載是否已註冊到網格:
kubectl get workloadentry -n forum-services
為了檢查流量是否路由到叢集服務,我們從 VM 向 webapp 工作負載發出 curl 請求:
curl webapp.istioinaction/api/catalog/items/1
要路由從叢集到 VM 工作負載的流量,我們建立一個 Kubernetes 服務,使用標籤選擇器選擇例項:
apiVersion: v1
kind: Service
metadata:
labels:
app: forum
name: forum
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8080
selector:
app: forum
kubectl apply -f forum-svc.yaml -n forum-services
在 VM 中啟動 forum 應用程式:
wget -O forum https://git.io/J3QrT
chmod +x forum
./forum
然後,我們可以驗證從叢集到 VM 工作負載的流量流:
curl -is -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/users | grep HTTP
強制相互認證
由於 VM 已整合到網格中,sidecar 代理管理網路流量,我們可以將 Istio 的豐富功能應用於 VM。為了展示這一點,我們建立一個 PeerAuthentication 來強制相互認證的流量並提高安全性:
kubectl apply -f strict-peer-auth.yaml
現在,非相互認證的流量被禁止,但服務間流量繼續工作。
DNS 代理的工作原理
DNS 代理是 Istio 的 sidecar 中的新元件,它引發了不少問題。讓我們透過仔細研究來揭開它的神秘面紗。
DNS 代理如何解析叢集主機名
要理解析網格內主機名的所有步驟,我們將跟蹤一個具體範例,即如何解析 webapp.istioinaction 主機名:
- 客戶端發出 DNS 查詢以解析 webapp.istioinaction。
- 作業系統處理 DNS 解析。它首先檢查主機名是否比對主機檔案中定義的任何條目。如果沒有比對,請求將轉發到預設 DNS 解析器。
- Ubuntu 的預設解析器是 systemd-resolved(一個為本地應用程式提供主機名解析的系統服務),它監聽環回地址 127.0.0.53 上的連線埠 53。然而,請求永遠不會到達它,因為 istio-agent 設定了 Iptable 規則將其重定向到 DNS 代理。
- DNS 代理包含條目以解析服務網格內已知的服務。如果主機名比對,它就會被解析,這對於 webapp.istioinaction 來説是這樣的,因為它由控制平面使用 NDS 設定。否則,如果它不是叢集服務,DNS 代理回退到 resolv.conf 檔案中指定的名稱伺服器。
我們可以驗證 Iptable 規則是否將查詢重定向到 DNS 代理:
sudo iptables-save | grep 'to-ports 15053'
我們還可以驗證 istio-agent 是否在該連線埠上啟動了 DNS 代理:
sudo netstat -ltunp | grep 15053
我們甚至可以手動使用 dig 命令列工具發出臨時請求來解析網格內主機名:
dig +short @localhost -p 15053 webapp.istioinaction
DNS 代理知道哪些主機名
要發現 DNS 代理知道的所有條目,我們需要使用 istiod 的除錯端點。使用它們,我們可以查詢每個工作負載的 sidecar 的 NDS 設定:
kubectl -n istio-system exec deploy/istiod -- curl -Ls \
"localhost:8080/debug/ndsz?proxyID=forum-vm.forum-services"
輸出顯示了 webapp 服務的條目,其中包含 webapp.istioinaction.svc.cluster.local 解析到的 IP 地址列表。
總結主要點:
- DNS 代理由 istiod 設定,包含它所知道的服務。
- istio-agent 生成主機名的較短變體(以比對 Kubernetes 中的體驗)。
- 這些條目(在 DNS 代理中)用於解析網格內服務主機名。
- 對於非叢集主機名(如公共域)的查詢,解析回退到機器最初設定的名稱伺服器。
在請求路徑上擴充套件 Istio
正如我們在本文中所見,Istio 可以為組織帶來很多價值,提供應用網路功能。然而,採用 Istio 的組織可能有其他約束或假設,Istio 可能無法開箱即用地滿足。你可能需要擴充套件 Istio 的功能,使其更好地適應這些約束。
Envoy 代理是 Istio 服務網格的基礎元件。Envoy 是與應使用案例項一起生活的服務代理,位於網格中服務之間的請求路徑上。雖然 Envoy 有大量功能可以簡化服務的應用網路,但你很可能會遇到需要增強 Envoy 進行"最後一英里"或自定義整合的情況,例如:
- 與速率限制或外部授權服務整合
- 增加、刪除或修改標頭
- 呼叫其他服務以豐富請求負載
- 實作自定義協定,如 HMAC 簽名/驗證
- 非標準安全令牌處理
Envoy 可能提供了你幾乎所有需要的功能,但最終,你需要為特定使用案例自定義它。
Envoy 的擴充套件功能
Envoy 代理的優勢之一是它被設計為可擴充套件的。Envoy 的 API 設計得非常周到,它的流行很大程度上是因為其他人為它編寫的擴充套件。Envoy 可以透過濾器擴充套件進行擴充套件。要理解我們可以在哪裡擴充套件 Envoy 以及什麼對應用最有益,我們應該瞭解 Envoy 的一些架構。
理解 Envoy 的過濾器鏈
Envoy 的最基本過濾器是網路過濾器,它們對位元組流進行編碼或解碼操作。你可以設定多個過濾器按順序操作流,這個序列稱為過濾器鏈,這些鏈可以用來實作代理的功能。
一個最常用的網路過濾器是 HttpConnectionManager。這個過濾器負責抽象出將位元組流轉換為根據 HTTP 的協定(即 HTTP 1.1、HTTP 2、gRPC 等)的 HTTP 標頭、正文和尾部的細節。
HttpConnectionManager(有時稱為 HCM)也有一個根據過濾器的架構,允許你構建或設定 HTTP 過濾器到一個處理 HTTP 請求的過濾器序列或鏈中。
HTTP 過濾器可以設定在一個序列中操作 HTTP 請求。HTTP 過濾器必須以一個終端過濾器結束,該過濾器將請求傳送到上游叢集。負責此功能的 HTTP 過濾器是路由器過濾器。
使用者還可以編寫自己的過濾器,並將它們層疊在代理之上,而無需更改 Envoy 的任何核心程式碼。
使用 EnvoyFilter 資源設定 Envoy 過濾器
擴充套件 Istio 資料平面的第一步是確定 Envoy 中的現有過濾器是否足以完成我們正在尋找的擴充套件型別。如果存在,我們可以使用 EnvoyFilter 資源直接設定 Istio 的資料平面。
Istio 的 API 通常抽象了底層的 Envoy 設定,專注於特定的網路或安全場景。像 VirtualService、DestinationRule 和 AuthorizationPolicy 這樣的資源最終都會被轉換為 Envoy 設定,並可能設定過濾器鏈中的特定 HTTP 過濾器。Istio 不試圖暴露底層 Envoy 代理的每個可能的過濾器或設定,可能有些情況下我們需要直接設定 Envoy。Istio 的 EnvoyFilter 資源旨在用於高階使用案例,使用者需要調整或設定 Istio 更高階別 API 未暴露的 Envoy 部分。
讓我們看一個例子,瞭解它是如何工作的。假設我們想用一些工具來擴充套件我們的資料平面,以除錯流經 webapp 服務的某些請求。我們可以使用自定義過濾器擴充套件 Envoy,但如果我們仔細檢視,我們會發現存在一個 Tap 過濾器用於這種功能。它不被 Istio 的 API 暴露,所以我們可以使用 EnvoyFilter 資源為我們的 webapp 服務設定這個過濾器。
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: tap-filter
namespace: istioinaction
spec:
workloadSelector:
labels:
app: webapp
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND
listener:
portNumber: 8080
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
subFilter:
name: "envoy.filters.http.router"
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.tap
typed_config:
"@type": "type.googleapis.com/envoy.extensions.filters.http.tap.v3.Tap"
commonConfig:
adminConfig:
configId: tap_config
使用外部呼叫進行速率限制
在前一節中,我們使用現成的 HTTP 過濾器擴充套件了 Istio 資料平面。還有一些現成的過濾器,可以透過呼叫來增強資料平面。使用這些過濾器,我們呼叫外部服務,讓它執行一些功能,這些功能可以決定如何或是否繼續請求。在本文中,我們探討如何設定 Istio 的資料平面,呼叫速率限制服務,為特定工作負載強制執行伺服器端速率限制。
就像 Istio 使用 Envoy 作為資料平面一樣,速率限制的特定呼叫來自 Envoy HTTP 過濾器。使用全域速率限制,你讓特定工作負載的所有 Envoy 代理呼叫相同的速率限制服務,該服務呼叫後端全域鍵值儲存。使用這種架構,我們可以確保無論服務有多少副本,都強制執行速率限制。
要設定速率限制,我們需要佈署速率限制伺服器,它來自 Envoy 社群 — 或者更準確地説,是實作 Envoy 速率限制 API 的速率限制伺服器。這個伺服器設定為與後端 Redis 快取通訊,並在 Redis 中儲存速率限制計數器。
理解 Envoy 速率限制
在設定 Envoy 速率限制伺服器(RLS)之前,我們需要了解速率限制是如何工作的。我們特別關注 Envoy 的 HTTP 全域速率限制,它作為 HTTP 過濾器存在,需要設定到 HCM 上的 HTTP 過濾器鏈中。
當速率限制過濾器處理 HTTP 請求時,它從請求中取得某些屬性並將它們傳送到 RLS 進行評估。Envoy 速率限制術語使用單詞描述符來指代屬性或屬性組。這些描述符或請求的屬性可以是遠端地址、請求標頭、目的地或關於請求的任何其他通用屬性。
RLS 評估作為請求的一部分傳送的請求屬性,與一組預定義的屬性進行比較,並增加這些屬性的計數器。如果屬性或屬性集與 RLS 定義比對,則這些限制的計數將增加。如果計數超過閾值,則該請求被速率限制。
設定 Envoy 速率限制伺服器
讓我們用屬性計數器和限制設定 RLS。對於我們的範例使用案例,我們想根據使用者所屬的忠誠度等級限制某些使用者組。我們可以透過檢查 x-loyalty 標頭來確定請求中的忠誠度等級。
對於黃金等級(x-loyalty: gold)的特定使用者組,我們允許每分鐘 10 個請求。對於銀級(x-loyalty: