本文档介绍了如何在 Google Kubernetes Engine (GKE) 集群中使用 NodeLocal DNSCache 来缩短 DNS 查找延迟时间并提升应用性能。
NodeLocal DNSCache 是一项 GKE 插件,它通过在每个集群节点上以 DaemonSet 形式直接运行 DNS 缓存来提高 DNS 性能。当您的 Pod 发出 DNS 请求时,该请求会首先转到同一节点上的本地缓存。在本地处理请求可显著缩短平均 DNS 查找时间,并减轻集群的中央 DNS 提供商(例如 kube-dns 或 Cloud DNS for GKE)的负载。如需详细了解 DNS 架构和 NodeLocal DNSCache 的优势,请参阅关于服务发现。
在 GKE Autopilot 集群中,NodeLocal DNSCache 默认处于启用状态,您无法将其停用。在运行 1.33.1 及更高版本的 GKE Standard 集群中,NodeLocal DNSCache 默认处于启用状态,但您可以将其停用。
本文档适用于 GKE 用户,包括开发者、管理员和架构师。如需详细了解 Google Cloud中的常见角色和示例任务,请参阅常见的 GKE Enterprise 用户角色和任务。
本文档假定您熟悉以下内容:
架构
NodeLocal DNSCache 是一个 GKE 插件,除了 kube-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-upstream服务访问kube-dnsPod。 - 自定义存根网域或上游域名服务器:查询直接从 NodeLocal DNSCache Pod 转发。
- Cloud DNS:所有其他查询均会转发到与发起查询的节点上运行的本地元数据服务器。本地元数据服务器会访问 Cloud DNS。
在现有集群上启用 NodeLocal DNSCache 时,GKE 会按照节点升级过程重新创建所有运行 GKE 1.15 及更高版本的集群节点。
重新创建节点后,GKE 会自动向节点添加 addon.gke.io/node-local-dns-ds-ready=true 标签。您不得手动将此标签添加到集群节点。
NodeLocal DNSCache 的优点
NodeLocal DNSCache 具有以下优势:
- 减少了平均 DNS 查找时间
- 从 Pod 到其本地缓存的连接不会创建 conntrack 表条目。这样可以防止由于 conntrack 表耗尽和竞态条件而导致连接中断和连接被拒绝。
- 您可以将 NodeLocal DNSCache 与 Cloud DNS for GKE 搭配使用。
- 外部网址(不引用集群资源的网址)的 DNS 查询会直接转发到本地元数据服务器,并绕过
kube-dns。 - 本地 DNS 缓存会自动选取为存根网域添加自定义解析器部分中指定的存根网域和上游域名服务器。
要求和限制
- NodeLocal DNSCache 会消耗集群的每个节点上的计算资源。
- Windows Server 节点池不支持 NodeLocal DNSCache。
- NodeLocal DNSCache 需要 GKE 1.15 或更高版本。
- NodeLocal DNSCache 使用 TCP 访问
kube-dnsPod。 - NodeLocal DNSCache 在 GKE 1.18 版或更高版本上使用 TCP 和 UDP 访问
upstreamServers和stubDomains。DNS 服务器必须可通过 TCP 和 UDP 访问。 - 系统会在以下时间段缓存 DNS 记录:
- 记录的存留时间 (TTL) 或 30 秒(后者适用于 TTL 超过 30 秒的情况)。
- 5 秒(如果 DNS 响应为
NXDOMAIN)。
- NodeLocal DNSCache Pod 会侦听节点上的端口 53、9253、9353 和 8080。
如果您运行任何其他
hostNetworkPod 或使用这些端口配置hostPorts,则 NodeLocal DNSCache 会失败并发生 DNS 错误。如果 NodeLocal DNSCache Pod 使用 GKE Dataplane V2 且不使用 Cloud DNS for GKE,则不会使用hostNetwork模式。 - 本地 DNS 缓存仅在运行 GKE 1.15 及更高版本的节点池上运行。如果您在具有运行早期版本的节点的集群中启用 NodeLocal DNSCache,则这些节点上的 Pod 会使用
kube-dns。
准备工作
在开始之前,请确保您已执行以下任务:
- 启用 Google Kubernetes Engine API。 启用 Google Kubernetes Engine API
- 如果您要使用 Google Cloud CLI 执行此任务,请安装并初始化 gcloud CLI。 如果您之前安装了 gcloud CLI,请通过运行
gcloud components update命令来获取最新版本。较早版本的 gcloud CLI 可能不支持运行本文档中的命令。
- 确保您已有 Autopilot 或 Standard 集群。如果需要,请创建 Autopilot 集群。对于 Autopilot 集群,NodeLocal DNSCache 默认处于启用状态,且不能覆盖。
启用 NodeLocal DNSCache
对于 Standard 集群,您可以使用 Google Cloud CLI 或 Google Cloud 控制台启用 NodeLocal DNSCache。
gcloud
如需在现有集群中启用 NodeLocal DNSCache,请使用带有 NodeLocalDNS=ENABLED 实参的 --update-addons 标志:
gcloud container clusters update CLUSTER_NAME \
--location=COMPUTE_LOCATION \
--update-addons=NodeLocalDNS=ENABLED
替换以下内容:
CLUSTER_NAME:您的集群的名称。COMPUTE_LOCATION:集群的 Compute Engine 位置。
控制台
如需在新集群上启用 NodeLocal DNSCache,请按以下步骤操作:
前往 Google Cloud 控制台中的 Google Kubernetes Engine 页面。
点击要修改的集群的名称。
在网络下的 DNS 提供商字段中,点击 edit 修改 DNS 提供商。
选中启用 NodeLocal DNSCache 复选框。
点击保存更改。
此更改需要重新创建节点,这可能会导致正在运行的工作负载中断。如需详细了解此特定更改,请在使用节点升级策略并遵循维护政策重新创建节点的手动更改表格中找到相应的行。如需详细了解节点更新,请参阅规划节点更新中断。
验证 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 \
--location=COMPUTE_LOCATION \
--update-addons=NodeLocalDNS=DISABLED
替换以下内容:
CLUSTER_NAME:要停用的集群的名称。COMPUTE_LOCATION:集群的 Compute Engine 位置。
此更改需要重新创建节点,这可能会导致正在运行的工作负载中断。如需详细了解此特定更改,请在使用节点升级策略并遵循维护政策重新创建节点的手动更改表格中找到相应的行。如需详细了解节点更新,请参阅规划节点更新中断。
排查 NodeLocal DNSCache 问题
如需了解诊断 Kubernetes DNS 问题的一般信息,请参阅调试 DNS 解析。
NodeLocal DNSCache 未立即启用
在现有集群上启用 NodeLocal DNSCache 时,如果集群配置了维护期或排除选项,则 GKE 可能不会立即更新节点。 如需了解详情,请参阅节点重新创建和维护窗口的注意事项。
如果您不想等待,可通过调用 gcloud container clusters upgrade 命令并使用节点池已经运行的 GKE 版本传递 --cluster-version 标志,将更改手动应用到节点。您必须将 Google Cloud CLI 用于此临时解决方法。
将 NodeLocal DNSCache 与 Cloud DNS 搭配使用
如果您将 NodeLocal DNSCache 与 Cloud DNS 搭配使用,则集群会使用域名服务器 IP 地址 169.254.20.10,如下图所示:
因此,kube-dns Service 的 IP 地址可能与您的 Pod 使用的域名服务器 IP 地址不同。这种 IP 地址差异是正常的,因为 Cloud DNS 需要 169.254.20.10 域名服务器 IP 地址才能正常运行。
如需检查 IP 地址,请运行以下命令:
查看
kube-dnsService 的 IP 地址:kubectl get svc -n kube-system kube-dns -o jsonpath="{.spec.clusterIP}"输出为
kube-dns的 IP 地址,例如10.0.0.10在 Pod 中打开 shell 会话:
kubectl exec -it POD_NAME -- /bin/bash在 Pod shell 会话中,读取
/etc/resolv.conf文件的内容:cat /etc/resolv.conf输出为
169.254.20.10
使用 NodeLocal DNSCache 的网络政策
如果您将网络政策与 NodeLocal DNSCache 搭配使用,并且您没有使用 Cloud DNS 或 GKE Dataplane V2,则您必须配置规则来允许工作负载和 node-local-dns Pod 发送 DNS 查询。
在您的清单中使用 ipBlock 规则来允许在您的 Pod 和 kube-dns 之间进行通信。
以下清单说明了一项使用 ipBlock 规则的网络政策:
spec:
egress:
- ports:
- port: 53
protocol: TCP
- port: 53
protocol: UDP
to:
- ipBlock:
cidr: KUBE_DNS_SVC_CLUSTER_IP/32
podSelector: {}
policyTypes:
- Egress
将 KUBE_DNS_SVC_CLUSTER_IP 替换为 kube-dns 服务的 IP 地址。您可以使用以下命令获取 kube-dns 服务的 IP 地址:
kubectl get svc -n kube-system kube-dns -o jsonpath="{.spec.clusterIP}"
已知问题
本部分列出了 NodeLocal DNSCache 的已知问题。
使用 NodeLocal DNSCache 和 GKE Dataplane V2 时,ClusterFirstWithHostNet dnsPolicy 中的 DNS 超时
在使用 GKE Dataplane V2 和 NodeLocal DNSCache 的集群上,hostNetwork 字段设置为 true 且 dnsPolicy 字段设置为 ClusterFirstWithHostNet 的 Pod 无法访问集群 DNS 后端。DNS 日志可能包含类似如下内容的条目:
dnslookup: write to 'a.b.c.d': Operation not permitted
;; connection timed out; no servers could be reached
输出表明 DNS 请求无法连接到后端服务器。
解决方法是为 hostNetwork Pod 设置 dnsPolicy 和 dnsConfig 字段:
spec:
dnsPolicy: "None"
dnsConfig:
nameservers:
- KUBE_DNS_UPSTREAM
searches:
- NAMESPACE.svc.cluster.local
- svc.cluster.local
- cluster.local
- c.PROJECT_ID.internal
- google.internal
options:
- name: ndots
value: "5"
替换以下内容:
NAMESPACE:hostNetworkpod 的命名空间。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 请求的响应。此问题可能是由以下某种原因造成的:
- 底层网络连接问题。
- 来自工作负载或节点池扩容的 DNS 查询显著增加。
因此,现有的 kube-dns Pod 无法及时处理所有请求。解决方法是通过纵向伸缩 kube-dns 来增加 kube-dns 副本的数量。
扩充 kube-dns
您可以针对 nodesPerReplica 字段使用较低的值,以确保在集群节点纵向扩容时创建更多的 kube-dns Pod。我们强烈建议设置明确的 max 值,以帮助确保 GKE 控制平面虚拟机 (VM) 不会因监控 Kubernetes API 的 kube-dns Pod 数量太多而不堪重负。
您可以将 max 字段设置为集群中的节点数。如果集群的节点超过 500 个,请将 max 字段设置为 500。
对于 Standard 集群,您可以通过修改 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 副本数的计算公式如下:
replicas = max(ceil(cores * 1/coresPerReplica), ceil(nodes *
1/nodesPerReplica))
如果您在 ConfigMap 中定义了 min 和 max 字段,则副本数会受这些值的限制。如需纵向扩容,请将 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}'
后续步骤
- 简要了解 GKE 如何提供托管式 DNS。
- 阅读服务和 Pod 的 DNS,大致了解如何在 Kubernetes 集群中使用 DNS。
- 了解如何使用 Cloud DNS for GKE。