在 Kubernetes 中,Service 是一種公開網路應用程式的方法,該應用程式作為叢集中的一個或多個 Pod 執行。
Kubernetes Service 的核心目標是讓您無需為了使用不熟悉的服務發現機制而修改現有的應用程式。您可以在 Pod 中執行程式碼,無論是專為雲原生環境設計的程式碼,還是已容器化的舊版應用程式,都可以使用 Service 將這組 Pod 發佈到網路上,以便用戶端與其互動。
如果您使用 Deployment 來執行應用程式,該 Deployment 可以動態建立和刪除 Pod。在任何時刻,您都無法確定有多少 Pod 是正在運作且健康的;您甚至可能不知道這些健康的 Pod 名稱是什麼。Kubernetes Pod 是為了符合叢集的期望狀態而建立與刪除的。Pod 是臨時性的資源(不應預期單一 Pod 是可靠且持久的)。
每個 Pod 都有自己的 IP 位址(Kubernetes 預期網路外掛程式會確保這一點)。對於叢集中的特定 Deployment,某一時刻執行的一組 Pod,可能與下一時刻執行該應用程式的 Pod 組不同。
這導致了一個問題:如果叢集中有一組 Pod(稱為「後端」)為其他 Pod(稱為「前端」)提供功能,前端該如何找到並追蹤要連線的 IP 位址,以便使用該工作負載的後端部分?
這就是 Service 的作用。
Service API 是 Kubernetes 的一部分,是一種幫助您透過網路公開一組 Pod 的抽象概念。每個 Service 物件都定義了一組邏輯終端(通常這些終端是 Pod),以及關於如何讓這些 Pod 可被存取的策略。
例如,考慮一個無狀態的影像處理後端,它以 3 個副本執行。這些副本是可互換的——前端不介意使用哪個後端。雖然組成後端的實際 Pod 可能會變更,但前端用戶端不需要察覺這一點,也不需要自行追蹤後端列表。
Service 抽象化實現了這種解耦。
由 Service 定位的 Pod 集合通常由您定義的 selector(選擇器)決定。若要了解定義 Service 終端的其他方法,請參閱 不含選擇器的 Service。
如果您的工作負載使用 HTTP,您可以選擇使用 Ingress 來控制 Web 流量如何到達該工作負載。Ingress 並非 Service 類型,但它充當了叢集的入口點。Ingress 讓您可以將路由規則整合到單一資源中,從而可以將叢集中單獨執行的多個工作負載組件公開在單一監聽器後方。
Kubernetes 的 Gateway API 提供了超出 Ingress 和 Service 的額外功能。您可以將 Gateway 新增至您的叢集——它是一系列擴充 API,使用 CustomResourceDefinitions 實作——然後使用這些 API 來設定對叢集中執行的網路服務的存取。
如果您能夠在應用程式中使用 Kubernetes API 進行服務發現,則可以查詢 API 伺服器以獲取匹配的 EndpointSlice。每當 Service 中的 Pod 集合發生變更時,Kubernetes 就會更新該 Service 的 EndpointSlice。
對於非原生應用程式,Kubernetes 提供了在應用程式和後端 Pod 之間放置網路連接埠或負載平衡器的方法。
無論哪種方式,您的工作負載都可以使用這些 服務發現 機制來找到它想要連線的目標。
Service 是一個 物件(與 Pod 或 ConfigMap 是物件的方式相同)。您可以使用 Kubernetes API 建立、查看或修改 Service 定義。通常,您會使用諸如 kubectl 之類的工具來為您執行這些 API 呼叫。
例如,假設您有一組 Pod,每個 Pod 都監聽 TCP 連接埠 9376,並被標記為 app.kubernetes.io/name=MyApp。您可以定義一個 Service 來發佈該 TCP 監聽器。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
應用此清單會建立一個名為 "my-service" 的新 Service,其使用預設的 ClusterIP 服務類型。該 Service 定位任何帶有 app.kubernetes.io/name: MyApp 標籤的 Pod 的 TCP 連接埠 9376。
Kubernetes 為此 Service 指派一個 IP 位址(叢集 IP),該位址由虛擬 IP 位址機制使用。關於該機制的更多詳細資訊,請閱讀 虛擬 IP 與服務代理。
該 Service 的控制器會持續掃描與其選擇器匹配的 Pod,然後對該 Service 的 EndpointSlice 集合進行必要的更新。
Service 物件的名稱必須是有效的 RFC 1123 標籤名稱。
port 對應到 targetPort。為了方便起見,預設情況下,targetPort 會被設定為與 port 欄位相同的值。Pod 中的連接埠定義具有名稱,您可以在 Service 的 targetPort 屬性中參照這些名稱。例如,我們可以透過以下方式將 Service 的 targetPort 綁定到 Pod 連接埠:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app.kubernetes.io/name: proxy
ports:
- name: name-of-service-port
protocol: TCP
port: 80
targetPort: http-web-svc
---
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app.kubernetes.io/name: proxy
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
name: http-web-svc
即使 Service 中混合了使用單一設定名稱的 Pod,且透過不同的連接埠號碼提供相同的網路協定,這也能運作。這為部署和發展您的 Service 提供了極大的靈活性。例如,您可以在後端軟體的下一個版本中變更 Pod 公開的連接埠號碼,而不會中斷用戶端。
Service 的預設協定是 TCP;您也可以使用任何其他 支援的協定。
由於許多 Service 需要公開多個連接埠,Kubernetes 支援在單一 Service 中進行 多個連接埠定義。每個連接埠定義可以使用相同的 protocol,也可以使用不同的協定。
Service 最常透過選擇器來抽象化對 Kubernetes Pod 的存取,但當與一組對應的 EndpointSlices 物件一起使用且沒有選擇器時,Service 可以抽象化其他類型的後端,包括在叢集外執行的後端。
例如
在上述任何場景中,您都可以定義一個不指定選擇器來匹配 Pod 的 Service。例如:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
由於此 Service 沒有選擇器,因此不會自動建立對應的 EndpointSlice 物件。您可以透過手動新增 EndpointSlice 物件,將 Service 對應到它執行的網路位址和連接埠。例如:
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: my-service-1 # by convention, use the name of the Service
# as a prefix for the name of the EndpointSlice
labels:
# You should set the "kubernetes.io/service-name" label.
# Set its value to match the name of the Service
kubernetes.io/service-name: my-service
addressType: IPv4
ports:
- name: http # should match with the name of the service port defined above
appProtocol: http
protocol: TCP
port: 9376
endpoints:
- addresses:
- "10.4.5.6"
- addresses:
- "10.1.2.3"
當您為 Service 建立 EndpointSlice 物件時,可以使用任何名稱。命名空間中的每個 EndpointSlice 必須具有唯一的名稱。您可以透過在該 EndpointSlice 上設定 kubernetes.io/service-name 標籤,將 EndpointSlice 連結到 Service。
終端 IP 不得為:回送位址 (IPv4 為 127.0.0.0/8,IPv6 為 ::1/128),或鏈路本地位址 (IPv4 為 169.254.0.0/16 和 224.0.0.0/24,IPv6 為 fe80::/64)。
終端 IP 位址不能是其他 Kubernetes Service 的叢集 IP,因為 kube-proxy 不支援將虛擬 IP 作為目的地。
對於您自行建立或在您自己的程式碼中建立的 EndpointSlice,您也應該為標籤 endpointslice.kubernetes.io/managed-by 選擇一個值。如果您建立自己的控制器程式碼來管理 EndpointSlice,請考慮使用類似 "my-domain.example/name-of-controller" 的值。如果您使用的是第三方工具,請使用該工具的小寫名稱,並將空格和其他標點符號變更為短橫線 (-)。如果人們直接使用像 kubectl 這樣的工具來管理 EndpointSlice,請使用描述此類手動管理的名稱,例如 "staff" 或 "cluster-admins"。您應該避免使用保留值 "controller",該值標識由 Kubernetes 自身控制平面管理的 EndpointSlice。
存取不含選擇器的 Service 的運作方式與有選擇器的 Service 相同。在不含選擇器的 Service 範例中,流量會被路由到 EndpointSlice 清單中定義的兩個終端之一:透過連接埠 9376 對 10.1.2.3 或 10.4.5.6 建立 TCP 連線。
kubectl port-forward service/<service-name> forwardedPort:servicePort 的操作將因該限制而失敗。這防止了 Kubernetes API 伺服器被用作代理,去存取呼叫者可能未經授權存取的終端。ExternalName Service 是 Service 的一種特殊情況,它沒有選擇器,而是使用 DNS 名稱。更多資訊,請參閱 ExternalName 章節。
Kubernetes v1.21 [穩定]EndpointSlices 是代表 Service 後端網路終端子集(切片)的物件。
您的 Kubernetes 叢集會追蹤每個 EndpointSlice 代表多少個終端。如果 Service 的終端數量達到閾值,Kubernetes 就會新增另一個空的 EndpointSlice 並將新的終端資訊儲存其中。預設情況下,一旦現有的 EndpointSlice 全部包含至少 100 個終端,Kubernetes 就會建立一個新的 EndpointSlice。在需要新增額外終端之前,Kubernetes 不會建立新的 EndpointSlice。
關於此 API 的更多資訊,請參閱 EndpointSlices。
Kubernetes v1.33 [已棄用]EndpointSlice API 是較舊的 Endpoints API 的演進。已棄用的 Endpoints API 相對於 EndpointSlice 存在幾個問題:
因此,建議所有用戶端使用 EndpointSlice API 而非 Endpoints。
Kubernetes 限制了單一 Endpoints 物件可以容納的終端數量。當 Service 的後端終端超過 1000 個時,Kubernetes 會截斷 Endpoints 物件中的資料。因為一個 Service 可以連結多個 EndpointSlice,所以 1000 個後端終端的限制僅影響舊版的 Endpoints API。
在這種情況下,Kubernetes 會選擇最多 1000 個可能的後端終端儲存到 Endpoints 物件中,並在 Endpoints 上設定一個 註解 (annotation):endpoints.kubernetes.io/over-capacity: truncated。如果後端 Pod 的數量降至 1000 以下,控制平面也會移除該註解。
流量仍然會發送到後端,但任何依賴舊版 Endpoints API 的負載平衡機制,只會將流量發送到最多 1000 個可用的後端終端。
相同的 API 限制意味著您無法手動更新 Endpoints 以使其擁有超過 1000 個終端。
Kubernetes v1.20 [穩定]appProtocol 欄位提供了為每個 Service 連接埠指定應用程式協定的方法。這被用作實作的一種提示,以便為它們理解的協定提供更豐富的行為。此欄位的值會反映在對應的 Endpoints 和 EndpointSlice 物件中。
此欄位遵循標準的 Kubernetes 標籤語法。有效值包括:
實作定義的前綴名稱,例如 mycompany.com/my-custom-protocol。
Kubernetes 定義的前綴名稱
| 協定 | 描述 |
|---|---|
kubernetes.io/h2c | 如 RFC 7540 中所述的明文 HTTP/2。 |
kubernetes.io/ws | 如 RFC 6455 中所述的明文 WebSocket。 |
kubernetes.io/wss | 如 RFC 6455 中所述的 TLS WebSocket。 |
對於某些 Service,您需要公開多個連接埠。Kubernetes 允許您在 Service 物件上設定多個連接埠定義。當 Service 使用多個連接埠時,您必須為所有連接埠命名,以確保它們不會產生歧義。例如:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
與一般的 Kubernetes 名稱一樣,連接埠名稱必須僅包含小寫英數字元和 -。連接埠名稱也必須以英數字元開頭和結尾。
例如,名稱 123-abc 和 web 是有效的,但 123_abc 和 -web 則無效。
對於應用程式的某些部分(例如前端),您可能希望將 Service 公開到外部 IP 位址,即從叢集外部可存取的位址。
Kubernetes Service 類型允許您指定所需的 Service 種類。
可用的 type 值及其行為如下:
ClusterIPtype,這是預設值。您可以使用 Ingress 或 Gateway 將 Service 公開至公用網際網路。NodePortNodePort)公開 Service。為了使節點連接埠可用,Kubernetes 會設定一個叢集 IP 位址,就像您請求了 type: ClusterIP 的 Service 一樣。LoadBalancerExternalNameexternalName 欄位的內容(例如,主機名稱 api.foo.bar.example)。該對應會設定叢集的 DNS 伺服器,以回傳帶有該外部主機名稱值的 CNAME 記錄。不會設定任何形式的代理。Service API 中的 type 欄位設計為巢狀功能——每一層都增加了上一層的功能。不過,此巢狀設計有一個例外。您可以透過 停用負載平衡器的 NodePort 分配 來定義 LoadBalancer Service。
type: ClusterIP此預設 Service 類型會從您的叢集為此目的預留的 IP 位址池中分配一個 IP 位址。
其他幾種 Service 類型皆以 ClusterIP 類型為基礎進行擴充。
如果您定義的 Service 將 .spec.clusterIP 設定為 "None",則 Kubernetes 不會指派 IP 位址。更多資訊,請參閱 Headless Service。
您可以在 Service 建立請求中指定您自己的叢集 IP 位址。為此,請設定 .spec.clusterIP 欄位。例如,如果您已經有想要重複使用的現有 DNS 項目,或者舊版系統已針對特定 IP 位址進行設定且難以重新設定。
您選擇的 IP 位址必須是 API 伺服器設定的 service-cluster-ip-range CIDR 範圍內的有效 IPv4 或 IPv6 位址。如果您嘗試使用無效的 clusterIP 位址值建立 Service,API 伺服器將回傳 422 HTTP 狀態代碼,以指示存在問題。
閱讀 避免衝突 以了解 Kubernetes 如何幫助降低兩個不同 Service 同時嘗試使用相同 IP 位址的風險與影響。
type: NodePort如果您將 type 欄位設定為 NodePort,Kubernetes 控制平面會從 --service-node-port-range 旗標指定的範圍(預設為 30000-32767)分配一個連接埠。每個節點會將該連接埠(每個節點上相同的連接埠號碼)代理到您的 Service。您的 Service 會在其 .spec.ports[*].nodePort 欄位中報告所分配的連接埠。
使用 NodePort 讓您有自由設定自己的負載平衡解決方案、設定不完全被 Kubernetes 支援的環境,甚至直接公開一個或多個節點的 IP 位址。
對於節點連接埠 Service,Kubernetes 還會額外分配一個連接埠(TCP、UDP 或 SCTP,以匹配 Service 的協定)。叢集中的每個節點都會將自己設定為監聽該分配的連接埠,並將流量轉發到與該 Service 相關聯的就緒終端。您可以透過連線到任何節點並使用適當的協定(例如:TCP)以及適當的連接埠(分配給該 Service 的連接埠),從叢集外部聯絡該 type: NodePort Service。
如果您想要特定的連接埠號碼,可以在 nodePort 欄位中指定一個值。控制平面會為您分配該連接埠,或者回報 API 交易失敗。這意味著您需要自行處理可能的連接埠衝突。您還必須使用有效的連接埠號碼,該號碼必須在 NodePort 使用的設定範圍內。
以下是一個 type: NodePort 的 Service 清單範例,其中指定了一個 NodePort 值(在此範例中為 30007):
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: NodePort
selector:
app.kubernetes.io/name: MyApp
ports:
- port: 80
# By default and for convenience, the `targetPort` is set to
# the same value as the `port` field.
targetPort: 80
# Optional field
# By default and for convenience, the Kubernetes control plane
# will allocate a port from a range (default: 30000-32767)
nodePort: 30007
為 NodePort 服務分配連接埠的策略同時適用於自動分配和手動分配場景。當使用者想要建立使用特定連接埠的 NodePort 服務時,目標連接埠可能會與已分配的另一個連接埠發生衝突。
為了避免此問題,NodePort 服務的連接埠範圍被分為兩個區段。動態連接埠分配預設使用上方區段,一旦上方區段耗盡,可能會使用下方區段。使用者隨後可以從下方區段進行分配,以降低連接埠衝突的風險。
使用預設的 NodePort 範圍 30000-32767 時,區段劃分如下:
有關靜態和動態區段如何計算的更多詳細資訊,請參閱 避免為 NodePort 服務分配連接埠時發生衝突。
type: NodePort 服務的自訂 IP 位址設定您可以設定叢集中的節點,使其使用特定的 IP 位址來服務節點連接埠服務。如果您希望每個節點連線到多個網路(例如:一個用於應用程式流量的網路,另一個用於節點與控制平面之間流量的網路),您可能需要這麼做。
如果您想指定特定的 IP 位址來代理該連接埠,可以為 kube-proxy 設定 --nodeport-addresses 旗標,或在 kube-proxy 設定檔中設定對應的 nodePortAddresses 欄位,指向特定的 IP 區塊。
此旗標接受逗號分隔的 IP 區塊列表(例如 10.0.0.0/8, 192.0.2.0/25),以指定 kube-proxy 應視為該節點本地的 IP 位址範圍。
例如,如果您以 --nodeport-addresses=127.0.0.0/8 旗標啟動 kube-proxy,kube-proxy 將僅選擇回送介面用於 NodePort 服務。--nodeport-addresses 的預設值為空列表。這意味著 kube-proxy 應考慮所有可用的網路介面用於 NodePort。(這也與較早的 Kubernetes 版本相容。)
<NodeIP>:spec.ports[*].nodePort 和 .spec.clusterIP:spec.ports[*].port。如果設定了 kube-proxy 的 --nodeport-addresses 旗標或設定檔中的對應欄位,<NodeIP> 將會是一個經過篩選的節點 IP 位址(或多個 IP 位址)。type: LoadBalancer在支援外部負載平衡器的雲端提供者上,將 type 欄位設定為 LoadBalancer 會為您的 Service 配置一個負載平衡器。負載平衡器的實際建立是非同步進行的,關於已配置負載平衡器的資訊會發佈在 Service 的 .status.loadBalancer 欄位中。例如:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
clusterIP: 10.0.171.239
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 192.0.2.127
來自外部負載平衡器的流量會被導向後端 Pod。雲端提供者會決定如何進行負載平衡。
為了實作 type: LoadBalancer 的 Service,Kubernetes 通常會從執行等同於請求 type: NodePort Service 的變更開始。隨後,cloud-controller-manager 組件會設定外部負載平衡器,將流量轉發到該分配的節點連接埠。
如果雲端提供者實作支援,您可以設定負載平衡 Service 以 忽略分配節點連接埠。
某些雲端提供者允許您指定 loadBalancerIP。在這些情況下,負載平衡器會使用使用者指定的 loadBalancerIP 進行建立。如果未指定 loadBalancerIP 欄位,則負載平衡器會以臨時 IP 位址進行設定。如果您指定了 loadBalancerIP 但您的雲端提供者不支援此功能,則您設定的 loadbalancerIP 欄位將會被忽略。
Service 的 .spec.loadBalancerIP 欄位已在 Kubernetes v1.24 中棄用。
此欄位定義不足,且其含義在不同實作之間有所差異。它也無法支援雙堆疊網路。此欄位可能會在未來的 API 版本中移除。
如果您整合的提供者支援透過(提供者特定的)註解為 Service 指定負載平衡器 IP 位址,您應該切換到該方式。
如果您正在編寫與 Kubernetes 負載平衡器整合的程式碼,請避免使用此欄位。您可以與 Gateway 整合而非 Service,或者在 Service 上定義您自己的(提供者特定的)註解來指定詳細資訊。
負載平衡器的健康檢查對現代應用程式至關重要。它們用於確定負載平衡器應將流量分發到哪台伺服器(虛擬機器或 IP 位址)。Kubernetes API 並未定義如何為 Kubernetes 管理的負載平衡器實作健康檢查,相反,是由雲端提供者(以及實作整合程式碼的人員)來決定行為。負載平衡器健康檢查在支援 Service 的 externalTrafficPolicy 欄位的上下文中被廣泛使用。
Kubernetes v1.26 [穩定] (預設啟用)預設情況下,對於 LoadBalancer 類型的 Service,當定義了多於一個連接埠時,所有連接埠必須具有相同的協定,且該協定必須是雲端提供者所支援的協定。
功能閘道 MixedProtocolLBService(自 v1.24 起為 kube-apiserver 預設啟用)允許在定義多於一個連接埠時,為 LoadBalancer 類型的 Service 使用不同的協定。
Kubernetes v1.24 [stable]您可以透過將 spec.allocateLoadBalancerNodePorts 欄位設定為 false,選擇性地停用 type: LoadBalancer Service 的節點連接埠分配。這僅適用於將流量直接路由到 Pod 而不是使用節點連接埠的負載平衡器實作。預設情況下,spec.allocateLoadBalancerNodePorts 為 true,且 LoadBalancer 類型的 Service 將繼續分配節點連接埠。如果對現有已分配節點連接埠的 Service 將 spec.allocateLoadBalancerNodePorts 設定為 false,這些節點連接埠並不會自動解除分配。您必須明確移除每個 Service 連接埠中的 nodePorts 條目,才能解除分配這些節點連接埠。
Kubernetes v1.24 [stable]對於 type 設定為 LoadBalancer 的 Service,.spec.loadBalancerClass 欄位使您能夠使用非雲端提供者預設的負載平衡器實作。
預設情況下,.spec.loadBalancerClass 未設定,如果叢集設定了使用 --cloud-provider 組件旗標的雲端提供者,則 LoadBalancer 類型的 Service 會使用雲端提供者的預設負載平衡器實作。
如果您指定了 .spec.loadBalancerClass,則會假設與指定類別匹配的負載平衡器實作正在監聽 Service。任何預設的負載平衡器實作(例如雲端提供者提供的實作)都會忽略設定了此欄位的 Service。spec.loadBalancerClass 僅可在 LoadBalancer 類型的 Service 上設定。一旦設定,便無法變更。spec.loadBalancerClass 的值必須是標籤樣式的識別碼,並帶有選用的前綴,例如 "internal-vip" 或 "example.com/internal-vip"。未加前綴的名稱保留給終端使用者使用。
對於 type: LoadBalancer 的 Service,控制器可以設定 .status.loadBalancer.ingress.ipMode。.status.loadBalancer.ingress.ipMode 指定了負載平衡器 IP 的行為。它僅能在同時指定了 .status.loadBalancer.ingress.ip 欄位時進行指定。
.status.loadBalancer.ingress.ipMode 有兩個可能的值:「VIP」和「Proxy」。預設值為「VIP」,意指流量被傳送到節點,且目的地設定為負載平衡器的 IP 和連接埠。設定為「Proxy」時,根據雲端提供者的負載平衡器傳送流量的方式,有兩種情況:
Service 實作可能會使用此資訊來調整流量路由。
在混合環境中,有時需要從相同的(虛擬)網路位址區塊內的 Service 路由流量。
在分割視野 (split-horizon) DNS 環境中,您需要兩個 Service,才能將外部和內部流量路由到您的終端。
要設定內部負載平衡器,請根據您使用的雲端服務提供者,將下列其中一個註解新增至您的 Service:
選擇其中一個分頁。
metadata:
name: my-service
annotations:
networking.gke.io/load-balancer-type: "Internal"
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-scheme: "internal"
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
metadata:
name: my-service
annotations:
service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: "private"
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/openstack-internal-load-balancer: "true"
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/cce-load-balancer-internal-vpc: "true"
metadata:
annotations:
service.kubernetes.io/qcloud-loadbalancer-internal-subnetid: subnet-xxxxx
metadata:
annotations:
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: "intranet"
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/oci-load-balancer-internal: true
type: ExternalNametype: ExternalName 的 Service 將 Service 對應到 DNS 名稱,而不是對應到 my-service 或 cassandra 這樣的常見選擇器。您可以使用 spec.externalName 參數來指定這些 Service。
例如,此 Service 定義將 prod 命名空間中的 my-service Service 對應到 my.database.example.com。
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
type: ExternalName 的 Service 接受 IPv4 位址字串,但會將該字串視為由數字組成的 DNS 名稱,而不是 IP 位址(網際網路不允許在 DNS 中使用此類名稱)。外觀類似 IPv4 位址的外部名稱 Service 不會被 DNS 伺服器解析。
如果您想將 Service 直接對應到特定的 IP 位址,請考慮使用 Headless Service。
當查詢主機 my-service.prod.svc.cluster.local 時,叢集 DNS Service 會回傳一個值為 my.database.example.com 的 CNAME 記錄。存取 my-service 的運作方式與其他 Service 相同,但關鍵區別在於重新導向發生在 DNS 層級,而不是透過代理或轉發。如果您稍後決定將資料庫遷移到叢集中,您可以啟動其 Pod、新增適當的選擇器或終端,並變更 Service 的 type。
對於一些常見協定(包括 HTTP 和 HTTPS),使用 ExternalName 可能會遇到困難。如果您使用 ExternalName,叢集內用戶端使用的主機名稱與 ExternalName 所參照的名稱不同。
對於使用主機名稱的協定,這種差異可能會導致錯誤或意外的回應。HTTP 請求將具有原始伺服器無法識別的 Host: 標頭;TLS 伺服器將無法提供與用戶端連線的主機名稱相符的憑證。
有時您不需要負載平衡和單一 Service IP。在這種情況下,您可以透過明確指定 "None" 作為叢集 IP 位址 (.spec.clusterIP),建立所謂的無頭服務 (headless Services)。
您可以使用 Headless Service 與其他服務發現機制對接,而無需綁定到 Kubernetes 的實作。
對於 Headless Service,不會指派叢集 IP,kube-proxy 也不會處理這些 Service,平台也不會為它們進行負載平衡或代理。
Headless Service 允許用戶端直接連線到它偏好的任何 Pod。Headless Service 不使用 虛擬 IP 位址與代理 來設定路由和封包轉發;相反,Headless Service 會透過叢集的 DNS 服務 所提供的內部 DNS 記錄,報告個別 Pod 的終端 IP 位址。要定義 Headless Service,請將 Service 的 .spec.type 設定為 ClusterIP(這也是 type 的預設值),並額外將 .spec.clusterIP 設定為 None。
字串值 None 是一種特殊情況,與不設定 .spec.clusterIP 欄位並不相同。
DNS 如何自動設定,取決於 Service 是否定義了選擇器:
對於定義了選擇器的 Headless Service,Endpoints 控制器會在 Kubernetes API 中建立 EndpointSlice,並修改 DNS 設定以回傳直接指向支援該 Service 之 Pod 的 A 或 AAAA 記錄(IPv4 或 IPv6 位址)。
對於未定義選擇器的 Headless Service,控制平面不會建立 EndpointSlice 物件。不過,DNS 系統會尋找並設定下列其中一項:
type: ExternalName Service 的 DNS CNAME 記錄。ExternalName 之外的所有 Service 類型,將針對 Service 所有就緒終端的所有 IP 位址,設定 DNS A / AAAA 記錄。當您定義不含選擇器的 Headless Service 時,port 必須與 targetPort 相符。
對於在叢集內執行的用戶端,Kubernetes 支援兩種主要的尋找 Service 模式:環境變數和 DNS。
當 Pod 在節點上執行時,kubelet 會為每個作用中的 Service 新增一組環境變數。它會新增 {SVCNAME}_SERVICE_HOST 和 {SVCNAME}_SERVICE_PORT 變數,其中 Service 名稱被轉為大寫,且短橫線會轉換為底線。
例如,公開 TCP 連接埠 6379 且已指派叢集 IP 位址 10.0.0.11 的 Service redis-primary,會產生下列環境變數:
REDIS_PRIMARY_SERVICE_HOST=10.0.0.11
REDIS_PRIMARY_SERVICE_PORT=6379
REDIS_PRIMARY_PORT=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP_PROTO=tcp
REDIS_PRIMARY_PORT_6379_TCP_PORT=6379
REDIS_PRIMARY_PORT_6379_TCP_ADDR=10.0.0.11
當您有一個需要存取 Service 的 Pod,並且正在使用環境變數方法向用戶端 Pod 發佈連接埠和叢集 IP 時,您必須在用戶端 Pod 存在之前建立 Service。否則,這些用戶端 Pod 將不會填入環境變數。
如果您僅使用 DNS 來發現 Service 的叢集 IP,則無需擔心此排序問題。
Kubernetes 也支援並提供與 Docker Engine 的「舊版容器連結」功能相容的變數。您可以閱讀 makeLinkVariables 以查看這是如何在 Kubernetes 中實作的。
您可以(而且幾乎總是應該)使用 附加元件 (add-on) 為您的 Kubernetes 叢集設定 DNS 服務。
具備叢集意識的 DNS 伺服器(例如 CoreDNS)會監看 Kubernetes API 以獲取新 Service,並為每個 Service 建立一組 DNS 記錄。如果整個叢集已啟用 DNS,則所有 Pod 都應該能夠自動透過 DNS 名稱解析 Service。
例如,如果您在 Kubernetes 命名空間 my-ns 中有一個名為 my-service 的 Service,控制平面和 DNS 服務會共同為 my-service.my-ns 建立一個 DNS 記錄。my-ns 命名空間中的 Pod 應該能夠透過執行 my-service 的名稱查詢來找到該服務(my-service.my-ns 也有效)。
其他命名空間中的 Pod 必須將名稱限定為 my-service.my-ns。這些名稱將解析為指派給該 Service 的叢集 IP。
Kubernetes 也支援命名連接埠的 DNS SRV(服務)記錄。如果 my-service.my-ns Service 有一個名為 http 且協定設定為 TCP 的連接埠,您可以對 _http._tcp.my-service.my-ns 進行 DNS SRV 查詢,以發現 http 的連接埠號碼以及 IP 位址。
Kubernetes DNS 伺服器是存取 ExternalName Service 的唯一方式。您可以在 服務與 Pod 的 DNS 中找到有關 ExternalName 解析的更多資訊。
閱讀 虛擬 IP 與服務代理 解釋了 Kubernetes 用於以虛擬 IP 位址公開 Service 的機制。
您可以設定 .spec.internalTrafficPolicy 和 .spec.externalTrafficPolicy 欄位,以控制 Kubernetes 如何將流量路由到健康的(「就緒」)後端。
更多詳細資訊,請參閱 流量策略。
.spec.trafficDistribution 欄位提供了另一種影響 Kubernetes Service 內部流量路由的方法。雖然流量策略專注於嚴格的語意保證,但流量分配允許您表達偏好(例如路由到拓樸上更接近的終端)。這有助於優化效能、成本或可靠性。在 Kubernetes 1.36 中,支援下列值:
PreferSameZonePreferSameNodePreferClose (已棄用)PreferSameZone 的舊別名,其語意較不明確。如果未設定該欄位,實作將應用其預設的路由策略。
更多詳細資訊,請參閱 流量分配。
如果您想確保來自特定用戶端的連線每次都傳遞到同一個 Pod,可以根據用戶端的 IP 位址設定連線黏性 (session affinity)。閱讀 連線黏性 以了解更多。
externalIPs 功能自 Kubernetes v1.36 起已棄用,所有使用者都應開始遷移。請考慮改用外部負載平衡器控制器或 Gateway API 實作。如果存在可路由至一個或多個叢集節點的外部 IP,Kubernetes 服務(Services)可以透過這些 externalIPs 進行公開。當網路流量抵達叢集時,若其目標 IP 為外部 IP 且連接埠與該服務相符,Kubernetes 所配置的規則與路由將確保流量被導向至該服務的其中一個端點(endpoints)。
當您定義服務時,可以為任何服務類型指定 externalIPs。在下方範例中,名為 "my-service" 的服務可供客戶端透過 TCP,在 "198.51.100.32:80" 進行存取(由 .spec.externalIPs[] 與 .spec.ports[].port 計算得出)。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 49152
externalIPs:
- 198.51.100.32
externalIPs 的配置;這些 IP 由叢集管理員負責。Service 是 Kubernetes REST API 中的頂層資源。您可以在 Service API 物件中找到更多詳細資訊。
深入了解服務以及它們如何融入 Kubernetes
如需更多背景資訊,請閱讀下列內容