设置 NodeLocal DNSCache


本页面介绍如何使用 NodeLocal DNSCache 缩短在 Google Kubernetes Engine (GKE) 集群中执行 DNS 查找的延迟时间。

对于 GKE Autopilot 集群,NodeLocal DNSCache 默认启用且不能覆盖。

架构

NodeLocal DNSCache 是一个 GKE 插件,除了 kube-dns 之外,您还可以运行该插件。

GKE 将 NodeLocal DNSCache 作为 DaemonSet 实现,DaemonSet 会在集群中的每个节点上运行 DNS 缓存。

当 Pod 发出 DNS 请求时,该请求会转到与 Pod 相同的节点上运行的 DNS 缓存。如果该缓存无法解析该 DNS 请求,则会根据查询目标将该请求转发到以下位置之一:

  • kube-dns:集群 DNS 网域 (cluster.local) 的所有查询都将转发到 kube-dns。node-local-dns Pod 使用 kube-dns-upstream 服务访问 kube-dns Pod。在下图中,kube-dns 服务的 IP 地址为 10.0.0.10:53
  • 自定义存根网域或上行域名服务器:查询直接从 NodeLocal DNSCache Pod 转发。
  • Cloud DNS:所有其他查询均会转发到与发起查询的 Pod 所在节点上运行的本地元数据服务器。本地元数据服务器会访问 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 表条目。这样可以防止由于 conntrack 表耗尽和竞态条件而导致连接中断和连接被拒绝。
  • 您可以将 NodeLocal DNSCache 与 Cloud DNS for GKE 搭配使用。
  • 外部网址(不引用集群资源的网址)的 DNS 查询会绕过 kube-dns 而直接转发到本地 Cloud DNS 元数据服务器。
  • 本地 DNS 缓存会自动选取 kube-dns ConfigMap 中指定的存根网域和上行域名服务器。

要求和限制

  • NodeLocal DNSCache 会消耗集群的每个节点上的计算资源。
  • Windows Server 节点池不支持 NodeLocal DNSCache。
  • NodeLocal DNSCache 需要 GKE 1.15 或更高版本。
  • NodeLocal DNSCache 使用 TCP 访问 kube-dns Pod。
  • NodeLocal DNSCache 在 GKE 1.18 版或更高版本上使用 TCP 和 UDP 访问 upstreamServersstubDomains。DNS 服务器必须可通过 TCP 和 UDP 访问。
  • 系统会在以下时间段缓存 DNS 记录:
    • 记录的存留时间 (TTL) 或 30 秒(后者适用于 TTL 超过 30 秒的情况)。
    • 5 秒(如果 DNS 响应为 NXDOMAIN)。
  • NodeLocal DNSCache Pod 会侦听节点上的端口 53、9253、9353 和 8080。如果您运行任何其他 hostNetwork Pod 或使用这些端口配置 hostPorts,则 NodeLocal DNSCache 会失败并发生 DNS 错误。使用 GKE Dataplane V2Cloud DNS for GKE 时,NodeLocal DNSCache Pod 不会使用 hostNetwork 模式。
  • 本地 DNS 缓存仅在运行 GKE 1.15 及更高版本的节点池上运行。如果您在具有运行早期版本的节点的集群中启用 NodeLocal DNSCache,则这些节点上的 Pod 会使用 kube-dns。

启用 NodeLocal DNSCache

对于 Autopilot 集群,NodeLocal DNSCache 默认处于启用状态且不能覆盖。

对于 Standard 集群,您可以使用 Google Cloud CLI 在新集群或现有集群上启用 NodeLocal DNSCache。您可以使用 Google Cloud 控制台在新集群中启用 NodeLocal DNSCache。

gcloud

在新集群中启用 NodeLocal DNSCache

如需在新集群中启用 NodeLocal DNSCache,请使用带有参数 NodeLocalDNS--addons 标志:

gcloud container clusters create CLUSTER_NAME \
    --location=COMPUTE_LOCATION \
    --addons=NodeLocalDNS

替换以下内容:

在现有集群中启用 NodeLocal DNSCache

如需在现有集群中启用 NodeLocal DNSCache,请使用带有参数 NodeLocalDNS=ENABLED--update-addons 标志:

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

替换以下内容:

  • CLUSTER_NAME:您的集群的名称。

控制台

如需在新集群上启用 NodeLocal DNSCache,请按以下步骤操作:

  1. 转到 Google Cloud 控制台中的 Google Kubernetes Engine 页面。

    转到 Google Kubernetes Engine

  2. 点击“标准”旁边的配置

  3. 根据需要配置集群。

  4. 在导航窗格中,点击网络

  5. 高级网络选项部分中,选中启用 NodeLocal DNSCache 复选框。

  6. 点击创建

验证 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>

输出会为运行 GKE 1.15 或更高版本的每个节点显示一个 node-local-dns Pod。

停用 NodeLocal DNSCache

您可以使用以下命令停用 NodeLocal DNSCache:

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

替换以下内容:

  • CLUSTER_NAME:要停用的集群的名称。

排查 NodeLocal DNSCache 问题

如需了解诊断 Kubernetes DNS 问题的一般信息,请参阅调试 DNS 解析

NodeLocal DNSCache 未立即启用

在现有集群上启用 NodeLocal DNSCache 时,如果集群配置了维护期或排除选项,则 GKE 可能不会立即更新节点。 如需了解详情,请参阅节点重新创建和维护窗口的注意事项

如果您不想等待,可通过调用 gcloud container clusters upgrade 命令并使用节点池已经运行的 GKE 版本传递 --cluster-version 标志,将更改手动应用到节点。您必须将 Google Cloud CLI 用于此临时解决方法。

将 NodeLocal DNSCache 与 Cloud DNS 搭配使用

如果您将 NodeLocal DNSCache 与 Cloud DNS 搭配使用,则集群会使用域名服务器 IP 地址 169.254.20.10,如下图所示:

NodeLocal DNSCache 与 Cloud DNS 架构。

因此,kube-dns Service 的 IP 地址可能与您的 Pod 使用的域名服务器 IP 地址不同。这种 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:53

  2. 在 Pod 中打开 shell 会话:

    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 和 GKE Dataplane V2 时,ClusterFirstWithHostNet dnsPolicy 中的 DNS 超时

在使用 GKE Dataplane V2 和 NodeLocal DNSCache 的集群上,hostNetwork 设置为 truednsPolicy 设置为 ClusterFirstWithHostNet 的 Pod 无法访问集群 DNS 后端。DNS 日志可能包含类似如下内容的条目:

nslookup: 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:
     - cluster.local
     - svc.cluster.local
     - NAMESPACE.svc.cluster.local
     - c.PROJECT_ID.internal
     - google.internal
   options:
     - name: ndots
       value: "5"

替换以下内容:

  • NAMESPACEhostNetwork pod 的命名空间。
  • PROJECT_ID:您的 Google Cloud 项目的 ID。
  • KUBE_DNS_UPSTREAM:上游 kube-dns 服务的 ClusterIP 地址。您可以使用以下命令获取此值:

    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 集群 IP 服务的 IP 地址。在此示例中,未在 2 秒钟内从 kube-dns 收到对 DNS 请求的响应。这可能是由下列某种原因造成的:

  • 底层网络连接问题。
  • 来自工作负载的 DNS 查询显著增加或由于节点池扩容。

因此,现有的 kube-dns Pod 无法及时处理所有请求。解决方法是通过调整自动扩缩参数来增加 kube-dns 副本的数量。

纵向扩容 kube-dns

您可以针对 nodesPerReplica 使用较低的值,以确保在集群节点纵向扩容时创建更多的 kube-dns Pod。我们强烈建议设置明确的 max 值,以确保 GKE 控制平面虚拟机 (VM) 不会因监控 Kubernetes API 的 kube-dns Pod 数量太多而不堪重负。

您可以将 max 设置为集群中的节点数。如果集群的节点数量超过 500 个,请将 max 设置为 500。

对于 Standard 集群,您可以通过修改 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 副本数的计算公式如下:

副本 = max( ceil( cores × 1/coresPerReplica ) , ceil( nodes × 1/nodesPerReplica ), maxValue )

如需纵向扩容,请将 nodesPerReplica 更改为较小的值,并添加 max 值。

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

此配置会为集群中的每 8 个节点创建 1 个 kube-dns pod。24 个节点的集群会有 3 个副本,40 个节点集群会有 5 个副本。集群增加超过 120 个节点,kube-dns 副本数不会增加到超过 15(即 max 值)。

如需确保集群中 DNS 可用性的基准级别,请为 kube-dns 设置最小副本数。

具有 min 字段的 kube-dns-autoscaler ConfigMap 输出类似于以下内容:

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

后续步骤