使用NodeLocal DNSCache提升DNS性能

使用NodeLocal DNSCache提升DNS性能

 
NodeLocal DNS (缓存)


为什么需要本地 DNS 缓存
减轻集群 DNS 解析压力,提高 DNS 性能

避免 netfilter 做 DNAT 导致 conntrack 冲突引发 DNS 5 秒延时

镜像底层库 DNS 解析行为默认使用 UDP 在同一个 socket 并发 A 和 AAAA 记录请求,
由于 UDP 无状态,两个请求可能会并发创建 conntrack 表项,
如果最终 DNAT 成同一个集群 DNS 的 Pod IP 就会导致 conntrack 冲突,
由于 conntrack 的创建和插入是不加锁的,最终后面插入的 conntrack 表项就会被丢弃,
从而请求超时,默认 5s 后重试,造成现象就是 DNS 5 秒延时; 
底层库是 glibc 的容器镜像可以通过配 resolv.conf 参数来控制 DNS 解析行为,
不用 TCP 或者避免相同五元组并发(使用串行解析 A 和 AAAA 避免并发或者使用不同 socket 发请求避免相同源端口),
但像基于 alpine 镜像的容器由于底层库是 musl libc,
不支持这些 resolv.conf 参数,也就无法规避,所以最佳方案还是使用本地 DNS 缓存。


原理
本地 DNS 缓存以 DaemonSet 方式在每个节点部署一个使用 hostNetwork 的 Pod,
创建一个网卡绑上本地 DNS 的 IP,本机的 Pod 的 DNS 请求路由到本地 DNS,
然后取缓存或者继续使用 TCP 请求上游集群 DNS 解析。


IPtables 模式下部署方法
IPVS 模式下需要修改 kubelet 参数
有两点需要注意下:

ipvs 模式下需要改 kubelet --cluster-dns 参数,指向一个非 kube-dns service 的 IP,通常用 169.254.20.10,
Daemonset 会在每个节点创建一个网卡绑这个 IP,Pod 向本节点这个 IP 发 DNS 请求,本机 DNS 再代理到上游集群 DNS iptables 模式下不需要改 kubelet --cluster-dns 参数,Pod 还是向原来的集群 DNS 请求,节点上有这个 IP 监听,
被本机拦截,再请求集群上游 DNS (使用集群 DNS 的另一个 CLUSTER IP,来自事先创建好的 Service,
跟原集群 DNS 的 Service 有相同的 selector 和 endpoint) ipvs 模式下必须修改 kubelet 参数的原因是:如果不修改,DaemonSet Pod 在本机创建了网卡,会绑跟集群 DNS 的 CLUSTER IP, 但 kube-ipvs0 这个 dummy interface 上也会绑这个 IP (这是 ipvs 的机制,为了能让报文到达 INPUT 链被 ipvs 处理), 所以 Pod 请求集群 DNS 的报文最终还是会被 ipvs 处理, DNAT 成集群 DNS 的 Pod IP, 最终路由到集群 DNS,相当于本机 DNS 就没有作用了。 [root@master-1 ~]# kubectl -n kube-system get svc coredns NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE coredns ClusterIP 10.0.0.2
<none> 53/UDP,53/TCP,9153/TCP 4d3h #DNS配置 [root@master-1 ~]# kubectl -n kube-system get cm coredns -o yaml apiVersion: v1 data: Corefile: | .:53 { errors health kubernetes cluster.local. in-addr.arpa ip6.arpa { pods insecure upstream fallthrough in-addr.arpa ip6.arpa } prometheus :9153 proxy . /etc/resolv.conf cache 30 } kind: ConfigMap metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","data":{"Corefile":".:53 {\n errors\n health\n kubernetes cluster.local. in-addr.arpa ip6.arpa {\n pods insecure\n upstream\n fallthrough in-addr.arpa ip6.arpa\n }\n prometheus :9153\n proxy . /etc/resolv.conf\n cache 30\n}\n"},"kind":"ConfigMap","metadata":{"annotations":{},"labels":{"addonmanager.kubernetes.io/mode":"EnsureExists"},"name":"coredns","namespace":"kube-system"}} creationTimestamp: "2020-09-14T07:22:15Z" labels: addonmanager.kubernetes.io/mode: EnsureExists name: coredns namespace: kube-system resourceVersion: "851" selfLink: /api/v1/namespaces/kube-system/configmaps/coredns uid: 2366f397-9968-4a6e-acfb-2b6c8c6b050e #导入镜像 [root@node-1 ~]# docker load -i k8s-dns-node-cache.coredns-1.5.0.tar.gz #安装nodedns [root@node-1 ~]# mkdir /root/nodedns [root@node-1 nodedns]# yum install dos2unix -y [root@node-1 nodedns]# dos2unix install_localdns.sh #查看dns [root@master-2 ~]# kubectl get pods -n kube-system -o wide | grep node-local node-local-dns-65pnp 1/1 Running 0 43s 192.168.91.22 192.168.91.22 <none> <none> node-local-dns-gpv8p 1/1 Running 0 43s 192.168.91.21 192.168.91.21 <none> <none> 定制业务容器dnsConfig 为了使业务容器能够使用nodelocaldns,需要将nameserver配置为169.254.20.10,而不是ClusterDNS。定制dnsConfig有以下几点需要注意到: dnsPolicy: None。不使用ClusterDNS。 配置searches,保证集群内部域名能够被正常解析。 适当降低ndots值。当前ACK集群ndots值默认为5,降低ndots值有利于加速集群外部域名访问。如果业务容器没有使用带多个dots的集群内部域名,建议将值设为2。 #测DNS [root@master-1 nginx]# kubectl run -it --rm --restart=Never --image=infoblox/dnstools:latest dnstools dnstools# nslookup -qt=A WWW.BAIDU.COM 169.254.20.10 Server: 169.254.20.10 Address: 169.254.20.10#53 Non-authoritative answer: WWW.BAIDU.COM canonical name = www.a.shifen.COM. www.a.shifen.COM canonical name = www.wshifen.COM. Name: www.wshifen.COM Address: 104.193.88.77 Name: www.wshifen.COM Address: 104.193.88.123 dnstools# nslookup -qt=A grafana.monitoring 169.254.20.10 Server: 169.254.20.10 Address: 169.254.20.10#53 Name: grafana.monitoring.svc.cluster.local Address: 10.0.0.52 #修改DNS地址(所有node节点) cat >/etc/kubernetes/cfg/kubelet.config<<EOF kind: KubeletConfiguration apiVersion: kubelet.config.k8s.io/v1beta1 address: 192.168.91.21 port: 10250 readOnlyPort: 10255 cgroupDriver: cgroupfs clusterDNS: ["169.254.20.10"] clusterDomain: cluster.local. failSwapOn: false authentication: anonymous: enabled: true EOF #重启服务 [root@node-1 bin]#service kubelet restart [root@node-1 bin]#service kubelet status #启动一个新容器 [root@master-1 minio]# kubectl run nginx-dns --image=nginx --replicas=1 #DNS 地址修改 [root@master-1 minio]# kubectl exec -i -t nginx-dns-54bdc96bb5-cclmt -- cat /etc/resolv.conf nameserver 169.254.20.10 search default.svc.cluster.local. svc.cluster.local. cluster.local. options ndots:5

 

应用现状

当集群中的DNS请求量增加时,CoreDNS将会承受更大的压力,可能会导致如下影响:

  • 延迟增加:CoreDNS需要处理更多的请求,可能会导致DNS查询变慢,从而影响业务性能。
  • 资源占用率增加:为保证DNS性能,CoreDNS往往需要更高规格的配置。

解决方案

为了避免DNS延迟的影响,可以在集群中部署NodeLocal DNSCache来提升服务发现的稳定性和性能。NodeLocal DNSCache会在集群节点上运行DNS缓存代理,所有注入DNS配置的Pod都会使用节点上运行的DNS缓存代理进行域名解析,而不是使用CoreDNS服务,以此来减少CoreDNS服务的压力,提高集群DNS性能。

启用NodeLocal DNSCache之后,DNS查询所遵循的路径如下图所示。

图1 NodeLocal DNSCache查询路径
其中解析线路说明如下:
  • ①:已注入DNS本地缓存的Pod,默认会通过NodeLocal DNSCache解析请求域名。
  • ②:NodeLocal DNSCache本地缓存如果无法解析请求,则会请求集群CoreDNS进行解析。
  • ③:对于非集群内的域名,CoreDNS会通过VPC的DNS服务器进行解析。
  • ④:已注入DNS本地缓存的Pod,如果无法连通NodeLocal DNSCache,则会直接通过CoreDNS解析域名。
  • ⑤:未注入DNS本地缓存的Pod,默认会通过CoreDNS解析域名。

约束与限制

posted @ 2024-11-03 21:13  滴滴滴  阅读(51)  评论(0编辑  收藏  举报