Server-Speaks-First 有點坑,Linkerd 2.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 就可以解析請求、響應和響應代碼,并報告響應延遲、請求量和錯誤率等指標。這些指標非常有價值,以至于它們成為谷歌 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 將建立一個跨端點的連接池,并平衡這個池中的請求。由于它現在可以訪問 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,而不管協議是什么。
Refs
- Protocol Detection and Opaque Ports in Linkerd
https://linkerd.io/2021/02/23/protocol-detection-and-opaque-ports-in-linkerd