Envoy 中的 xDS REST 和 gRPC 協議詳解

Envoy 經過查詢文件或管理服務器來動態發現資源。歸納地講,對應的發現服務及其相應的 API 被稱做 xDS。Envoy 經過訂閱(subscription)方式來獲取資源,如監控指定路徑下的文件、啓動 gRPC 流或輪詢 REST-JSON URL。後兩種方式會發送 DiscoveryRequest 請求消息,發現的對應資源則包含在響應消息 DiscoveryResponse 中。下面,咱們將具體討論每種訂閱類型。node

文件訂閱

發現動態資源的最簡單方式就是將其保存於文件,並將路徑配置在 ConfigSource 中的 path 參數中。Envoy 使用 inotify(Mac OS X 上爲 kqueue)來監控文件的變化,在文件被更新時,Envoy 讀取保存的 DiscoveryResponse 數據進行解析,數據格式能夠爲二進制 protobuf、JSON、YAML 和協議文本等。git

譯者注:core.ConfigSource 配置格式以下:github

{
  "path": "...",
  "api_config_source": "{...}",
  "ads": "{...}"
}複製代碼

文件訂閱方式可提供統計數據和日誌信息,可是缺乏 ACK/NACK 更新的機制。若是更新的配置被拒絕,xDS API 則繼續使用最後一個的有效配置。json

gRPC 流式訂閱

單資源類型發現

每一個 xDS API 能夠單獨配置 ApiConfigSource,指向對應的上游管理服務器的集羣地址。每一個 xDS 資源類型會啓動一個獨立的雙向 gRPC 流,可能對應不一樣的管理服務器。API 交付方式採用最終一致性。能夠參考後續聚合服務發現(ADS) 章節來了解必要的顯式控制序列。bootstrap

譯者注:core.ApiConfigSource 配置格式以下:api

{
  "api_type": "...",
  "cluster_names": [],
  "grpc_services": [],
  "refresh_delay": "{...}",
  "request_timeout": "{...}"
}複製代碼

類型 URL

每一個 xDS API 都與給定的資源的類型存在 1:1 對應。關係以下:安全

的概念以下所示,其採用 type.googleapis.com/<resource type> 的形式,例如 CDS 對應於 type.googleapis.com/envoy.api.v2.Cluster。在 Envoy 的請求和管理服務器的響應中,都包括了資源類型 URL。

ACK/NACK 和版本

每一個 Envoy 流以 DiscoveryRequest 開始,包括了列表訂閱的資源、訂閱資源對應的類型 URL、節點標識符和空的 version_info。EDS 請求示例以下:

version_info:
node: { id: envoy }
resource_names:
- foo
- bar
type_url: type.googleapis.com/envoy.api.v2.ClusterLoadAssignment
response_nonce:複製代碼

管理服務器可馬上或等待資源就緒時發送 DiscoveryResponse做爲響應,示例以下:

version_info: X
resources:
- foo ClusterLoadAssignment proto encoding
- bar ClusterLoadAssignment proto encoding
type_url: type.googleapis.com/envoy.api.v2.ClusterLoadAssignment
nonce: A複製代碼

Envoy 在處理 DiscoveryResponse 響應後,將經過流發送一個新的請求,請求包含應用成功的最後一個版本號和管理服務器提供的 nonce。若是本次更新已成功應用,則 version_info 的值設置爲 X,以下序列圖所示:

在此序列圖及後續中,將統一使用如下縮寫格式:

  • DiscoveryRequest: (V=version_info,R=resource_names,N=response_nonce,T=type_url)

  • DiscoveryResponse: (V=version_info,R=resources,N=nonce,T=type_url)

譯者注:在信息安全中,Nonce是一個在加密通訊只能使用一次的數字。在認證協議中,它每每是一個隨機僞隨機數,以免重放攻擊。Nonce也用於流密碼以確保安全。若是須要使用相同的密鑰加密一個以上的消息,就須要Nonce來確保不一樣的消息與該密鑰加密的密鑰流不一樣。(引用自維基百科)在本文中nonce是每次更新的數據包的惟一標識。

版本爲 Envoy 和管理服務器提供了共享當前應用配置的概念和經過 ACK/NACK 來進行配置更新的機制。若是 Envoy 拒絕配置更新 X,則回覆 error_detail 及前一個的版本號,在當前狀況下爲空的初始版本號,error_detail 包含了有關錯誤的更加詳細的信息:

後續,API 更新可能會在新版本 Y 上成功:

每一個流都有本身的版本概念,但不存在跨資源類型的共享版本。在不使用 ADS 的狀況下,每一個資源類型可能具備不一樣的版本,由於 Envoy API 容許指向不一樣的 EDS/RDS 資源配置並對應不一樣的 ConfigSources

什麼時候發送更新

管理服務器應該只向 Envoy 客戶端發送上次 DiscoveryResponse 後更新過的資源。Envoy 則會根據接受或拒絕 DiscoveryResponse 的狀況,當即回覆包含 ACK/NACK 的 DiscoveryRequest 請求。若是管理服務器每次發送相同的資源集結果,而不是根據其更新狀況,則會致使 Envoy 和管理服務器通信效率大打折扣。

在同一個流中,新的 DiscoveryRequests 將取代此前具備相同的資源類型 DiscoveryRequest 請求。這意味着管理服務器只須要響應給定資源類型最新的 DiscoveryRequest 請求便可。

資源提示

DiscoveryRequest 中的 resource_names 信息做爲資源提示出現。一些資源類型,例如 ClusterListener 將使用一個空的 resource_names,由於 Envoy 須要獲取管理服務器對應於節點標識的全部 Cluster(CDS)和 Listener(LDS)。對於其餘資源類型,如 RouteConfigurations(RDS)和 ClusterLoadAssignments(EDS),則遵循此前的 CDS/LDS 更新,Envoy 可以明確地枚舉這些資源。

LDS/CDS 資源提示信息將始終爲空,而且指望管理服務器的每一個響應都提供 LDS/CDS 資源的完整狀態。缺席的 ListenerCluster 將被刪除。

對於 EDS/RDS,管理服務器並不須要爲每一個請求的資源進行響應,並且還可能提供額外未請求的資源。resource_names 只是一個提示。Envoy 將默默地忽略返回的多餘資源。若是請求的資源中缺乏相應的 RDS 或 EDS 更新,Envoy 將保留對應資源的最後的值。管理服務器可能會依據 DiscoveryRequestnode 標識推斷其所需的 EDS/RDS 資源,在這種狀況下,提示信息可能會被丟棄。從相應的角度來看,空的 EDS/RDS DiscoveryResponse 響應其實是代表在 Envoy 中爲一個空的資源。

ListenerCluster 被刪除時,其對應的 EDS 和 RDS 資源也須要在 Envoy 實例中刪除。爲使 EDS 資源被 Envoy 已知或跟蹤,就必須存在應用過的 Cluster 定義(如經過 CDS 獲取)。RDS 和 Listeners 之間存在相似的關係(如經過 LDS 獲取)。

對於 EDS/RDS ,Envoy 能夠爲每一個給定類型的資源生成不一樣的流(如每一個 ConfigSource 都有本身的上游管理服務器的集羣)或當指定資源類型的請求發送到同一個管理服務器的時候,容許將多個資源請求組合在一塊兒發送。雖然能夠單個實現,但管理服務器應具有處理每一個給定資源類型中對單個或多個 resource_names 請求的能力。下面的兩個序列圖對於獲取兩個 EDS 資源都是有效的 {foo,bar}

資源更新

如上所述,Envoy 可能會更新 DiscoveryRequest 中出現的 resource_names 列表,其中 DiscoveryRequest 是用來 ACK/NACK 管理服務器的特定的 DiscoveryResponse 。此外,Envoy 後續可能會發送額外的 DiscoveryRequests ,用於在特定 version_info 上使用新的資源提示來更新管理服務器。例如,若是 Envoy 在 EDS 版本 X 時僅知道集羣 foo,但在隨後收到的 CDS 更新時額外獲取了集羣 bar ,它可能會爲版本 X 發出額外的 DiscoveryRequest 請求,並將 {foo,bar} 做爲請求的 resource_names

這裏可能會出現競爭情況;若是 Envoy 在版本 X 上發佈了資源提示更新請求,但在管理服務器處理該請求以前發送了新的版本號爲 Y 的響應,針對 version_infoX 的版本,資源提示更新可能會被解釋爲拒絕 Y 。爲避免這種狀況,經過使用管理服務器提供的 nonce,Envoy 可用來保證每一個 DiscoveryRequest 對應到相應的 DiscoveryResponse

管理服務器不該該爲含有過時 nonceDiscoveryRequest 發送 DiscoveryResponse 響應。在向 Envoy 發送的 DiscoveryResponse 中包含了的新 nonce ,則此前的 nonce 將過時。在資源新版本就緒以前,管理服務器不須要向 Envoy 發送更新。同版本的早期請求將會過時。在新版本就緒時,管理服務器可能會處理同一個版本號的多個 DiscoveryRequests請求。

上述資源更新序列代表 Envoy 並不能期待其發出的每一個 DiscoveryRequest 都獲得 DiscoveryResponse 響應。

最終一致性考慮

因爲 Envoy 的 xDS API 採用最終一致性,所以在更新期間可能致使流量被丟棄。例如,若是經過 CDS/EDS 僅獲取到了集羣 X,並且 RouteConfiguration 引用了集羣 X;在 CDS/EDS 更新集羣 Y 配置以前,若是將 RouteConfiguration 將引用的集羣調整爲 Y ,那麼流量將被吸入黑洞而丟棄,直至集羣 Y 被 Envoy 實例獲取。

對某些應用程序,可接受臨時的流量丟棄,客戶端重試或其餘 Envoy sidecar 會掩蓋流量丟棄。那些對流量丟棄不能容忍的場景,能夠經過如下方式避免流量丟失,CDS/EDS 更新同時攜帶 XY ,而後發送 RDS 更新從 X 切換到 Y ,此後發送丟棄 X 的 CDS/EDS 更新。

通常來講,爲避免流量丟棄,更新的順序應該遵循 make before break 模型,其中

  • 必須始終先推送 CDS 更新(若是有)。

  • EDS 更新(若是有)必須在相應集羣的 CDS 更新後到達。

  • LDS 更新必須在相應的 CDS/EDS 更新後到達。

  • 與新添加的監聽器相關的 RDS 更新必須在最後到達。

  • 最後,刪除過時的 CDS 集羣和相關的 EDS 端點(再也不被引用的端點)。

若是沒有新的集羣/路由/監聽器或者容許更新時臨時流量丟失的狀況下,能夠獨立推送 xDS 更新。請注意,在 LDS 更新的狀況下,監聽器須在接收流量以前被預熱,例如如其配置了依賴的路由,則先需先從 RDS 進行獲取。添加/刪除/更新集羣信息時,集羣也須要進行預熱。另外一方面,若是管理平面確保路由更新時所引用的集羣已經準備就緒,路由能夠不用預熱。

聚合服務發現(ADS)

當管理服務器進行資源分發時,經過上述保證交互順序的方式來避免流量丟棄是一項頗有挑戰的工做。ADS 容許單一管理服務器經過單個 gRPC 流,提供全部的 API 更新。配合仔細規劃的更新順序,ADS 可規避更新過程當中流量丟失。使用 ADS,在單個流上可經過類型 URL 來進行復用多個獨立的 DiscoveryRequest/DiscoveryResponse 序列。對於任何給定類型的 URL,以上 DiscoveryRequestDiscoveryResponse 消息序列都適用。 更新序列可能以下所示:

每一個 Envoy 實例可以使用單獨的 ADS 流。

最小化 ADS 配置的 bootstrap.yaml 片斷示例以下:

node:
 id: <node identifier>
dynamic_resources:
 cds_config: {ads: {}}
 lds_config: {ads: {}}
 ads_config:
 api_type: GRPC
 grpc_services:
 envoy_grpc:
 cluster_name: ads_cluster
static_resources:
 clusters:
 - name: ads_cluster
 connect_timeout: { seconds: 5 }
 type: STATIC
 hosts:
 - socket_address:
 address: <ADS management server IP address>
 port_value: <ADS management server port>
 lb_policy: ROUND_ROBIN
 http2_protocol_options: {}
admin:
  ...
複製代碼

增量 xDS

增量 xDS 是可用於容許的 ADS、CDS 和 RDS 單獨 xDS 端點:

  • xDS 客戶端對跟蹤資源列表進行增量更新。這支持 Envoy 按需/惰性地請求額外資源。例如,當與未知集羣相對應的請求到達時,可能會發生這種狀況。

  • xDS 服務器能夠增量更新客戶端上的資源。這支持 xDS 資源可伸縮性的目標。管理服務器只需交付更改的單個集羣,而不是在修改單個集羣時交付全部上萬個集羣。

xDS 增量會話始終位於 gRPC 雙向流的上下文中。這容許 xDS 服務器可以跟蹤到鏈接的 xDS 客戶端的狀態。xDS REST 版本不支持增量。

在增量 xDS 中,nonce 字段是必需的,用於匹配 IncrementalDiscoveryResponse 關聯的 ACK 或 NACK IncrementalDiscoveryRequest。可選地,存在響應消息級別的 system_version_info,但僅用於調試目的。

IncrementalDiscoveryRequest 可在如下 3 種狀況下發送:

  1. xDS 雙向 gRPC 流的初始消息。

  2. 做爲對先前的 IncrementalDiscoveryResponse 的 ACK 或 NACK 響應。在這種狀況下,response_nonce 被設置爲響應中的 nonce 值。ACK 或 NACK 由可由 error_detail 字段是否出現來區分。

  3. 客戶端自發的 IncrementalDiscoveryRequest。此場景下能夠採用動態添加或刪除被跟蹤的 resource_names 集。這種場景下,必須忽略 response_nonce

在第一個示例中,客戶端鏈接並接收它的第一個更新並 ACK。第二次更新失敗,客戶端發送 NACK 拒絕更新。xDS客戶端後續會自發地請求 「wc」 相關資源。

在從新鏈接時,支持增量的 xDS 客戶端可能會告訴服務器其已知資源從而避免經過網絡從新發送它們。

REST-JSON 輪詢訂閱

單個 xDS API 可對 REST 端點進行的同步(長)輪詢。除了無持久流與管理服務器交互外,消息順序與上述類似。在任什麼時候間點,只存在一個未完成的請求,所以響應消息中的 nonce 在 REST-JSON 中是可選的。DiscoveryRequestDiscoveryResponse 的消息編碼遵循 JSON 變換 proto3 規範。ADS 不支持 REST-JSON 輪詢。

當輪詢期間設置爲較小的值時,則能夠等同於長輪詢,這時要求避免發送 DiscoveryResponse除非對請求的資源發生了更改

本文轉自:www.servicemesher.com/blog/envoy-…

相關文章
相關標籤/搜索