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

  • 2021 年 8 月 10 日
  • 筆記

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

什麼是協議檢測?

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

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

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

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

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

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

可觀察性

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

Linkerd 可觀察特性的核心是流量的測量。這種檢測需要理解正在使用的協議,因為對協議的了解可以提供豐富的度量。例如,知道一個連接正在使用 HTTP,就允許 Linkerd 解析請求、響應和響應代碼,並報告響應延遲、請求量和錯誤率等指標。這些指標非常有價值,它們是谷歌的 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 將建立一個跨端點的連接池,並平衡這個池中的請求。 由於它現在可以訪問 requestsresponsesLinkerd 在平衡請求方面可以非常複雜; 事實上,它根據每個可能端點的最近性能(使用稱為「指數加權移動平均(exponentially weighted moving average)」或 EWMA 的指標)來平衡請求,以避免從慢速端點引起尾部延遲(tail latency)。

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

當協議檢測失敗時

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

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

為了避免這種情況,Linkerd 引入了 skip-inbound-portsskip-outbound-ports 配置選項。 這些選項指示 Linkerd 通過修改 Linkerd 用於通過其 sidecar 代理連接 podiptables 規則來完全繞過某些端口的代理。例如,將 annotation config.linkerd.io/skip-outbound-ports: 3306 添加到工作負載的 PodSpec 指示 Linkerd 創建一個 iptables 規則,以確保 Linkerd 代理永遠不會處理到端口 3306MySQL 端口)的任何流量 . 同樣,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 detectionLinkerd 最強大的功能之一,也是 Linkerd 「just works」 原則的基礎。雖然協議檢測不是萬靈藥,但 Linkerd 2.10 中引入的 opaque-ports 應該解決早期 skip-ports 特性的大部分缺點,並允許 Linkerd 使用者在整個 Kubernetes 環境中擴展 mTLS,而不管協議是什麼。

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