為 Istio Ambient Service Mesh 引入基於 Rust 的 Ztunnel

為 Istio ambient mesh 量身打造的每個節點代理。

2023 年 2 月 28 日 | 作者:Lin Sun - Solo.io, John Howard - Google

ztunnel(零信任隧道)組件是為 Istio ambient mesh 量身打造的每個節點代理。它負責安全地連接和驗證 ambient mesh 內的工作負載。 Ztunnel 的設計重點在於 ambient mesh 中工作負載的一小部分功能,例如 mTLS、身份驗證、L4 授權和遙測,而不會終止工作負載 HTTP 流量或解析工作負載 HTTP 標頭。 ztunnel 確保流量被有效且安全地傳輸到路點代理,其中實施了 Istio 的全套功能,例如 HTTP 遙測和負載平衡。

由於 ztunnel 設計為在您的所有 Kubernetes 工作節點上執行,因此保持其資源佔用量小至關重要。 Ztunnel 的設計宗旨是作為服務網格中不可見的(或「環境的」)一部分,對您的工作負載影響最小。

Ztunnel 架構

與 sidecar 類似,ztunnel 也作為 xDS 客戶端和 CA 客戶端

  1. 在啟動期間,它使用其服務帳戶令牌安全地連接到 Istiod 控制平面。一旦從 ztunnel 到 Istiod 的連接使用 TLS 安全建立,它就會開始作為 xDS 客戶端提取 xDS 配置。這與 sidecar 或閘道或路點代理類似,只是 Istiod 會識別來自 ztunnel 的請求,並發送為 ztunnel 量身打造的 xDS 配置,您很快就會了解更多。
  2. 它也充當 CA 客戶端,代表它管理的所有共同定位的工作負載管理和配置 mTLS 憑證。
  3. 當流量進入或流出時,它會充當核心代理,處理它管理的所有共同定位工作負載的入站和出站流量(網格外純文字或網格內 HBONE)。
  4. 它提供 L4 遙測(指標和日誌)以及一個管理伺服器,其中包含除錯資訊,以幫助您在需要時除錯 ztunnel。
Ztunnel architecture
Ztunnel 架構

為什麼不重複使用 Envoy?

當 Istio ambient service mesh 於 2022 年 9 月 7 日發布時,ztunnel 是使用 Envoy 代理實施的。 鑑於我們將 Envoy 用於 Istio 的其餘部分 - sidecar、閘道和路點代理 - 我們很自然地開始使用 Envoy 實施 ztunnel。

然而,我們發現雖然 Envoy 非常適合其他用例,但在 Envoy 中實施 ztunnel 具有挑戰性,因為許多權衡、需求和用例與 sidecar 代理或入口閘道的需求有很大不同。 此外,大多數使 Envoy 非常適合其他用例的功能,例如其豐富的 L7 功能集和可擴展性,在 ztunnel 中都被浪費了,而 ztunnel 並不需要這些功能。

量身打造的 ztunnel

在難以將 Envoy 調整到我們的需求之後,我們開始研究為 ztunnel 建立一個量身打造的實施。 我們的假設是,通過從一開始就考慮到一個重點明確的用例進行設計,我們可以開發出一種比將通用專案塑造成我們客製化的用例更簡單、效能更高的解決方案。 做出使 ztunnel 簡單化的明確決策是這個假設的關鍵;例如,類似的邏輯無法支持重寫閘道,閘道有大量受支援的功能和整合。

這個量身打造的 ztunnel 涉及兩個關鍵領域

配置協定

Envoy 代理使用 xDS 協定進行配置。 這是使 Istio 運作良好的關鍵部分,它提供了豐富且動態的配置更新。 然而,當我們偏離既定路線時,配置變得越來越客製化,這意味著生成配置的成本更高。 在 sidecar 中,具有 1 個 pod 的單個服務會生成大約 ~350 行 xDS(以 YAML 格式),這已經很難擴展。 基於 Envoy 的 ztunnel 情況更糟,在某些方面具有 N^2 擴展屬性。

為了盡可能縮小 ztunnel 配置,我們研究使用量身打造的配置協定,其中包含我們需要的精確資訊(沒有多餘資訊),並以有效格式表示。 例如,可以用簡潔的方式表示單個 pod

name: helloworld-v1-55446d46d8-ntdbk
namespace: default
serviceAccount: helloworld
node: ambient-worker2
protocol: TCP
status: Healthy
waypointAddresses: []
workloadIp: 10.244.2.8
canonicalName: helloworld
canonicalRevision: v1
workloadName: helloworld-v1
workloadType: deployment

此資訊通過 xDS 傳輸 API 傳輸,但使用自定義的環境特定類型。 請參閱工作負載 xDS 配置部分以了解有關配置詳細資訊的更多資訊。

通過擁有量身打造的 API,我們可以將邏輯推送到代理中,而不是在 Envoy 配置中。 例如,要在 Envoy 中配置 mTLS,我們需要添加一組相同的配置,以調整每個服務的精確 TLS 設定;對於 ztunnel,我們只需要一個枚舉來宣告是否應使用 mTLS。 其餘複雜的邏輯直接嵌入到 ztunnel 程式碼中。

通過 Istiod 和 ztunnel 之間的這個高效 API,我們發現可以使用更少數量的配置來配置有關大型網格(例如具有 100,000 個 pod 的網格)的 ztunnel 資訊,這意味著更少的 CPU、記憶體和網路成本。

運行時實施

顧名思義,ztunnel 使用 HTTPS 隧道 來傳輸使用者請求。 雖然 Envoy 支援此隧道傳輸,但我們發現配置模型限制了我們的需求。 粗略地說,Envoy 的運作方式是將請求通過一系列「篩選器」發送,從接受請求開始到發送請求結束。 根據我們的要求,這些要求具有多層請求(隧道本身和使用者的請求),並且需要在負載平衡後應用每個 pod 策略,我們發現當我們實施之前的基於 Envoy 的 ztunnel 時,我們需要每個連線循環遍歷這些篩選器 4 次。 雖然 Envoy 對於記憶體中「向自身發送請求」具有 一些最佳化,但這仍然非常複雜且成本高昂。

通過建立我們自己的實施,我們可以從頭開始圍繞這些限制進行設計。 此外,我們在設計的所有方面都有更大的彈性。 例如,我們可以選擇在線程之間共用連線,或實施有關服務帳戶之間隔離的更多客製化要求。 在確定量身打造的代理是可行的之後,我們開始選擇實施細節。

基於 Rust 的 ztunnel

為了使 ztunnel 快速、安全且輕量,Rust 是一個顯而易見的選擇。 然而,這並不是我們的首選。 鑑於 Istio 目前廣泛使用 Go,我們曾希望我們可以建立基於 Go 的實施來滿足這些目標。 在最初的原型中,我們建立了一些基於 Go 的實施和基於 Rust 的實施的簡單版本。 從我們的測試中,我們發現基於 Go 的版本未能滿足我們的效能和佔用空間要求。 雖然我們可能會進一步最佳化它,但我們認為基於 Rust 的代理會為我們提供長期的最佳實施。

也考慮了 C++ 實施,可能會重複使用 Envoy 的部分程式碼。 然而,由於缺乏記憶體安全性、開發人員體驗問題以及整個產業朝向 Rust 的趨勢,因此沒有採用此選項。

這個排除過程讓我們選擇了 Rust,這是一個完美的選擇。 Rust 在高效能、低資源利用率應用程式(尤其是在網路應用程式(包括服務網格))方面有著悠久的成功歷史。 我們選擇建立在 TokioHyper 程式庫之上,這兩個程式庫是生態系統中經過廣泛實戰測試且易於編寫高效能異步程式碼的實際標準。

基於 Rust 的 ztunnel 快速瀏覽

工作負載 xDS 配置

工作負載 xDS 配置非常容易理解和除錯。 您可以通過從您的 ztunnel pod 之一向 localhost:15000/config_dump 發送請求來檢視它們,或者使用方便的 istioctl pc workload 命令。 有兩個關鍵的工作負載 xDS 配置:工作負載和原則。

在將您的工作負載包含在您的環境網格中之前,您仍然可以在 ztunnel 的配置轉儲中看到它們,因為 ztunnel 知道所有工作負載,無論它們是否啟用環境。 例如,下面包含一個新部署的 helloworld v1 pod 的範例工作負載配置,該 pod 是網格外的,由 protocol: TCP 指示

{
  "workloads": {
    "10.244.2.8": {
      "workloadIp": "10.244.2.8",
      "protocol": "TCP",
      "name": "helloworld-v1-cross-node-55446d46d8-ntdbk",
      "namespace": "default",
      "serviceAccount": "helloworld",
      "workloadName": "helloworld-v1-cross-node",
      "workloadType": "deployment",
      "canonicalName": "helloworld",
      "canonicalRevision": "v1",
      "node": "ambient-worker2",
      "authorizationPolicies": [],
      "status": "Healthy"
    }
  }
}

在 pod 包含在環境中之後(通過使用 istio.io/dataplane-mode=ambient 標記命名空間預設值),protocol 值將被 HBONE 取代,指示 ztunnel 將來自 helloworld-v1 pod 的所有傳入和傳出通訊升級為 HBONE。

{
  "workloads": {
    "10.244.2.8": {
      "workloadIp": "10.244.2.8",
      "protocol": "HBONE",
      ...
}

在您部署任何工作負載級別授權原則之後,原則配置將作為 xDS 配置從 Istiod 推送到 ztunnel,並顯示在 policies

{
  "policies": {
    "default/hw-viewer": {
      "name": "hw-viewer",
      "namespace": "default",
      "scope": "WorkloadSelector",
      "action": "Allow",
      "groups": [[[{
        "principals": [{"Exact": "cluster.local/ns/default/sa/sleep"}]
      }]]]
    }
  }
  ...
}

您還會注意到工作負載的配置已更新,其中參考了授權原則。

{
  "workloads": {
    "10.244.2.8": {
    "workloadIp": "10.244.2.8",
    ...
    "authorizationPolicies": [
        "default/hw-viewer"
    ],
  }
  ...
}

ztunnel 提供的 L4 遙測

您可能會驚喜地發現 ztunnel 日誌很容易理解。 例如,您會在目標 ztunnel 上看到 HTTP Connect 請求,其中指示來源 pod IP (peer_ip) 和目標 pod IP。

2023-02-15T20:40:48.628251Z  INFO inbound{id=4399fa68cf25b8ebccd472d320ba733f peer_ip=10.244.2.5 peer_id=spiffe://cluster.local/ns/default/sa/sleep}: ztunnel::proxy::inbound: got CONNECT request to 10.244.2.8:5000

您可以通過存取 localhost:15020/metrics API 來檢視工作負載的 L4 指標,該 API 提供完整的 TCP 標準指標,並具有 sidecar 公開的相同標籤。 例如

istio_tcp_connections_opened_total{
  reporter="source",
  source_workload="sleep",
  source_workload_namespace="default",
  source_principal="spiffe://cluster.local/ns/default/sa/sleep",
  destination_workload="helloworld-v1",
  destination_workload_namespace="default",
  destination_principal="spiffe://cluster.local/ns/default/sa/helloworld",
  request_protocol="tcp",
  connection_security_policy="mutual_tls"
  ...
} 1

如果您安裝 Prometheus 和 Kiali,則可以輕鬆地從 Kiali 的 UI 中檢視這些指標。

Kiali dashboard - L4 telemetry provided by ztunnel
Kiali 儀表板 - ztunnel 提供的 L4 遙測

總結

我們非常興奮地宣布,新的基於 Rust 的 ztunnel 已大幅簡化,比先前的基於 Envoy 的 ztunnel 更輕巧且效能更高。透過專為基於 Rust 的 ztunnel 設計的工作負載 xDS,您不僅能更容易理解 xDS 設定,還能大幅減少 Istiod 控制平面和 ztunnel 之間的網路流量和成本。隨著 Istio ambient 現在合併到上游主分支,您可以按照我們的入門指南來嘗試新的基於 Rust 的 ztunnel。

分享此貼文