k8s中的service解析
在k8s中,我們可以通過pod來創建服務。
然而,當我們創建多個 Pod 來提供同一項服務時,直接通過 Pod IP 進行訪問會變得復雜且不可維護。因此,Kubernetes 提供了 Service 這一抽象概念,用于對外暴露和管理 Pod 組,使得外部可以通過 Service 訪問應用,而無需記住和維護多個 Pod 的 IP。
在 Kubernetes 中,Service 是一種邏輯抽象,專門用于定義一組 Pod 的訪問策略,例如 負載均衡 和 服務發現。它本身并不涉及 Pod 的調度,也不會占用任何節點資源。Service 的 IP 是 集群內部的虛擬 IP,由 Kubernetes 控制平面自動分配和管理。
值得注意的是,Service 不運行任何代碼,它只是一個規則集合,指示集群如何將流量正確路由到后端 Pod。
在創建 Service 時,可以通過 selector 字段指定一組標簽(例如 app: my-app)。Kubernetes 會自動關聯所有匹配這些標簽的 Pod,并將其納入該 Service 的管理范圍。
Kubernetes 內部的 Endpoints Controller 會持續監控 Pod 的狀態,并動態更新 Service 的后端目標。當某個 Pod 被調度到節點上(由調度器決定)且其標簽匹配 Service 的 selector 時,該 Pod 的 IP 和端口會自動加入到 Service 的 Endpoints 列表中。
此外,每個節點上的 kube-proxy 組件會監聽 Service 和 Endpoints 的變化,并相應地更新本地的 iptables 或 IPVS 規則。當請求到達 ClusterIP(即 Service 的虛擬 IP)時,流量會根據這些規則被分發到實際運行的 Pod,確保服務能夠正確訪問。
為什么要用到service
在 Kubernetes 中,Service 是一種核心資源對象,專門用于將一組 Pod 上運行的應用程序公開為網絡服務的抽象方式。
當我們使用 Deployment、DaemonSet 或 StatefulSet 等控制器部署 Pod 時,可以為這些 Pod 創建一個 Service。Service 負責監控這些控制器管理的 Pod,無論是新增還是移除 Pod,Service 都會自動更新其后端目標,并提供穩定的網絡訪問入口。此外,Service 通過 Label Selector 機制,動態管理與其匹配的 Pod,確保流量能夠正確轉發。
舉個例子,假設我們有一組 Web 服務 Pod,如果由于 自動擴縮容 或 Pod 重新調度,導致 Pod 的 IP 地址或端口發生變化,客戶端將很難直接跟蹤和訪問這些 Web 服務。又比如,Web 服務和 MySQL 數據庫分別部署在不同的 Pod 中,Web 應用如何準確查找并連接 MySQL 的 IP 地址?
Service 可以完美解決這個問題。 Kubernetes Service 定義了一種通常稱為 微服務 的抽象,提供了一種穩定的訪問方式,無論 Pod 規模如何變化,Service 都能確保訪問路徑的一致性。當 Web 服務訪問后端數據庫時,不需要關心 MySQL Pod 具體運行在哪個節點,也無需跟蹤其 IP 地址的變化,因為 Service 通過抽象封裝了 Pod 之間的關聯,解耦了服務的依賴關系,提高了可維護性和可靠性。
Service 的定義和創建
-
首先,我們需要創建一個 Deployment 對象,該 Deployment 將管理 3 個 nginx Pod 實例:
kubectl create deployment nginx --image=nginx:latest --replicas=3
-
接著,我們為這些 Pod 創建一個 Service,用于對外提供穩定的訪問入口:
kubectl expose deployment nginx --type=ClusterIP --port=6666 --target-port=80
需要注意的是,Pod 和 Deployment 并不是一一對應的關系。Deployment 會監控所有標簽(Labels)與其選擇器(selector)中定義的標簽相匹配的 Pod。
-
可以使用以下命令查看 Service 的詳細信息:
kubectl get service -o wide
示例輸出:
root@master:~# kubectl get service -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) nginx ClusterIP 10.107.200.232 <none> 6666/TCP
從上面的信息可以看到,Kubernetes 為 Service 隨機分配了一個集群內部的 IP 地址(如 10.107.200.232),并將 Service 的 6666 端口 映射到 Pod 的 80 端口。
-
測試 Service
我們可以使用 curl 命令測試該 Service 是否可用:curl 10.107.200.232:6666
注意:
- 該 IP 地址僅在 Kubernetes 集群內部 可用,因此只能在集群內部的機器上進行訪問。
- Service 會自動代理所有符合條件的 Pod,并動態感知 Pod 的變化,無論 Pod 的數量如何增減,Service 都能確保流量正確轉發。
-
通過 YAML 定義 Service
除了使用 kubectl expose 命令,我們還可以使用 YAML 文件來定義 Service。以下是一個 Service 的 YAML 配置示例:apiVersion: v1 kind: Service metadata: name: my-service spec: selector:app: MyApp ports:- protocol: TCPport: 6666targetPort: 80 type: ClusterIP
在此 YAML 配置中:
- selector 字段定義了 Service 需要管理的 Pod(即 app=MyApp 的 Pod)。
- port 定義了 Service 對外暴露的端口(6666)。
- targetPort 指定了 Pod 內部實際監聽的端口(80)。
- type 設為 ClusterIP,表示 Service 僅在集群內部可訪問。
service服務提供
clusterIP類型的Service 的訪問和路由機制
訪問方式
在上一部分中,我們通過 ClusterIP 創建了 Service。ClusterIP 是 Kubernetes 集群內部的虛擬 IP,僅在集群內部可見(可供 Pod、節點及 Kubernetes 組件訪問)。
無論 Pod 運行在哪個節點,集群內的任何組件(如其他 Pod、節點上的進程)都可以通過 ClusterIP:Port 訪問該 Service。這種機制使得應用無需關注 Pod 的具體調度位置,只需通過 Service 訪問,即可透明地連接到后端 Pod。
然而,ClusterIP 僅適用于集群內部通信,不會綁定到節點的公網 IP,因此外部請求無法直接訪問。
路由路徑
在使用 ClusterIP 時,集群內部的請求訪問路徑如下:
-
發起請求的客戶端(如另一個 Pod)向 ClusterIP:Port 發送請求。
-
kube-proxy 通過 ?iptables/IPVS 規則,將請求轉發到后端 Pod 的 IP 和端口。
-
負載均衡方式默認采用 輪詢(Round Robin),無論 Pod 分布在哪個節點,請求都會均勻地分發給所有可用的 Pod。
那么,我們如何將service向外部暴漏呢?
Kubernetes 提供了 ServiceType 選項,允許用戶控制 Service 的暴露方式,從而使外部流量能夠訪問集群內部的服務。不同的 ServiceType 適用于不同的外部訪問需求。
四種 Service 類型 :
-
ClusterIP(默認)
通過集群內部 IP 暴露服務,ClusterIP 是 ServiceType 的默認值。 -
NodePort
通過集群每個節點的 Node IP + 靜態端口(NodePort) 對外暴露,允許外部流量訪問。 -
LoadBalancer
通過云平臺提供的負載均衡器向外暴露服務,通常用于生產環境。 -
ExternalName
通過返回 CNAME 和對應值,可以將服務映射到 externalName 字段的內容(例如,foo.bar.example.com)。
ClusterIP、NodePort、LoadBalancer 三者是有關系的,前者是后者的基礎。創建一個 NodePort 類型的 Service,必定帶有一個 ClusterIP。
NodePort類型的Service 的訪問和路由機制
訪問方式
當使用 NodePort 類型創建 Service 時,Kubernetes 會在每個節點上分配一個靜態端口(通常在 30000-32767 范圍內),并將該端口映射到 Service。此時,外部用戶可以通過 節點 IP + NodePort 訪問該服務,而無需直接與 Pod 交互。
- 可以在本機節點通過 127.0.0.1:NodePort 訪問 Service。
- 也可以使用 任意集群節點的公網 IP + NodePort 訪問 Service,即使 Pod 運行在不同的節點上,Kubernetes 仍能確保流量正確轉發。
路由機制
-
外部請求到達 節點IP:NodePort(如 203.0.113.1:30080)。
-
kube-proxy 通過 ?iptables/IPVS 規則,將請求轉發到后端 Pod 的 IP 和端口。
-
?跨節點轉發:如果 Pod 不在當前節點,請求會通過集群網絡(如 CNI 插件)轉發到目標 Pod 所在節點。
關鍵特性
?負載均衡:無論請求到達哪個節點的 NodePort,流量會被均勻分發到所有 Pod(包括其他節點的 Pod)。
?高可用性:即使某個節點宕機,其他節點的 NodePort 仍可響應請求。
LoadBalancer類型的Service 的訪問和路由機制
訪問方式
在通過 NodePort 將服務暴露到外部時,雖然 Pod 之間已經實現了負載均衡,但節點本身并未具備負載均衡能力。此外,外部用戶需要知道節點的 公網 IP 才能訪問 Service,而節點 IP 可能較多,不利于統一管理。
為了解決這些問題,Kubernetes 提供了 LoadBalancer 類型的 Service,它依賴于 云服務商提供的負載均衡器,為 Service 綁定一個 外部 IP(公網 IP),使外部用戶能夠直接通過該 IP 訪問服務。
路由機制
請求流程如下:
-
外部用戶訪問 LoadBalancer 分配的公網 IP(如 35.233.49.12:80)。
-
負載均衡器會將流量分發至 集群節點的 NodePort(如 NodeIP:30080)。
-
kube-proxy 根據 iptables/IPVS 規則,將流量轉發至后端 Pod。
-
若目標 Pod 并不在當前節點,流量將通過集群網絡轉發到目標節點。
特點
-
自動分配公網 IP:需要云服務商(如 AWS、GCP、Azure)支持,Kubernetes 會自動申請和管理負載均衡器的外部 IP。
-
更優的負載均衡:流量先由 外部負載均衡器 進行全局均衡,然后進入集群內部的 NodePort 和 Pod 級負載均衡。
-
簡化外部訪問:外部用戶無需感知集群節點 IP,直接通過 LoadBalancer 分配的 公網 IP 訪問服務。
注意:LoadBalancer 類型需要集群運行在支持云平臺負載均衡器的環境中(如 AWS ELB、GCP Cloud Load Balancing)。在本地或開發環境中,可通過 MetalLB 或 kubectl port-forward 臨時暴露服務。
Endpoints
在 Kubernetes 中,Service 通過 Endpoints 動態綁定 后端實際的服務地址,無論這些地址是 集群內的 Pod,還是 集群外的服務。
通常,當我們為 Deployment 等資源創建 Service 時,Kubernetes 會 自動 生成對應的 Endpoints,并將匹配 selector 的 Pod 綁定到該 Service。
然而,如果 Service 未定義 selector,Kubernetes 不會自動創建 Endpoints,此時需要 手動定義 Endpoints,以便將外部服務的 IP 和端口 綁定到 Service,從而讓集群內部可以透明地訪問外部資源。
-
創建無 Selector 的 Service
# 創建一個沒有 selector 的 Service apiVersion: v1 kind: Service metadata: name: external-mysql spec:ports:- protocol: TCPport: 3306 # Service 暴露的端口targetPort: 3306 # 后端實際服務的端口(這里是 MySQL 的端口)
-
手動定義 Endpoints,指向外部 MySQL 實例
apiVersion: v1 kind: Endpoints metadata:name: external-mysql # 必須與 Service 同名 subsets:- addresses:- ip: 111.111.111.111 # 外部 MySQL 實例的 IP- ip: 222.222.222.222 # 另一個 MySQL 實例的 IPports:- port: 3306 # 必須與 Service 的 targetPort 一致
運行機制
當 kube-proxy 監聽到 Kubernetes API Server 發現 Endpoints 配置了外部 IP 時,它會自動更新本地的 iptables/IPVS 規則,以實現流量轉發。整個請求流程如下:
-
應用向 external-mysql:3306(即 Service 的 ClusterIP)發起訪問請求。
-
kube-proxy 根據 iptables/IPVS 規則,將請求均衡地分發至 111.111.111.111:3306 或 222.222.222.222:3306。
-
請求通過 集群網絡 轉發至外部 MySQL 實例,完成數據交互。
這種方式允許 Kubernetes 無縫集成外部服務,使集群內的應用能夠像訪問本地服務一樣訪問外部資源,同時提供透明的負載均衡和服務發現能力。
參考癡者工良的博客