← 返回上一頁
Kubernetes

Kubernetes Service 特殊類型解析:Headless Service 與 ExternalName 實戰教學

本頁目錄
Kubernetes 四種 Service 類型架構圖 - ClusterIP NodePort LoadBalancer ExternalName

前言

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

Kubernetes 四種 Service 類型架構圖 - ClusterIP NodePort LoadBalancer ExternalName

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。

Kubernetes Headless Service DNS 解析架構示意圖

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 的使用場景

主要有兩個常見情境:

  1. Client 自主選擇後端:某些應用希望自己決定要連接哪個 Pod,而不是讓 Service 幫你做 Round-Robin。透過 DNS 查詢取得所有 Pod IP 後,Client 可以實作自己的選擇邏輯。
  2. Pod 之間需要互相訪問:Headless Service 搭配 StatefulSet 時,每個 Pod 都會有對應的 DNS 域名,Pod 之間可以透過 DNS 名稱直接互相溝通,這在分散式資料庫(如 MySQL 主從架構、Cassandra 叢集)中非常實用。

為什麼 Headless Service 經常搭配 StatefulSet?

三個關鍵原因:

  1. Headless Service 會為關聯的 Pod 分配一個域:
    <service-name>.<namespace>.svc.cluster.local
  2. StatefulSet 會為每個 Pod 保持一個固定且有序的名稱:
    <statefulset-name>-<序號>(例如 mysql-0、mysql-1)
  3. 結合起來,每個 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 是建構穩健微服務架構不可或缺的工具,值得好好理解和運用。

分享這篇
X LinkedIn Facebook Hacker News Reddit

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料