參考:https://kubernetes.io/zh/docs/concepts/services-networking/endpoint-slices/
FEATURE STATE: Kubernetes v1.21 [stable]
端點切片(EndpointSlices) 提供了一種簡單的方法來跟蹤 Kubernetes 集群中的網絡端點 (network endpoints)。它們為 Endpoints 提供了一種可伸縮和可拓展的替代方案。
動機
Endpoints API 提供了在 Kubernetes 跟蹤網絡端點的一種簡單而直接的方法。 不幸的是,隨着 Kubernetes 集群和 服務 逐漸開始為更多的后端 Pods 處理和發送請求,原來的 API 的局限性變得越來越明顯。 最重要的是那些因為要處理大量網絡端點而帶來的挑戰。
由於任一服務的所有網絡端點都保存在同一個 Endpoints 資源中,這類資源可能變得 非常巨大,而這一變化會影響到 Kubernetes 組件(比如主控組件)的性能,並 在 Endpoints 變化時需要處理大量的網絡流量和處理。 EndpointSlice 能夠幫助你緩解這一問題,還能為一些諸如拓撲路由這類的額外 功能提供一個可擴展的平台。
Endpoint Slice 資源
在 Kubernetes 中,EndpointSlice 包含對一組網絡端點的引用。 指定選擇器后控制面會自動為設置了 選擇算符 的 Kubernetes 服務創建 EndpointSlice。 這些 EndpointSlice 將包含對與服務選擇算符匹配的所有 Pod 的引用。 EndpointSlice 通過唯一的協議、端口號和服務名稱將網絡端點組織在一起。 EndpointSlice 的名稱必須是合法的 DNS 子域名。
例如,下面是 Kubernetes 服務 example 的 EndpointSlice 資源示例。
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: example-abc
labels:
kubernetes.io/service-name: example
addressType: IPv4
ports:
- name: http
protocol: TCP
port: 80
endpoints:
- addresses:
- "10.1.2.3"
conditions:
ready: true
hostname: pod-1
nodeName: node-1
zone: us-west2-a
默認情況下,控制面創建和管理的 EndpointSlice 將包含不超過 100 個端點。 你可以使用 kube-controller-manager 的 --max-endpoints-per-slice 標志設置此值,最大值為 1000。
當涉及如何路由內部流量時,EndpointSlice 可以充當 kube-proxy 的決策依據。 啟用該功能后,在服務的端點數量龐大時會有可觀的性能提升。
地址類型
EndpointSlice 支持三種地址類型:
- IPv4
- IPv6
- FQDN (完全合格的域名)
狀況
EndpointSlice API 存儲了可能對使用者有用的、有關端點的狀況。 這三個狀況分別是 ready、serving 和 terminating。
- Ready(就緒)
ready 狀況是映射 Pod 的 Ready 狀況的。 處於運行中的 Pod,它的 Ready 狀況被設置為 True,應該將此 EndpointSlice 狀況也設置為 true。 出於兼容性原因,當 Pod 處於終止過程中,ready 永遠不會為 true。 消費者應參考 serving 狀況來檢查處於終止中的 Pod 的就緒情況。 該規則的唯一例外是將 spec.publishNotReadyAddresses 設置為 true 的服務。 這些服務(Service)的端點將始終將 ready 狀況設置為 true。
- Serving(服務中)
FEATURE STATE: Kubernetes v1.20 [alpha]
serving 狀況與 ready 狀況相同,不同之處在於它不考慮終止狀態。 如果 EndpointSlice API 的使用者關心 Pod 終止時的就緒情況,就應檢查此狀況。
說明:
盡管 serving 與 ready 幾乎相同,但是它是為防止破壞 ready 的現有含義而增加的。 如果對於處於終止中的端點,ready 可能是 true,那么對於現有的客戶端來說可能是有些意外的, 因為從始至終,Endpoints 或 EndpointSlice API 從未包含處於終止中的端點。 出於這個原因,ready 對於處於終止中的端點 總是 false, 並且在 v1.20 中添加了新的狀況 serving,以便客戶端可以獨立於 ready 的現有語義來跟蹤處於終止中的 Pod 的就緒情況。
- Terminating(終止中)
FEATURE STATE: Kubernetes v1.20 [alpha]
Terminating 是表示端點是否處於終止中的狀況。 對於 Pod 來說,這是設置了刪除時間戳的 Pod。
拓撲信息
EndpointSlice 中的每個端點都可以包含一定的拓撲信息。 拓撲信息包括端點的位置,對應節點、可用區的信息。 這些信息體現為 EndpointSlices 的如下端點字段:
- nodeName - 端點所在的 Node 名稱;
- zone - 端點所處的可用區。
說明:
在 v1 API 中,逐個端點設置的 topology 實際上被去除,以鼓勵使用專用 的字段 nodeName 和 zone。
對 EndpointSlice 對象的 endpoint 字段設置任意的拓撲結構信息這一操作已被 廢棄,不再被 v1 API 所支持。取而代之的是 v1 API 所支持的 nodeName 和 zone 這些獨立的字段。這些字段可以在不同的 API 版本之間自動完成轉譯。 例如,v1beta1 API 中 topology 字段的 topology.kubernetes.io/zone 取值可以 在 v1 API 中通過 zone 字段訪問。
管理
通常,控制面(尤其是端點切片的 控制器) 會創建和管理 EndpointSlice 對象。EndpointSlice 對象還有一些其他使用場景, 例如作為服務網格(Service Mesh)的實現。這些場景都會導致有其他實體 或者控制器負責管理額外的 EndpointSlice 集合。
為了確保多個實體可以管理 EndpointSlice 而且不會相互產生干擾,Kubernetes 定義了 標簽 endpointslice.kubernetes.io/managed-by,用來標明哪個實體在管理某個 EndpointSlice。端點切片控制器會在自己所管理的所有 EndpointSlice 上將該標簽值設置 為 endpointslice-controller.k8s.io。 管理 EndpointSlice 的其他實體也應該為此標簽設置一個唯一值。
屬主關系
在大多數場合下,EndpointSlice 都由某個 Service 所有,(因為)該端點切片正是 為該服務跟蹤記錄其端點。這一屬主關系是通過為每個 EndpointSlice 設置一個 屬主(owner)引用,同時設置 kubernetes.io/service-name 標簽來標明的, 目的是方便查找隸屬於某服務的所有 EndpointSlice。
EndpointSlice 鏡像
在某些場合,應用會創建定制的 Endpoints 資源。為了保證這些應用不需要並發 的更改 Endpoints 和 EndpointSlice 資源,集群的控制面將大多數 Endpoints 映射到對應的 EndpointSlice 之上。
控制面對 Endpoints 資源進行映射的例外情況有:
- Endpoints 資源上標簽 endpointslice.kubernetes.io/skip-mirror 值為 true。
- Endpoints 資源包含標簽 control-plane.alpha.kubernetes.io/leader。
- 對應的 Service 資源不存在。
- 對應的 Service 的選擇算符不為空。
每個 Endpoints 資源可能會被翻譯到多個 EndpointSlices 中去。 當 Endpoints 資源中包含多個子網或者包含多個 IP 地址族(IPv4 和 IPv6)的端點時, 就有可能發生這種狀況。 每個子網最多有 1000 個地址會被鏡像到 EndpointSlice 中。
EndpointSlices 的分布問題
每個 EndpointSlice 都有一組端口值,適用於資源內的所有端點。 當為服務使用命名端口時,Pod 可能會就同一命名端口獲得不同的端口號,因而需要 不同的 EndpointSlice。這有點像 Endpoints 用來對子網進行分組的邏輯。
控制面嘗試盡量將 EndpointSlice 填滿,不過不會主動地在若干 EndpointSlice 之間 執行再平衡操作。這里的邏輯也是相對直接的:
1.列舉所有現有的 EndpointSlices,移除那些不再需要的端點並更新那些已經 變化的端點。
2.列舉所有在第一步中被更改過的 EndpointSlices,用新增加的端點將其填滿。
3.如果還有新的端點未被添加進去,嘗試將這些端點添加到之前未更改的切片中, 或者創建新切片。
這里比較重要的是,與在 EndpointSlice 之間完成最佳的分布相比,第三步中更看重 限制 EndpointSlice 更新的操作次數。例如,如果有 10 個端點待添加,有兩個 EndpointSlice 中各有 5 個空位,上述方法會創建一個新的 EndpointSlice 而不是 將現有的兩個 EndpointSlice 都填滿。換言之,與執行多個 EndpointSlice 更新操作 相比較,方法會優先考慮執行一個 EndpointSlice 創建操作。
由於 kube-proxy 在每個節點上運行並監視 EndpointSlice 狀態,EndpointSlice 的 每次變更都變得相對代價較高,因為這些狀態變化要傳遞到集群中每個節點上。 這一方法嘗試限制要發送到所有節點上的變更消息個數,即使這樣做可能會導致有 多個 EndpointSlice 沒有被填滿。
在實踐中,上面這種並非最理想的分布是很少出現的。大多數被 EndpointSlice 控制器 處理的變更都是足夠小的,可以添加到某已有 EndpointSlice 中去的。並且,假使無法 添加到已有的切片中,不管怎樣都會快就會需要一個新的 EndpointSlice 對象。 Deployment 的滾動更新為重新為 EndpointSlice 打包提供了一個自然的機會,所有 Pod 及其對應的端點在這一期間都會被替換掉。
重復的端點
由於 EndpointSlice 變化的自身特點,端點可能會同時出現在不止一個 EndpointSlice 中。鑒於不同的 EndpointSlice 對象在不同時刻到達 Kubernetes 的監視/緩存中, 這種情況的出現是很自然的。 使用 EndpointSlice 的實現必須能夠處理端點出現在多個切片中的狀況。 關於如何執行端點去重(deduplication)的參考實現,你可以在 kube-proxy 的 EndpointSlice 實現中找到。