关于 GKE 的 kube-dns

如果您在 Standard 集群中运行应用,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 服务互动:

图 1:图表显示了 Pod 如何将 DNS 查询发送到由 `kube-dns` Pod 提供支持的 `kube-dns` 服务。`kube-dns` Pod 处理内部 DNS 解析,并将外部查询转发到上行 DNS 服务器。

关键组件

kube-dns 包含以下关键组件:

  • kube-dns Pod:这些 Pod 运行 kube-dns 服务器软件。这些 Pod 的多个副本在 kube-system 命名空间中运行,可提供高可用性和冗余。
  • kube-dns 服务:下表比较了基于 CoreDNS 的 kube-dns 版本与旧版 kube-dns 的可伸缩性和配置限制:
    功能 旧版(kube-dns 1.35 及更早版本) CoreDNS 上的 kube-dns(1.36 及更高版本)
    端点感知 每个服务最多可感知 1,000 个端点。如果某项服务拥有超过 1,000 个 Pod,kube-dns 将无法识别额外的端点。 了解所有端点。此版本使用 EndpointSlices 来确保大型服务的正确性并提高效率。
    上行域名服务器 最多 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 向 kube-dns 服务发送 DNS 查询。
  3. 域名解析

    • GKE 1.36 版或更高版本:基于 CoreDNS 的实现使用 EndpointSlice,以便 kube-dns 了解服务中的所有 Pod。这有助于提高大规模服务的正确性和效率。
    • GKE 版本 1.35 或更低版本kube-dns 基于旧版 Cloud Endpoints API 解析名称,该 API 仅限 1,000 个端点。如果某个 Service 有 1,000 个以上的后端 Pod,kube-dns 就无法识别额外的端点。
  4. 通信:然后,Pod 使用解析出的 IP 地址与目标服务进行通信。

外部 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 会自动重新加载配置。

存根网域

桩网域可让您为特定网域定义自定义 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

当您为桩网域(例如 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 运行。自定义 kube-dns 部署是指,作为集群管理员,您可以控制部署并根据需要对其进行自定义,而不是使用 GKE 提供的默认部署。

自定义部署的原因

出于以下原因,请考虑自定义 kube-dns 部署:

  • 资源分配:针对 kube-dns Pod 微调 CPU 和内存资源,以优化 DNS 流量较高的集群中的性能。
  • 映像版本:使用特定版本的 kube-dns 映像,或切换到 CoreDNS 等替代 DNS 提供商。
  • 高级配置:自定义日志记录级别、安全政策和 DNS 缓存行为。

自定义Deployment的自动扩缩

内置的 kube-dns-autoscaler 可与默认的 kube-dns Deployment 搭配使用。如果您创建自定义 kube-dns 部署,内置的自动扩缩器不会管理该部署。因此,您必须设置一个单独的自动扩缩器,专门用于监控和调整自定义 Deployment 的副本数量。此方法需要在集群中创建并部署您自己的自动扩缩器配置。

当您管理自定义部署时,需要负责其所有组件,例如确保自动扩缩器映像保持最新状态。使用过时的组件可能会导致性能下降或 DNS 故障。

如需详细了解如何配置和管理自己的 kube-dns 部署,请参阅设置自定义 kube-dns Deployment

问题排查

如需了解如何排查 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 的数量限制为 3 个。如果您定义的数量超过三个,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

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

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

您可以通过修改 kube-dns-autoscaler ConfigMap 来修改 kube-dns 副本的数量。

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}'

此配置会为集群中的每 8 个节点创建一个 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}'

缩短 DNS 查找时间

有若干因素可能会导致在使用默认的 kube-dns 提供商时出现 DNS 查找延迟过高或 DNS 解析失败的问题。应用可能会遇到这些问题,并显示为 getaddrinfo EAI_AGAIN 错误,这表示名称解析暂时失败。原因包括:

  • 在工作负载中频繁执行 DNS 查找。
  • 每个节点的 Pod 密度过高。
  • 在 Spot 虚拟机或抢占式虚拟机上运行 kube-dns,这可能会导致意外的节点删除。
  • 连接限制:旧版 kube-dns(GKE 1.35 及更早版本)最多支持 200 个并发 TCP 连接。CoreDNS 上的 kube-dns(GKE 1.36 版及更高版本)移除了这些入站连接的固定限制,并为出站连接提供了明显更高的容量。

如需缩短 DNS 查找时间,请执行以下操作:

  • 避免在 Spot 虚拟机或抢占式虚拟机上运行 kube-dns 等关键系统组件。创建至少一个具有标准虚拟机且不具有 Spot 虚拟机或抢占式虚拟机的节点池。使用污点和容忍机制有助于确保关键工作负载调度到这些可靠的节点上。
  • 启用 NodeLocal DNSCache。 NodeLocal DNSCache 直接在每个节点上缓存 DNS 响应,从而缩短延迟时间并减轻 kube-dns 服务的负载。如果您启用 NodeLocal DNSCache 并使用具有默认拒绝规则的网络政策,请添加一项政策,以允许工作负载向 node-local-dns Pod 发送 DNS 查询。
  • 纵向扩容 kube-dns
  • 请确保您的应用使用基于 dns.resolve* 的函数,而非基于 dns.lookup 的函数,因为 dns.lookup 是同步的。
  • 使用完全限定域名 (FQDN),例如 https://google.com./,而不是 https://google.com/

在 GKE 集群升级期间,由于控制平面组件(包括 kube-dns)的并发升级,可能会发生 DNS 解析失败。这些故障通常只会影响一小部分节点。在将集群升级应用到生产集群之前,请先在非生产环境中全面测试这些升级。

确保服务可被发现

kube-dns 只会为具有端点的服务创建 DNS 记录。如果某个 Service 没有端点,kube-dns 就不会为该 Service 创建 DNS 记录。

管理 DNS TTL 差异

如果 kube-dns 从具有较大或无限 TTL 的上行 DNS 解析器接收 DNS 响应,则会保留此 TTL 值。此行为可能会导致缓存条目与实际 IP 地址之间出现差异。

GKE 在特定控制平面版本(例如 1.21.14-gke.9100 及更高版本或 1.22.15-gke.2100 及更高版本)中解决了此问题。这些版本针对 TTL 较高的任何 DNS 响应,将 TTL 值上限设置为 30 秒。此行为类似于 NodeLocal DNSCache。

查看kube-dns指标

您可以直接从 kube-dns Pod 中检索有关 DNS 查询的指标。 您如何检索这些指标取决于您的 GKE 版本。

GKE 1.36 版及更高版本

如果您的集群运行的是 GKE 1.36 版或更高版本(kube-dns 基于 CoreDNS),您可以使用 Cloud Monitoring 中的预定义信息中心监控 DNS 性能,也可以手动从 Pod 中检索指标。

在 Google Cloud 控制台中查看指标

  1. 在 Google Cloud 控制台中,前往信息中心页面。
  2. 选择 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
    

后续步骤