Server-Speaks-First 有點坑,Linkerd 2.10 中的協議檢測和不透明埠

  • 2021 年 8 月 10 日
  • 筆記

協議檢測(Protocol detection),顧名思義,允許 Linkerd 自動檢測 TCP 連接中使用的協議。 Linkerd 的設計原則之一是「just work」,協議檢測是 Linkerd 如何實現這一目標的重要組成部分。

什麼是協議檢測?

簡而言之,協議檢測是通過檢查連接上的流量來確定 TCP 連接上使用的協議的能力。

Linkerd 使用 Protocol detection 來避免要求用戶指定協議。 Linkered 的代理不需要用戶配置每個埠使用的協議,而是簡單地執行協議檢測來回答問題。

Linkerd 的 Protocol detection 通過查看客戶端連接的前幾個位元組來獲取有關流量的資訊來工作。 這種實現有一些後果,我們將在下面介紹。

但首先,讓我們首先回答為什麼 Linkerd 關心任何協議的問題。

可觀察性、可靠性和安全性

我們通常將 Linkerd 的廣泛功能分為三類:可觀察性(Observability)、可靠性(reliability)和安全性(security)。 了解連接(connection)上使用的協議是每個類別的基礎。

可觀察性

Linkerd 可觀察性功能的核心是流量檢測。 這種儀器需要了解正在使用的協議,因為協議的知識可以提供豐富的指標。 例如,知道連接正在使用 HTTP,Linkerd 就可以解析請求、響應和響應程式碼,並報告響應延遲、請求量和錯誤率等指標。 這些指標非常有價值,以至於它們成為Google SRE 書中所謂的「黃金訊號」的一部分。 另一方面,如果 Linkerd 只知道連接是 TCP,則它僅限於記錄非常基本的資訊,例如讀取和寫入的位元組數——無法進一步解釋位元組。

Linkerd 可觀察特性的核心是流量的測量。這種檢測需要理解正在使用的協議,因為對協議的了解可以提供豐富的度量。例如,知道一個連接正在使用 HTTP,就允許 Linkerd 解析請求、響應和響應程式碼,並報告響應延遲、請求量和錯誤率等指標。這些指標非常有價值,它們是Google的 SRE 書中所謂的「黃金訊號」的一部分。另一方面,如果 Linkerd 只知道一個連接是 TCP,那麼它只能記錄非常基本的資訊,比如讀取和寫入的位元組數——沒有進一步解釋位元組的能力。

安全

雙向 TLS (mTLS) 是 Linkerd 的核心功能。從 Linkerd 2.9 開始,網狀端點(meshed endpoints)之間的所有 TCP 流量默認由 Linkerd 代理進行 mTLS。 (有一些警告 - 請參閱下面有關 skip-ports 的部分。)

在這裡,再次了解連接的協議至關重要。例如,如果連接已經是 TLS 的(例如,通過應用程式),則沒有理由重新 TLS。(嚴格來說,TLS 是一種傳輸層協議,而不是像 HTTP 那樣的應用層協議,但就本文而言,兩者之間的區別並不重要。)

可靠性

最後,了解底層連接的協議允許 Linkerd 提供複雜的可靠性功能。 這裡的一個例子是負載平衡。 在不知道連接協議的情況下,Linkerd 僅限於平衡連接(balancing connections):一旦與伺服器建立了 TCP 連接,它就無法進一步操作該連接。

但是,如果 Linkerd 知道連接是 HTTP,它可以從連接平衡(connection balancing)轉移到請求平衡(request balancing)。Linkerd 將建立一個跨端點的連接池,並平衡這個池中的請求。 由於它現在可以訪問 requests 和 responses,Linkerd 在平衡請求方面可以非常複雜; 事實上,它根據每個可能端點的最近性能(使用稱為「指數加權移動平均(exponentially weighted moving average)」或 EWMA 的指標)來平衡請求,以避免從慢速端點引起尾部延遲(tail latency)。

( Linkerd 也是 Kubernetes 中負載平衡 gRPC 連接的一個簡單解決方案。)

當協議檢測失敗時

雖然協議檢測旨在允許 Linkerd 「just work」,但在某些情況下它不能:臭名昭著的伺服器優先協議(server-speaks-first)。 這些協議(包括 MySQL 和 SMTP)通過讓客戶端建立連接然後等待伺服器響應來工作。從 TCP 的角度來看,這是一種完全合法的行為,但這意味著 Linkerd 無法檢測到協議,因為相關資訊來自伺服器,而不是客戶端。

(為什麼不簡單地使用伺服器的位元組來檢測協議?因為在檢測協議的時候,Linkerd 甚至還沒有建立到伺服器的連接。選擇與哪個伺服器對話是負載均衡器的一個功能,而使用哪個負載均衡器是協議的一個功能。這是一個 delicious、帶有 TCP-flavored 的「先有雞還是先有蛋(chicken-and-egg)」問題。)

為了避免這種情況,Linkerd 引入了 skip-inbound-ports 和 skip-outbound-ports 配置選項。 這些選項指示 Linkerd 通過修改 Linkerd 用於通過其 sidecar 代理連接 pod 的 iptables 規則來完全繞過某些埠的代理。例如,將 annotation config.linkerd.io/skip-outbound-ports: 3306 添加到工作負載的 PodSpec 指示 Linkerd 創建一個 iptables 規則,以確保 Linkerd 代理永遠不會處理到埠 3306(MySQL 埠)的任何流量 . 同樣,annotation config.linkerd.io/skip-inbound-ports: 3306 將編寫一個 iptables 規則,以便代理永遠不會處理髮送給它的 MySQL 流量。

Skip Ports 配置

這些選項為 protocol detection 無法處理 server-speaks-first 協議提供了一種解決方法。 然而,它們有一個明顯的缺點:因為它們完全繞過 Linkerd 代理,Linkerd 無法應用 mTLS 或捕獲這些埠的任何指標。

Linkerd 2.10 中的不透明埠和改進的協議檢測

為了解決 skip-ports 的不足,在 2.10 版本中,Linkerd 將添加不透明埠(opaque ports)的概念(以及相應的 opaque-ports annotation)。不透明埠就是 Linkerd 將代理而不執行協議檢測的埠。雖然這種方法仍然需要配置,但將埠標記為不透明允許 Linkerd 應用 mTLS 並報告 TCP-level metrics —— 這比完全跳過它是一個很大的改進。

Opaque Ports 配置

Linkerd 2.10 還將通過使其「fail open」來改進協議檢測的工作方式:如果協議檢測程式碼在 10 秒後沒有看到客戶端位元組,它會將連接視為 TCP 連接並繼續,而不是像 2.9 那樣失敗 . 這意味著不使用 opaque-ports(或 skip-ports)annotating server-speaks-first 埠的最壞情況行為是 10 秒的連接時間延遲,而不是連接失敗。

總結

Protocol detection 是 Linkerd 最強大的功能之一,也是 Linkerd 「just works」 原則的基礎。雖然協議檢測不是萬靈藥,但 Linkerd 2.10 中引入的 opaque-ports 應該解決早期 skip-ports 特性的大部分缺點,並允許 Linkerd 使用者在整個 Kubernetes 環境中擴展 mTLS,而不管協議是什麼。

我是為少
微信:uuhells123
公眾號:黑客下午茶
加我微信(互相學習交流),關注公眾號(獲取更多學習資料~)
Tags: