排查 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
    

    此消息表明 Kubernetes 因资源消耗过多而终止了某个进程。Kubernetes 会根据资源请求调度 Pod,但允许 Pod 消耗不超过其资源限制的资源。如果限制高于请求或没有限制,Pod 的资源使用量可能会超过系统的资源。

    如需解决此错误,您可以删除有问题的工作负载,也可以设置内存或 CPU 限制。如需详细了解如何设置限制,请参阅 Kubernetes 文档中的为 Pod 和容器管理资源

  3. 如果您没有发现任何 OOM 错误消息,请重启 kube-dns Deployment:

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

    重启 Deployment 后,检查 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 Service IP 地址应与域名服务器 IP 地址相同。如需比较 IP 地址,请完成以下步骤:

      1. 获取 kube-dns Service 的 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. 记下“Cluster IP”列中的值。在此示例中为 192.0.2.10

      3. 将 kube-dns Service 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 Service:

    dig kubernetes
    

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

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

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

    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 Deployment 执行滚动重启:

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
      

      节点的名称会列在节点列中。

    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 Service 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 创建一个 Pod。对 kube-dns Deployment 所做的任何更改都会自动还原。

启用临时 DNS 查询日志记录是一个需要耗费大量资源的过程,因此我们建议您在收集到合适的日志样本后立即删除您创建的 Pod。

如需启用临时 DNS 查询日志记录,请完成以下步骤:

  1. 检索 kube-dns Pod,并将其存储在名为 POD 的变量中:

    POD=$(kubectl -n kube-system get pods --selector=k8s-app=kube-dns -o jsonpath="{.items[0].metadata.name}")
    
  2. 创建一个名为 kube-dns-debug 的 Pod。此 Pod 是存储在 POD 变量中的 Pod 的副本,但启用了 dnsmasq 日志记录。此命令不会修改原始的 kube-dns Pod:

    kubectl apply -f <(kubectl get pod -n kube-system ${POD} -o json | jq -e '
    
    (
    
    (.spec.containers[] | select(.name == "dnsmasq") | .args) += ["--log-queries"]
    
    )
    
    | (.metadata.name = "kube-dns-debug")
    
    | (del(.metadata.labels."pod-template-hash"))
    
    ')
    
  3. 检查日志:

    kubectl logs -f --tail 100 -c dnsmasq -n kube-system kube-dns-debug
    

    您也可以在 Cloud Logging 中看到查询。

  4. 查看 DNS 查询日志后,删除 kube-dns-debug Pod:

    kubectl -n kube-system delete pod kube-dns-debug
    

调查 kube-dns Pod

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

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

  1. 在 Google Cloud 控制台中,转到日志浏览器页面。

    转到日志浏览器

  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 控制台中,转到日志浏览器页面。

    转到日志浏览器

  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 控制台中,转到日志浏览器页面。

    转到日志浏览器

  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 缓存

  • 外部域名的 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 Service IP 地址或 Pod IP 地址运行 dig 命令时,DNS 查询间歇性失败并超时。
  • 在与 kube-dns Pod 位于同一节点上的 Pod 上运行 dig 命令失败。

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

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

    1. 在 Google Cloud 控制台中,转到日志浏览器页面。

      转到日志浏览器

    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 解析