在 Kubernetes 集群中使用 NodeLocal DNSCache
Kubernetes v1.18 [稳定版]
本页概述了 Kubernetes 中的 NodeLocal DNSCache 功能。
准备开始
您需要一个 Kubernetes 集群,并且必须配置 kubectl 命令行工具以与您的集群通信。建议在至少有两个节点(不充当控制平面主机)的集群上运行本教程。如果您还没有集群,可以使用 minikube 创建一个集群,也可以使用这些 Kubernetes playground 之一
要检查版本,请输入kubectl version
。简介
NodeLocal DNSCache 通过在集群节点上作为 DaemonSet 运行 DNS 缓存代理来提高集群 DNS 性能。在当今的架构中,处于“ClusterFirst”DNS 模式下的 Pod 会访问 kube-dns serviceIP
进行 DNS 查询。这通过 kube-proxy 添加的 iptables 规则转换为 kube-dns/CoreDNS 端点。有了这种新架构,Pod 将访问在同一节点上运行的 DNS 缓存代理,从而避免了 iptables DNAT 规则和连接跟踪。本地缓存代理将查询 kube-dns 服务,以获取集群主机名的缓存未命中(默认情况下为“cluster.local
”后缀)。
动机
在当前的 DNS 架构中,如果不存在本地 kube-dns/CoreDNS 实例,则具有最高 DNS QPS 的 Pod 可能必须访问不同的节点。在这种情况下,拥有本地缓存将有助于缩短延迟。
跳过 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 秒超时))而导致的尾部延迟。由于节点本地缓存侦听 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 的“唯一本地地址”范围“fd00::/8”。可以使用以下步骤启用此功能
准备一个类似于示例
nodelocaldns.yaml
的清单,并将其另存为nodelocaldns.yaml
。如果使用 IPv6,则 CoreDNS 配置文件需要将所有 IPv6 地址用方括号括起来(如果在“IP:端口”格式中使用)。如果您使用上一点中的示例清单,则需要修改 配置行 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>
,因此 Pod 可以使用任一 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>
。node-local-dns
接口无法绑定 kube-dns 集群 IP,因为用于 IPVS 负载均衡的接口已使用此地址。__PILLAR__UPSTREAM__SERVERS__
将由 node-local-dns Pod 填充。
运行
kubectl create -f nodelocaldns.yaml
如果使用 IPVS 模式下的 kube-proxy,则需要修改 kubelet 的
--cluster-dns
标志以使用 NodeLocal DNSCache 正在侦听的<node-local-address>
。否则,无需修改--cluster-dns
标志的值,因为 NodeLocal DNSCache 同时侦听 kube-dns 服务 IP 和<node-local-address>
。
启用后,node-local-dns
Pod 将在每个集群节点的 kube-system
命名空间中运行。此 Pod 以缓存模式运行 CoreDNS,因此不同插件公开的所有 CoreDNS 指标都将在每个节点的基础上可用。
您可以通过使用 kubectl delete -f <manifest>
删除 DaemonSet 来禁用此功能。您还应该还原对 kubelet 配置所做的任何更改。
StubDomains 和上游服务器配置
kube-system
命名空间中 kube-dns
ConfigMap 中指定的 StubDomains 和上游服务器会自动被 node-local-dns
Pod 拾取。ConfigMap 内容需要遵循示例中所示的格式。也可以直接使用 Corefile 格式的 stubDomain 配置修改 node-local-dns
ConfigMap。一些云提供商可能不允许直接修改 node-local-dns
ConfigMap。在这些情况下,可以更新 kube-dns
ConfigMap。
设置内存限制
node-local-dns
Pod 使用内存来存储缓存条目和处理查询。由于它们不监视 Kubernetes 对象,因此集群大小或服务/EndpointSlices 的数量不会直接影响内存使用情况。内存使用受 DNS 查询模式的影响。从 CoreDNS 文档中得知,
默认缓存大小为 10000 个条目,完全填充时大约使用 30 MB。
这将是每个服务器块的内存使用量(如果缓存完全填充)。可以通过指定较小的缓存大小来减少内存使用量。
并发查询的数量与内存需求相关,因为用于处理查询的每个额外 goroutine 都需要一定数量的内存。您可以使用 forward 插件中的 max_concurrent
选项设置上限。
如果 node-local-dns
Pod 尝试使用比可用内存更多的内存(由于总系统资源或由于配置的 资源限制),则操作系统可能会关闭该 Pod 的容器。如果发生这种情况,则终止(“OOMKilled”)的容器不会清理它在启动期间先前添加的自定义数据包过滤规则。node-local-dns
容器应重新启动(因为作为 DaemonSet 的一部分进行管理),但这将导致每次容器失败时出现短暂的 DNS 停机时间:数据包过滤规则将 DNS 查询定向到不正常的本地 Pod。
您可以通过在没有限制的情况下运行 node-local-dns pod 并测量峰值使用量来确定合适的内存限制。您还可以设置并使用处于推荐模式的 VerticalPodAutoscaler,然后检查其建议。