服務
在 Kubernetes 中,服務是一種公開網路應用程式的方法,該應用程式以叢集中的一個或多個 Pods 形式執行。
Kubernetes 中服務的主要目標是您不需要修改現有的應用程式來使用不熟悉的服務發現機制。您可以在 Pods 中執行程式碼,無論這是為雲原生世界設計的程式碼,還是您已容器化的舊應用程式。您可以使用服務讓該組 Pods 在網路上可用,以便用戶端可以與其互動。
如果您使用 Deployment 來執行您的應用程式,則該 Deployment 可以動態建立和銷毀 Pods。從一個時刻到下一個時刻,您不知道有多少 Pods 正在運作且健康;您甚至可能不知道這些健康的 Pods 的名稱。Kubernetes Pods 會被建立和銷毀以符合您叢集的所需狀態。Pods 是臨時資源(您不應期望個別 Pod 是可靠且持久的)。
每個 Pod 都有自己的 IP 位址(Kubernetes 期望網路外掛程式確保這一點)。對於叢集中的給定 Deployment,在某個時刻執行的 Pods 集合可能與稍後時刻執行該應用程式的 Pods 集合不同。
這導致一個問題:如果某些 Pods 集合(稱它們為「後端」)為叢集內的其他 Pods(稱它們為「前端」)提供功能,則前端如何找出並追蹤要連線的 IP 位址,以便前端可以使用工作負載的後端部分?
服務登場。
Kubernetes 中的服務
Service API 是 Kubernetes 的一部分,它是一種抽象化,可協助您透過網路公開 Pods 群組。每個 Service 物件都定義一組邏輯端點(通常這些端點是 Pods)以及關於如何使這些 Pods 可存取的策略。
例如,考慮一個以 3 個副本執行的無狀態影像處理後端。這些副本是可互換的——前端不在乎它們使用哪個後端。雖然組成後端集合的實際 Pods 可能會變更,但前端用戶端不應需要意識到這一點,也不應需要追蹤後端集合本身。
服務抽象化實現了這種解耦。
服務所針對的 Pods 集合通常由您定義的 選擇器 決定。若要瞭解定義服務端點的其他方式,請參閱 沒有選擇器的服務。
如果您的工作負載使用 HTTP,您可能會選擇使用 Ingress 來控制網路流量如何到達該工作負載。Ingress 不是服務類型,但它充當您叢集的進入點。Ingress 可讓您將路由規則整合到單一資源中,以便您可以將工作負載的多個元件(在叢集中單獨執行)公開在單一監聽器之後。
Kubernetes 的 Gateway API 除了 Ingress 和 Service 之外,還提供額外的功能。您可以將 Gateway 新增至您的叢集 - 它是一系列擴展 API,使用 CustomResourceDefinitions 實作 - 然後使用這些 API 來組態對叢集中執行之網路服務的存取權。
雲原生服務發現
如果您能夠在應用程式中使用 Kubernetes API 進行服務發現,您可以查詢 API 伺服器 以取得相符的 EndpointSlices。每當服務中的 Pods 集合變更時,Kubernetes 就會更新服務的 EndpointSlices。
對於非原生應用程式,Kubernetes 提供在您的應用程式和後端 Pods 之間放置網路連接埠或負載平衡器的方法。
無論哪種方式,您的工作負載都可以使用這些 服務發現 機制來尋找它想要連線的目標。
定義服務
服務是一個 物件(與 Pod 或 ConfigMap 是物件的方式相同)。您可以使用 Kubernetes API 建立、檢視或修改服務定義。通常您會使用諸如 kubectl
之類的工具為您發出這些 API 呼叫。
例如,假設您有一組 Pods,每個 Pods 都監聽 TCP 連接埠 9376,並標記為 app.kubernetes.io/name=MyApp
。您可以定義服務來發佈該 TCP 監聽器
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
套用此 Manifest 會建立名為「my-service」的新 Service,並採用預設的 ClusterIP 服務類型。此 Service 的目標是任何具有 app.kubernetes.io/name: MyApp
標籤之 Pod 上的 TCP 埠 9376。
Kubernetes 會為此 Service 指派一個 IP 位址(即叢集 IP),虛擬 IP 位址機制會使用此位址。如需該機制的更多詳細資訊,請閱讀虛擬 IP 和 Service Proxy。
該 Service 的控制器會持續掃描符合其選取器的 Pod,然後對 Service 的 EndpointSlice 集合進行任何必要的更新。
Service 物件的名稱必須是有效的 RFC 1035 標籤名稱。
注意
Service 可以將任何傳入的port
對應到 targetPort
。預設情況下,為了方便起見,targetPort
會設定為與 port
欄位相同的值。埠定義
Pod 中的埠定義具有名稱,您可以在 Service 的 targetPort
屬性中參考這些名稱。例如,我們可以透過以下方式將 Service 的 targetPort
繫結到 Pod 埠
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
---
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
即使 Service 中混合使用了多個 Pod,且這些 Pod 使用單一設定的名稱,並透過不同的埠號提供相同的網路協定,這也能運作。這為部署和發展您的 Service 提供了很大的彈性。例如,您可以變更 Pod 在下一版後端軟體中公開的埠號,而不會中斷用戶端。
Service 的預設協定是 TCP;您也可以使用任何其他支援的協定。
由於許多 Service 需要公開多個埠,因此 Kubernetes 支援單一 Service 的多個埠定義。每個埠定義可以具有相同的 protocol
,也可以具有不同的協定。
沒有選取器的 Service
Service 最常見的功能是透過選取器抽象化對 Kubernetes Pod 的存取,但當與一組對應的 EndpointSlice 物件搭配使用且沒有選取器時,Service 可以抽象化其他種類的後端,包括在叢集外部執行的後端。
例如
- 您想要在生產環境中擁有外部資料庫叢集,但在測試環境中使用您自己的資料庫。
- 您想要將您的 Service 指向不同命名空間或另一個叢集中的 Service。
- 您正在將工作負載移轉到 Kubernetes。在評估方法時,您只在 Kubernetes 中執行部分後端。
在任何這些情境中,您都可以定義沒有指定選取器來比對 Pod 的 Service。例如
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
由於此 Service 沒有選取器,因此不會自動建立對應的 EndpointSlice(和舊版 Endpoints)物件。您可以透過手動新增 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"
自訂 EndpointSlice
當您為 Service 建立 EndpointSlice 物件時,您可以為 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 運作方式相同。在沒有選取器的 Service 的 範例 中,流量會路由到 EndpointSlice Manifest 中定義的兩個端點之一:埠 9376 上與 10.1.2.3 或 10.4.5.6 的 TCP 連線。
注意
Kubernetes API 伺服器不允許 Proxy 連線到未對應至 Pod 的端點。諸如kubectl port-forward service/<service-name> forwardedPort:servicePort
之類的動作(其中 Service 沒有選取器)會因為此限制而失敗。這可防止 Kubernetes API 伺服器被用作 Proxy 連線到呼叫者可能未經授權存取的端點。ExternalName
Service 是一種特殊的 Service,它沒有選取器,而是使用 DNS 名稱。如需更多資訊,請參閱 ExternalName 區段。
EndpointSlices
Kubernetes v1.21 [穩定]
EndpointSlice 是代表 Service 後端網路端點子集(slice)的物件。
您的 Kubernetes 叢集會追蹤每個 EndpointSlice 代表多少個端點。如果 Service 的端點數量過多而達到閾值,則 Kubernetes 會新增另一個空的 EndpointSlice,並在其中儲存新的端點資訊。預設情況下,一旦現有的 EndpointSlice 都至少包含 100 個端點,Kubernetes 就會建立新的 EndpointSlice。Kubernetes 不會在需要新增額外端點之前建立新的 EndpointSlice。
請參閱 EndpointSlice 以取得有關此 API 的更多資訊。
端點
在 Kubernetes API 中,Endpoints(資源種類為複數)定義了網路端點清單,通常由 Service 參考,以定義可以將流量傳送到哪些 Pod。
EndpointSlice API 是 Endpoints 的建議取代方案。
容量過大的端點
Kubernetes 限制單一 Endpoints 物件中可以容納的端點數量。當 Service 的後端端點超過 1000 個時,Kubernetes 會截斷 Endpoints 物件中的資料。由於 Service 可以連結到多個 EndpointSlice,因此 1000 個後端端點的限制僅影響舊版 Endpoints API。
在這種情況下,Kubernetes 最多會選取 1000 個可能的後端端點來儲存到 Endpoints 物件中,並在 Endpoints 上設定 註解: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
對於某些 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 類型
對於應用程式的某些部分(例如,前端),您可能希望將 Service 公開到外部 IP 位址,即從叢集外部可存取的位址。
Kubernetes Service 類型可讓您指定您想要的 Service 種類。
可用的 type
值及其行為如下
ClusterIP
- 在叢集內部 IP 上公開 Service。選擇此值會使 Service 只能從叢集內部存取。如果您未明確指定 Service 的
type
,則會使用此預設值。您可以使用 Ingress 或 Gateway 將 Service 公開到公共網際網路。 NodePort
- 在每個節點的 IP 上以靜態埠 (
NodePort
) 公開 Service。為了使節點埠可用,Kubernetes 會設定叢集 IP 位址,就像您請求type: ClusterIP
的 Service 一樣。 LoadBalancer
- 使用外部負載平衡器在外部公開 Service。Kubernetes 不直接提供負載平衡元件;您必須提供一個,或者您可以將您的 Kubernetes 叢集與雲端供應商整合。
ExternalName
- 將 Service 對應到
externalName
欄位的內容(例如,主機名稱api.foo.bar.example
)。此對應會設定您叢集的 DNS 伺服器以傳回具有該外部主機名稱值的CNAME
記錄。不會設定任何種類的 Proxy 連線。
Service API 中的 type
欄位設計為巢狀功能 - 每個層級都會新增到前一個層級。但是,此巢狀設計有一個例外。您可以透過停用負載平衡器 NodePort
配置來定義 LoadBalancer
Service。
type: ClusterIP
此預設 Service 類型會從您的叢集為此目的保留的 IP 位址池中指派 IP 位址。
其他幾種 Service 類型都以 ClusterIP
類型為基礎建構。
如果您定義的 Service 將 .spec.clusterIP
設定為 "None"
,則 Kubernetes 不會指派 IP 位址。如需更多資訊,請參閱 無 Head Service。
選擇您自己的 IP 位址
您可以在 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)中配置埠。每個節點都會將該埠(每個節點上的相同埠號)Proxy 連線到您的 Service。您的 Service 會在其 .spec.ports[*].nodePort
欄位中報告配置的埠。
使用 NodePort 可讓您自由設定自己的負載平衡解決方案、設定 Kubernetes 未完全支援的環境,甚至直接公開一個或多個節點的 IP 位址。
對於節點埠 Service,Kubernetes 還會額外配置一個埠(TCP、UDP 或 SCTP 以符合 Service 的協定)。叢集中的每個節點都會將自身設定為監聽該指派的埠,並將流量轉發到與該 Service 關聯的其中一個就緒端點。您將能夠從叢集外部連線到 type: NodePort
Service,方法是使用適當的協定(例如:TCP)和適當的埠(指派給該 Service 的埠)連線到任何節點。
選擇您自己的埠
如果您想要特定的埠號,您可以在 nodePort
欄位中指定值。控制平面將配置該埠給您,或報告 API 交易失敗。這表示您需要自行處理可能的埠衝突。您還必須使用有效的埠號,即在為 NodePort 使用設定的範圍內的埠號。
以下是 type: NodePort
的 Service 的 Manifest 範例,其中指定了 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 Service 的原則適用於自動指派和手動指派情境。當使用者想要建立使用特定埠的 NodePort Service 時,目標埠可能會與已指派的其他埠衝突。
為了避免此問題,NodePort Service 的埠範圍分為兩個頻帶。動態埠指派預設使用較高的頻帶,並且在較高的頻帶耗盡後,可能會使用較低的頻帶。然後,使用者可以從較低的頻帶配置,從而降低埠衝突的風險。
type: NodePort
Service 的自訂 IP 位址設定
您可以設定叢集中的節點以使用特定的 IP 位址來服務節點埠 Service。如果每個節點都連線到多個網路(例如:一個網路用於應用程式流量,另一個網路用於節點和控制平面之間的流量),您可能會想要執行此操作。
如果您想要指定特定的 IP 位址來 Proxy 連線埠,您可以為 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 Service。--nodeport-addresses
的預設值是空清單。這表示 kube-proxy 應考慮所有可用的網路介面用於 NodePort。(這也與較早版本的 Kubernetes 相容。)
注意
此 Service 可見為<NodeIP>:spec.ports[*].nodePort
和 .spec.clusterIP:spec.ports[*].port
。如果設定了 kube-proxy 的 --nodeport-addresses
旗標或 kube-proxy 設定檔中的對等欄位,則 <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 [穩定]
(預設啟用:true)預設情況下,對於 LoadBalancer 類型的 Service,當定義了多個埠時,所有埠都必須具有相同的協定,並且該協定必須是雲端供應商支援的協定之一。
功能閘道 MixedProtocolLBService
(從 v1.24 開始預設為 kube-apiserver 啟用)允許在定義了多個埠時,為 LoadBalancer 類型的 Service 使用不同的協定。
注意
可用於負載平衡 Service 的協定集由您的雲端供應商定義;它們可能會施加超出 Kubernetes API 強制執行的限制。停用負載平衡器 NodePort 配置
Kubernetes v1.24 [穩定]
您可以選擇性地停用 type: LoadBalancer
的 Service 的節點埠配置,方法是將欄位 spec.allocateLoadBalancerNodePorts
設定為 false
。這僅適用於將流量直接路由到 Pod 而不是使用節點埠的負載平衡器實作。預設情況下,spec.allocateLoadBalancerNodePorts
為 true
,並且 LoadBalancer 類型的 Service 將繼續配置節點埠。如果在已配置節點埠的現有 Service 上將 spec.allocateLoadBalancerNodePorts
設定為 false
,則這些節點埠不會自動取消配置。您必須明確移除每個 Service 埠中的 nodePorts
項目,才能取消配置這些節點埠。
指定負載平衡器實作類別
Kubernetes v1.24 [穩定]
對於 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
"。未加前綴的名稱保留供終端使用者使用。
負載平衡器 IP 位址模式
Kubernetes v1.32 [穩定]
(預設啟用:true)對於 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」,具體取決於雲端供應商的負載平衡器如何傳遞流量
- 如果流量傳遞到節點,然後 DNAT 到 Pod,則目的地將設定為節點的 IP 和節點埠;
- 如果流量直接傳遞到 Pod,則目的地將設定為 Pod 的 IP 和埠。
Service 實作可以使用此資訊來調整流量路由。
內部負載平衡器
在混合環境中,有時需要路由來自相同(虛擬)網路位址區塊內 Service 的流量。
在水平分割 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-internal: "true"
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: ExternalName
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 位址,請考慮使用 無 Head Service。
當查詢主機 my-service.prod.svc.cluster.local
時,叢集 DNS Service 會傳回值為 my.database.example.com
的 CNAME
記錄。存取 my-service
的方式與其他 Service 相同,但關鍵差異在於重新導向發生在 DNS 層級,而不是透過 Proxy 連線或轉發。如果您稍後決定將資料庫移至您的叢集,您可以啟動其 Pod、新增適當的選取器或端點,並變更 Service 的 type
。
注意
您在使用 ExternalName 處理某些常見協定(包括 HTTP 和 HTTPS)時可能會遇到問題。如果您使用 ExternalName,則叢集內部用戶端使用的主機名稱與 ExternalName 參考的名稱不同。
對於使用主機名稱的協定,此差異可能會導致錯誤或非預期的回應。HTTP 請求將具有來源伺服器無法辨識的 Host:
標頭;TLS 伺服器將無法提供與用戶端連線的主機名稱相符的憑證。
無 Head Service
有時您不需要負載平衡和單一 Service IP。在這種情況下,您可以明確地為叢集 IP 位址 (.spec.clusterIP
) 指定 "None"
,來建立所謂的無 Head Service。
您可以使用無 Head Service 與其他服務探索機制介接,而不會受限於 Kubernetes 的實作。
對於無 Head Service,不會配置叢集 IP,kube-proxy 不會處理這些 Service,並且平台不會為它們執行負載平衡或 Proxy 連線。
無 Head Service 允許用戶端直接連線到它偏好的任何 Pod。無 Head Service 不會使用虛擬 IP 位址和 Proxy 設定路由和封包轉發;相反地,無 Head Service 會透過內部 DNS 記錄報告個別 Pod 的端點 IP 位址,這些記錄透過叢集的 DNS 服務提供。若要定義無 Head Service,您可以建立 .spec.type
設定為 ClusterIP(這也是 type
的預設值)的 Service,並且額外將 .spec.clusterIP
設定為 None。
字串值 None 是一種特殊情況,與將 .spec.clusterIP
欄位保持未設定不同。
DNS 的自動設定方式取決於 Service 是否已定義選取器
具有選取器
對於定義選取器的無 Head Service,端點控制器會在 Kubernetes API 中建立 EndpointSlice,並修改 DNS 設定以傳回直接指向支援 Service 的 Pod 的 A 或 AAAA 記錄(IPv4 或 IPv6 位址)。
沒有選取器
對於未定義選取器的 Headless 服務,控制平面不會建立 EndpointSlice 物件。但是,DNS 系統會尋找並設定下列其中一種:
- 用於
type: ExternalName
服務的 DNS CNAME 記錄。 - 用於服務所有就緒端點的 IP 位址的 DNS A / AAAA 記錄,適用於
ExternalName
以外的所有服務類型。- 對於 IPv4 端點,DNS 系統會建立 A 記錄。
- 對於 IPv6 端點,DNS 系統會建立 AAAA 記錄。
當您定義沒有選取器的 Headless 服務時,port
必須符合 targetPort
。
探索服務
對於在叢集內部執行的用戶端,Kubernetes 支援兩種主要尋找服務的模式:環境變數和 DNS。
環境變數
當 Pod 在節點上執行時,kubelet 會為每個作用中的服務新增一組環境變數。它會新增 {SVCNAME}_SERVICE_HOST
和 {SVCNAME}_SERVICE_PORT
變數,其中服務名稱會轉換為大寫,破折號會轉換為底線。
例如,公開 TCP 通訊埠 6379 且已分配叢集 IP 位址 10.0.0.11 的服務 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
注意
當您有一個 Pod 需要存取服務,並且您使用環境變數方法將通訊埠和叢集 IP 發布到用戶端 Pod 時,您必須在用戶端 Pod 出現之前建立服務。否則,這些用戶端 Pod 將不會填入其環境變數。
如果您僅使用 DNS 來探索服務的叢集 IP,則無需擔心此順序問題。
Kubernetes 也支援並提供與 Docker Engine 的「舊版容器連結」功能相容的變數。您可以閱讀 makeLinkVariables
以了解這在 Kubernetes 中是如何實作的。
DNS
您可以(而且幾乎總是應該)使用附加元件為您的 Kubernetes 叢集設定 DNS 服務。
叢集感知 DNS 伺服器(例如 CoreDNS)會監看 Kubernetes API 以尋找新的服務,並為每個服務建立一組 DNS 記錄。如果已在您的整個叢集中啟用 DNS,則所有 Pod 應該都能夠透過其 DNS 名稱解析服務。
例如,如果您在 Kubernetes 命名空間 my-ns
中有一個名為 my-service
的服務,則控制平面和 DNS 服務會共同作用,為 my-service.my-ns
建立 DNS 記錄。my-ns
命名空間中的 Pod 應該能夠透過對 my-service
執行名稱查詢來找到該服務(my-service.my-ns
也會有效)。
其他命名空間中的 Pod 必須將名稱限定為 my-service.my-ns
。這些名稱將解析為為服務分配的叢集 IP。
Kubernetes 也支援具名通訊埠的 DNS SRV(服務)記錄。如果 my-service.my-ns
服務有一個名為 http
的通訊埠,且協定設定為 TCP
,您可以對 _http._tcp.my-service.my-ns
執行 DNS SRV 查詢,以探索 http
的通訊埠號碼以及 IP 位址。
Kubernetes DNS 伺服器是存取 ExternalName
服務的唯一方法。您可以在 服務和 Pod 的 DNS 中找到有關 ExternalName
解析的更多資訊。
虛擬 IP 定址機制
閱讀 虛擬 IP 和服務代理,其中說明 Kubernetes 提供的機制,以使用虛擬 IP 位址公開服務。
流量策略
您可以設定 .spec.internalTrafficPolicy
和 .spec.externalTrafficPolicy
欄位,以控制 Kubernetes 如何將流量路由到健康(“就緒”)的後端。
請參閱 流量策略 以了解更多詳細資訊。
流量分配
Kubernetes v1.31 [beta]
(預設啟用:true).spec.trafficDistribution
欄位提供了另一種影響 Kubernetes 服務中流量路由的方式。雖然流量策略側重於嚴格的語意保證,但流量分配允許您表達偏好(例如路由到拓樸位置更近的端點)。這有助於優化效能、成本或可靠性。如果您已為您的叢集及其所有節點啟用 ServiceTrafficDistribution
功能閘道,則可以使用此選填欄位。在 Kubernetes 1.32 中,支援以下欄位值:
PreferClose
- 表示偏好將流量路由到拓樸位置上更接近用戶端的端點。「拓樸位置接近」的解釋可能因實作而異,並且可能包含同一節點、機架、區域甚至區域內的端點。設定此值允許實作進行不同的權衡,例如,針對接近性而非負載的均等分配進行優化。如果此類權衡不可接受,則使用者不應設定此值。
如果未設定該欄位,則實作將套用其預設路由策略。
請參閱 流量分配 以了解更多詳細資訊
工作階段黏性
如果您想確保來自特定用戶端的連線每次都傳遞到相同的 Pod,您可以根據用戶端的 IP 位址設定工作階段親和性。閱讀 工作階段親和性 以了解更多資訊。
外部 IP
如果有路由到一個或多個叢集節點的外部 IP,則 Kubernetes 服務可以透過這些 externalIPs
公開。當網路流量到達叢集時,其目的地 IP 為外部 IP,且通訊埠與該服務的通訊埠相符,Kubernetes 已設定的規則和路由可確保流量路由到該服務的其中一個端點。
當您定義服務時,您可以為任何服務類型指定 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
注意
Kubernetes 不管理externalIPs
的分配;這些是叢集管理員的責任。API 物件
Service 是 Kubernetes REST API 中的頂層資源。您可以找到有關 Service API 物件 的更多詳細資訊。
接下來是什麼
進一步了解服務以及它們如何適用於 Kubernetes
- 依照使用服務連接應用程式教學課程進行操作。
- 閱讀有關 Ingress 的資訊,Ingress 將來自叢集外部到叢集內服務的 HTTP 和 HTTPS 路由公開。
- 閱讀有關 Gateway 的資訊,Gateway 是 Kubernetes 的延伸,可提供比 Ingress 更大的彈性。
如需更多背景資訊,請閱讀以下內容: