设置 NodeLocal DNSCache

本页面介绍如何在 Google Kubernetes Engine (GKE) 集群上配置 NodeLocal DNSCache。

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

通过在集群中每个节点上运行 DNS 缓存,NodeLocal DNSCache 可以缩短 DNS 查找的延迟时间、使 DNS 查找时间更加一致,以及减少发送到 kube-dns 的 DNS 查询次数。

如需简要了解服务发现和托管式 DNS 在 GKE 上的工作原理,请参阅服务发现和 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 服务访问 kube-dns Pod。
  • Cloud DNS:所有其他查询均会转发到与发起查询的 Pod 所在节点上运行的本地元数据服务器。本地元数据服务器会访问 Cloud DNS。

DNS 请求的路径图(如前一段所述)

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 来访问 upstreamServers。这意味着上游服务器必须支持使用 TCP 和 UDP 进行 DNS 解析。对于使用 kube-dns ConfigMap 提供的自定义配置,GKE 1.17.5-gke.6 和 1.18.3-gke.0 及更高版本中提供了 UDP。最佳做法是使用可通过 TCP 和 UDP 访问的 DNS 服务器。
  • 系统会在以下时间段缓存 DNS 记录:
    • 记录的存留时间 (TTL) 或 30 秒(后者适用于 TTL 超过 30 秒的情况)。
    • 5 秒(如果 DNS 响应为 NXDOMAIN)。
  • NodeLocal DNSCache Pod 会侦听节点上的端口 53、9253、9353 和 8080。如果您运行任何其他 hostNetwork Pod 或使用这些端口配置 hostPorts,则 NodeLocal DNSCache 会失败并发生 DNS 错误。使用 GKE Dataplane V2 时,NodeLocal DNSCache Pod 不会使用 hostNetwork 模式。
  • 如果您的集群已启用 PodSecurityPolicy,则集群必须是以下某个版本才能使用 NodeLocal DNSCache:
    • 1.15.12-gke.9 及更高版本
    • 1.16.11-gke.5 及更高版本
    • 1.17.7-gke.15 及更高版本
  • 本地 DNS 缓存仅在运行 GKE 1.15 及更高版本的节点池上运行。如果您在具有运行早期版本的节点的集群中启用 NodeLocal DNSCache,则这些节点上的 Pod 会使用 kube-dns。

启用 NodeLocal DNSCache

您可以使用 gcloud 命令行工具在新集群或现有集群上启用 NodeLocal DNSCache。您可以使用 Google Cloud Console 在新集群中启用 NodeLocal DNSCache。

gcloud

在新集群中启用 NodeLocal DNSCache

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

gcloud container clusters create CLUSTER_NAME \
    --region=COMPUTE_REGION \
    --cluster-version=CLUSTER_VERSION \
    --addons=NodeLocalDNS

替换以下内容:

  • CLUSTER_NAME:新集群的名称。
  • COMPUTE_REGION:集群的 Compute Engine 区域
  • CLUSTER_VERSION:集群的版本(1.15 或更高版本)。

在现有集群中启用 NodeLocal DNSCache

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

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

替换以下内容:

  • CLUSTER_NAME:您的集群的名称。

控制台

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

  1. 转到 Cloud Console 中的 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 与 Cloud DNS 或 GKE Dataplane V2 搭配使用

如果您将 NodeLocal DNSCache 与 Cloud DNSGKE Dataplane V2 搭配使用,则集群将使用域名服务器 IP 地址 169.254.20.10

您可以使用以下命令查看集群的 IP 地址:

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

输出内容类似如下:

169.254.20.10

使用 NodeLocal DNSCache 的网络政策

如果您将网络政策与 NodeLocal DNSCache 搭配使用,并且您没有使用 Cloud DNSGKE Dataplane V2,则您必须配置规则以允许 node-local-dns Pod 发送和接收 DNS 查询。

在您的清单中使用 ipBlock 规则以允许在 node-local-dns Pod 和 kube-dns 之间进行通信。

以下清单说明了一项使用 ipBlock 规则的网络政策:

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

KUBE_DNS_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 请求的响应。这可能是由下列某种原因造成的:

  • 底层网络连接问题。
  • 处理 TCP 连接的 dnsmasq 存在已知问题。

node-local-dns Pod 使用 TCP 来访问 kube-dns,以实现更高的可靠性。处理来自多个来源 IP 的连接时,dnsmasq 会优先处理现有连接,而不是新连接。因此,在 DNS 每秒查询次数 (QPS) 较高的集群上,新创建的节点上的 node-local-dns Pod 的 DNS 延迟时间可能会较长。启用了集群自动扩缩器的集群也可能会发生这种情况,因为集群自动扩缩器会动态地更改节点数量。

此问题已在以下 GKE 版本中得到解决:

  • 1.19.7-gke.1500 及更高版本
  • 1.18.16-gke.1200 及更高版本
  • 1.17.17-gke.5400 及更高版本

解决方法是通过调整自动扩缩参数来增加 kube-dns 副本的数量。

纵向扩容 kube-dns

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

您可以将 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 副本数的计算公式如下:

副本 = 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 值)。

后续步骤