前言
在前兩篇 Kubernetes Service 系列文章中,我們已經認識了 ClusterIP、NodePort 和 LoadBalancer 這三種常見的 Service 類型。今天要來聊的是兩種比較特殊、但在實務上同樣重要的 Service 類型:Headless Service 與 ExternalName Service。這兩者分別在有狀態應用部署和外部服務整合上扮演著不可或缺的角色。

Kubernetes 四種 Service 類型總覽(圖片來源:ByteByteGo)
Headless Service(無頭服務)
什麼是 Headless Service?
在某些場景下,我們並不需要 Kubernetes 替我們做負載均衡,也不需要一個固定的 Service ClusterIP。這時候就可以透過將 spec.clusterIP 設定為 "None" 來建立所謂的 Headless Service。
建立之後,這個 Service 不會被分配 ClusterIP,kube-proxy 也不會處理它,平台不會對它做負載均衡或路由。取而代之的是,當你對 Headless Service 做 DNS 查詢時,會直接回傳後端所有 Pod 的 IP 位址,讓 Client 端自行決定要連到哪一個 Pod。

Headless Service 架構示意:DNS 直接解析到 Pod IP
Headless Service vs 一般 Service 差異比較
| 比較項目 | 一般 Service(ClusterIP) | Headless Service |
|---|---|---|
| 是否分配 ClusterIP | 是 | 否(clusterIP: None) |
| DNS 解析結果 | 回傳 Service 的 ClusterIP | 回傳所有後端 Pod 的 IP |
| 負載均衡 | 由 kube-proxy 處理 | 無,由 Client 自行選擇 |
| Pod DNS 紀錄 | 無個別 Pod DNS | 搭配 StatefulSet 時,每個 Pod 有獨立 DNS |
Headless Service 的使用場景
主要有兩個常見情境:
- Client 自主選擇後端:某些應用希望自己決定要連接哪個 Pod,而不是讓 Service 幫你做 Round-Robin。透過 DNS 查詢取得所有 Pod IP 後,Client 可以實作自己的選擇邏輯。
- Pod 之間需要互相訪問:Headless Service 搭配 StatefulSet 時,每個 Pod 都會有對應的 DNS 域名,Pod 之間可以透過 DNS 名稱直接互相溝通,這在分散式資料庫(如 MySQL 主從架構、Cassandra 叢集)中非常實用。
為什麼 Headless Service 經常搭配 StatefulSet?
三個關鍵原因:
- Headless Service 會為關聯的 Pod 分配一個域:
<service-name>.<namespace>.svc.cluster.local - StatefulSet 會為每個 Pod 保持一個固定且有序的名稱:
<statefulset-name>-<序號>(例如 mysql-0、mysql-1) - 結合起來,每個 Pod 都會有一個穩定的 DNS 名稱:
<pod-name>.<service-name>.<namespace>.svc.cluster.local
這使得有狀態應用可以透過固定的 DNS 名稱進行節點發現和通訊,即使 Pod 重啟、IP 改變,DNS 名稱依然不變。
實戰範例:建立 Headless Service 並驗證 DNS
首先建立 Headless Service 的 YAML:
apiVersion: v1
kind: Service
metadata:
name: demoapp-headless-svc
spec:
clusterIP: None # 關鍵設定:必須為 None
selector:
app: demoapp
ports:
- port: 80
targetPort: 80
name: http
接著進入叢集中的 Pod 進行 DNS 解析測試:
# 對 Headless Service 做 A 記錄查詢,直接回傳所有 Pod IP
$ nslookup -query=A demoapp-headless-svc
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: demoapp-headless-svc.default.svc.cluster.local
Address: 10.244.1.103
Name: demoapp-headless-svc.default.svc.cluster.local
Address: 10.244.2.99
Name: demoapp-headless-svc.default.svc.cluster.local
Address: 10.244.2.97
Name: demoapp-headless-svc.default.svc.cluster.local
Address: 10.244.1.102
可以看到 DNS 直接回傳了四個 Pod 的 IP,而不是一個 ClusterIP。透過 curl 測試可以發現,每次請求會輪流連到不同的 Pod(由 DNS 輪詢決定,而非 kube-proxy 負載均衡):
# 多次 curl,每次可能連到不同的 Pod
$ curl demoapp-headless-svc
iKubernetes demoapp v1.0 !! ServerName: demoapp-xxxxx-5dp5n, ServerIP: 10.244.1.102!
$ curl demoapp-headless-svc
iKubernetes demoapp v1.0 !! ServerName: demoapp-xxxxx-z682r, ServerIP: 10.244.2.99!
注意:Headless Service 只能在叢集內部透過 DNS 訪問,宿主機上因為無法解析 Service DNS 名稱,所以無法直接存取。
ExternalName Service
什麼是 ExternalName Service?
ExternalName 是 Kubernetes Service 的第四種類型,它的運作方式與其他類型截然不同。它不需要設定 selector 來選擇後端 Pod,而是利用 DNS CNAME 機制將 Service 名稱指向一個外部域名。
簡單來說,ExternalName Service 就像是叢集內部的一個 DNS 別名(Alias),當你在 Pod 裡面存取這個 Service 時,CoreDNS 會回傳一筆 CNAME 記錄,指向你設定的外部域名。
ExternalName 的運作原理
| 特性 | 說明 |
|---|---|
| Service Type | ExternalName |
| 是否需要 Selector | 不需要 |
| DNS 機制 | 回傳 CNAME 記錄,指向 externalName 欄位的值 |
| 是否分配 ClusterIP | 否 |
| 使用場景 | 將外部服務映射為叢集內部可存取的 Service 名稱 |
為什麼需要 ExternalName?
CNAME 機制讓你可以在不同域名之間建立橋樑。舉個例子,假設你的應用原本連接的是叢集內部的資料庫,之後決定遷移到雲端託管的 RDS 服務。使用 ExternalName Service,你只需要修改 Service 的 externalName 欄位,應用程式碼完全不需要改動。
這就像是一層抽象——應用只認識 Service 名稱,至於背後指向叢集內部還是外部服務,可以隨時切換,對使用者完全透明。
實戰範例:將外部網站映射為叢集內 Service
建立 ExternalName Service 的 YAML:
apiVersion: v1
kind: Service
metadata:
name: externalname-http-svc
namespace: default
spec:
type: ExternalName
externalName: www.baidu.com
ports:
- protocol: TCP
port: 80
targetPort: 80
建立後查看 Service 狀態,可以看到 EXTERNAL-IP 顯示的是外部域名:
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
externalname-http-svc ExternalName <none> www.baidu.com 80/TCP 87m
接著在 Pod 裡面進行 DNS 查詢驗證:
$ nslookup -query=A externalname-http-svc
Server: 10.96.0.10
Address: 10.96.0.10#53
externalname-http-svc.default.svc.cluster.local canonical name = www.baidu.com.
www.baidu.com canonical name = www.a.shifen.com.
Name: www.a.shifen.com
Address: 180.101.49.11
Name: www.a.shifen.com
Address: 180.101.49.12
DNS 查詢會先得到 CNAME 指向 www.baidu.com,然後再解析到實際的 IP 位址。在 Pod 裡面可以直接透過 Service 名稱存取外部服務:
$ curl -H "host:www.baidu.com" externalname-http-svc.default.svc.cluster.local
# 成功回傳百度首頁 HTML 內容
總結
| Service 類型 | ClusterIP | DNS 回傳 | 典型場景 |
|---|---|---|---|
| Headless Service | None | 所有 Pod IP | 有狀態應用(StatefulSet)、Client 端自主負載均衡 |
| ExternalName | 無 | CNAME 到外部域名 | 整合外部服務、服務遷移抽象層 |
Headless Service 讓我們在需要精確控制連線目標時,繞過 kube-proxy 的負載均衡機制,直接與 Pod 溝通;而 ExternalName Service 則提供了一個優雅的方式,將外部服務以叢集內 Service 的形式呈現,實現服務位置的透明切換。
在實際的生產環境中,這兩種特殊類型的 Service 是建構穩健微服務架構不可或缺的工具,值得好好理解和運用。

發佈留言