不改一行代碼輕松玩轉 Go 應用微服務治理
01、Go 應用微服務治理簡介
Go 語言具有簡潔、高效、并發性強等特性,已經被廣泛認為是構建微服務的理想選擇之一。Go 語言作為構建 Kubernetes、Docker 的主要編程語言,目前不僅在云原生基礎組件領域中被廣泛使用,也逐漸被越來越多的開發者應用于各類業務場景中,基于微服務架構構建業務應用。
微服務架構通過模塊化體系結構,提高了系統的靈活性、敏捷性和擴展性,縮短了團隊開發周期、增加了資源利用效率,這也是越來越多的公司選擇采用微服務架構的主要原因之一。然而,隨著業務不斷發展與持續迭代,微服務帶來的系統復雜性也使得運維管理難度逐漸增加,從而影響開發效率和系統穩定性。
為了保證系統穩定性,我們在享受微服務帶來的優勢的同時,也不得不持續解決微服務帶來的問題和風險。例如:
- 在開發測試態,敏捷的迭代需要提供與線上完全隔離的環境用于研發和調試、業務發展帶來的服務增長需要有統一的面板來管理和觀測。
- 在變更態,為了防止新引入的 bug 給業務帶來的損失,需要具備灰度發布、快速回滾的能力。
- 在運行態,為了避免不確定流量、不穩定調用和基礎設施給業務帶來的穩定性問題,需要具備限流、熔斷、降級等能力,來規避運行時風險。
目前,在 Go 語言生態中,主流的微服務框架主要專注于解決如何快速構建微服務應用、以及微服務間通信問題,在微服務治理能力上仍有欠缺;主流的微服務治理組件,如 Sentinel-Golang、OpenTelemetry 等,雖然較好的解決了流量防護、應用可觀測等方面的微服務治理問題,但需要開發手動在業務代碼中通過 SDK 進行埋點,無法專注于業務邏輯的實現,這無疑在一定程度上降低了開發效率。
為了更好的進行 Go 應用微服務治理,提高研發效率和系統穩定性,本文將介紹 MSE 微服務治理方案,無需修改業務代碼,即可實現上述治理能力。
02、原理說明
不同于 JAVA,Golang 作為靜態編譯型語言,它在編譯時直接將源代碼轉換為機器碼,不依賴于虛擬機,也不具有字節碼。這種方式雖然無法屏蔽底層操作系統,但是由于可以直接在硬件上運行,會具有更高的性能。
我們通過 Go Build 工具原生提供的 -toolexec 機制,在編譯期進行代碼劫持,對特定 Go SDK 進行埋點注入來實現代碼增強(如框架 SDK、Go 內置 runtime、net/http 包等),從而使微服務應用具備了治理能力。
- Compile Front:在編譯過程中,我們通過 dry run 機制和抽象語法樹解析每個待編譯 .go 文件,通過 module + path + receive type name + function name 可以唯一識別一個方法,從而能夠判斷出該方法是否需要進行代碼增強和埋點注入。
- Code Inject:判斷出當前方法需要進行注入時,會在對該文件進行編譯之前,通過修改語法樹,插入預定義好的代碼,隨后編譯生成 .a 文件。
- Compile Backend:編譯器繼續執行原有編譯流程,最終生成二進制可執行文件,里面包含了服務治理邏輯。
編譯時注入框架現已開源,歡迎參與社區討論和貢獻,詳情請點擊 opentelemetry-go-auto-instrumentation[1]。
03、接入并治理 Go 應用
為了更直觀的展示如何進行 Go 應用的微服務治理,接下來我們將通過一個 Demo,演示 Go 應用從接入到使用微服務治理并生效的全過程。
3.1 Demo 服務說明
Demo 服務部署在阿里云 ACK 集群中,調用順序為網關->A->B->C。其中網關采用阿里云 MSE 云原生網關,應用間調用方式為 http、服務發現方式采用 K8s 標準的 CoreDNS,應用 A、C 各部署了一個灰度版本,用于某需求迭代發布過程中的灰度驗證。
Demo 服務的實現源代碼請詳見 mse-go-demo/multiframe[2]。
應用名稱 | 語言及版本 | 微服務框架及版本 | Client 調用方式 | 服務發現方式 |
A | go 1.20 | gin 1.8.1 | http | CoreDNS |
B | go 1.19 | kratos 2.7.1 | http | CoreDNS |
C | go 1.19 | go-zero 1.5.1 | / | CoreDNS |
3.2 接入 MSE 服務治理中心
一個 Go 應用接入 MSE 服務治理中心,只需執行以下四步即可,其中步驟 1、2 只需首次接入時執行,后續無需再操作。
1. 在 MSE 服務治理中心中,為 ACK 集群一鍵安裝 ACK-Onepilot 組件
2. 在 MSE 服務治理中心中,為 ACK 集群一鍵開啟高階治理能力
3. 下載并使用我們提供的 Instgo 工具編譯 Go 應用,來代替 go build 命令,生成二進制可執行文件
# 生成當前操作系統可執行文件
./instgo build --mse --licenseKey="{licenseKey}"
# 交叉編譯,例如在MacOS中生成linux操作系統可執行文件
#amd
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 ./instgo build --mse --licenseKey="{licenseKey}"
#arm
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 ./instgo build --mse --licenseKey="{licenseKey}"
4. 將應用打包成鏡像之后,部署到集群之前,在相應的 Deployment YAML 文件中 spec.template.metadata.labels 中添加以下標簽后,部署應用即可完成接入
spec:
template:
metadata:
labels:
# required
msePilotAutoEnable: "on" #標識開啟MSE微服務治理
mseNamespace: your-namespace #標識應用所屬微服務治理空間
msePilotCreateAppName: "your-app-name" #標識應用名稱
aliyun.com/app-language: golang #標識為Golang應用
# optional
alicloud.service.tag: pod-tag #在全鏈路灰度中,用于標識灰度節點,如gray、blue...
可以看到,整個接入過程中不會涉及到業務代碼的修改,相比自主魔改框架、手動引入 SDK 埋點等方式,更加清爽和簡潔。
關于上述各步驟更詳細的指引和說明請詳見 ACK 微服務應用接入 MSE 治理中心(Golang 版)[3]。
3.3 使用服務治理能力
按照 3.2 所述步驟完成應用接入后,即可在 MSE 治理中心控制臺中看到具體的應用及服務信息,并且進行相應的服務治理規則配置。本節將從應用觀測與管理、流量治理、全鏈路灰度三個常見治理場景,演示操作過程與實際效果。
3.3.1 應用觀測與管理
將 3.1 中所述 Demo 的 A、B、C 三個 Go 應用接入到 MSE 治理中心,并設置:
- mseNamespace=mse-go-agent-demo
- 應用 A 的 msePilotCreateAppName=go-gin-demo-a
- 應用 B 的 msePilotCreateAppName=go-kratos-demo-b
- 應用 C 的 msePilotCreateAppName=go-zero-demo-c
接入完成后,可以在 MSE 治理中心 mse-go-agent-demo namespace 下看到對應的應用詳情。
3.3.1.1 服務信息查看
進入 MSE 服務治理中心,點擊服務查詢,可以查看服務信息,包括應用內創建的 http 或 rpc 服務,以及對應服務的元信息,如接口元信息、服務元信息等。
在左上角分別選擇 Gin、Kratos 和 Go-zero 框架,可以分別看到 A、B、C 應用的服務信息。由于我們在編碼實現時,分別為應用 B 和 C 各創建了一個 Http Server、一個 Grpc Server,用于接收不同類型的請求,因此可以在控制臺中看到兩個服務。
點擊相應的服務,能夠看到服務的詳細元信息,以應用 B 為例,以下為 B 應用的 Http 服務和 Grpc 服務信息。
3.3.1.2 應用信息查看
進入 MSE 服務治理中心,點擊應用治理,能夠從應用、接口、節點等不同維度查看應用的運行數據以及系統數據。
在應用列表處,可以看到不同應用的節點數、標簽數、請求數、QPS 等數據。
點擊對應應用卡片,即可從不同維度查看應用更詳細的信息,包括應用整體數據、接口數據、節點數據等。
1)應用概覽數據
2)接口數據
3)節點數據
3.3.2 流量治理
目前,我們提供了以下流量治理能力,支持用戶自主配置并應對不同的場景,同時支持通過控制臺配置規則快速啟停:
- 接口流控:設置單接口最大 QPS,超過閾值的請求將會被拒絕或進入等待隊列。
- 并發隔離:設置單接口的最大并發協程數,超過閾值的請求將會被拒絕。
- 熔斷:設置接口的熔斷防護規則,閾值可以設置為慢調用比例或者失敗率,達到閾值后會觸發熔斷,在熔斷時長內,該接口的請求都會快速失敗,熔斷狀態結束后通過單次探測或漸進式策略恢復。
- 熱點參數防護:相比接口流控,防護的規則精細到參數級別,例如可以設置某接口第 N 個參數占用最大并發資源數不超過 10,如果超過閾值則對應的請求會快速失敗。
- 行為降級:觸發接口流控后,可以自定義防護行為,例如返回指定狀態碼和內容、重定向等。
- 自適應過載保護:以 CPU 使用率作為衡量實例負載的依據,自適應地調整對入口流量的防護策略,避免因 CPU 資源打滿導致服務崩潰。
3.3.2.1 規則配置
以應用 A 為例,假設我們希望為接口 /greet1a 設置單機 QPS 閾值為 1 的接口流控規則,如果達到閾值之后立即失敗,并且返回 429 作為 Http 狀態碼,返回內容為"Too Many Request! The Server A will not process!"。
為了達到以上效果,可以直接在 MSE 治理中心配置如下:
1. 點擊應用治理,點擊 go-gin-demo-a 應用卡片,點擊流量治理
2. 點擊流量防護-行為管理,點擊新增行為,按下圖所示配置行為后,點擊新建
3. 點擊流量防護-接口流控,按下圖所示步驟配置流控防護規則,然后點擊新增
4. 在規則列表處,點擊規則狀態置為開啟后,規則即生效
3.3.2.2 結果觀測
持續一段時間后,可以看到應用 A 單節點通過的 QPS 穩定在 1,超過閾值的請求已經被拒絕;應用 B 的 QPS 穩定為 2,這是因為應用 A 一共有 2 個節點,最多允許通過的 QPS 為 2。
1)應用 A 單節點 QPS 數據
2)應用 B 整體 QPS 數據
使用云原生網關對應用 A/greet1a 發起調用,可以觀察到由于觸發了流控,接口返回了我們剛剛定義的錯誤狀態碼和內容。
3.3.3 全鏈路灰度
3.3.3.1 規則配置
假設在某次需求迭代中,我們變更了應用 A 和應用 C 的部分邏輯,并且希望通過灰度發布的方式測試并驗證功能的正確性,希望 Header 內容中 x-user-uuid=123456789 的請求路由到灰度節點,其他請求仍然路由到常規節點。
為了達到以上效果,我們可以按照以下步驟進行操作和配置:
- 為應用 A 和應用 C 部署灰度節點,通過在 YAML 文件中 spec.template.metatada.labels 增加 alicloud.servicetag=gray 標識節點類型。
- 進入 MSE 治理中心,點擊全鏈路灰度,創建泳道組,泳道組主要用于業務模塊隔離,例如,同部門下業務小組 A 可以創建并使用泳道組 a,業務小組 B 可以創建并使用泳道組 b,兩者相互隔離,互不干擾。
- 在泳道組內創建標簽為 gray 的灰度泳道,如下圖所示填寫泳道信息、配置基線路由和灰度規則。
泳道組及泳道創建完成后,全鏈路灰度的配置就已經完成,如下圖所示,泳道組的流量入口為 MSE 云原生網關,接下來我們通過對應的網關發起請求,Demo 應用中的業務返回結果打印了請求的調用鏈以及每一跳所路由到的節點標簽和 IP,用于方便觀察調用鏈路。
3.3.3.2 結果觀測
1)設置請求 Header 中無 x-user-uuid,請求均路由到基線節點
2)設置請求 Header x-user-uuid=1,請求均路由到基線節點
3)設置請求 Header x-user-uuid=123456789,請求路由到 A、C 的灰度節點,由于應用 B 不存在灰度節點,因此路由到基線節點
由此,我們驗證了全鏈路灰度規則配置已經生效。
本文鏈接:
[1] opentelemetry-go-auto-instrumentation
https://github.com/alibaba/opentelemetry-go-auto-instrumentation
[2] mse-go-demo/multiframe
https://github.com/aliyun/alibabacloud-microservice-demo/tree/master/mse-go-demo/multiframe
[3] ACK 微服務應用接入 MSE 治理中心(Golang 版)
相關鏈接:
[1] Go 應用治理接入指引
[2] Go 應用治理兼容性要求與功能說明
[3] 15 分鐘快速體驗 Go 應用服務治理