排查 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 和容器管理资源。 如需详细了解 OOM 事件,请参阅排查 OOM 事件

  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. 在 Trusted 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. 在 Trusted 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. 在 Trusted 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. 在 Trusted 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 缓存

  • 外部域名的 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. 在 Trusted 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 以及是否成功解析。

后续步骤