GKE 的 kube-dns 簡介

如果您在標準叢集中執行應用程式,kube-dns 是預設的 DNS 供應商,可協助您啟用服務探索和通訊功能。本文說明如何使用 kube-dns 管理 DNS,包括架構、設定,以及在 GKE 環境中最佳化 DNS 解析的最佳做法。

本文適用於負責管理 GKE 中 DNS 的開發人員、管理員和架構師。如要瞭解 Google Cloud的常見角色和工作,請參閱「常見的 GKE 使用者角色和工作」。

開始之前,請務必先熟悉 Kubernetes 服務和一般 DNS 概念

瞭解 kube-dns 架構

kube-dns 會在 GKE 叢集內運作,以便在 Pod 和服務之間解析 DNS。

下圖顯示 Pod 如何與 kube-dns Service 互動:

圖 1:此圖顯示 Pod 如何將 DNS 查詢傳送至 `kube-dns` 服務,而該服務是由 `kube-dns` Pod 支援。`kube-dns` Pod 會處理內部 DNS 解析,並將外部查詢轉送至上游 DNS 伺服器。

重要元件

kube-dns 包含下列重要元件:

  • kube-dns Pod:這些 Pod 會執行 kube-dns 伺服器軟體。這些 Pod 的多個副本會在 kube-system 命名空間中執行,提供高可用性和備援功能。
  • kube-dns 服務:下表比較 kube-dns 的舊版和以 CoreDNS 為基礎的版本,在可擴充性和設定限制方面的差異:
    功能 舊版 (kube-dns 1.35 和更早版本) CoreDNS 上的 kube-dns (1.36 以上版本)
    端點認知度 每個服務最多可感知 1,000 個端點。如果服務的 Pod 超過 1,000 個,kube-dns 就無法識別額外的端點。 瞭解所有端點。這個版本使用 EndpointSlice,確保大型服務的正確性並提升效率。
    上游名稱伺服器 最多 3 個 最多可支援 15 個
    並行輸出 TCP 連線 上限為 200 個 最多支援 1,500 個字元
  • kube-dns-autoscaler這個 Pod 會根據叢集大小 (包括節點和 CPU 核心數量),調整副本數量。kube-dns這種做法可確保 kube-dns 能處理不同的 DNS 查詢負載。

內部 DNS 解析

當 Pod 需要解析叢集網域內的 DNS 名稱 (例如 myservice.my-namespace.svc.cluster.local) 時,會發生下列程序:

  1. Pod DNS 設定:每個節點上的 kubelet 會設定 Pod 的 /etc/resolv.conf 檔案。這個檔案會使用 kube-dns 服務的 ClusterIP 做為名稱伺服器。
  2. DNS 查詢:Pod 會將 DNS 查詢傳送至 kube-dns 服務。
  3. 名稱解析:

    • GKE 1.36 以上版本:以 CoreDNS 為基礎的實作項目會使用 EndpointSlices,讓 kube-dns 瞭解服務中的所有 Pod。這有助於提高大規模服務的正確性和效率。
    • GKE 1.35 版或更早版本: kube-dns會根據舊版 Cloud Endpoints API 解析名稱,但最多只能有 1,000 個端點。如果 Service 有超過 1,000 個支援 Pod,kube-dns 就會忽略額外的端點。
  4. 通訊:Pod 接著會使用已解析的 IP 位址,與目標 Service 通訊。

外部 DNS 解析

當 Pod 需要解析外部 DNS 名稱或叢集網域外的名稱時,kube-dns 會做為遞迴解析器。並將查詢轉送至 ConfigMap 檔案中設定的上游 DNS 伺服器。您也可以為特定網域設定自訂解析器,這類網域也稱為存根網域。這項設定會指示 kube-dns 將這些網域的要求轉送至特定上游 DNS 伺服器。

設定 Pod DNS

在 GKE 中,每個節點上的 kubelet 代理程式會為該節點上執行的 Pod 設定 DNS 設定。

設定 /etc/resolv.conf 檔案

GKE 建立 Pod 時,kubelet 代理程式會修改 Pod 的 /etc/resolv.conf 檔案。這個檔案會設定用於名稱解析的 DNS 伺服器,並指定搜尋網域。根據預設,kubelet 會將 Pod 的名稱伺服器設為叢集的內部 DNS 服務 kube-dns。並在檔案中填入搜尋網域。您可以在 DNS 查詢中使用不合格的名稱,舉例來說,如果 Pod 查詢 myservice,Kubernetes 會先嘗試解析 myservice.default.svc.cluster.local,然後解析 myservice.svc.cluster.local,接著解析 search 清單中的其他網域。

以下範例顯示預設的 /etc/resolv.conf 設定:

nameserver 10.0.0.10
search default.svc.cluster.local svc.cluster.local cluster.local c.my-project-id.internal google.internal
options ndots:5

這個檔案包含下列項目:

  • nameserver:定義 kube-dns 服務的 ClusterIP
  • search:定義在 DNS 查詢期間附加至不合格名稱的搜尋網域。
  • options ndots:5:設定 GKE 判斷名稱是否為完整名稱的門檻。如果名稱包含五個以上的點,即視為完整名稱。

使用 hostNetwork: true 設定的 Pod 會從主機繼承 DNS 設定,且不會直接查詢 kube-dns,除非使用 ClusterFirstWithHostNet dnsPolicy

自訂kube-dns

kube-dns 提供強大的預設 DNS 解析功能。您可以根據特定需求調整其行為,例如提高解析效率或使用偏好的 DNS 解析器。如要設定存根網域和上游名稱伺服器,請修改 kube-system 命名空間中的 kube-dns ConfigMap。

修改 kube-dns ConfigMap

如要修改 kube-dns ConfigMap,請按照下列步驟操作:

  1. 開啟 ConfigMap 進行編輯:

    kubectl edit configmap kube-dns -n kube-system
    
  2. data 部分中,新增 stubDomainsupstreamNameservers 欄位,如下所示:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      labels:
        addonmanager.kubernetes.io/mode: EnsureExists
      name: kube-dns
      namespace: kube-system
    data:
      stubDomains: |
        {
          "example.com": [
            "8.8.8.8",
            "8.8.4.4"
          ],
          "internal": [ # Required if your upstream nameservers can't resolve GKE internal domains
            "169.254.169.254" # IP of the metadata server
          ]
        }
      upstreamNameservers: |
        [
          "8.8.8.8", # Google Public DNS
          "8.8.4.4" # Google Public DNS Backup
        ]
    
  3. 儲存 ConfigMap。kube-dns 會自動重新載入設定。

Stub 網域

透過存根網域,您可以為特定網域定義自訂 DNS 解析器。當 Pod 查詢該存根網域中的名稱時,kube-dns 會將查詢轉送至指定的解析器,而不是使用預設的解析機制。

kube-dns ConfigMap 中加入 stubDomains 區段。

本節會指定網域和對應的上游名稱伺服器。 kube-dns 接著會將該網域內名稱的查詢轉送至指定伺服器。舉例來說,您可以將 internal.mycompany.com 的所有 DNS 查詢路徑設定為 192.168.0.10,並將 "internal.mycompany.com": ["192.168.0.10"] 新增至 stubDomains

為 Stub 網域 (例如 example.com) 設定自訂解析器時,kube-dns 會將該網域的所有名稱解析要求 (包括 *.example.com 等子網域) 轉送至指定伺服器。

上游名稱伺服器

您可以設定 kube-dns 使用自訂上游名稱伺服器,解析外部網域名稱。這項設定會指示 kube-dns 將所有 DNS 要求 (叢集內部網域 (*.cluster.local) 的要求除外) 轉送至指定的上游伺服器。自訂上游伺服器可能無法解析 metadata.internal*.google.internal 等內部網域。如果您啟用 Workload Identity Federation for GKE,或有工作負載依附於這些網域,請在 ConfigMap 中為 internal 新增虛設常式網域。使用中繼資料伺服器的 IP 位址 169.254.169.254,做為這個存根網域的解析程式。

管理自訂 kube-dns 部署作業

在標準叢集中,kube-dns 會以 Deployment 形式執行。自訂部署作業是指叢集管理員可以控管部署作業,並根據需求自訂部署作業,而不是使用 GKE 提供的預設部署作業。kube-dns

自訂部署作業的原因

基於下列原因,請考慮自訂 kube-dns 部署作業:

  • 資源分配:針對 kube-dns Pod 微調 CPU 和記憶體資源,在 DNS 流量高的叢集中最佳化效能。
  • 映像檔版本:使用特定版本的 kube-dns 映像檔,或改用 CoreDNS 等替代 DNS 供應商。
  • 進階設定:自訂記錄層級、安全性政策和 DNS 快取行為。

自訂部署項目的自動調度資源

內建 kube-dns-autoscaler 適用於預設的 kube-dns Deployment。 如果您建立自訂 kube-dns Deployment,內建的自動配置器不會管理該 Deployment。因此,您必須設定專門用於監控及調整自訂 Deployment 副本數量的獨立自動配置器。這種做法是在叢集中建立及部署自己的自動調度資源設定。

管理自訂 Deployment 時,您必須負責所有元件,例如確保自動調整比例的映像檔為最新版本。使用過時的元件可能會導致效能降低或 DNS 失敗。

如需如何設定及管理自有部署作業的詳細操作說明,請參閱「設定自訂 kube-dns 部署作業」。kube-dns

疑難排解

如要瞭解如何排解 kube-dns 問題,請參閱下列頁面:

最佳化 DNS 解析

本節說明在 GKE 中管理 DNS 的常見問題和最佳做法。

Pod 的 dnsConfig 搜尋網域數量上限

Kubernetes 將 DNS 搜尋網域數量限制為 32 個。如果您嘗試在 Pod 的 dnsConfig 中定義超過 32 個搜尋網域,kube-apiserver 就不會建立 Pod,並顯示類似下列內容的錯誤訊息:

The Pod "dns-example" is invalid: spec.dnsConfig.searches: Invalid value: []string{"ns1.svc.cluster-domain.example", "my.dns.search.suffix1", "ns2.svc.cluster-domain.example", "my.dns.search.suffix2", "ns3.svc.cluster-domain.example", "my.dns.search.suffix3", "ns4.svc.cluster-domain.example", "my.dns.search.suffix4", "ns5.svc.cluster-domain.example", "my.dns.search.suffix5", "ns6.svc.cluster-domain.example", "my.dns.search.suffix6", "ns7.svc.cluster-domain.example", "my.dns.search.suffix7", "ns8.svc.cluster-domain.example", "my.dns.search.suffix8", "ns9.svc.cluster-domain.example", "my.dns.search.suffix9", "ns10.svc.cluster-domain.example", "my.dns.search.suffix10", "ns11.svc.cluster-domain.example", "my.dns.search.suffix11", "ns12.svc.cluster-domain.example", "my.dns.search.suffix12", "ns13.svc.cluster-domain.example", "my.dns.search.suffix13", "ns14.svc.cluster-domain.example", "my.dns.search.suffix14", "ns15.svc.cluster-domain.example", "my.dns.search.suffix15", "ns16.svc.cluster-domain.example", "my.dns.search.suffix16", "my.dns.search.suffix17"}: must not have more than 32 search paths.

kube-apiserver 會在嘗試建立 Pod 時傳回這則錯誤訊息。如要解決這個問題,請從設定中移除額外的搜尋路徑。

kube-dns的上游nameservers限制

舊版 kube-dns (1.35 版和更早版本) 的數量上限為三個。upstreamNameservers如果定義超過三個,Cloud Logging 會顯示類似下列內容的錯誤:

Invalid configuration: upstreamNameserver cannot have more than three entries (value was &TypeMeta{Kind:,APIVersion:,}), ignoring update

在這種情況下,kube-dns 會忽略 upstreamNameservers 設定,並繼續使用先前的有效設定。如要解決這個問題,請從 kube-dns ConfigMap 中移除多餘的 upstreamNameservers

擴充 kube-dns

在標準叢集中,您可以將 nodesPerReplica 的值調低,以便在叢集節點擴充時建立更多 kube-dns Pod。強烈建議您為 max 欄位設定明確值,確保大量 kube-dns Pod 監控 Kubernetes API 時,不會造成 GKE 控制層虛擬機器 (VM) 負載過重。

您可以將 max 欄位的值設為叢集中的節點數。如果叢集有超過 500 個節點,請將 max 欄位的值設為 500

如要修改 kube-dns 副本數量,請編輯 kube-dns-autoscaler ConfigMap

kubectl edit configmap kube-dns-autoscaler --namespace=kube-system

輸出結果會與下列內容相似:

linear: '{"coresPerReplica":256, "nodesPerReplica":16,"preventSinglePointFailure":true}'

kube-dns 副本數量的計算公式如下:

replicas = max( ceil( cores * 1/coresPerReplica ) , ceil( nodes * 1/nodesPerReplica ) )

如要擴大範圍,請將 nodesPerReplica 欄位的值改為較小的值,並加入 max 欄位的值。

linear: '{"coresPerReplica":256, "nodesPerReplica":8,"max": 15,"preventSinglePointFailure":true}'

這項設定會為叢集中的每八個節點建立一個 kube-dns Pod。24 節點叢集有三個副本,40 節點叢集則有五個副本。如果叢集成長超過 120 個節點,kube-dns 副本數不會超過 15,也就是 max 欄位的值。

為確保叢集中的 DNS 達到基本可用性,請為 kube-dns 欄位設定最少副本數。

設定 min 欄位後,kube-dns-autoscaler ConfigMap 的輸出內容會與下列內容類似:

linear: '{"coresPerReplica":256, "nodesPerReplica":8,"max": 15,"min": 5,"preventSinglePointFailure":true}'

縮短 DNS 查詢時間

使用預設 kube-dns 供應商時,DNS 查詢或 DNS 解析失敗可能會導致延遲時間過長。應用程式可能會將這些問題視為 getaddrinfo EAI_AGAIN 錯誤,表示名稱解析暫時失敗。可能原因如下:

  • 工作負載內頻繁的 DNS 查詢。
  • 每個節點的 Pod 密度過高。
  • 在 Spot VM 或先占 VM 上執行 kube-dns,這可能會導致節點遭到意外刪除。
  • 連線限制:舊版 kube-dns (GKE 1.35 版和更早版本) 最多只能有 200 個並行 TCP 連線。在 CoreDNS (GKE 1.36 以上版本) 上啟用 kube-dns,即可移除這些連入連線的固定限制,並大幅提升連出連線的容量。

如要縮短 DNS 查詢時間,請採取下列做法:

  • 請避免在 Spot VM 或先占 VM 上執行 kube-dns 等重要系統元件。建立至少一個具有標準 VM,且沒有 Spot VM 或先占 VM 的節點集區。使用汙點和容許度,確保重要工作負載排程在這些可靠節點上。
  • 啟用 NodeLocal DNSCache。NodeLocal DNSCache 會直接在每個節點上快取 DNS 回應,藉此縮短延遲時間,並降低 kube-dns 服務的負載。如果啟用 NodeLocal DNSCache,並使用含有預設拒絕規則的網路政策,請新增政策,允許工作負載將 DNS 查詢傳送至 node-local-dns Pod。
  • 擴充 kube-dns
  • 請確保應用程式使用以 dns.resolve* 為基礎的函式,而非以 dns.lookup 為基礎的函式,因為 dns.lookup 是同步的。
  • 請使用完整網域名稱 (FQDN),例如 https://google.com./,而非 https://google.com/

由於控制層元件 (包括 kube-dns) 會同步升級,因此 GKE 叢集升級期間可能會發生 DNS 解析失敗的情況。這類故障通常只會影響一小部分的節點。在非正式環境中徹底測試叢集升級,確認無誤後再套用至正式叢集。

確保服務可探索性

kube-dns 只會為有 Endpoints 的服務建立 DNS 記錄。如果服務沒有任何端點,kube-dns 就不會為該服務建立 DNS 記錄。

管理 DNS TTL 差異

如果 kube-dns 從上游 DNS 解析器收到 DNS 回應,且該回應的存留時間很長或無限期,kube-dns 會保留這個存留時間值。這類行為可能會導致快取項目與實際 IP 位址不一致。

GKE 會在特定控制層版本中解決這個問題,例如 1.21.14-gke.9100 以上版本,或 1.22.15-gke.2100 以上版本。這些版本會將存留時間較高的 DNS 回應存留時間上限值設為 30 秒。這項行為與 NodeLocal DNSCache 類似。

查看 kube-dns 指標

您可以直接從 kube-dns Pod 擷取 DNS 查詢的指標。擷取這些指標的方式取決於 GKE 版本。

GKE 1.36 以上版本

如果叢集執行的是 GKE 1.36 以上版本 (CoreDNS 上的 kube-dns),您可以透過 Cloud Monitoring 中的預先定義資訊主頁監控 DNS 效能,或從 Pod 手動擷取指標。

在 Google Cloud 控制台中查看指標

  1. 前往 Google Cloud 控制台的「Dashboards」(資訊主頁) 頁面。
  2. 選取「GKE DNS Observability - Cluster View」(GKE DNS 可觀測性 - 叢集檢視) 資訊主頁。

或者,您也可以直接在 Google Cloud 控制台中查詢這些指標,方法是依序前往「Monitoring」>「Metrics explorer」,然後搜尋特定指標名稱。

手動擷取指標

如要手動從 Pod 擷取指標,請按照下列步驟操作:

  1. 找到 kube-dns Pod。

    kubectl get pods -n kube-system --selector=k8s-app=kube-dns
    
  2. 將通訊埠 9153 轉送至其中一個 Pod。

    kubectl port-forward pod/POD_NAME -n kube-system 9153:9153
    

    POD_NAME 替換為上一個輸出內容中的其中一個 kube-dns Pod 名稱。

  3. 存取指標。

    curl http://127.0.0.1:9153/metrics
    

GKE 1.35 以下版本

這個版本的 kube-dns 使用多容器 Pod。如要擷取指標,請按照下列步驟操作:

  1. kube-system 命名空間中找出 kube-dns Pod。

    kubectl get pods -n kube-system --selector=k8s-app=kube-dns
    
  2. 將通訊埠轉送至通訊埠 10055 (適用於 kube-dns 容器) 和 10054 (適用於 dnsmasq 容器):

    #For the kube-dns container
    kubectl port-forward pod/POD_NAME -n kube-system 10055:10055
    #For the dnsmasq container
    kubectl port-forward pod/POD_NAME -n kube-system 10054:10054
    

    POD_NAME 替換為上一個輸出內容中的其中一個 kube-dns Pod 名稱。在不同的終端機工作階段中執行這些連接埠轉送指令。

  3. 存取指標。

    #Metrics from the kube-dns container
    curl http://127.0.0.1:10055/metrics
    
    #Metrics from the dnsmasq container
    curl http://127.0.0.1:10054/metrics
    

後續步驟