Kubernetes v1.33:繼續從 Endpoints 過渡到 EndpointSlices
自從 EndpointSlices (KEP-752) 在 v1.15 中作為 alpha 版本引入,並在 v1.21 中正式釋出(GA)以來,Kubernetes 中的 Endpoints API 就一直被擱置。新的 Service 功能,如雙棧網路和流量分發,僅透過 EndpointSlice API 支援,因此所有服務代理、Gateway API 實現和類似的控制器都必須從使用 Endpoints 移植到使用 EndpointSlices。目前,Endpoints API 實際上只是為了避免破壞仍然使用它的終端使用者工作負載和指令碼而存在。
從 Kubernetes 1.33 開始,Endpoints API 已被正式棄用,API 伺服器將向讀取或寫入 Endpoints 資源而不是使用 EndpointSlices 的使用者返回警告。
最終,計劃(如 KEP-4974 中所述)是更改 Kubernetes 一致性標準,不再要求叢集執行 Endpoints 控制器(該控制器根據 Services 和 Pods 生成 Endpoints 物件),以避免在大多數現代叢集中進行不必要的工作。
因此,雖然 Kubernetes 棄用策略意味著 Endpoints 型別本身可能永遠不會完全消失,但仍有工作負載或指令碼使用 Endpoints API 的使用者應開始將其遷移到 EndpointSlices。
關於從 Endpoints 遷移到 EndpointSlices 的說明
消費 EndpointSlices 而非 Endpoints
對於終端使用者來說,Endpoints API 和 EndpointSlice API 之間最大的變化是,雖然每個帶有 selector
的 Service 只有一個 Endpoints 物件(與 Service 同名),但一個 Service 可以關聯任意數量的 EndpointSlices。
$ kubectl get endpoints myservice
Warning: v1 Endpoints is deprecated in v1.33+; use discovery.k8s.io/v1 EndpointSlice
NAME ENDPOINTS AGE
myservice 10.180.3.17:443 1h
$ kubectl get endpointslice -l kubernetes.io/service-name=myservice
NAME ADDRESSTYPE PORTS ENDPOINTS AGE
myservice-7vzhx IPv4 443 10.180.3.17 21s
myservice-jcv8s IPv6 443 2001:db8:0123::5 21s
在這種情況下,由於該服務是雙棧的,它有兩個 EndpointSlices:一個用於 IPv4 地址,一個用於 IPv6 地址。(Endpoints API 不支援雙棧,因此 Endpoints 物件只顯示叢集主地址族中的地址。)儘管任何具有多個端點的 Service 都*可以*有多個 EndpointSlices,但您會看到這種情況主要有三種情況:
一個 EndpointSlice 只能表示單個 IP 族的端點,因此雙棧服務將為 IPv4 和 IPv6 分別設定 EndpointSlices。
一個 EndpointSlice 中的所有端點必須指向相同的埠。因此,例如,如果您有一組在埠 80 上監聽的端點 Pod,然後推出更新使其在埠 8080 上監聽,那麼在部署過程中,該 Service 將需要 2 個 EndpointSlices:一個用於在埠 80 上監聽的端點,另一個用於在埠 8080 上監聽的端點。
當一個 Service 有超過 100 個端點時,EndpointSlice 控制器會將端點拆分為多個 EndpointSlices,而不是像 Endpoints 控制器那樣將它們聚合到一個過大的物件中。
由於 Services 和 EndpointSlices 之間沒有可預測的一對一對映,因此無法預先知道一個 Service 的 EndpointSlice 資源的實際名稱;因此,您不是按名稱獲取 EndpointSlice,而是請求所有帶有指向該 Service 的 "kubernetes.io/service-name
" 標籤的 EndpointSlices。
$ kubectl get endpointslice -l kubernetes.io/service-name=myservice
在 Go 程式碼中也需要進行類似的更改。對於 Endpoints,您會這樣做:
// Get the Endpoints named `name` in `namespace`.
endpoint, err := client.CoreV1().Endpoints(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
// No Endpoints exists for the Service (yet?)
...
}
// handle other errors
...
}
// process `endpoint`
...
對於 EndpointSlices,這變成了:
// Get all EndpointSlices for Service `name` in `namespace`.
slices, err := client.DiscoveryV1().EndpointSlices(namespace).List(ctx,
metav1.ListOptions{LabelSelector: discoveryv1.LabelServiceName + "=" + name})
if err != nil {
// handle errors
...
} else if len(slices.Items) == 0 {
// No EndpointSlices exist for the Service (yet?)
...
}
// process `slices.Items`
...
生成 EndpointSlices 而非 Endpoints
對於生成 Endpoints 的人(或控制器)來說,遷移到 EndpointSlices 稍微容易一些,因為在大多數情況下,您不必擔心多個分片。您只需更新您的 YAML 或 Go 程式碼以使用新型別(它以與 Endpoints 略有不同的方式組織資訊)。
例如,這個 Endpoints 物件:
apiVersion: v1
kind: Endpoints
metadata:
name: myservice
subsets:
- addresses:
- ip: 10.180.3.17
nodeName: node-4
- ip: 10.180.5.22
nodeName: node-9
- ip: 10.180.18.2
nodeName: node-7
notReadyAddresses:
- ip: 10.180.6.6
nodeName: node-8
ports:
- name: https
protocol: TCP
port: 443
將變成類似這樣:
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: myservice
labels:
kubernetes.io/service-name: myservice
addressType: IPv4
endpoints:
- addresses:
- 10.180.3.17
nodeName: node-4
- addresses:
- 10.180.5.22
nodeName: node-9
- addresses:
- 10.180.18.12
nodeName: node-7
- addresses:
- 10.180.6.6
nodeName: node-8
conditions:
ready: false
ports:
- name: https
protocol: TCP
port: 443
一些需要注意的點:
此示例使用顯式的
name
,但您也可以使用generateName
並讓 API 伺服器追加一個唯一的字尾。名稱本身並不重要:重要的是"kubernetes.io/service-name"
標籤指回 Service。您必須明確指出
addressType: IPv4
(或IPv6
)。一個 EndpointSlice 類似於 Endpoints 中
"subsets"
陣列的單個元素。一個具有多個子集的 Endpoints 物件通常需要表示為多個 EndpointSlices,每個都有不同的"ports"
。endpoints
和addresses
欄位都是陣列,但按照約定,每個addresses
陣列只包含一個元素。如果您的 Service 有多個端點,那麼您需要在endpoints
陣列中有多個元素,每個元素的addresses
陣列中只有一個元素。Endpoints API 分別列出“就緒”和“未就緒”的端點,而 EndpointSlice API 允許每個端點關聯條件(例如 "
ready: false
")。
當然,一旦您移植到 EndpointSlice,就可以利用 EndpointSlice 特有的功能,例如拓撲提示和終止端點。有關更多資訊,請查閱 EndpointSlice API 文件。