本文轉載自:zhongfox.github.io/2019/01/30/…
今天分享的內容主要包括以下4個話題:
1 Service Mesh: 下一代微服務
2 Istio: 第二代 Service Mesh
3 Istio 資料面
4 Istio 控制面
首先我會和大家一起過一下 Service Mesh的發展歷程, 並看看Istio 為 Service Mesh 帶來了什麼, 這部分相對比較輕鬆. 接下來我將和大家分析一下Istio的主要架構, 重點是資料面和控制面的實現, 包括sidecar的注入, 流量攔截, xDS介紹, Istio流量模型, 分散式跟蹤, Mixer 的介面卡模型等等, 中間也會穿插著 istio的現場使用demo.
1. Service Mesh: 下一代微服務
應用通訊模式演進
Service Mesh(服務網格)的出現
第二代 Service Mesh
Service Mesh 的定義
Service Mesh 產品簡史
國內Service Mesh 發展情況
1.1 應用通訊模式演進: 網路流控進入作業系統
在計算機網路發展的初期, 開發人員需要在自己的程式碼中處理伺服器之間的網路連線問題, 包括流量控制, 快取佇列, 資料加密等. 在這段時間內底層網路邏輯和業務邏輯是混雜在一起.
隨著技術的發展,TCP/IP 等網路標準的出現解決了流量控制等問題。儘管網路邏輯程式碼依然存在,但已經從應用程式裡抽離出來,成為作業系統網路層的一部分, 形成了經典的網路分層模式.
1.2 應用通訊模式演進: 微服務架構的出現
微服務架構是更為複雜的分散式系統,它給運維帶來了更多挑戰, 這些挑戰主要包括資源的有效管理和服務之間的治理, 如:
服務註冊, 服務發現
服務伸縮
健康檢查
快速部署
服務容錯: 斷路器, 限流, 隔離艙, 熔斷保護, 服務降級等等
認證和授權
灰度釋出方案
服務呼叫可觀測性, 指標收集
配置管理
在微服務架構的實現中,為提升效率和降低門檻,應用開發者會基於微服務框架來實現微服務。微服務框架一定程度上為使用者遮蔽了底層網路的複雜性及分散式場景下的不確定性。通過API/SDK的方式提供服務註冊發現、服務RPC通訊、服務配置管理、服務負載均衡、路由限流、容錯、服務監控及治理、服務釋出及升級等通用能力, 比較典型的產品有:
分散式RPC通訊框架: COBRA, WebServices, Thrift, GRPC 等
服務治理特定領域的類庫和解決方案: Hystrix, Zookeeper, Zipkin, Sentinel 等
對多種方案進行整合的微服務框架: SpringCloud、Finagle、Dubbox 等
實施微服務的成本往往會超出企業的預期(內容多, 門檻高), 花在服務治理上的時間成本甚至可能高過進行產品研發的時間. 另外上述的方案會限制可用的工具、執行時和程式語言。微服務軟體庫一般專注於某個平臺, 這使得異構系統難以相容, 存在重複的工作, 系統缺乏可移植性.
Docker 和Kubernetes 技術的流行, 為Pass資源的分配管理和服務的部署提供了新的解決方案, 但是微服務領域的其他服務治理問題仍然存在.
1.3 Sidecar 模式的興起
Sidecar(有時會叫做agent) 在原有的客戶端和服務端之間加多了一個代理, 為應用程式提供的額外的功能, 如服務發現, 路由代理, 認證授權, 鏈路跟蹤 等等.
業界使用Sidecar 的一些先例:
2013 年,Airbnb 開發了Synapse 和 Nerve,是sidecar的一種開源實現
2014 年, Netflix 釋出了Prana,它也是一個sidecar,可以讓非 JVM 應用接入他們的 NetflixOSS 生態系統
1.4 Service Mesh(服務網格)的出現
直觀地看, Sidecar 到 Service Mesh 是一個規模的升級, 不過Service Mesh更強調的是:
不再將Sidecar(代理)視為單獨的元件,而是強調由這些代理連線而形成的網路
基礎設施, 對應用程式透明
1.5 Service Mesh 定義
以下是Linkerd的CEO Willian Morgan給出的Service Mesh的定義:
A Service Mesh is a dedicated infrastructure layer for handling service-to-service communication. It’s responsible for the reliable delivery of requests through the complex topology of services that comprise a modern, cloud native application. In practice, the Service Mesh is typically implemented as an array of lightweight network proxies that are deployed alongside application code, without the application needing to be aware.
服務網格(Service Mesh)是致力於解決服務間通訊的基礎設施層。它負責在現代雲原生應用程式的複雜服務拓撲來可靠地傳遞請求。實際上,Service Mesh 通常是通過一組輕量級網路代理(Sidecar proxy),與應用程式程式碼部署在一起來實現,且對應用程式透明。
1.6 第二代 Service Mesh
控制皮膚對每一個代理例項瞭如指掌,通過控制皮膚可以實現代理的訪問控制和度量指標收集, 提升了服務網格的可觀測性和管控能力, Istio 正是這類系統最為突出的代表.
1.7 Service Mesh 產品簡史
2016 年 1 月 15 日,前 Twitter 的基礎設施工程師 William Morgan 和 Oliver Gould,在 GitHub 上釋出了 Linkerd 0.0.7 版本,採用Scala編寫, 他們同時組建了一個創業小公司 Buoyant,這是業界公認的第一個Service Mesh
2016 年,Matt Klein在 Lyft 默默地進行 Envoy 的開發。Envoy 誕生的時間其實要比 Linkerd 更早一些,只是在 Lyft 內部不為人所知
2016 年 9 月 29 日在 SF Microservices 上,“Service Mesh”這個詞彙第一次在公開場合被使用。這標誌著“Service Mesh”這個詞,從 Buoyant 公司走向社群.
2016 年 9 月 13 日,Matt Klein 宣佈 Envoy 在 GitHub 開源,直接釋出 1.0.0 版本。
2016 年下半年,Linkerd 陸續釋出了 0.8 和 0.9 版本,開始支援 HTTP/2 和 gRPC,1.0 釋出在即;同時,藉助 Service Mesh 在社群的認可度,Linkerd 在年底開始申請加入 CNCF
2017 年 1 月 23 日,Linkerd 加入 CNCF。
2017 年 3 月 7 日,Linkerd 宣佈完成千億次產品請求
2017 年 4 月 25 日,Linkerd 1.0 版本釋出
2017 年 7 月 11 日,Linkerd 釋出版本 1.1.1,宣佈和 Istio 專案整合
2017 年 9 月, nginx突然宣佈要搞出一個Servicemesh來, Nginmesh: github.com/nginxinc/ng…, 可以作為istio的資料面, 不過這個專案目前處於不活躍開發(This project is no longer under active development)
2017 年 12 月 5 日,Conduit 0.1.0 版本釋出
Envoy 和 Linkerd 都是在資料面上的實現, 屬於同一個層面的競爭, 是用 C++ 語言實現的,在效能和資源消耗上要比採用 Scala 語言實現的 Linkerd 小,這一點對於延遲敏感型和資源敏的服務尤為重要.
Envoy 對 作為 Istio 的標準資料面實現, 其最主要的貢獻是提供了一套標準資料面API, 將服務資訊和流量規則下發到資料面的sidecar中, 另外Envoy還支援熱重啟. Istio早期採用了Envoy v1 API,目前的版本中則使用V2 API,V1已被廢棄.
通過採用該標準API,Istio將控制面和資料面進行了解耦,為多種資料面sidecar實現提供了可能性。事實上基於該標準API已經實現了多種Sidecar代理和Istio的整合,除Istio目前整合的Envoy外,還可以和Linkerd, Nginmesh等第三方通訊代理進行整合,也可以基於該API自己編寫Sidecar實現.
將控制面和資料面解耦是Istio後來居上,風頭超過Service mesh鼻祖Linkerd的一招妙棋。Istio站在了控制面的高度上,而Linkerd則成為了可選的一種sidecar實現.
Conduit 的整體架構和 Istio 一致,借鑑了 Istio 資料平面 + 控制平面的設計,而且選擇了 Rust 程式語言來實現資料平面,以達成 Conduit 宣稱的更輕、更快和超低資源佔用.
1.8 似曾相識的競爭格局
Kubernetes | Istio | |
---|---|---|
領域 | 容器編排 | 服務網格 |
主要競品 | Swarm, Mesos | Linkerd, Conduit |
主要盟友 | RedHat, CoreOS | IBM, Lyft |
主要競爭對手 | Docker 公司 | Buoyant 公司 |
標準化 | OCI: runtime spec, image spec | XDS |
外掛化 | CNI, CRI | Istio CNI, Mixer Adapter |
結果 | Kubernetes 成為容器編排事實標準 | ? |
google 主導的Kubernetes 在容器編排領域取得了完勝, 目前在服務網格領域的打法如出一轍, 社群對Istio前景也比較看好.
Istio CNI 計劃在1.1 作為實驗特性, 使用者可以通過擴充套件方式定製sidecar的網路.
1.9 國內Service Mesh 發展情況
螞蟻金服開源SOFAMesh:
從istio fork
使用Golang語言開發全新的Sidecar,替代Envoy
為了避免Mixer帶來的效能瓶頸,合併Mixer部分功能進入Sidecar
Pilot和Citadel模組進行了大幅的擴充套件和增強
擴充套件RPC協議: SOFARPC/HSF/Dubbo
華為:
go-chassis: github.com/go-chassis/… golang 微服務框架, 支援istio平臺
mesher: github.com/go-mesh/mes… mesh 資料面解決方案
國內首家提供Service Mesh公共服務的雲廠商
目前(2019年1月)公有云Istio 產品線上已經支援申請公測, 產品形態比較完善
騰訊雲 TSF:
基於 Istio、envoy 進行改造
支援 Kubernetes、虛擬機器以及裸金屬的服務
對 Istio 的能力進行了擴充套件和增強, 對 Consul 的完整適配
對於其他二進位制協議進行擴充套件支援
唯品會
OSP (Open Service Platform)
新浪:
Motan: 是一套基於java開發的RPC框架, Weibo Mesh 是基於Motan
2. Istio: 第二代 Service Mesh
Istio來自希臘語,英文意思是「sail」, 意為「啟航」
2.1 Istio 架構
2.2 核心功能
2.3 Istio 演示: BookInfo
2.1 Istio 架構
Istio Architecture(圖片來自Isio官網文件)
資料面
Sidecar
控制面
Pilot:服務發現、流量管理
Mixer:訪問控制、遙測
Citadel:終端使用者認證、流量加密
2.2 核心功能
流量管理
安全
可觀察性
多平臺支援
整合和定製
下面是我對Istio架構總結的思維導圖:
2.3 Istio 演示: BookInfo
以下是Istio官網經典的 BookInfo Demo, 這是一個多語言組成的異構微服務系統:
Bookinfo Application(圖片來自Isio官網文件)
下面我將現場給大家進行演示, 從demo安裝開始, 並體驗一下istio的流控功能:
使用helm管理istio
下載istio release: istio.io/docs/setup/…
安裝istio:
1kubectl apply -f install/kubernetes/helm/istio/templates/crds.yaml2helm install install/kubernetes/helm/istio --name istio --namespace istio-system複製程式碼
注意事項, 若要開啟sidecar自動注入功能, 需要:
確保 kube-apiserver 啟動引數 開啟了ValidatingAdmissionWebhook 和 MutatingAdmissionWebhook
給namespace 增加 label:
kubectl label namespace default istio-injection=enabled
同時還要保證 kube-apiserver 的 aggregator layer 開啟:
--enable-aggregator-routing=true
且證照和api server連通性正確設定.
如需解除安裝istio:
1helm delete --purge istio2kubectl delete -f install/kubernetes/helm/istio/templates/crds.yaml -n istio-system複製程式碼
更多安裝選擇請參考: istio.io/docs/setup/…
安裝Bookinfo Demo:
Bookinfo 是一個多語言異構的微服務demo, 其中 productpage 微服務會呼叫 details 和 reviews 兩個微服務, reviews 會呼叫ratings 微服務, reviews 微服務有 3 個版本. 關於此專案更多細節請參考: istio.io/docs/exampl…
部署應用:
1kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml複製程式碼
這將建立 productpage, details, ratings, reviews 對應的deployments 和 service, 其中reviews 有三個deployments, 代表三個不同的版本.
1 % kubectl get pod2NAME READY STATUS RESTARTS AGE3details-v1-6865b9b99d-mnxbt 2/2 Running 0 1m4productpage-v1-f8c8fb8-zjbhh 2/2 Running 0 59s5ratings-v1-77f657f55d-95rcz 2/2 Running 0 1m6reviews-v1-6b7f6db5c5-zqvkn 2/2 Running 0 59s7reviews-v2-7ff5966b99-lw72l 2/2 Running 0 59s8reviews-v3-5df889bcff-w9v7g 2/2 Running 0 59s910 % kubectl get svc11NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE12details ClusterIP 172.18.255.240 <none> 9080/TCP 1m13productpage ClusterIP 172.18.255.137 <none> 9080/TCP 1m14ratings ClusterIP 172.18.255.41 <none> 9080/TCP 1m15reviews ClusterIP 172.18.255.140 <none> 9080/TCP 1m複製程式碼
對入口流量進行配置:
1kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml複製程式碼
該操作會建立bookinfo-gateway 的Gateway, 並將流量傳送到productpage服務
1kubectl get gateway2NAME AGE3bookinfo-gateway 1m複製程式碼
此時通過bookinfo-gateway 對應的LB或者nodeport 訪問/productpage 頁面, 可以看到三個版本的reviews服務在隨機切換
基於權重的路由
通過CRD DestinationRule建立3 個reviews 子版本:
1kubectl apply -f samples/bookinfo/networking/destination-rule-reviews.yaml複製程式碼
通過CRD VirtualService 調整個 reviews 服務子版本的流量比例, 設定 v1 和 v3 各佔 50%
1kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-50-v3.yaml複製程式碼
重新整理頁面, 可以看到無法再看到reviews v2的內容, 頁面在v1和v3之間切換.
基於內容路由
修改reviews CRD, 將jason 登入的使用者版本路由到v2, 其他使用者路由到版本v3.
1kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-jason-v2-v3.yaml複製程式碼
重新整理頁面, 使用jason登入的使用者, 將看到v2 黑色星星版本, 其他使用者將看到v3 紅色星星版本.
更多BookInfo 示例, 請參閱: istio.io/docs/exampl…, 若要刪除應用: 執行指令碼 ./samples/bookinfo/platform/kube/cleanup.sh
3. Istio 資料面
3.1 資料面元件
3.2 sidecar 流量劫持原理
3.3 資料面標準API: xDS
3.4 分散式跟蹤
3.1 資料面元件
Istio 注入sidecar實現:
自動注入: 利用 Kubernetes Dynamic Admission Webhooks 對 新建的pod 進行注入: init container + sidecar
手動注入: 使用
istioctl kube-inject
注入Pod內容:
istio-init: 通過配置iptables來劫持Pod中的流量
istio-proxy: 兩個程式pilot-agent和envoy, pilot-agent 進行初始化並啟動envoy
Sidecar 自動注入實現
Istio 利用 Kubernetes Dynamic Admission Webhooks 對pod 進行sidecar注入
檢視istio 對這2個Webhooks 的配置 ValidatingWebhookConfiguration 和 MutatingWebhookConfiguration:
1% kubectl get ValidatingWebhookConfiguration -oyaml2% kubectl get mutatingWebhookConfiguration -oyaml複製程式碼
可以看出:
名稱空間
istio-system
中的服務istio-galley
, 通過路由/admitpilot
, 處理config.istio.io部分, rbac.istio.io, authentication.istio.io, networking.istio.io等資源的Validating 工作名稱空間istio-system 中的服務
istio-galley
, 通過路由/admitmixer
, 處理其他config.istio.io資源的Validating 工作名稱空間istio-system 中的服務
istio-sidecar-injector
, 通過路由/inject
, 處理其他v1/pods
的CREATE, 同時需要滿足名稱空間istio-injection: enabled
istio-init
資料面的每個Pod會被注入一個名為istio-init
的initContainer, initContrainer是K8S提供的機制,用於在Pod中執行一些初始化任務.在Initialcontainer執行完畢並退出後,才會啟動Pod中的其它container.
1initContainers:2- image: docker.io/istio/proxy_init:1.0.53 args:4 - -p5 - "15001"6 - -u7 - "1337"8 - -m9 - REDIRECT10 - -i11 - '*'12 - -x13 - ""14 - -b15 - "9080"16 - -d17 - ""複製程式碼
istio-init ENTRYPOINT 和 args 組合的啟動命令:
1/usr/local/bin/istio-iptables.sh -p 15001 -u 1337 -m REDIRECT -i '*' -x "" -b 9080 -d ""複製程式碼
istio-iptables.sh 原始碼地址為 github.com/istio/istio…
1$ istio-iptables.sh -p PORT -u UID -g GID [-m mode] [-b ports] [-d ports] [-i CIDR] [-x CIDR] [-h]2 -p: 指定重定向所有 TCP 流量的 Envoy 埠(預設為 $ENVOY_PORT = 15001)3 -u: 指定未應用重定向的使用者的 UID。通常,這是代理容器的 UID(預設為 $ENVOY_USER 的 uid,istio_proxy 的 uid 或 1337)4 -g: 指定未應用重定向的使用者的 GID。(與 -u param 相同的預設值)5 -m: 指定入站連線重定向到 Envoy 的模式,“REDIRECT” 或 “TPROXY”(預設為 $ISTIO_INBOUND_INTERCEPTION_MODE)6 -b: 逗號分隔的入站埠列表,其流量將重定向到 Envoy(可選)。使用萬用字元 “*” 表示重定向所有埠。為空時表示禁用所有入站重定向(預設為 $ISTIO_INBOUND_PORTS)7 -d: 指定要從重定向到 Envoy 中排除(可選)的入站埠列表,以逗號格式分隔。使用萬用字元“*” 表示重定向所有入站流量(預設為 $ISTIO_LOCAL_EXCLUDE_PORTS)8 -i: 指定重定向到 Envoy(可選)的 IP 地址範圍,以逗號分隔的 CIDR 格式列表。使用萬用字元 “*” 表示重定向所有出站流量。空列表將禁用所有出站重定向(預設為 $ISTIO_SERVICE_CIDR)9 -x: 指定將從重定向中排除的 IP 地址範圍,以逗號分隔的 CIDR 格式列表。使用萬用字元 “*” 表示重定向所有出站流量(預設為 $ISTIO_SERVICE_EXCLUDE_CIDR)。1011環境變數位於 $ISTIO_SIDECAR_CONFIG(預設在:/var/lib/istio/envoy/sidecar.env)複製程式碼
istio-init 通過配置iptable來劫持Pod中的流量:
引數
-p 15001
: Pod中的資料流量被iptable攔截,併發向15001埠, 該埠將由 envoy 監聽引數
-u 1337
: 用於排除使用者ID為1337,即Envoy自身的流量,以避免Iptable把Envoy發出的資料又重定向到Envoy, UID 為 1337,即 Envoy 所處的使用者空間,這也是 istio-proxy 容器預設使用的使用者, 見Sidecaristio-proxy
配置引數securityContext.runAsUser
引數
-b 9080
-d ""
: 入站埠控制, 將所有訪問 9080 埠(即應用容器的埠)的流量重定向到 Envoy 代理引數
-i '*'
-x ""
: 出站IP控制, 將所有出站流量都重定向到 Envoy 代理
Init 容器初始化完畢後就會自動終止,但是 Init 容器初始化結果(iptables)會保留到應用容器和 Sidecar 容器中.
istio-proxy
istio-proxy 以 sidecar 的形式注入到應用容器所在的pod中, 簡化的注入yaml:
1- image: docker.io/istio/proxyv2:1.0.52 name: istio-proxy3 args:4 - proxy5 - sidecar6 - --configPath7 - /etc/istio/proxy8 - --binaryPath9 - /usr/local/bin/envoy10 - --serviceCluster11 - ratings12 - --drainDuration13 - 45s14 - --parentShutdownDuration15 - 1m0s16 - --discoveryAddress17 - istio-pilot.istio-system:1500718 - --discoveryRefreshDelay19 - 1s20 - --zipkinAddress21 - zipkin.istio-system:941122 - --connectTimeout23 - 10s24 - --proxyAdminPort25 - "15000"26 - --controlPlaneAuthPolicy27 - NONE28 env:29 ......30 ports:31 - containerPort: 1509032 name: http-envoy-prom33 protocol: TCP34 securityContext:35 runAsUser: 133736 ......複製程式碼
istio-proxy容器中有兩個程式pilot-agent和envoy:
1~ % kubectl exec productpage-v1-f8c8fb8-wgmzk -c istio-proxy -- ps -ef2UID PID PPID C STIME TTY TIME CMD3istio-p+ 1 0 0 Jan03 ? 00:00:27 /usr/local/bin/pilot-agent proxy sidecar --configPath /etc/istio/proxy --binaryPath /usr/local/bin/envoy --serviceCluster productpage --drainDuration 45s --parentShutdownDuration 1m0s --discoveryAddress istio-pilot.istio-system:15007 --discoveryRefreshDelay 1s --zipkinAddress zipkin.istio-system:9411 --connectTimeout 10s --proxyAdminPort 15000 --controlPlaneAuthPolicy NONE4istio-p+ 21 1 0 Jan03 ? 01:26:24 /usr/local/bin/envoy -c /etc/istio/proxy/envoy-rev0.json --restart-epoch 0 --drain-time-s 45 --parent-shutdown-time-s 60 --service-cluster productpage --service-node sidecar~172.18.3.12~productpage-v1-f8c8fb8-wgmzk.default~default.svc.cluster.local --max-obj-name-len 189 --allow-unknown-fields -l warn --v2-config-only複製程式碼
可以看到:
/usr/local/bin/pilot-agent
是/usr/local/bin/envoy
的父程式, Pilot-agent程式根據啟動引數和K8S API Server中的配置資訊生成Envoy的初始配置檔案(/etc/istio/proxy/envoy-rev0.json
),並負責啟動Envoy程式pilot-agent 的啟動引數裡包括: discoveryAddress(pilot服務地址), Envoy 二進位制檔案的位置, 服務叢集名, 監控指標上報地址, Envoy 的管理埠, 熱重啟時間等
Envoy配置初始化流程:
Pilot-agent根據啟動引數和K8S API Server中的配置資訊生成Envoy的初始配置檔案envoy-rev0.json,該檔案告訴Envoy從xDS server中獲取動態配置資訊,並配置了xDS server的地址資訊,即控制面的Pilot
Pilot-agent使用envoy-rev0.json啟動Envoy程式
Envoy根據初始配置獲得Pilot地址,採用xDS介面從Pilot獲取到Listener,Cluster,Route等d動態配置資訊
Envoy根據獲取到的動態配置啟動Listener,並根據Listener的配置,結合Route和Cluster對攔截到的流量進行處理
檢視envoy 初始配置檔案:
1kubectl exec productpage-v1-f8c8fb8-wgmzk -c istio-proxy -- cat /etc/istio/proxy/envoy-rev0.json複製程式碼
3.2 sidecar 流量劫持原理
sidecar 既要作為服務消費者端的正向代理,又要作為服務提供者端的反向代理, 具體攔截過程如下:
Pod 所在的network namespace內, 除了envoy發出的流量外, iptables規則會對進入和發出的流量都進行攔截,通過nat redirect重定向到Envoy監聽的15001埠.
envoy 會根據從Pilot拿到的 XDS 規則, 對流量進行轉發.
envoy 的 listener 0.0.0.0:15001 接收進出 Pod 的所有流量,然後將請求移交給對應的virtual listener
對於本pod的服務, 有一個http listener
podIP+埠
接受inbound 流量每個service+非http埠, 監聽器配對的 Outbound 非 HTTP 流量
每個service+http埠, 有一個http listener:
0.0.0.0+埠
接受outbound流量
整個攔截轉發過程對業務容器是透明的, 業務容器仍然使用 Service 域名和埠進行通訊, service 域名仍然會轉換為service IP, 但service IP 在sidecar 中會被直接轉換為 pod IP, 從容器中出去的流量已經使用了pod IP會直接轉發到對應的Pod, 對比傳統kubernetes 服務機制, service IP 轉換為Pod IP 在node上進行, 由 kube-proxy維護的iptables實現.
3.3 資料面標準API: xDS
xDS是一類發現服務的總稱,包含LDS,RDS,CDS,EDS以及 SDS。Envoy通過xDS API可以動態獲取Listener(監聽器), Route(路由),Cluster(叢集),Endpoint(叢集成員)以 及Secret(證照)配置
xDS API 涉及的概念:
Host
Downstream
Upstream
Listener
Cluster
Envoy 配置熱更新: 配置的動態變更,而不需要重啟 Envoy:
新老程式採用基本的RPC協議使用Unix Domain Socket通訊.
新程式啟動並完成所有初始化工作後,向老程式請求監聽套接字的副本.
新程式接管套接字後,通知老程式關閉套接字.
通知老程式終止自己.
xDS 除錯
Pilot在9093埠提供了下述除錯介面:
1# What is sent to envoy2# Listeners and routes3curl $PILOT/debug/adsz45# Endpoints6curl $PILOT/debug/edsz78# Clusters9curl $PILOT/debug/cdsz複製程式碼
Sidecar Envoy 也提供了管理介面,預設為localhost的15000埠,可以獲取listener,cluster以及完整的配置資料
可以通過以下命令檢視支援的除錯介面:
1kubectl exec productpage-v1-f8c8fb8-zjbhh -c istio-proxy curl http://127.0.0.1:15000/help複製程式碼
或者forward到本地就行除錯
1kubectl port-forward productpage-v1-f8c8fb8-zjbhh 15000複製程式碼
相關的除錯介面:
1http://127.0.0.1:150002http://127.0.0.1:15000/help3http://127.0.0.1:15000/config_dump4http://127.0.0.1:15000/listeners5http://127.0.0.1:15000/clusters複製程式碼
使用istioctl 檢視代理配置:
1istioctl pc {xDS型別} {POD_NAME} {過濾條件} {-o json/yaml}23eg:4istioctl pc routes productpage-v1-f8c8fb8-zjbhh --name 9080 -o json複製程式碼
xDS 型別包括: listener, route, cluster, endpoint
對xDS 進行分析: productpage 訪問 reviews 服務
檢視 product 的所有listener:
1% istioctl pc listener productpage-v1-f8c8fb8-zjbhh2ADDRESS PORT TYPE3172.18.255.178 15011 TCP4172.18.255.194 44134 TCP5172.18.255.110 443 TCP6172.18.255.190 50000 TCP7172.18.255.203 853 TCP8172.18.255.2 443 TCP9172.18.255.239 16686 TCP100.0.0.0 80 TCP11172.18.255.215 3306 TCP12172.18.255.203 31400 TCP13172.18.255.111 443 TCP14172.18.255.203 8060 TCP15172.18.255.203 443 TCP16172.18.255.40 443 TCP17172.18.255.1 443 TCP18172.18.255.53 53 TCP19172.18.255.203 15011 TCP20172.18.255.105 14268 TCP21172.18.255.125 42422 TCP22172.18.255.105 14267 TCP23172.18.255.52 80 TCP240.0.0.0 15010 HTTP250.0.0.0 9411 HTTP260.0.0.0 8060 HTTP270.0.0.0 9080 HTTP280.0.0.0 15004 HTTP290.0.0.0 20001 HTTP300.0.0.0 9093 HTTP310.0.0.0 8080 HTTP320.0.0.0 15030 HTTP330.0.0.0 9091 HTTP340.0.0.0 9090 HTTP350.0.0.0 15031 HTTP360.0.0.0 3000 HTTP370.0.0.0 15001 TCP38172.18.3.50 9080 HTTP 這是當前pod ip 暴露的服務地址, 會路由到迴環地址, 各個pod 會不一樣複製程式碼
envoy 流量入口的listener:
1% istioctl pc listener productpage-v1-f8c8fb8-zjbhh --address 0.0.0.0 --port 15001 -o json2[3 {4 "name": "virtual",5 "address": {6 "socketAddress": {7 "address": "0.0.0.0",8 "portValue": 150019 }10 },11 "filterChains": [12 {13 "filters": [14 {15 "name": "envoy.tcp_proxy",16 "config": {17 "cluster": "BlackHoleCluster",18 "stat_prefix": "BlackHoleCluster"19 }20 }21 ]22 }23 ],24 "useOriginalDst": true # 這意味著它將請求交給最符合請求原始目標的監聽器。如果找不到任何匹配的虛擬監聽器,它會將請求傳送給返回 404 的 BlackHoleCluster25 }26]複製程式碼
以下是reviews的所有pod IP
1 % kubectl get ep reviews2NAME ENDPOINTS AGE3reviews 172.18.2.35:9080,172.18.3.48:9080,172.18.3.49:9080 1d複製程式碼
對於目的地址是以上ip的http訪問, 這些 ip 並沒有對應的listener, 因此會通過埠9080 匹配到listener 0.0.0.0 9080
檢視listener 0.0.0.0 9080
:
1% istioctl pc listener productpage-v1-f8c8fb8-zjbhh --address 0.0.0.0 --port 9080 -ojson2 {3 "name": "0.0.0.0_9080",4 "address": {5 "socketAddress": {6 "address": "0.0.0.0",7 "portValue": 90808 }9 },10 ......1112 "rds": {13 "config_source": {14 "ads": {}15 },16 "route_config_name": "9080"17 },18 ......複製程式碼
檢視名為9080
的 route:
1% istioctl pc routes productpage-v1-f8c8fb8-zjbhh --name 9080 -o json23[4 {5 "name": "9080",6 "virtualHosts": [7 {8 "name": "details.default.svc.cluster.local:9080",9 "domains": [10 "details.default.svc.cluster.local",11 "details.default.svc.cluster.local:9080",12 "details",13 "details:9080",14 "details.default.svc.cluster",15 "details.default.svc.cluster:9080",16 "details.default.svc",17 "details.default.svc:9080",18 "details.default",19 "details.default:9080",20 "172.18.255.240",21 "172.18.255.240:9080"22 ],23 "routes": [24 {25 "match": {26 "prefix": "/"27 },28 "route": {29 "cluster": "outbound|9080||details.default.svc.cluster.local",30 "timeout": "0.000s",31 "maxGrpcTimeout": "0.000s"32 },33 ......34 {35 "name": "productpage.default.svc.cluster.local:9080",36 "domains": [37 "productpage.default.svc.cluster.local",38 "productpage.default.svc.cluster.local:9080",39 "productpage",40 "productpage:9080",41 "productpage.default.svc.cluster",42 "productpage.default.svc.cluster:9080",43 "productpage.default.svc",44 "productpage.default.svc:9080",45 "productpage.default",46 "productpage.default:9080",47 "172.18.255.137",48 "172.18.255.137:9080"49 ],50 "routes": [ ...... ]51 },52 {53 "name": "ratings.default.svc.cluster.local:9080",54 "domains": [55 "ratings.default.svc.cluster.local",56 "ratings.default.svc.cluster.local:9080",57 "ratings",58 "ratings:9080",59 "ratings.default.svc.cluster",60 "ratings.default.svc.cluster:9080",61 "ratings.default.svc",62 "ratings.default.svc:9080",63 "ratings.default",64 "ratings.default:9080",65 "172.18.255.41",66 "172.18.255.41:9080"67 ],68 "routes": [ ...... ]69 },70 {71 "name": "reviews.default.svc.cluster.local:9080",72 "domains": [73 "reviews.default.svc.cluster.local",74 "reviews.default.svc.cluster.local:9080",75 "reviews",76 "reviews:9080",77 "reviews.default.svc.cluster",78 "reviews.default.svc.cluster:9080",79 "reviews.default.svc",80 "reviews.default.svc:9080",81 "reviews.default",82 "reviews.default:9080",83 "172.18.255.140",84 "172.18.255.140:9080"85 ],86 "routes": [87 {88 "match": {89 "prefix": "/",90 "headers": [91 {92 "name": "end-user",93 "exactMatch": "jason"94 }95 ]96 },97 "route": {98 "cluster": "outbound|9080|v2|reviews.default.svc.cluster.local",99 "timeout": "0.000s",100 "maxGrpcTimeout": "0.000s"101 },102 ......103 },104 {105 "match": {106 "prefix": "/"107 },108 "route": {109 "cluster": "outbound|9080|v3|reviews.default.svc.cluster.local",110 "timeout": "0.000s",111 "maxGrpcTimeout": "0.000s"112 },113 .......114 }115 ]116 }117 ],118 "validateClusters": false119 }120]複製程式碼
可以看到, 在9080 這個route 中, 包含所有這個埠的http 路由資訊, 通過virtualHosts列表進行服務域名分發到各個cluster.
檢視virtualHosts reviews.default.svc.cluster.local:9080
中的routes資訊, 可以看到jason 路由到了cluster outbound|9080|v2|reviews.default.svc.cluster.local
檢視該cluster:
1% istioctl pc cluster productpage-v1-f8c8fb8-zjbhh --fqdn reviews.default.svc.cluster.local --subset v2 -o json2[3 {4 "name": "outbound|9080|v2|reviews.default.svc.cluster.local",5 "type": "EDS",6 "edsClusterConfig": {7 "edsConfig": {8 "ads": {}9 },10 "serviceName": "outbound|9080|v2|reviews.default.svc.cluster.local"11 },12 "connectTimeout": "1.000s",13 "lbPolicy": "RANDOM",14 "circuitBreakers": {15 "thresholds": [16 {}17 ]18 }19 }20]複製程式碼
檢視其對應的endpoint:
1 % istioctl pc endpoint productpage-v1-f8c8fb8-zjbhh --cluster 'outbound|9080|v2|reviews.default.svc.cluster.local'2ENDPOINT STATUS CLUSTER3172.18.2.35:9080 HEALTHY outbound|9080|v2|reviews.default.svc.cluster.local複製程式碼
該endpoint 即為 reviews 服務 V2 對應的 pod IP
XDS服務介面的最終一致性考慮
遵循 make before break 模型
3.4 分散式跟蹤
以下是分散式全鏈路跟蹤示意圖:
一個典型的Trace案例(圖片來自opentracing文件中文版)
Jaeger 是Uber 開源的全鏈路跟蹤系統, 符合OpenTracing協議, OpenTracing 和 Jaeger 均是CNCF 成員專案, 以下是Jaeger 架構的示意圖:
Jaeger 架構示意圖(圖片來自Jaeger官方文件)
分散式跟蹤系統讓開發者能夠得到視覺化的呼叫流程展示。這對複雜的微服務系統進行問題排查和效能優化時至關重要.
Envoy 原生支援http 鏈路跟蹤:
生成 Request ID:Envoy 會在需要的時候生成 UUID,並操作名為 [x-request-id] 的 HTTP Header。應用可以轉發這個 Header 用於統一的記錄和跟蹤.
支援整合外部跟蹤服務:Envoy 支援可插接的外部跟蹤視覺化服務。目前支援有:
LightStep
Zipkin 或者 Zipkin 相容的後端(比如說 Jaeger)
Datadog
客戶端跟蹤 ID 連線:x-client-trace-id Header 可以用來把不信任的請求 ID 連線到受信的 x-request-id Header 上
跟蹤上下文資訊的傳播
不管使用的是哪個跟蹤服務,都應該傳播 x-request-id,這樣在被呼叫服務中啟動相關性的記錄
如果使用的是 Zipkin,Envoy 要傳播的是 B3 Header。(x-b3-traceid, x-b3-spanid, x-b3-parentspanid, x-b3-sampled, 以及 x-b3-flags. x-b3-sampled)
上下文跟蹤並非零修改, 在呼叫下游服務時, 上游應用應該自行傳播跟蹤相關的 HTTP Header
4. Istio 控制面
4.1 Pilot 架構
4.2 流量管理模型
4.3 故障處理
4.4 Mixer 架構
4.5 Mixer介面卡模型
4.6 Mixer 快取機制
4.1 Pilot 架構
Pilot Architecture(圖片來自Isio官網文件)
Rules API: 對外封裝統一的 API,供服務的開發者或者運維人員呼叫,可以用於流量控制。
Envoy API: 對內封裝統一的 API,供 Envoy 呼叫以獲取註冊資訊、流量控制資訊等。
抽象模型層: 對服務的註冊資訊、流量控制規則等進行抽象,使其描述與平臺無關。
平臺適配層: 用於適配各個平臺如 Kubernetes、Mesos、Cloud Foundry 等,把平臺特定的註冊資訊、資源資訊等轉換成抽象模型層定義的平臺無關的描述。例如,Pilot 中的 Kubernetes 介面卡實現必要的控制器來 watch Kubernetes API server 中 pod 註冊資訊、ingress 資源以及用於儲存流量管理規則的第三方資源的更改
4.2 流量管理模型
VirtualService
DestinationRule
ServiceEntry
Gateway
VirtualService
VirtualService 中定義了一系列針對指定服務的流量路由規則。每個路由規則都是針對特定協議的匹配規則。如果流量符合這些特徵,就會根據規則傳送到服務登錄檔中的目標服務, 或者目標服務的子集或版本, 匹配規則中還包含了對流量發起方的定義,這樣一來,規則還可以針對特定客戶上下文進行定製.
Gateway
Gateway 描述了一個負載均衡器,用於承載網格邊緣的進入和發出連線。這一規範中描述了一系列開放埠,以及這些埠所使用的協議、負載均衡的 SNI 配置等內容
ServiceEntry
Istio 服務網格內部會維護一個與平臺無關的使用通用模型表示的服務登錄檔,當你的服務網格需要訪問外部服務的時候,就需要使用 ServiceEntry 來新增服務註冊, 這類服務可能是網格外的 API,或者是處於網格內部但卻不存在於平臺的服務登錄檔中的條目(例如需要和 Kubernetes 服務溝通的一組虛擬機器服務).
EnvoyFilter
EnvoyFilter 描述了針對代理服務的過濾器,用來定製由 Istio Pilot 生成的代理配置.
Kubernetes Ingress vs Istio Gateway
合併了L4-6和L7的規範, 對傳統技術棧使用者的應用遷入不方便
表現力不足:
只能對 service、port、HTTP 路徑等有限欄位匹配來路由流量
埠只支援預設80/443
Istio Gateway:·
定義了四層到六層的負載均衡屬性 (通常是SecOps或NetOps關注的內容)
埠
埠所使用的協議(HTTP, HTTPS, GRPC, HTTP2, MONGO, TCP, TLS)
Hosts
TLS SNI header 路由支援
TLS 配置支援(http 自動301, 證照等)
ip / unix domain socket
Kubernetes, Istio, Envoy xDS 模型對比
以下是對Kubernetes, Istio, Envoy xDS 模型的不嚴格對比
Kubernetes | Istio | Envoy xDS | |
---|---|---|---|
入口流量 | Ingress | GateWay | Listener |
服務定義 | Service | - | Cluster+Listener |
外部服務定義 | - | ServiceEntry | Cluster+Listener |
版本定義 | - | DestinationRule | Cluster+Listener |
版本路由 | - | VirtualService | Route |
例項 | Endpoint | - | Endpoint |
Kubernetes 和 Istio 服務定址的區別:
Kubernetes:
kube-dns: service domain -> service ip
kube-proxy(node iptables): service ip -> pod ip
Istio:
kube-dns: service domain -> service ip
sidecar envoy: service ip -> pod ip
4.3 故障處理
隨著微服務的拆分粒度增強, 服務呼叫會增多, 更復雜, 扇入 扇出, 呼叫失敗的風險增加, 以下是常見的服務容錯處理方式:
控制端 | 目的 | 實現 | Istio | |
---|---|---|---|---|
超時 | client | 保護client | 請求等待超時/請求執行超時 | timeout |
重試 | client | 容忍server臨時錯誤, 保證業務整體可用性 | 重試次數/重試的超時時間 | retries.attempts, retries.perTryTimeout |
熔斷 | client | 降低效能差的服務或例項的影響 | 通常會結合超時+重試, 動態進行服務狀態決策(Open/Closed/Half-Open) | trafficPolicy.outlierDetection |
降級 | client | 保證業務主要功能可用 | 主邏輯失敗採用備用邏輯的過程(映象服務分級, 呼叫備用服務, 或者返回mock資料) | 暫不支援, 需要業務程式碼按需實現 |
隔離 | client | 防止異常server佔用過多client資源 | 隔離對不同服務呼叫的資源依賴: 執行緒池隔離/訊號量隔離 | 暫不支援 |
冪等 | server | 容忍client重試, 保證資料一致性 | 唯一ID/加鎖/事務等手段 | 暫不支援, 需要業務程式碼按需實現 |
限流 | server | 保護server | 常用演算法: 計數器, 漏桶, 令牌桶 | trafficPolicy.connectionPool |
Istio 沒有無降級處理支援: Istio可以提高網格中服務的可靠性和可用性。但是,應用程式仍然需要處理故障(錯誤)並採取適當的回退操作。例如,當負載均衡池中的所有例項都失敗時,Envoy 將返回 HTTP 503。應用程式有責任實現必要的邏輯,對這種來自上游服務的 HTTP 503 錯誤做出合適的響應。
4.4 Mixer 架構
Mixer Topology(圖片來自Isio官網文件)
Istio 的四大功能點連線, 安全, 控制, 觀察, 其中「控制」和「觀察」的功能主要都是由Mixer元件來提供, Mixer 在Istio中角色:
功能上: 負責策略控制和遙測收集
架構上:提供外掛模型,可以擴充套件和定製
4.5 Mixer Adapter 模型
Attribute
Template
Adapter
Instance
Handler
Rule
Attribute
Attribute 是策略和遙測功能中有關請求和環境的基本資料, 是用於描述特定服務請求或請求環境的屬性的一小段資料。例如,屬性可以指定特定請求的大小、操作的響應程式碼、請求來自的 IP 地址等.
Istio 中的主要屬性生產者是 Envoy,但專用的 Mixer 介面卡也可以生成屬性
屬性詞彙表見: Attribute Vocabulary
資料流向: envoy -> mixer
Template
Template 是對 adapter 的資料格式和處理介面的抽象, Template定義了:
當處理請求時傳送給adapter 的資料格式
adapter 必須實現的gRPC service 介面
每個Template 通過 template.proto
進行定義:
名為
Template
的一個messageName: 通過template所在的package name自動生成
template_variety: 可選Check, Report, Quota or AttributeGenerator, 決定了adapter必須實現的方法. 同時決定了在mixer的什麼階段要生成template對應的instance:
Check: 在Mixer’s Check API call時建立併傳送instance
Report: 在Mixer’s Report API call時建立併傳送instance
Quota: 在Mixer’s Check API call時建立併傳送instance(查詢配額時)
AttributeGenerator: for both Check, Report Mixer API calls
Istio 內建的Templates: istio.io/docs/refere…
Adapter
封裝了 Mixer 和特定外部基礎設施後端進行互動的必要介面,例如 Prometheus 或者 Stackdriver
定義了需要處理的模板(在yaml中配置template)
定義了處理某個Template資料格式的GRPC介面
定義 Adapter需要的配置格式(Params)
可以同時處理多個資料(instance)
Istio 內建的Adapter: istio.io/docs/refere…
Instance
代表符合某個Template定義的資料格式的具體實現, 該具體實現由使用者配置的 CRD, CRD 定義了將Attributes 轉換為具體instance 的規則, 支援屬性表示式
Instance CRD 是Template 中定義的資料格式 + 屬性轉換器
內建的Instance 型別(其實就是內建 Template): Templates
屬性表示式見: Expression Language
資料流向: mixer -> adapter 例項
Handler
使用者配置的 CRD, 為具體Adapter提供一個具體配置, 對應Adapter的可執行例項
Rule
使用者配置的 CRD, 配置一組規則,這些規則描述了何時呼叫特定(通過Handler對應的)介面卡及哪些Instance
結語
電腦科學中的所有問題,都可以用另一個層來解決,除了層數太多的問題
Kubernetes 本身已經很複雜, Istio 為了更高層控制的抽象, 又增加了很多概念. 複雜度堪比kubernetes.
可以看出istio 設計精良, 在處理微服務的複雜場景有很多優秀之處, 不過目前istio目前的短板還是很明顯, 高度的抽象帶來了很多效能的損耗, 社群現在也有很多優化的方向, 像螞蟻金服開源的SofaMesh 主要是去精簡層, 試圖在sidecar裡去做很多mixer 的事情, 減少sidecar和mixer的同步請求依賴, 而一些其他的sidecar 網路方案, 更多的是考慮去優化層, 優化sidecar 這一層的效能開銷.
在Istio 1.0 之前, 主要還是以功能的實現為主, 不過後面隨著社群的積極投入, 相信Istio的效能會有長足的提升.
筆者之前從事過多年的服務治理相關的工作, 過程中切身體會到微服務治理的痛點, 所以也比較關注 service mesh的發展, 個人對istio也非常看好, 剛好今年我們中心容器產品今年也有這方面的計劃, 期待我們能在這個方向進行一些產品和技術的深耕.
參考資料:
- Service Mesh年度總結:群雄逐鹿烽煙起
- Why You Should Care About Istio Gateways
- Pattern: Service Mesh
- Mixer Out Of Process Adapter Dev Guide
- Mixer Out of Process Adapter Walkthrough
- Envoy 中的 xDS REST 和 gRPC 協議詳解
- Delayering Istio with AppSwitch
- servicemesher 中文社群