服務

即使工作負載分佈在多個後端,也要將叢集中執行的應用程式透過單個對外暴露的端點對外提供。

在 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 的作用。

Kubernetes 中的 Service

Service API 是 Kubernetes 的一部分,它是一種抽象,可幫助你透過網路公開 Pod 組。每個 Service 物件都定義了一組邏輯端點(通常這些端點是 Pod),以及有關如何使這些 Pod 可訪問的策略。

例如,考慮一個執行 3 個副本的無狀態影像處理後端。這些副本是可互換的——前端不關心它們使用哪個後端。雖然構成後端集合的實際 Pod 可能會改變,但前端客戶端不應需要知道這一點,也不應需要自行跟蹤後端集合。

Service 抽象實現了這種解耦。

Service 所針對的 Pod 集合通常由你定義的選擇器決定。要了解定義 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

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 代理

該 Service 的控制器持續掃描匹配其選擇器的 Pod,然後對 Service 的 EndpointSlice 集合進行任何必要的更新。

Service 物件的名稱必須是有效的 RFC 1035 標籤名稱

埠定義

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,並透過不同的埠號提供相同的網路協議,這仍然有效。這為部署和演進你的 Service 提供了很大的靈活性。例如,你可以在後端軟體的下一個版本中更改 Pod 公開的埠號,而不會破壞客戶端。

服務的預設協議是 TCP;你也可以使用任何其他支援的協議

由於許多 Service 需要公開多個埠,Kubernetes 支援為單個 Service 定義多個埠。每個埠定義可以具有相同的 `protocol`,也可以具有不同的 `protocol`。

沒有選擇器的 Service

Service 最常見的是透過選擇器抽象對 Kubernetes Pod 的訪問,但當與相應的 EndpointSlice 物件集一起使用且沒有選擇器時,Service 可以抽象其他型別的後端,包括在叢集外部執行的後端。

例如

  • 你希望在生產環境中擁有一個外部資料庫叢集,但在測試環境中你使用自己的資料庫。
  • 你希望將你的 Service 指向不同 名稱空間或另一個叢集中的 Service。
  • 你正在將工作負載遷移到 Kubernetes。在評估方法時,你只在 Kubernetes 中執行一部分後端。

在任何這些場景中,你都可以定義一個 Service,**而無需**指定選擇器來匹配 Pod。例如

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"

自定義 EndpointSlice

當您為 Service 建立 EndpointSlice 物件時,您可以為 EndpointSlice 使用任何名稱。名稱空間中的每個 EndpointSlice 都必須具有唯一的名稱。您透過在 EndpointSlice 上設定 `kubernetes.io/service-name` 標籤來將 EndpointSlice 連結到 Service。

對於您自己建立的 EndpointSlice 或在您自己的程式碼中建立的 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 清單中定義的兩個端點之一:一個到 10.1.2.3 或 10.4.5.6 的 TCP 連線,埠為 9376。

`ExternalName` Service 是一種特殊的 Service 型別,它沒有選擇器並使用 DNS 名稱。有關更多資訊,請參閱 ExternalName 部分。

EndpointSlice

特性狀態: Kubernetes v1.21 [stable]

EndpointSlice 是代表 Service 後備網路端點子集(一個**切片**)的物件。

你的 Kubernetes 叢集跟蹤每個 EndpointSlice 表示的端點數量。如果 Service 的端點太多以至於達到閾值,Kubernetes 會新增另一個空的 EndpointSlice 並在其中儲存新的端點資訊。預設情況下,一旦現有 EndpointSlice 都包含至少 100 個端點,Kubernetes 就會建立一個新的 EndpointSlice。直到需要新增額外端點時,Kubernetes 才建立新的 EndpointSlice。

有關此 API 的更多資訊,請參閱EndpointSlice

端點(已棄用)

功能狀態: `Kubernetes v1.33 [deprecated]`

EndpointSlice API 是舊版 Endpoints API 的演進。已棄用的 Endpoints API 相對於 EndpointSlice 有幾個問題

  • 它不支援雙棧叢集。
  • 它不包含支援新功能所需的資訊,例如 trafficDistribution
  • 如果端點列表太長而無法容納在一個物件中,它將被截斷。

因此,建議所有客戶端使用 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 [stable]

`appProtocol` 欄位提供了一種為每個 Service 埠指定應用程式協議的方法。這被用作實現提供更豐富行為的提示,對於它們理解的協議。此欄位的值由相應的 Endpoints 和 EndpointSlice 物件映象。

此欄位遵循標準 Kubernetes 標籤語法。有效值之一是

  • IANA 標準服務名稱.

  • 實現定義的帶字首名稱,例如 `mycompany.com/my-custom-protocol`。

  • Kubernetes 定義的帶字首名稱

協議描述
kubernetes.io/h2cRFC 7540 中所述的明文 HTTP/2
kubernetes.io/wsRFC 6455 中所述的明文 WebSocket
kubernetes.io/wssRFC 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

服務型別

對於應用程式的某些部分(例如,前端),你可能希望將 Service 暴露到外部 IP 地址,該地址可從叢集外部訪問。

Kubernetes Service 型別允許你指定所需 Service 的型別。

可用的 `type` 值及其行為如下

ClusterIP
將 Service 公開在叢集內部 IP 上。選擇此值使 Service 只能從叢集內部訪問。這是如果你未明確指定 Service 的 `type` 時使用的預設值。你可以使用 IngressGateway 將 Service 公開到公共網際網路。
NodePort
在每個節點的 IP 地址上以靜態埠(`NodePort`)公開 Service。為了使節點埠可用,Kubernetes 會設定一個叢集 IP 地址,與你請求 `type: ClusterIP` 的 Service 相同。
LoadBalancer
使用外部負載均衡器在外部公開 Service。Kubernetes 不直接提供負載均衡元件;你必須提供一個,或者你可以將你的 Kubernetes 叢集與雲提供商整合。
ExternalName
將 Service 對映到 `externalName` 欄位的內容(例如,到主機名 `api.foo.bar.example`)。該對映會將你的叢集 DNS 伺服器配置為返回一個包含該外部主機名值的 `CNAME` 記錄。不設定任何型別的代理。

Service API 中的 `type` 欄位被設計為巢狀功能——每個級別都建立在前一個級別之上。但這種巢狀設計有一個例外。你可以透過停用負載均衡器 `NodePort` 分配來定義 `LoadBalancer` Service。

型別:ClusterIP

此預設 Service 型別從你的叢集為此目的保留的 IP 地址池中分配一個 IP 地址。

Service 的其他幾種型別都是以 `ClusterIP` 型別為基礎構建的。

如果你定義的 Service 的 `spec.clusterIP` 設定為 `None`,那麼 Kubernetes 不會分配 IP 地址。有關更多資訊,請參閱無頭 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 地址的風險和影響。

型別: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 服務的埠範圍分為兩個波段。預設情況下,動態埠分配使用上部波段,一旦上部波段用完,它可以使用下部波段。然後,使用者可以從下部波段分配,從而降低埠衝突的風險。

`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 版本相容。)

型別: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 等效的更改。然後,雲控制器管理器元件配置外部負載均衡器,將流量轉發到該分配的節點埠。

你可以配置一個負載均衡的 Service,以省略分配節點埠,前提是雲提供商的實現支援此功能。

有些雲提供商允許你指定 `loadBalancerIP`。在這些情況下,負載均衡器會使用使用者指定的 `loadBalancerIP` 建立。如果未指定 `loadBalancerIP` 欄位,則負載均衡器會設定一個臨時 IP 地址。如果你指定了 `loadBalancerIP` 但你的雲提供商不支援此功能,你設定的 `loadbalancerIP` 欄位將被忽略。

節點存活對負載均衡器流量的影響

負載均衡器健康檢查對現代應用程式至關重要。它們用於確定負載均衡器應將流量分派到哪個伺服器(虛擬機器或 IP 地址)。Kubernetes API 未定義如何為 Kubernetes 管理的負載均衡器實現健康檢查,而是由雲提供商(以及實現整合程式碼的人員)決定其行為。負載均衡器健康檢查在支援 Service 的 `externalTrafficPolicy` 欄位的上下文中被廣泛使用。

混合協議型別的負載均衡器

特性狀態: Kubernetes v1.26 [stable] (預設啟用:true)

預設情況下,對於 LoadBalancer 型別的 Service,當定義了多個埠時,所有埠都必須使用相同的協議,並且該協議必須是雲提供商支援的協議。

特性門控 `MixedProtocolLBService`(從 v1.24 開始預設啟用 kube-apiserver)允許 LoadBalancer 型別的 Service 在定義了多個埠時使用不同的協議。

停用負載均衡器 NodePort 分配

特性狀態: Kubernetes v1.24 [stable]

您可以選擇性地為 type: LoadBalancer 的 Service 停用節點埠分配,方法是將欄位 spec.allocateLoadBalancerNodePorts 設定為 false。這僅應在負載均衡器實現直接將流量路由到 Pod 而非使用節點埠的情況下使用。預設情況下,spec.allocateLoadBalancerNodePortstrue,型別為 LoadBalancer 的 Service 將繼續分配節點埠。如果將現有 Service 的 spec.allocateLoadBalancerNodePorts 設定為 false,並且該 Service 已分配節點埠,則這些節點埠將不會自動取消分配。您必須在每個 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"。未加字首的名稱保留給終端使用者。

負載均衡器 IP 地址模式

特性狀態: Kubernetes v1.32 [stable] (預設啟用: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

型別:ExternalName

ExternalName 型別的 Service 將 Service 對映到 DNS 名稱,而不是典型的選擇器(如 my-servicecassandra)。您使用 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

當查詢主機 my-service.prod.svc.cluster.local 時,叢集 DNS Service 返回一個 CNAME 記錄,其值為 my.database.example.com。訪問 my-service 的方式與其他 Service 相同,但關鍵區別在於重定向發生在 DNS 級別,而不是透過代理或轉發。如果您以後決定將資料庫移入叢集,則可以啟動其 Pod、新增適當的選擇器或端點,並更改 Service 的 type

無頭 Service

有時您不需要負載均衡和單個 Service IP。在這種情況下,您可以透過顯式將叢集 IP 地址(.spec.clusterIP)指定為 "None" 來建立所謂的無頭 Service

您可以使用無頭 Service 與其他服務發現機制進行介面,而無需與 Kubernetes 的實現繫結。

對於無頭 Service,不會分配叢集 IP,kube-proxy 不處理這些 Service,並且平臺不會對其進行負載均衡或代理。

無頭 Service 允許客戶端直接連線到其首選的任何 Pod。無頭 Service 不使用虛擬 IP 地址和代理配置路由和資料包轉發;相反,無頭 Service 透過內部 DNS 記錄報告單個 Pod 的端點 IP 地址,這些記錄透過叢集的DNS 服務提供。要定義無頭 Service,您需要建立一個 .spec.type 設定為 ClusterIP(這也是 type 的預設值)的 Service,並且您還需要將 .spec.clusterIP 設定為 None。

字串值 None 是一種特殊情況,它與將 .spec.clusterIP 欄位留空不同。

DNS 的自動配置方式取決於 Service 是否定義了選擇器:

帶選擇器

對於定義了選擇器的無頭 Service,端點控制器在 Kubernetes API 中建立 EndpointSlice,並修改 DNS 配置以返回直接指向 Service 後端 Pod 的 A 或 AAAA 記錄(IPv4 或 IPv6 地址)。

不帶選擇器

對於不定義選擇器的無頭 Service,控制平面不建立 EndpointSlice 物件。但是,DNS 系統會查詢並配置:

  • 用於type: ExternalName Service 的 DNS CNAME 記錄。
  • 對於除 ExternalName 之外的所有 Service 型別,DNS A / AAAA 記錄用於 Service 就緒端點的所有 IP 地址。
    • 對於 IPv4 端點,DNS 系統建立 A 記錄。
    • 對於 IPv6 端點,DNS 系統建立 AAAA 記錄。

當您定義沒有選擇器的無頭 Service 時,port 必須與 targetPort 匹配。

服務發現

對於在叢集內部執行的客戶端,Kubernetes 支援兩種主要的服務發現模式:環境變數和 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

Kubernetes 還支援並提供與 Docker Engine 的“傳統容器連結”功能相容的變數。您可以閱讀makeLinkVariables以瞭解 Kubernetes 中如何實現此功能。

DNS

您可以使用附加元件為您的 Kubernetes 叢集設定 DNS 服務(幾乎總是應該這樣做)。

一個叢集感知的 DNS 伺服器(例如 CoreDNS)會監視 Kubernetes API 以獲取新的 Service,併為每個 Service 建立一組 DNS 記錄。如果整個叢集都啟用了 DNS,那麼所有 Pod 都應該能夠自動透過其 DNS 名稱解析 Service。

例如,如果您在 Kubernetes 名稱空間 my-ns 中有一個名為 my-service 的 Service,則控制平面和 DNS Service 協同工作,為 my-service.my-ns 建立一個 DNS 記錄。my-ns 名稱空間中的 Pod 應該能夠透過查詢 my-servicemy-service.my-ns 也可以)來找到該服務。

其他名稱空間中的 Pod 必須將名稱限定為 my-service.my-ns。這些名稱將解析到為 Service 分配的叢集 IP。

Kubernetes 還支援命名埠的 DNS SRV(Service)記錄。如果 my-service.my-ns Service 有一個名為 http 的埠,協議設定為 TCP,您可以透過 DNS SRV 查詢 _http._tcp.my-service.my-ns 來發現 http 的埠號以及 IP 地址。

Kubernetes DNS 伺服器是訪問 ExternalName Service 的唯一方式。您可以在Service 和 Pod 的 DNS中找到有關 ExternalName 解析的更多資訊。

虛擬 IP 地址機制

閱讀虛擬 IP 和 Service 代理解釋了 Kubernetes 為使用虛擬 IP 地址暴露 Service 而提供的機制。

流量策略

您可以設定 .spec.internalTrafficPolicy.spec.externalTrafficPolicy 欄位來控制 Kubernetes 如何將流量路由到健康的(“就緒”)後端。

有關更多詳細資訊,請參見流量策略

流量分發

特性狀態: Kubernetes v1.33 [stable] (預設啟用:true)

.spec.trafficDistribution 欄位提供了另一種影響 Kubernetes Service 內流量路由的方式。流量策略側重於嚴格的語義保證,而流量分發允許您表達偏好(例如路由到拓撲上更近的端點)。這有助於最佳化效能、成本或可靠性。在 Kubernetes 1.34 中,支援以下欄位值:

PreferClose
表示優先將流量路由到與客戶端位於同一區域的端點。
特性狀態: Kubernetes v1.34 [beta] (預設啟用:true)

在 Kubernetes 1.34 中,還提供了兩個附加值(除非 PreferSameTrafficDistribution 特性門控被停用):

PreferSameZone
這是 PreferClose 的別名,能更清楚地表達預期的語義。
PreferSameNode
表示優先將流量路由到與客戶端位於同一節點的端點。

如果未設定此欄位,則實現將應用其預設路由策略。

有關更多詳細資訊,請參見流量分發

會話粘滯性

如果您想確保來自特定客戶端的連線每次都傳遞到同一個 Pod,您可以根據客戶端的 IP 地址配置會話親和性。閱讀會話親和性以瞭解更多資訊。

外部 IP

如果有外部 IP 路由到一個或多個叢集節點,Kubernetes Service 可以透過這些 externalIPs 公開。當網路流量帶著外部 IP(作為目標 IP)和與該 Service 匹配的埠到達叢集時,Kubernetes 配置的規則和路由將確保流量被路由到該 Service 的一個端點。

當您定義 Service 時,可以為任何服務型別指定 externalIPs。在下面的示例中,名為 "my-service" 的 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

API 物件

Service 是 Kubernetes REST API 中的頂級資源。您可以在Service API 物件中找到更多詳細資訊。

下一步

瞭解有關 Service 及其如何融入 Kubernetes 的更多資訊

  • 遵循使用 Service 連線應用程式教程。
  • 閱讀Ingress,它將 HTTP 和 HTTPS 路由從叢集外部暴露給叢集內部的 Service。
  • 閱讀Gateway,它是 Kubernetes 的一個擴充套件,比 Ingress 提供更高的靈活性。

要獲取更多背景資訊,請閱讀以下內容:

上次修改於 2025 年 6 月 5 日上午 11:01 PST:KEP-3015: PreferSameTrafficDistribution to Beta (7527373d5a)