Kubernetes Service 使用指南
什麼是 Kubernetes Service?
Kubernetes Service 是一個穩定的網路抽象層,用來將流量導向一組符合條件的 Pod。由於 Pod 會隨著部署、擴展、重啟或節點異動而重新建立,Pod IP 並不適合作為固定存取入口;Service 則提供固定的服務名稱、虛擬 IP 與連線方式,讓應用程式可以穩定地彼此溝通。
Service 通常會透過 selector 找到帶有指定 label 的 Pod,並把流量轉送到 Pod 的 targetPort。對使用者或其他服務來說,只需要存取 Service,就不需要關心後方 Pod 的實際位置。
Service 的主要功能:
- 穩定入口:即使 Pod 被重新建立,Service 名稱與 IP 仍能維持穩定
- 服務發現:透過 Kubernetes DNS 使用 Service 名稱存取服務
- 負載分散:將請求分配到多個符合 selector 的 Pod
- 對外暴露:依照不同 Service Type,提供叢集內或叢集外的存取方式
為什麼需要 Service?
在 Kubernetes 中,Deployment 負責管理 Pod 的生命週期,但 Deployment 本身不提供固定的網路入口。當應用程式需要被其他 Pod、節點外部使用者,或雲端負載平衡器存取時,就需要透過 Service 建立對應的流量入口。
Service 的基本運作方式
以下是一個常見的 Service 設定:
1 | apiVersion: v1 |
重要欄位說明
| 欄位 | 說明 |
|---|---|
type |
Service 的類型,決定服務如何被存取 |
selector |
用來選取後端 Pod 的 label 條件 |
port |
Service 對外提供的連接埠 |
targetPort |
Pod 容器實際接收流量的連接埠 |
nodePort |
NodePort 類型使用的節點連接埠 |
在這個範例中,Service 會尋找 label 為 app: dockertest 的 Pod,並將送到 Service 8080 port 的流量轉送到 Pod 的 8080 port。
Service Type 類型介紹
Kubernetes Service 主要可以分成 ClusterIP、NodePort、LoadBalancer 與 ExternalName 四種類型。此外,Headless Service 雖然不是獨立的 type,但也是很常見的服務發現模式。
ClusterIP
ClusterIP 是預設的 Service Type。它會在叢集內建立一個虛擬 IP,只能被叢集內部的 Pod 或節點存取,適合用於服務之間的內部通訊。
適用情境:
- 後端 API 只需要被叢集內其他服務呼叫
- 資料庫、快取或內部微服務不需要直接暴露到叢集外
- 搭配 Ingress 作為 HTTP/HTTPS 的後端服務
NodePort
NodePort 會在每一個 Kubernetes Node 上開啟一個固定連接埠,外部使用者可以透過 NodeIP:NodePort 存取服務。Kubernetes 預設的 NodePort 範圍通常是 30000-32767。
適用情境:
- 本機測試或開發環境需要快速對外存取服務
- 沒有雲端 Load Balancer,但仍希望透過節點 IP 暴露服務
- 作為 LoadBalancer 或 Ingress Controller 的底層入口
LoadBalancer
LoadBalancer 會向雲端供應商申請外部負載平衡器,並將外部流量導向 Service。在本機環境中,通常需要搭配 MetalLB 或 Minikube tunnel 才能取得外部 IP。
適用情境:
- 需要將服務直接暴露給叢集外部使用者
- 使用雲端 Kubernetes 服務,例如 GKE、EKS、AKS
- 需要由雲端平台管理外部 IP 與負載平衡器
ExternalName
ExternalName 不會建立 ClusterIP,也不會使用 selector 選取 Pod。它會透過 DNS CNAME 的方式,將 Service 名稱對應到另一個外部 DNS 名稱。
適用情境:
- 讓叢集內服務用 Kubernetes Service 名稱存取外部系統
- 將外部資料庫、第三方 API 或既有服務包裝成叢集內 DNS 名稱
- 在不建立代理或端點的情況下統一服務名稱
Headless Service
Headless Service 是將 clusterIP 設定為 None 的 Service。它不會建立虛擬 IP,而是讓 DNS 直接回傳後端 Pod IP,常用於需要直接發現 Pod 的應用程式。
適用情境:
- StatefulSet 需要穩定的 Pod DNS 名稱
- 應用程式需要自行處理連線與負載分散
- 需要直接取得後端 Pod IP 清單
Service Type 比較
| 類型 | 是否有 ClusterIP | 是否可從叢集外存取 | 常見用途 |
|---|---|---|---|
ClusterIP |
是 | 否 | 叢集內部服務通訊 |
NodePort |
是 | 是,透過 NodeIP:NodePort |
本機測試、簡易外部入口 |
LoadBalancer |
是 | 是,透過外部 IP | 雲端或 MetalLB 對外服務 |
ExternalName |
否 | 視外部 DNS 而定 | 將 Service 名稱對應到外部服務 |
Headless Service |
否 | 否 | Pod DNS 發現、StatefulSet |
測試用 Deployment
以下範例使用 Spring Boot 應用程式作為測試基礎。為了避免和目前叢集內其他環境混合,所有測試資源都會放在獨立的 dockertest namespace 中。應用程式會提供 /hello API,測試時預期回應內容為 Hello, World!。
建立測試 Namespace 與 Registry Secret
先建立 dockertest namespace,讓 Deployment、Secret 與 Service 都集中在同一個範圍內管理。
1 | # 建立專案使用的命名空間,讓相關資源集中在 dockertest 底下管理。 |
1 | # 建立獨立測試 namespace |
由於測試映像放在 GitLab Container Registry 私有倉庫,還需要建立 image pull secret。實務上不建議把 registry token 直接寫進文章或提交到版本庫,請在本機或 CI 環境中用下列方式建立 Secret:
1 | kubectl create secret docker-registry gitlab-registry-secret \ |
如果需要使用 YAML 管理 Secret,可保留 Secret 結構,但請將 .dockerconfigjson 替換成實際環境產生的 base64 內容,不要把可用憑證寫入公開或共用筆記。
1 | # 儲存拉取 GitLab Container Registry 私有映像所需的認證資訊。 |
Spring Boot Deployment
1 | # 部署 Spring Boot 應用程式,並設定容器映像、環境變數與健康檢查。 |
部署 Spring Boot 應用程式
1 | # 建立 Deployment YAML |
確認 Pod 進入 Running 且 READY 顯示為 1/1 後,就可以開始建立不同類型的 Service。
本機 API 端點測試
在建立 Service 前,可以先透過 kubectl port-forward 驗證應用程式本身是否正常回應。這個測試會將本機 8080 port 轉發到 Deployment 後方 Pod 的 8080 port。
1 | # 將本機 8080 port 轉發到 Spring Boot 應用程式 |
開啟另一個終端機執行測試:
1 | curl http://localhost:8080/hello |
預期會取得下列回應:
1 | Hello, World! |
ClusterIP 測試案例
ClusterIP 適合測試叢集內部服務通訊。建立後,可以從另一個臨時 Pod 使用 Service 名稱存取 Spring Boot 應用程式。
ClusterIP Service
1 | apiVersion: v1 |
部署與測試
1 | # 建立 ClusterIP Service |
如果連線成功,會看到 Hello, World! 回應內容。
NodePort 測試案例
NodePort 可以讓叢集外部透過節點 IP 和指定連接埠存取應用程式。這個方式很適合在 Minikube、Kind 或測試環境中快速驗證服務。
NodePort Service
1 | apiVersion: v1 |
部署與測試
1 | # 建立 NodePort Service |
如果是在一般 Kubernetes 節點,可以使用節點 IP 測試:
1 | curl http://<NODE_IP>:30080/hello |
如果是在 Minikube,可以使用下列指令取得服務 URL:
1 | minikube service dockertest-nodeport -n dockertest --url |
接著使用輸出的 URL 測試 /hello API:
1 | curl <MINIKUBE_SERVICE_URL>/hello |
LoadBalancer 測試案例
LoadBalancer 適合將服務暴露給叢集外部。若使用雲端 Kubernetes,建立 Service 後通常會自動分配外部 IP;若使用 Minikube 或裸機環境,可能需要先啟用 minikube tunnel 或安裝 MetalLB。
LoadBalancer Service
1 | apiVersion: v1 |
部署與測試
1 | # 建立 LoadBalancer Service |
如果 EXTERNAL-IP 已經取得 IP,就可以直接測試:
1 | curl http://<EXTERNAL_IP>:8080/hello |
在 Minikube 環境中,可以使用下列方式測試:
1 | # 方法一:直接開啟 Service URL |
注意事項
如果
EXTERNAL-IP長時間顯示<pending>,通常代表目前環境沒有可用的 Load Balancer 實作。雲端環境需要確認雲端控制器是否正常;本機環境則需要使用 Minikube tunnel 或 MetalLB。
ExternalName 測試案例
ExternalName 不是用來直接暴露 Pod 的 Service Type,因為它不會透過 selector 連到 Pod。為了示範它的用途,可以先建立 dockertest-clusterip,再建立一個 ExternalName Service 作為 DNS 別名,讓叢集內部透過另一個服務名稱存取同一個應用程式。
ExternalName Service
1 | apiVersion: v1 |
部署與測試
1 | # 確認 ClusterIP Service 已存在 |
這個測試會透過 DNS CNAME 將 dockertest-externalname 對應到 dockertest-clusterip.dockertest.svc.cluster.local,再由原本的 ClusterIP Service 導向應用程式 Pod。
Headless Service 測試案例
Headless Service 不會建立 ClusterIP,而是直接透過 DNS 回傳後端 Pod IP。雖然這個範例只有一個應用程式 Pod,但仍可以用來觀察 Headless Service 的 DNS 與 Endpoints 行為。
Headless Service
1 | apiVersion: v1 |
部署與測試
1 | # 建立 Headless Service |
若要更明確觀察 DNS 解析結果,可以使用 nslookup:
1 | kubectl run dns-test -n dockertest --rm -it --image=busybox:1.36 --restart=Never -- nslookup dockertest-headless |
常用檢查指令
建立 Service 後,可以使用下列指令確認 Service 是否正確連到應用程式 Pod。
1 | # 查看所有 Service |
如果 Service 沒有正常轉送流量,優先檢查 selector 是否與 Pod label 一致:
1 | # 查看 Pod label |
清除測試資源
測試完成後,可以使用下列指令刪除文章中建立的資源。
1 | kubectl delete namespace dockertest |
總結
Kubernetes Service 是連接 Pod 與流量來源的重要抽象層。ClusterIP 適合叢集內部通訊,NodePort 適合快速從節點外部測試,LoadBalancer 適合正式對外提供服務,ExternalName 則適合將外部 DNS 包裝成 Kubernetes 內的服務名稱。至於 Headless Service,雖然不是獨立的 Service Type,但在需要直接發現 Pod IP 或搭配 StatefulSet 時非常實用。
在實務上,選擇哪一種 Service Type 取決於服務的存取範圍。如果只需要內部服務通訊,使用 ClusterIP 即可;如果需要對外開放,則可以依環境選擇 NodePort、LoadBalancer 或搭配 Ingress 進行更完整的 HTTP/HTTPS 路由管理。