在 Kubernetes 集群中使用 NodeLocal DNSCache
Kubernetes v1.18 [stable]
本页介绍了 Kubernetes 中 NodeLocal DNSCache 功能的概览。
准备工作
你需要拥有一个 Kubernetes 集群,并且 kubectl 命令行工具已配置为与你的集群通信。建议在至少有两个不是控制平面主机的节点上运行本教程。如果你还没有集群,可以使用 minikube 创建一个,或者使用这些 Kubernetes 操场之一
要检查版本,请输入 kubectl version
。
介绍
NodeLocal DNSCache 通过在集群节点上以 DaemonSet 形式运行 DNS 缓存代理来提高集群 DNS 性能。在当前的架构中,“ClusterFirst” DNS 模式下的 Pods 通过 `serviceIP` 访问 kube-dns 进行 DNS 查询。这通过 kube-proxy 添加的 iptables 规则转换为 kube-dns/CoreDNS 端点。通过这种新架构,Pods 将访问在同一节点上运行的 DNS 缓存代理,从而避免 iptables DNAT 规则和连接跟踪。本地缓存代理将查询 kube-dns 服务,以获取集群主机名(默认为“`cluster.local`”后缀)的缓存未命中。
动机
在当前的 DNS 架构中,如果本地没有 kube-dns/CoreDNS 实例,DNS QPS 最高的 Pods 可能需要访问不同的节点。拥有本地缓存将有助于在这些场景中改善延迟。
跳过 iptables DNAT 和连接跟踪将有助于减少 conntrack 竞争,并避免 UDP DNS 条目填满 conntrack 表。
从本地缓存代理到 kube-dns 服务的连接可以升级为 TCP。TCP conntrack 条目将在连接关闭时移除,而 UDP 条目必须超时(默认的 `nf_conntrack_udp_timeout` 是 30 秒)
将 DNS 查询从 UDP 升级到 TCP 将减少由于 UDP 数据包丢失和 DNS 超时(通常长达 30 秒,3 次重试 + 10 秒超时)造成的尾部延迟。由于 nodelocal 缓存侦听 UDP DNS 查询,应用程序无需更改。
节点级别的 DNS 请求的指标和可见性。
负缓存可以重新启用,从而减少对 kube-dns 服务的查询数量。
架构图
这是启用 NodeLocal DNSCache 后 DNS 查询遵循的路径
NodeLocal DNSCache 流程
此图片显示了 NodeLocal DNSCache 如何处理 DNS 查询。
配置
注意
NodeLocal DNSCache 的本地监听 IP 地址可以是任何保证不会与集群中任何现有 IP 地址冲突的地址。建议使用本地范围的地址,例如 IPv4 的“link-local”范围“169.254.0.0/16”,或 IPv6 的“Unique Local Address”范围“fd00::/8”。该功能可以通过以下步骤启用
准备一个类似于示例 `nodelocaldns.yaml` 的清单,并将其保存为 `nodelocaldns.yaml`。
如果使用 IPv6,并且 CoreDNS 配置文件中使用的 IPv6 地址采用“IP:Port”格式,则需要将所有 IPv6 地址用方括号括起来。如果您使用上一点中的示例清单,这将要求您修改配置行 L70,如下所示:“`health [__PILLAR__LOCAL__DNS__]:8080`”
用正确的值替换清单中的变量
kubedns=`kubectl get svc kube-dns -n kube-system -o jsonpath={.spec.clusterIP}` domain=<cluster-domain> localdns=<node-local-address>
默认情况下,`<cluster-domain>` 为 "
cluster.local
"。`<node-local-address>` 是为 NodeLocal DNSCache 选择的本地监听 IP 地址。如果 kube-proxy 在 IPTABLES 模式下运行
sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__/$kubedns/g" nodelocaldns.yaml
`__PILLAR__CLUSTER__DNS__` 和 `__PILLAR__UPSTREAM__SERVERS__` 将由 `node-local-dns` Pod 填充。在此模式下,`node-local-dns` Pod 侦听 kube-dns 服务 IP 和 `<node-local-address>`,因此 Pods 可以使用任一 IP 地址查找 DNS 记录。
如果 kube-proxy 在 IPVS 模式下运行
sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/,__PILLAR__DNS__SERVER__//g; s/__PILLAR__CLUSTER__DNS__/$kubedns/g" nodelocaldns.yaml
在此模式下,`node-local-dns` Pod 仅监听 `<node-local-address>`。由于用于 IPVS 负载均衡的接口已经使用了 kube-dns 集群 IP,因此 `node-local-dns` 接口不能绑定该地址。`__PILLAR__UPSTREAM__SERVERS__` 将由 node-local-dns Pod 填充。
运行 `kubectl create -f nodelocaldns.yaml`
如果 kube-proxy 以 IPVS 模式运行,则需要修改 kubelet 的 `--cluster-dns` 标志,使其使用 NodeLocal DNSCache 正在监听的 `<node-local-address>`。否则,无需修改 `--cluster-dns` 标志的值,因为 NodeLocal DNSCache 同时监听 kube-dns 服务 IP 和 `<node-local-address>`。
启用后,`node-local-dns` Pods 将在集群中每个节点的 `kube-system` 命名空间中运行。此 Pod 在缓存模式下运行 CoreDNS,因此 CoreDNS 不同插件暴露的所有指标都将按节点提供。
您可以通过使用 `kubectl delete -f <manifest>` 删除 DaemonSet 来禁用此功能。您还应该还原对 kubelet 配置所做的任何更改。
StubDomains 和上游服务器配置
在 `kube-system` 命名空间中的 `kube-dns` ConfigMap 中指定的 StubDomains 和上游服务器会自动被 `node-local-dns` Pods 识别。ConfigMap 的内容需要遵循 示例 中所示的格式。`node-local-dns` ConfigMap 也可以直接以 Corefile 格式修改 StubDomain 配置。一些云提供商可能不允许直接修改 `node-local-dns` ConfigMap。在这种情况下,可以更新 `kube-dns` ConfigMap。
设置内存限制
`node-local-dns` Pods 使用内存来存储缓存条目和处理查询。由于它们不监视 Kubernetes 对象,因此集群大小或服务/端点切片数量不会直接影响内存使用。内存使用量受 DNS 查询模式的影响。根据 CoreDNS 文档:
默认缓存大小为 10000 个条目,完全填满时占用约 30 MB 内存。
这将是每个服务器块的内存使用量(如果缓存完全填满)。通过指定更小的缓存大小可以减少内存使用。
并发查询的数量与内存需求相关,因为用于处理查询的每个额外协程都需要一定量的内存。您可以使用 forward 插件中的 `max_concurrent` 选项设置上限。
如果 `node-local-dns` Pod 尝试使用超出可用内存的量(由于总系统资源,或由于配置的 资源限制),操作系统可能会关闭该 Pod 的容器。如果发生这种情况,被终止的容器(“OOMKilled”)不会清理其在启动期间添加的自定义包过滤规则。`node-local-dns` 容器应该重新启动(因为它作为 DaemonSet 的一部分进行管理),但这将导致每次容器失败时短暂的 DNS 停机:包过滤规则将 DNS 查询定向到一个不健康的本地 Pod。
您可以通过在不设置限制的情况下运行 node-local-dns Pod 并测量峰值使用量来确定合适的内存限制。您也可以以“推荐模式”设置并使用 VerticalPodAutoscaler,然后检查其推荐值。