你的 Sleep 服務會夢到服務網格外的 bookinfo 嗎

語言: CN / TW / HK

作者:尹航

作為業內首個全託管 Istio 相容的阿里雲服務網格產品 ASM,一開始從架構上就保持了與社群、業界趨勢的一致性,控制平面的元件託管在阿里雲側,與資料面側的使用者叢集獨立。ASM 產品是基於社群 Istio 定製實現的,在託管的控制面側提供了用於支撐精細化的流量管理和安全管理的元件能力。通過託管模式,解耦了 Istio 元件與所管理的 K8s 叢集的生命週期管理,使得架構更加靈活,提升了系統的可伸縮性。從 2022 年 4 月 1 日起,阿里雲服務網格 ASM 正式推出商業化版本, 提供了更豐富的能力、更大的規模支援及更完善的技術保障,更好地滿足客戶的不同需求場景,詳情可點選原文連結檢視。

前言

服務網格是一個通過“Sidecar”模式進行服務治理簡化的平臺。整個服務網格可以劃分為包括核心元件 Istiod 的“控制面”以及包括了每個服務的 Sidecar 的“資料面”。如果各位使用過服務網格,相信對上面的概念也算是略有了解了。

在服務網格 Istio 中,我們知道每個 Sidecar 都是一個 envoy 應用,內部有著包含著 listener、route、cluster、secret 等部分的完整配置;envoy 就是根據這些配置來改變自身行為,實現不同的流量控制手段的。而控制面元件的主要任務,就是將網格中的 VirtualService、DestinationRule 等資源“翻譯”成 envoy 的具體配置,並將配置傳送給資料面的每個 envoy,我們簡稱這個過程為“配置推送”。

在 envoy 中,cluster 配置對應著 envoy 內部的“叢集”這個概念,一個 cluster 是指“Envoy 連線的一組邏輯相同的上游主機”,其實大多數時候也就是對應著一個具體的服務。

如果我們實際檢視 envoy 的完整配置會發現,我們在什麼都沒做的時候,envoy 內部就已經包含著一些動態下發的 cluster 了,也就是說,Sidecar 自動發現幷包含了叢集中的服務資訊。這裡就引出一個核心問題:服務網格的“服務發現”是如何完成的呢?我們通常會用“網格內服務”來稱呼對應 Pod 注入了 Sidecar 的服務;那麼,是不是說服務網格會將注入了 Sidecar 的“網格內服務”自動的加入每個 Sidecar 的 cluster 配置呢?

今天這篇文章就來主要聊一聊服務網格的“服務發現”和“配置推送”。

服務網格的“服務發現”

一個簡單的小實驗就可以解答上面的這些問題。我們在 Kubernetes 叢集中安裝服務網格平臺(可以使用 aliyun ASM + ACK 來快速搭建服務網格平臺),然後在 default 預設名稱空間之外,再建立一個 test 名稱空間。

然後,我們為 default 名稱空間開啟 Sidecar 自動注入,也就是說,default 名稱空間內的服務將自動成為“網格內”的服務,而 test 名稱空間內的服務將不會注入 Sidecar,也就是“網格外”的服務。

在服務網格 ASM 中,我們可以在控制檯的“全域性名稱空間”管理中來方便地開啟和關閉自動注入(開啟/關閉後別忘了同步一下自動注入哦):

1.png

我們可以隨意在這兩個名稱空間內部署一些中意的服務,我在這裡向 default 名稱空間部署了一個 sleep 服務。這個服務就如字面意思,裡面是一個 curl 容器,並且一進去就一直在睡大覺 ( ̄o ̄).zZ ,可以進入這個服務的 Pod 方便地 curl 其它服務。

在 Istio 的社群官方 github 倉庫中,可以找到 sleep 這個示例服務的 YAML 檔案:istio/samples/sleep/sleep.yaml,我們可以拷貝這個檔案後執行:

kubectl apply -f sleep.yaml

同理,在 test 名稱空間下,我們隨便部署點啥服務,我這裡部署了一個 Istio 使用者的老朋友 bookinfo,我們同樣可以在 Istio 的官方 github 中找到它的 YAML 檔案:istio/samples/bookinfo/platform/kube/bookinfo.yaml,將其拷貝後執行:

kubectl apply -f bookinfo.yaml -n test

順帶一提,為了不讓 sleep 太寂寞,我還在 default 名稱空間部署了一個 httpbin 應用陪他,不過不部署也無所謂┐(゚~゚)┌。

我們現在準備好了,大家可能猜到接下來要幹啥了。我們就來看看——這個 Sidecar 裡都有哪些 cluster 配置。

如果你使用 Istio,可以用 istioctl 命令列工具方便地檢視 Sidecar 中的各項配置的總結資訊;在服務網格 ASM 中這招可能行不通,不過 ASM 也有一個能夠部分相容的命令列工具 asmctl,我們可以進入阿里雲幫助中心,搜尋 asmctl,找到“安裝和使用診斷工具 asmctl”,按照文件提示下載安裝此工具。

以 asmctl 為例,可以用這個姿勢來檢視 Sidecar 內部的 cluster 配置(需要提前配置好資料面叢集的 Kubeconfig):

$ asmctl proxy-config cluster sleep-557747455f-g4lcs SERVICE FQDN PORT SUBSET DIRECTION TYPE DESTINATION RULE 80 - inbound ORIGINAL_DST BlackHoleCluster - - - STATIC InboundPassthroughClusterIpv4 - - - ORIGINAL_DST InboundPassthroughClusterIpv6 - - - ORIGINAL_DST PassthroughCluster - - - ORIGINAL_DST agent - - - STATIC asm-validation.istio-system.svc.cluster.local 443 - outbound EDS controlplane-metrics-aggregator.kube-system.svc.cluster.local 443 - outbound ORIGINAL_DST details.test.svc.cluster.local 9080 - outbound EDS envoy_accesslog_service - - - STRICT_DNS heapster.kube-system.svc.cluster.local 80 - outbound EDS httpbin.default.svc.cluster.local 8000 - outbound EDS istio-ingressgateway.istio-system.svc.cluster.local 80 - outbound EDS istio-ingressgateway.istio-system.svc.cluster.local 443 - outbound EDS istio-sidecar-injector.istio-system.svc.cluster.local 443 - outbound EDS istio-sidecar-injector.istio-system.svc.cluster.local 15014 - outbound EDS istiod.istio-system.svc.cluster.local 15012 - outbound ORIGINAL_DST kiali.istio-system.svc.cluster.local 20001 - outbound EDS kube-dns.kube-system.svc.cluster.local 53 - outbound EDS kube-dns.kube-system.svc.cluster.local 9153 - outbound EDS kubernetes.default.svc.cluster.local 443 - outbound EDS metrics-server.kube-system.svc.cluster.local 443 - outbound EDS productpage.test.svc.cluster.local 9080 - outbound EDS prometheus_stats - - - STATIC ratings.test.svc.cluster.local 9080 - outbound EDS reviews.test.svc.cluster.local 9080 - outbound EDS sds-grpc - - - STATIC sleep.default.svc.cluster.local 80 - outbound EDS storage-crd-validate-service.kube-system.svc.cluster.local 443 - outbound EDS storage-monitor-service.kube-system.svc.cluster.local 11280 - outbound EDS xds-grpc - - - STATIC zipkin

這裡就有一個有意思的事情了,雖然整套 bookinfo 應用都沒有注入 Sidecar,但我們還是能在 sleep 的 Sidecar 中找到 productpage、reviews、ratings 等 bookinfo 應用的服務資訊。

這一切是怎麼完成的呢?實際上 Istio 官方在社群文章《Traffic Management》中,有對這一過程進行解釋:

In order to direct traffic within your mesh, Istio needs to know where all your endpoints are, and which services they belong to. To populate its own service registry, Istio connects to a service discovery system. For example, if you’ve installed Istio on a Kubernetes cluster, then Istio automatically detects the services and endpoints in that cluster.

簡要地說,服務網格 Istio 並不進行服務發現。所有的服務都經由服務網格底層平臺的服務發現(Kubernetes、Consul 等,大多數情況下都是 Kubernetes),通過控制面適配後傳入網格自己的服務註冊中心(也就是 Sidecar 的那一堆 cluster 配置了)。

此外,如果你檢視 Sidecar 中記錄的 endpoint,你會發現無論是否注入 Sidecar,Kubernetes 叢集內所有 Pod 的 ip 地址都會記錄在內。

嘗試在 test 名稱空間內部署下面這麼個 VirtualService:

apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: namespace: test name: test-vs spec: hosts: - productpage.test.svc.cluster.local http: - match: - uri: prefix: /test/ rewrite: uri: / route: - destination: host: productpage.test.svc.cluster.local port: number: 9080

這個 VirtualService 實現了一個 uri 的重寫,但目標 host 是一個“網格外”的服務 productpage。

嘗試一下,curl 一個會被重寫的路徑/test/productpage

kubectl exec -it sleep-557747455f-g4lcs -c sleep -- curl productpage.test:9080/test/productpage

會發現重寫生效了,請求正常有返回。

上述行為可以說明,所謂“網格內外”只是以是否注入 Sidecar 確定的一個區分手段,並不是說網格本身和網格外的服務有著嚴格的隔離邊界。在上面的例子中,VirtualService 實際上生效於請求傳送方 Sidecar 的 route 配置之中,而 productpage 服務即使沒有 Sidecar,也是會被服務網格控制面發現、並加入 cluster 配置之中的,有對應的 cluster。因此,網格當然可以設定針對此 cluster 的路由規則,只要請求傳送方的 Pod 是注入 Sidecar 的,VirtualService 的功能就能正常工作。

當然,實際使用中,服務網格的其它資源可能生效於入站流量配置之中,此時請求接收方也必須注入 Sidecar 才行。但 Sidecar 注入並不對網格的服務發現起到決定作用,這一點是可以肯定的 。

服務網格的“配置推送”

上面我們探索了服務網格的“服務發現”機制,可以說還是十分巧妙,這套機制既讓服務網格免於再去實現一套冗餘的服務發現機制,也方便網格本身與不同的底層平臺進行對接。然而,仔細思考就會發現這其中存在的問題與隱藏的反直覺現實情況。

就以上面測試中我們在 Kubernetes 叢集中部署的應用為例。Sleep 應用與 bookinfo 應用是兩個獨立的應用,彼此之間其實沒有太大關係(只不過想訪問的話彼此還是訪問的通而已)。在實際的生產環境中,相信很多使用者都會有這樣的實踐:利用 Kubernetes 名稱空間的隔離機制,在同一個叢集中部署多個應用對外提供服務,每個應用包含幾個微服務,而應用之間彼此的關係則比較獨立。而其中又只有部分的應用需要使用服務網格的治理能力(多語言服務互通、藍綠髮布等),因此注入了 Sidecar。

問題是,服務網格的控制面也不能假設使用者服務之間的關係,因此還是需要一視同仁地 watch 叢集內所有的服務與端點:-( 

更難受的是,控制面還需要向及時向資料面的每一位 Sidecar 同步叢集內最新的服務資訊,這就導致了以下的“費力不討好”局面:

1、一開始大家相安無事,歲月靜好

2.jpeg

2、Service2 擴容了!

3.jpeg

3、Istiod 下發新的配置

4.jpeg

4、尷尬的事情發生了,Service1 和 Service2 其實是兩個獨立的應用,網格內的 Sidecar 不需要記錄 Service2 的任何資訊。

5.jpeg

如果網格內的服務不多,這倒也不能造成什麼巨大的問題,無非就是網格的控制面元件多做點無用功罷了。可是正所謂量變引起質變,無用功堆積起來就會成為不可忽視的大問題。如果您的叢集中部署著成千上百個服務,而其中只有少量的服務加入了網格,網格的控制面就會淹沒在大量的無效資訊中,控制面元件的負載居高不下,並且不斷向所有 Sidecar 推送他們並不需要的資訊。

由於控制面元件不比閘道器,只是負責向 Sidecar 推送配置,控制面的負載過高可能很難引起服務網格使用者的注意。然而一旦負載過高導致 Pilot SLB 超限/控制面元件重啟等狀況,使用者就會輕則面臨新的路由配置推送過慢、重則面臨注入 Sidecar 的 Pod 起不來的窘境。因此,在享受服務網格為我們帶來的便利的流量治理能力的同時,適度地關心服務網格控制面的身心健康,對控制面進行配置推送的優化,也是非常有必要的。

配置推送優化-之選擇性服務發現

針對上面所述的狀況,我們可以手動地去配置服務網格,讓網格的控制面只去“發現”特定名稱空間內的服務,而對其它的名稱空間置之不理。在社群中,Istio 於 1.10 版本後提供了“discovery selector”的能力。而服務網格 ASM 也對應提供了“選擇性服務發現”的能力,二者的生效機制是完全相同的。

我們以服務網格 ASM 的“選擇性服務發現”為例。首先給網格內應用所在的名稱空間打一個特定的標籤,用來區分網格內與網格外應用所在的名稱空間(注意是給資料面的名稱空間打標籤):

```

在這裡,default名稱空間開啟了自動注入,屬於“網格內”名稱空間

kubectl label namespace default in-mesh=yes

其實我們也可以直接用 istio-injection:enabled標籤,不用再打一個

```

進入我們的 ASM 例項管理頁面,在左側選單中選擇“配置推送優化 -> 選擇性服務發現”。

在“選擇性服務發現”頁面中,選擇“新增標籤選擇器” -> “新增標籤精確匹配規則”,輸入我們剛才打好的標籤。

6.png

然後點選“確定”就好了,真是非常地方便。這之後網格會經過一個短暫的更新階段,更新我們剛才寫下的這個配置,然後重新進入“執行中”狀態。

再來執行一遍我們最開始的指令,檢視一下 sleep 服務的 Sidecar 中的 cluster 配置:

$ asmctl proxy-config cluster sleep-557747455f-g4lcs SERVICE FQDN PORT SUBSET DIRECTION TYPE DESTINATION RULE 80 - inbound ORIGINAL_DST BlackHoleCluster - - - STATIC InboundPassthroughClusterIpv4 - - - ORIGINAL_DST InboundPassthroughClusterIpv6 - - - ORIGINAL_DST PassthroughCluster - - - ORIGINAL_DST agent - - - STATIC envoy_accesslog_service - - - STRICT_DNS httpbin.default.svc.cluster.local 8000 - outbound EDS kubernetes.default.svc.cluster.local 443 - outbound EDS prometheus_stats - - - STATIC sds-grpc - - - STATIC sleep.default.svc.cluster.local 80 - outbound EDS xds-grpc - - - STATIC zipkin - - - STRICT_DNS

可以看到 sleep 服務的 Sidecar 中已經沒有任何一個 bookinfo 應用中服務的資訊了,現在 Sidecar 中的配置看起來精簡多了。可喜可賀可喜可賀(^o^)/

當然,我們做的不只是減少 Sidecar 中的配置數量而已。如上文所說,使用“選擇性服務發現後”,控制面將不會 watch 除 default 名稱空間外的任何服務,也因此控制面的工作負擔得到了大幅削減,服務網格重回高效運轉狀態~我們也可以沒有後顧之憂地向叢集中部署其它服務了。

總結

服務網格的服務發現機制其實說起來是一個十分簡單的東西,畢竟服務網格根本就沒有服務發現機制呢!

不過不去實際瞭解的話,想必很少有人能夠直接確定每個 Sidecar 裡記錄的那些林林總總的服務到底是如何發現的。同時,Istiod 作為服務網格中負責配置推送的核心元件,做的卻是修改翻譯和修改配置這樣的後臺工作,也導致網格的使用者通常很難意識到控制面元件到底在進行什麼工作,以及現有的實踐是否給控制面元件造成了過大的多餘負擔。

希望這篇文章能讓更多的服務網格使用者意識到,配置推送的優化也是網格平臺維護的重要一環。使用“選擇性服務發現”的話,僅用1分鐘的時間就能夠大幅度優化網格的配置優化,減少使用網格時無謂的隱患,豈不美哉?

對於大多數使用者來說,“選擇性服務發現”就已經足夠對配置推送進行優化了。不過如果您想做的更“絕”,還可以直接使用服務網格的 Sidecar 資源來對 Sidecar 的配置進行最大限度地優化。服務網格 ASM 針對 Sidecar 資源這一機制,也提供了基於訪問日誌分析自動推薦的 Sidecar 資源來幫助客戶進行使用上的深度簡化。歡迎使用服務網格 ASM,即刻讓您擁有簡化的無侵入式服務治理能力!

從 2022 年 4 月 1 日起,阿里雲服務網格 ASM 正式推出商業化版本, 提供了更豐富的能力、更大的規模支援及更完善的技術保障,更好地滿足客戶的不同需求場景。