設定 NodeLocal DNSCache

本文說明如何使用 NodeLocal DNSCache 縮短 DNS 查詢延遲時間,並提升 Google Kubernetes Engine (GKE) 叢集中應用程式的效能。

NodeLocal DNSCache 是 GKE 外掛程式,可直接在每個叢集節點上執行 DNS 快取 (以 DaemonSet 形式),藉此提升 DNS 效能。當 Pod 發出 DNS 要求時,要求會先傳送到同一節點上的本機快取。在本地處理要求可大幅縮短平均 DNS 查詢時間,並降低叢集中央 DNS 供應商 (例如 kube-dns 或 GKE 適用的 Cloud DNS) 的負載。 如要詳細瞭解 DNS 架構和 NodeLocal DNSCache 的優點,請參閱「關於服務探索」。

在 GKE Autopilot 叢集中,NodeLocal DNSCache 預設為啟用,且無法停用。在執行 1.33.1 以上版本的 GKE Standard 叢集中,NodeLocal DNSCache 預設為啟用,但您可以停用。

本文適用於 GKE 使用者,包括開發人員、管理員和架構師。如要進一步瞭解 Google Cloud中的常見角色和範例工作,請參閱「常見的 GKE Enterprise 使用者角色和工作」。

本文假設您已詳讀以下文章:

架構

NodeLocal DNSCache 是 GKE 外掛程式,可與 kube-dns 一併執行。

GKE 會將 NodeLocal DNSCache 實作為 DaemonSet,在叢集的每個節點上執行 DNS 快取。

當 Pod 發出 DNS 要求時,要求會傳送至與 Pod 位於同一節點的 DNS 快取。如果快取無法解析 DNS 要求,快取會根據查詢目的地,將要求轉送至下列其中一個位置:

  • kube-dns:叢集 DNS 網域 (cluster.local) 的所有查詢都會轉送至 kube-dns。node-local-dns Pod 會使用 kube-dns-upstream Service 存取 kube-dns Pod。
  • 自訂存根網域或上游名稱伺服器:查詢會直接從 NodeLocal DNSCache Pod 轉送。
  • Cloud DNS:所有其他查詢都會轉送至本機中繼資料伺服器,該伺服器與查詢來源節點位於同一位置。本機中繼資料伺服器會存取 Cloud DNS。

DNS 要求的路徑,如上一段所述。

在現有叢集上啟用 NodeLocal DNSCache 時,GKE 會根據節點升級程序,重新建立執行 GKE 1.15 以上版本的所有叢集節點。

GKE 重新建立節點後,會自動將 addon.gke.io/node-local-dns-ds-ready=true 標籤新增至節點。您不得手動將這個標籤新增至叢集節點。

NodeLocal DNSCache 的優點

NodeLocal DNSCache 具有下列優點:

  • 縮短平均 DNS 查詢時間
  • 從 Pod 連線至本機快取時,不會建立 conntrack 表格項目。這種行為可防止因連線追蹤資料表耗盡和競爭狀況而導致連線遭捨棄和拒絕。
  • 您可以搭配 Cloud DNS for GKE 使用 NodeLocal DNSCache。
  • 外部網址 (未參照叢集資源的網址) 的 DNS 查詢會直接轉送至本機中繼資料伺服器,並略過 kube-dns
  • 本機 DNS 快取會自動擷取「為存根網域新增自訂解析器」一節中指定的存根網域和上游名稱伺服器。

需求條件和限制

  • NodeLocal DNSCache 會耗用叢集每個節點的運算資源。
  • NodeLocal DNSCache 不支援 Windows Server 節點集區。
  • NodeLocal DNSCache 必須搭配 GKE 1.15 以上版本。
  • NodeLocal DNSCache 會使用 TCP 存取 kube-dns Pod。
  • 在 GKE 1.18 以上版本中,NodeLocal DNSCache 會使用 TCP 和 UDP 存取 upstreamServersstubDomains。DNS 伺服器必須可透過 TCP 和 UDP 連線。
  • DNS 記錄的快取時間如下:
    • 記錄的存留時間 (TTL),或 30 秒 (如果 TTL 超過 30 秒)。
    • 如果 DNS 回應為 NXDOMAIN,則為 5 秒。
  • NodeLocal DNSCache Pod 會監聽節點上的通訊埠 53、9253、9353 和 8080。如果您執行任何其他 hostNetwork Pod 或設定使用這些連接埠的 hostPorts,NodeLocal DNSCache 就會失敗,並發生 DNS 錯誤。使用 GKE Dataplane V2 且不使用 Cloud DNS for GKE 時,NodeLocal DNSCache Pod 不會使用 hostNetwork 模式。
  • 本機 DNS 快取只會在執行 GKE 1.15 以上版本的節點集區上執行。如果您在含有執行舊版節點的叢集中啟用 NodeLocal DNSCache,這些節點上的 Pod 會使用 kube-dns

事前準備

開始之前,請確認您已完成下列工作:

  • 啟用 Google Kubernetes Engine API。
  • 啟用 Google Kubernetes Engine API
  • 如要使用 Google Cloud CLI 執行這項工作,請安裝初始化 gcloud CLI。如果您先前已安裝 gcloud CLI,請執行 gcloud components update 指令,取得最新版本。較舊的 gcloud CLI 版本可能不支援執行本文中的指令。
  • 請確認您有現有的 Autopilot 或 Standard 叢集。如需叢集,請建立 Autopilot 叢集。如為 Autopilot 叢集,NodeLocal DNSCache 預設為啟用,且無法覆寫。

啟用 NodeLocal DNSCache

如果是 Standard 叢集,您可以使用 Google Cloud CLI 或 Google Cloud 控制台啟用 NodeLocal DNSCache。

gcloud

如要在現有叢集中啟用 NodeLocal DNSCache,請使用 --update-addons 旗標和 NodeLocalDNS=ENABLED 引數:

gcloud container clusters update CLUSTER_NAME \
    --location=COMPUTE_LOCATION \
    --update-addons=NodeLocalDNS=ENABLED

更改下列內容:

控制台

如要在新叢集上啟用 NodeLocal DNSCache,請按照下列步驟操作:

  1. 前往 Google Cloud 控制台的「Google Kubernetes Engine」頁面。

    前往 Kubernetes 叢集

  2. 按一下您要修改的叢集名稱。

  3. 在「Networking」(網路) 下方的「DNS provider」(DNS 供應商) 欄位中,按一下「Edit DNS provider」(編輯 DNS 供應商)

  4. 勾選「Enable NodeLocal DNSCache」核取方塊

  5. 按一下 [儲存變更]。

這項變更需要重新建立節點,可能會導致執行中的工作負載中斷。如要瞭解這項特定變更的詳細資料,請在「手動變更,使用節點升級策略並遵守維護政策,重新建立節點」表格中找到對應的資料列。如要進一步瞭解節點更新,請參閱「規劃節點更新中斷」。

確認 NodeLocal DNSCache 是否已啟用

您可以列出 node-local-dns Pod,確認 NodeLocal DNSCache 是否正在執行:

kubectl get pods -n kube-system -o wide | grep node-local-dns

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

node-local-dns-869mt    1/1   Running   0   6m24s   10.128.0.35   gke-test-pool-69efb6b8-5d7m   <none>   <none>
node-local-dns-htx4w    1/1   Running   0   6m24s   10.128.0.36   gke-test-pool-69efb6b8-wssk   <none>   <none>
node-local-dns-v5njk    1/1   Running   0   6m24s   10.128.0.33   gke-test-pool-69efb6b8-bhz3   <none>   <none>

輸出內容會顯示每個節點的 node-local-dns Pod,這些節點執行的是 GKE 1.15 以上版本。

停用 NodeLocal DNSCache

您可以使用下列指令停用 NodeLocal DNSCache:

gcloud container clusters update CLUSTER_NAME \
    --location=COMPUTE_LOCATION \
    --update-addons=NodeLocalDNS=DISABLED

更改下列內容:

這項變更需要重新建立節點,可能會導致執行中的工作負載中斷。如要瞭解這項特定變更的詳細資料,請在「手動變更,使用節點升級策略並遵守維護政策,重新建立節點」表格中找到對應的資料列。如要進一步瞭解節點更新,請參閱「規劃節點更新中斷」。

排解 NodeLocal DNSCache 問題

如需診斷 Kubernetes DNS 問題的一般資訊,請參閱「偵錯 DNS 解析」。

NodeLocal DNSCache 不會立即啟用

在現有叢集上啟用 NodeLocal DNSCache 時,如果叢集已設定維護期間或排除時段,GKE 可能不會立即更新節點。詳情請參閱「節點重新建立和維護期間的注意事項」。

如果不想等待,可以呼叫 gcloud container clusters upgrade 指令並傳遞 --cluster-version 旗標,將節點手動變更為節點集區目前執行的 GKE 版本。如要使用此解決方法,必須使用 Google Cloud CLI。

搭配 Cloud DNS 使用 NodeLocal DNSCache

如果您搭配使用 NodeLocal DNSCache 與 Cloud DNS,叢集會使用名稱伺服器 IP 位址 169.254.20.10,如下圖所示:

採用 Cloud DNS 架構的 NodeLocal DNSCache。

因此,kube-dns 服務的 IP 位址可能與 Pod 使用的名稱伺服器 IP 位址不同。這是正常現象,因為 Cloud DNS 必須使用 169.254.20.10 名稱伺服器 IP 位址,才能正常運作。

如要檢查 IP 位址,請執行下列指令:

  1. 查看 kube-dns Service 的 IP 位址:

    kubectl get svc -n kube-system kube-dns -o jsonpath="{.spec.clusterIP}"
    

    輸出是 kube-dns 的 IP 位址,例如 10.0.0.10

  2. 在 Pod 中開啟殼層工作階段:

    kubectl exec -it POD_NAME -- /bin/bash
    
  3. 在 Pod Shell 工作階段中,讀取 /etc/resolv.conf 檔案的內容:

    cat /etc/resolv.conf
    

    輸出內容為 169.254.20.10

搭配 NodeLocal DNSCache 使用的網路政策

如果您使用 NodeLocal DNSCache 搭配網路政策,且未使用 Cloud DNSGKE Dataplane V2,則必須設定規則,允許工作負載和 node-local-dns Pod 傳送 DNS 查詢。

在資訊清單中使用 ipBlock 規則,允許 Pod 與 kube-dns 之間的通訊。

下列資訊清單說明使用 ipBlock 規則的網路政策:

spec:
  egress:
  - ports:
    - port: 53
      protocol: TCP
    - port: 53
      protocol: UDP
    to:
    - ipBlock:
        cidr: KUBE_DNS_SVC_CLUSTER_IP/32
  podSelector: {}
  policyTypes:
    - Egress

KUBE_DNS_SVC_CLUSTER_IP 替換為 kube-dns 服務的 IP 位址。您可以使用下列指令取得 kube-dns 服務的 IP 位址:

kubectl get svc -n kube-system kube-dns -o jsonpath="{.spec.clusterIP}"

已知問題

本節列出 NodeLocal DNSCache 的已知問題。

使用 NodeLocal DNSCache 和 GKE Dataplane V2 時,ClusterFirstWithHostNet dnsPolicy 中的 DNS 超時

在採用 GKE Dataplane V2 和 NodeLocal DNSCache 的叢集上,hostNetwork 欄位設為 truednsPolicy 欄位設為 ClusterFirstWithHostNet 的 Pod 無法連線至叢集 DNS 後端。DNS 記錄可能包含類似下列內容的項目:

dnslookup: write to 'a.b.c.d': Operation not permitted

;; connection timed out; no servers could be reached

輸出內容表示 DNS 要求無法連上後端伺服器。

解決方法是為 hostNetwork Pod 設定 dnsPolicydnsConfig 欄位:

spec:
 dnsPolicy: "None"
 dnsConfig:
   nameservers:
     - KUBE_DNS_UPSTREAM
   searches:
     - NAMESPACE.svc.cluster.local
     - svc.cluster.local
     - cluster.local
     - c.PROJECT_ID.internal
     - google.internal
   options:
     - name: ndots
       value: "5"

更改下列內容:

  • NAMESPACEhostNetwork Pod 的命名空間。
  • PROJECT_ID: Google Cloud 專案的 ID。
  • KUBE_DNS_UPSTREAM:上游 ClusterIP 服務的 ClusterIPkube-dns您可以使用下列指令取得這個值:

    kubectl get svc -n kube-system kube-dns-upstream -o jsonpath="{.spec.clusterIP}"
    

Pod 的 DNS 要求現在可以連上 kube-dns,並略過 NodeLocal DNSCache。

NodeLocal DNSCache 逾時錯誤

在啟用 NodeLocal DNSCache 的叢集上,記錄可能包含類似下列的項目:

[ERROR] plugin/errors: 2 <hostname> A: read tcp <node IP: port>-><kubedns IP>:53: i/o timeout

輸出內容包含 kube-dns-upstream Cluster IP Service 的 IP 位址。在這個範例中,系統在兩秒內未收到來自 kube-dns 的 DNS 要求回應。這個問題可能是下列原因所致:

  • 網路連線問題。
  • 工作負載或節點集區擴充作業大幅增加 DNS 查詢。

因此現有的 kube-dns Pod 無法及時處理所有要求。解決方法是調度資源,增加 kube-dns 副本數量。

擴充 kube-dns

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

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

如果是標準叢集,您可以編輯 kube-dns-autoscaler ConfigMap,修改 kube-dns 副本數量。Autopilot 叢集不支援這項設定。

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))

如果您在 ConfigMap 中定義 minmax 欄位,副本會受限於這些值。如要調高規模,請將 nodesPerReplica 欄位的值改為較小的值,並加入 max 欄位的值:

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

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

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

如果 kube-dns-autoscaler ConfigMap 定義了 min 欄位,輸出內容會類似於下列內容:

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

後續步驟