排查 GKE 中的 kube-dns 问题


本页介绍了如何解决 Google Kubernetes Engine (GKE) 中的 kube-dns 问题。

确定 kube-dns 中 DNS 问题的来源

dial tcp: i/o timeoutno such hostCould not resolve host 等错误通常表示 kube-dns 无法解析查询。

如果您看到了其中某个错误,但不知道原因,请参阅以下部分,帮助您找出原因。以下各部分的排列顺序是从最有可能帮助到您的步骤开始,因此请按顺序尝试各部分。

检查 kube-dns Pod 是否正在运行

Kube-dns Pod 对集群内的名称解析至关重要。如果这些服务未运行,您可能会遇到 DNS 解析问题。

如需验证 kube-dns Pod 是否正在运行且最近未重启,请查看这些 Pod 的状态:

kubectl get pods -l k8s-app=kube-dns -n kube-system

输出类似于以下内容:

NAME                   READY          STATUS          RESTARTS       AGE
kube-dns-POD_ID_1      5/5            Running         0              16d
kube-dns-POD_ID_2      0/5            Terminating     0              16d

在此输出中,POD_ID_1POD_ID_2 代表自动附加到 kube-dns Pod 的唯一标识符。

如果输出显示您的任何 kube-dns Pod 的状态不是 Running,请按以下步骤操作:

  1. 使用管理员活动审核日志调查最近是否发生了任何更改,例如集群或节点池版本升级,或 kube-dns ConfigMap 的更改。如需详细了解审核日志,请参阅 GKE 审核日志记录信息。如果您发现了更改,请还原更改,然后再次查看 Pod 的状态。

  2. 如果您没有发现任何相关的近期更改,请调查 kube-dns Pod 运行的节点上是否出现了 OOM 错误。如果您在 Cloud Logging 日志消息中看到类似于以下内容的错误,则表示这些 Pod 遇到了 OOM 错误:

    Warning: OOMKilling Memory cgroup out of memory
    

    如需解决此错误,请参阅错误消息:“Warning: OOMKilling Memory cgroup out of memory”

  3. 如果您没有找到任何 OOM 错误消息,请重启 kube-dns 部署:

    kubectl rollout restart deployment/kube-dns --namespace=kube-system
    

    重启部署后,检查您的 kube-dns Pod 是否正在运行。

如果这些步骤不起作用,或者您的所有 kube-dns Pod 的状态均为 Running,但您仍然遇到 DNS 问题,请检查 /etc/resolv.conf 文件是否配置正确。

检查 /etc/resolv.conf 是否已正确配置

查看遇到 DNS 问题的 Pod 的 /etc/resolv.conf 文件,并确保其中包含的条目正确无误:

  1. 查看 pod 的 /etc/resolv.conf 文件:

    kubectl exec -it POD_NAME -- cat /etc/resolv.conf
    

    POD_NAME 替换为遇到 DNS 问题的 Pod 的名称。如果有多个 Pod 存在问题,请针对每个 Pod 重复执行本部分中的步骤。

    如果 Pod 二进制文件不支持 kubectl exec 命令,此命令可能会失败。如果出现这种情况,请创建一个简单的 Pod 用作测试环境。通过此过程,您可以在存在问题的 Pod 所在的命名空间中运行测试 Pod。

  2. 验证 /etc/resolv.conf 文件中的域名服务器 IP 地址是否正确:

    • 使用主机网络的 Pod 应使用节点的 /etc/resolv.conf 文件中的值。域名服务器 IP 地址应为 169.254.169.254
    • 对于不使用主机网络的 Pod,kube-dns 服务 IP 地址应与域名服务器 IP 地址相同。如需比较 IP 地址,请完成以下步骤:

      1. 获取 kube-dns 服务的 IP 地址:

        kubectl get svc kube-dns -n kube-system
        

        输出类似于以下内容:

        NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
        kube-dns   ClusterIP   192.0.2.10   <none>        53/UDP,53/TCP   64d
        
      2. 记下“集群 IP”列中的值。在此示例中,该网址为 192.0.2.10

      3. 将 kube-dns 服务 IP 地址与 /etc/resolv.conf 文件中的 IP 地址进行比较:

        # cat /etc/resolv.conf
        
        search default.svc.cluster.local svc.cluster.local cluster.local c.PROJECT_NAME google.internal
        nameserver 192.0.2.10
        options ndots:5
        

        在此示例中,这两个值匹配,因此问题不是由错误的域名服务器 IP 地址导致的。

        但是,如果 IP 地址不匹配,则表示在应用 Pod 的清单中配置了 dnsConfig 字段。

        如果 dnsConfig.nameservers 字段中的值正确无误,请检查您的 DNS 服务器,确保其正常运行。

        如果您不想使用自定义域名服务器,请移除该字段并滚动重启 pod:

        kubectl rollout restart deployment POD_NAME
        

        POD_NAME 替换为您的 Pod 名称。

  3. 验证 /etc/resolv.conf 中的 searchndots 条目。确保没有拼写错误、过时配置,并且失败的请求指向正确命名空间中的现有服务。

执行 DNS 查找

确认 /etc/resolv.conf 配置正确且 DNS 记录正确后,请使用 dig 命令行工具从报告 DNS 错误的 Pod 执行 DNS 查找:

  1. 通过在 Pod 中打开 shell 直接查询 Pod:

    kubectl exec -it POD_NAME -n NAMESPACE_NAME -- SHELL_NAME
    

    替换以下内容:

    • POD_NAME:报告 DNS 错误的 Pod 的名称。
    • NAMESPACE_NAME:Pod 所属的命名空间。
    • SHELL_NAME:您要打开的 shell 的名称。例如 sh/bin/bash

    如果您的 Pod 不允许使用 kubectl exec 命令,或者 Pod 没有 dig 二进制文件,此命令可能会失败。如果发生这种情况,请使用已安装 dig 的映像创建一个测试 Pod:

    kubectl run "test-$RANDOM" ti --restart=Never --image=thockin/dnsutils - bash
    
  2. 检查 Pod 是否可以正确解析集群的内部 DNS 服务:

    dig kubernetes
    

    由于 /etc/resolv.conf 文件指向 kube-dns 服务 IP 地址,因此当您运行此命令时,DNS 服务器就是 kube-dns 服务。

    您应该会看到包含 Kubernetes API 服务 IP 地址(通常类似于 10.96.0.1)的成功 DNS 响应。如果您看到 SERVFAIL 或没有响应,这通常表示 kube-dns Pod 无法解析内部服务名称。

  3. 检查 kube-dns 服务是否可以解析外部域名:

    dig example.com
    
  4. 如果特定 kube-dns Pod 在响应 DNS 查询时遇到问题,请检查该 Pod 是否可以解析外部域名:

     dig example.com @KUBE_DNS_POD_IP
    

    KUBE_DNS_POD_IP 替换为 kube-dns Pod 的 IP 地址。如果您不知道此 IP 地址的值,请运行以下命令:

     kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide
    

    IP 地址位于 IP 列中。

    如果命令的解析成功,您会看到 status: NOERROR 和 A 记录的详细信息,如下例所示:

     ; <<>> DiG 9.16.27 <<>> example.com
     ;; global options: +cmd
     ;; Got answer:
     ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 31256
     ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
    
     ;; OPT PSEUDOSECTION:
     ; EDNS: version: 0, flags:; udp: 512
     ;; QUESTION SECTION:
     ;example.com.                   IN      A
    
     ;; ANSWER SECTION:
     example.com.            30      IN      A       93.184.215.14
    
     ;; Query time: 6 msec
     ;; SERVER: 10.76.0.10#53(10.76.0.10)
     ;; WHEN: Tue Oct 15 16:45:26 UTC 2024
     ;; MSG SIZE  rcvd: 56
    
  5. 退出 shell:

    exit
    

如果上述任一命令失败,请对 kube-dns 部署进行滚动重启:

kubectl rollout restart deployment/kube-dns --namespace=kube-system

重启完成后,请重新尝试 dig 命令,看看它们现在能否成功执行。如果仍然失败,请继续进行数据包捕获。

捕获数据包

进行数据包捕获,以验证 kube-dns Pod 是否正在正确接收和回答 DNS 查询:

  1. 使用 SSH 连接到运行 kube-dns Pod 的节点。例如:

    1. 在 Google Cloud 控制台中,前往虚拟机实例页面。

      转到虚拟机实例

    2. 找到要连接到的节点。如果您不知道 kube-dns Pod 上节点的名称,请运行以下命令:

      kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide
      

      节点的名称列在 Node 列中。

    3. 连接列中,点击 SSH

  2. 在终端中,启动预安装的调试工具 toolbox

    toolbox
    
  3. 在根提示符下,安装 tcpdump 软件包:

    apt update -y && apt install -y tcpdump
    
  4. 使用 tcpdump 对 DNS 流量进行数据包捕获:

    tcpdump -i eth0 port 53" -w FILE_LOCATION
    

    FILE_LOCATION 替换为您要保存屏幕截图的位置的路径。

  5. 查看数据包捕获内容。检查是否存在目标 IP 地址与 kube-dns 服务 IP 地址匹配的数据包。这可确保 DNS 请求到达正确的目标以进行解析。如果未看到 DNS 流量到达正确的 Pod,则可能表示存在阻止请求的网络政策。

检查网络政策

限制性网络政策有时可能会干扰 DNS 流量。如需验证 kube-system 命名空间中是否存在网络政策,请运行以下命令:

kubectl get networkpolicy -n kube-system

如果您找到网络政策,请对其进行审核,确保该政策允许进行必要的 DNS 通信。例如,如果您有一项网络政策会阻止所有出站流量,该政策也会阻止 DNS 请求。

如果输出为 No resources found in kube-system namespace,则表示您没有任何网络政策,因此可以排除此原因。调查日志有助于您发现更多失败点。

启用临时 DNS 查询日志记录

为了帮助您找出 DNS 响应不正确等问题,请暂时启用 DNS 查询的调试日志记录

此过程会消耗大量资源,因此我们建议您在收集合适的日志示例后停用此日志记录。

调查 kube-dns Pod

使用 Cloud Logging 查看 kube-dns Pod 如何接收和解析 DNS 查询。

如需查看与 kube-dns Pod 相关的日志条目,请完成以下步骤:

  1. 在 Google Cloud 控制台中,前往 Logs Explorer 页面。

    转到日志浏览器

  2. 在查询窗格中,输入以下过滤条件以查看与 kube-dns 容器相关的事件:

    resource.type="k8s_container"
    resource.labels.namespace_name="kube-system"
    resource.labels.Pod_name:"kube-dns"
    resource.labels.cluster_name="CLUSTER_NAME"
    resource.labels.location="CLUSTER_LOCATION"
    

    替换以下内容:

    • CLUSTER_NAME:kube-dns Pod 所属集群的名称。
    • CLUSTER_LOCATION:您的集群的位置。
  3. 点击运行查询

  4. 查看输出。以下示例输出显示了您可能会看到的一种可能错误:

    {
       "timestamp": "2024-10-10T15:32:16.789Z",
       "severity": "ERROR",
       "resource": {
          "type": "k8s_container",
          "labels": {
          "namespace_name": "kube-system",
          "Pod_name": "kube-dns",
          "cluster_name": "CLUSTER_NAME",
          "location": "CLUSTER_LOCATION"
          }
       },
       "message": "Failed to resolve 'example.com': Timeout."
    },
    

    在此示例中,kube-dns 无法在合理的时间内解析 example.com。此类错误可能由多种问题导致。例如,上游服务器在 kube-dns ConfigMap 中可能配置有误,或者网络流量可能较高。

如果您未启用 Cloud Logging,请改为查看 Kubernetes 日志:

Pod=$(kubectl get Pods -n kube-system -l k8s-app=kube-dns -o name | head -n1)
kubectl logs -n kube-system $Pod -c dnsmasq
kubectl logs -n kube-system $Pod -c kubedns
kubectl logs -n kube-system $Pod -c sidecar

调查 kube-dns ConfigMap 中的近期更改

如果您在集群中突然遇到 DNS 解析失败问题,其中一个原因可能是对 kube-dns ConfigMap 所做的配置更改不正确。特别是,对存根网域和上游服务器定义的配置更改可能会导致问题。

如需检查桩网域设置的更新,请完成以下步骤:

  1. 在 Google Cloud 控制台中,前往 Logs Explorer 页面。

    转到日志浏览器

  2. 在查询窗格中,输入以下查询:

    resource.labels.cluster_name="clouddns"
    resource.type="k8s_container"
    resource.labels.namespace_name="kube-system"
    labels.k8s-pod/k8s-app="kube-dns" jsonPayload.message=~"Updated stubDomains to"
    
  3. 点击运行查询

  4. 查看输出。如果有任何更新,输出类似于以下内容:

    Updated stubDomains to map[example.com: [8.8.8.8 8.8.4.4 1.1.3.3 1.0.8.111]]
    

    如果您看到更新,请展开相应结果,详细了解相关更改。 验证所有存根网域及其相应的上游 DNS 服务器是否已正确定义。如果此处的条目不正确,可能会导致这些网域无法解析。

如需检查上游服务器是否发生了更改,请完成以下步骤:

  1. 在 Google Cloud 控制台中,前往 Logs Explorer 页面。

    转到日志浏览器

  2. 在查询窗格中,输入以下查询:

    resource.labels.cluster_name="clouddns"
    resource.type="k8s_container" resource.labels.namespace_name="kube-system"
    labels.k8s-pod/k8s-app="kube-dns" jsonPayload.message=~"Updated upstreamNameservers to"
    
  3. 点击运行查询

  4. 查看输出。如果发生了任何更改,输出将类似于以下内容:

    Updated upstreamNameservers to [8.8.8.8]
    

    展开相应结果,详细了解相关变更。验证上游 DNS 服务器列表是否准确,以及这些服务器是否可从集群访问。如果这些服务器不可用或配置错误,常规 DNS 解析可能会失败。

如果您检查了桩网域和上游服务器的更改,但没有找到任何结果,请使用以下过滤条件检查所有更改:

resource.type="k8s_cluster"
protoPayload.resourceName:"namespaces/kube-system/configmaps/kube-dns"
protoPayload.methodName=~"io.k8s.core.v1.configmaps."

查看列出的所有更改,看看它们是否导致了错误。

与 Cloud Customer Care 团队联系

如果您已完成上述部分,但仍无法诊断问题原因,请与 Cloud Customer Care 团队联系

解决常见问题

如果您遇到了特定错误或问题,请参考以下部分中的建议。

问题:DNS 间歇性超时

如果您发现在 DNS 流量增加或开始营业时出现间歇性 DNS 解析超时,请尝试以下解决方案来优化 DNS 性能:

  • 检查集群上运行的 kube-dns Pod 的数量,并将其与 GKE 节点的总数进行比较。如果资源不足,请考虑扩容 kube-dns Pod。

  • 如需缩短平均 DNS 查找时间,请启用 NodeLocal DNS Cache

  • 对外部名称进行 DNS 解析可能会导致 kube-dns Pod 过载。如需减少查询数量,请调整 /etc/resolv.conf 文件中的 ndots 设置。ndots 表示域名中必须显示的点数,以便在初始绝对查询之前解析查询。

    以下示例是应用 Pod 的 /etc/resolv.conf 文件:

    search default.svc.cluster.local svc.cluster.local cluster.local c.PROJECT_ID.internal google.internal
    nameserver 10.52.16.10
    options ndots:5
    

    在此示例中,kube-dns 会在查询的网域中查找五个圆点。如果 Pod 针对 example.com 发出 DNS 解析调用,则日志类似于以下示例:

    "A IN example.com.default.svc.cluster.local." NXDOMAIN
    "A IN example.com.svc.cluster.local." NXDOMAIN
    "A IN example.com.cluster.local." NXDOMAIN
    "A IN example.com.google.internal." NXDOMAIN
    "A IN example.com.c.PROJECT_ID.internal." NXDOMAIN
    "A IN example.com." NOERROR
    

    如需解决此问题,请将 ndots 的值更改为 1,以便仅查找单个点,或者在您查询或使用的网域末尾附加一个点 (.)。例如:

    dig example.com.
    

问题:某些节点的 DNS 查询会间歇性失败

如果您发现某些节点的 DNS 查询会间歇性失败,则可能会看到以下症状:

  • 当您对 kube-dns 服务 IP 地址或 Pod IP 地址运行 dig 命令时,DNS 查询会间歇性失败并超时。
  • 在与 kube-dns Pod 位于同一节点上的 Pod 中运行 dig 命令会失败。

如需解决此问题,请完成以下步骤:

  1. 执行连接性测试。将有问题的 Pod 或节点设置为来源,将目标设置为 kube-dns Pod 的 IP 地址。这样,您就可以检查自己是否已设置了允许此类流量的必要防火墙规则。
  2. 如果测试未成功,并且流量被防火墙规则阻止,请使用 Cloud Logging 列出对防火墙规则所做的任何手动更改。查找导致屏蔽特定类型流量的更改:

    1. 在 Google Cloud 控制台中,前往 Logs Explorer 页面。

      转到日志浏览器

    2. 在查询窗格中,输入以下查询:

      logName="projects/project-name/logs/cloudaudit.googleapis.com/activity"
      resource.type="gce_firewall_rule"
      
    3. 点击运行查询。使用查询的输出来确定是否进行了任何更改。如果您发现任何错误,请更正错误,然后重新应用防火墙规则。

      请确保您未更改任何自动防火墙规则

  3. 如果防火墙规则没有任何更改,请检查节点池版本,确保其与控制平面和其他正常运行的节点池兼容。如果集群的任一节点池比控制平面高两个次要版本,则可能会导致问题。如需详细了解此不兼容性,请参阅节点版本与控制平面版本不兼容

  4. 如需确定请求是否发送到正确的 kube-dns 服务 IP,请捕获有问题节点上的网络流量,并过滤出端口 53(DNS 流量)。捕获 kube-dns Pod 本身上的流量,以了解请求是否到达预期的 Pod 以及是否成功解析。

后续步骤

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