本节介绍网络的用户指南:NodeLocal DNSCache加速。
在集群中部署NodeLocal DNSCache可以提升服务发现的稳定性和性能,NodeLocal DNSCache通过在集群节点运行DNS缓存代理来提高集群DNS性能。
DNS解析链路全景图
以下介绍三种应用部署形式下应用解析域名的链路:
说明
关于图中的timeout、attempts等术语的含义,请参考下文解析策略和缓存策略。
非容器化应用直接运行于ECS之上。
示例:App运行于ECS上。
-
容器化应用运行于Kubernetes中,DNSPolicy注入ClusterFirst的Pod里。示例:App运行于Kubernetes容器Pod中。
容器化应用运行于Kubernetes中,DNSPolicy注入了NodeLocal DNSCache的Pod里。
示例:App运行于Kubernetes容器Pod中,同时部署了NodeLocal DNSCache。
NodeLocal DNSCache简介
云容器引擎NodeLocal DNSCache插件是基于社区开源项目NodeLocal DNSCache的一套DNS本地缓存解决方案,该方案主要由DNS本地缓存和DNSConfig动态注入控制器组成:
- DNSConfig动态注入控制器,基于Admission Webhook机制拦截Pod创建的请求,自动注入使用DNS缓存的Pod DNSConfig配置;
- DNS缓存代理,在每个集群节点上创建一个虚拟网络接口(默认监听IP 169.254.20.10),配合Pod的DNSConfig和节点上的网络配置,Pod内产生的DNS请求会被该服务所代理。
工作原理图如下图所示:
1、已注入DNS本地缓存的Pod,默认会通过NodeLocal DNSCache的服务地址(169.254.20.10)解析域名;
2、NodeLocal DNSCache本地若无缓存应答解析请求,则将请求转发到CoreDNS进行解析;
3、CoreDNS对于非集群内域名,会通过节点配置的DNS服务进行解析;
4、已注入DNS本地缓存的Pod,当无法连通NodeLocal DNSCache时,会继续通过CoreDNS进行解析,此链路为备用链路;
5、未注入DNS本地缓存的Pod,会通过CoreDNS进行解析。
NodeLocal DNSCache
NodeLocal DNSCache
通过在集群节点上运行一个 DaemonSet 来提高集群 DNS 性能和可靠性。
处于 ClusterFirst 的 DNS 模式下的 Pod 可以连接到 kube-dns 的 serviceIP 进行 DNS 查询,通过 kube-proxy 组件添加的 iptables 规则将其转换为 CoreDNS 端点。
通过在每个集群节点上运行 DNS 缓存,NodeLocal DNSCache
可以缩短 DNS 查找的延迟时间、使 DNS 查找时间更加一致,以及减少发送到 kube-dns 的 DNS 查询次数。
在集群中运行 NodeLocal DNSCache
有如下几个好处:
- 如果本地没有 CoreDNS 实例,则具有最高 DNS QPS 的 Pod 可能必须到另一个节点进行解析,使用
NodeLocal DNSCache
后,拥有本地缓存将有助于改善延迟 - 跳过 iptables DNAT 和连接跟踪将有助于减少 conntrack 竞争并避免 UDP DNS 条目填满 conntrack 表(上面提到的 5s 超时问题就是这个原因造成的)
- 从本地缓存代理到 kube-dns 服务的连接可以升级到 TCP,TCP conntrack 条目将在连接关闭时被删除,而 UDP 条目必须超时(默认
nfconntrackudp_timeout
是 30 秒) - 将 DNS 查询从 UDP 升级到 TCP 将减少归因于丢弃的 UDP 数据包和 DNS 超时的尾部等待时间,通常长达 30 秒(3 次重试+ 10 秒超时)
要安装 NodeLocal DNSCache
也非常简单,直接获取官方的资源清单即可:
wget https://github.com/kubernetes/kubernetes/raw/master/cluster/addons/dns/nodelocaldns/nodelocaldns.yaml
该资源清单文件中包含几个变量值得注意,其中:
__PILLAR__DNS__SERVER__
:表示 kube-dns 这个 Service 的 ClusterIP,可以通过命令kubectl get svc -n kube-system | grep kube-dns | awk'{ print $3 }'
获取(我们这里就是10.96.0.10
)__PILLAR__LOCAL__DNS__
:表示 DNSCache 本地的 IP,默认为169.254.20.10
__PILLAR__DNS__DOMAIN__
:表示集群域,默认就是cluster.local
另外还有两个参数 __PILLAR__CLUSTER__DNS__
和 __PILLAR__UPSTREAM__SERVERS__
,这两个参数会通过镜像 1.15.16 版本去进行自动配置,对应的值来源于 kube-dns 的 ConfigMap 和定制的 Upstream Server
配置。直接执行如下所示的命令即可安装:
$ sed 's/k8s.gcr.io\/dns/cnych/g
s/__PILLAR__DNS__SERVER__/10.96.0.10/g
s/__PILLAR__LOCAL__DNS__/169.254.20.10/g
s/__PILLAR__DNS__DOMAIN__/cluster.local/g' nodelocaldns.yaml |
kubectl apply -f -
可以通过如下命令来查看对应的 Pod 是否已经启动成功:
$ kubectl get pods -n kube-system -l k8s-app=node-local-dns
NAME READY STATUS RESTARTS AGE
node-local-dns-4wclp 1/1 Running 0 5m43s
node-local-dns-gxq57 1/1 Running 0 5m43s
node-local-dns-v7gtz 1/1 Running 0 5m43s
需要注意的是这里使用 DaemonSet 部署 node-local-dns 使用了 hostNetwork=true
,会占用宿主机的 8080 端口,所以需要保证该端口未被占用。
但是到这里还没有完,如果 kube-proxy 组件使用的是 ipvs 模式的话我们还需要修改 kubelet 的 --cluster-dns
参数,将其指向 169.254.20.10
,Daemonset 会在每个节点创建一个网卡来绑这个 IP,Pod 向本节点这个 IP 发 DNS 请求,缓存没有命中的时候才会再代理到上游集群 DNS 进行查询。
iptables 模式下 Pod 还是向原来的集群 DNS 请求,节点上有这个 IP 监听,会被本机拦截,再请求集群上游 DNS,所以不需要更改 --cluster-dns
参数。
如果担心线上环境修改
--cluster-dns
参数会产生影响,我们也可以直接在新部署的 Pod 中通过 dnsConfig 配置使用新的 localdns 的地址来进行解析。
由于我这里使用的是 kubeadm 安装的 1.19 版本的集群,所以我们只需要替换节点上 /var/lib/kubelet/config.yaml
文件中的 clusterDNS 这个参数值,然后重启即可:
sed -i 's/10.96.0.10/169.254.20.10/g' /var/lib/kubelet/config.yaml
systemctl daemon-reload && systemctl restart kubelet
待 node-local-dns 安装配置完成后,我们可以部署一个新的 Pod 来验证下:
# test-node-local-dns.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-node-local-dns
spec:
containers:
- name: local-dns
image: busybox
command: ["/bin/sh", "-c", "sleep 60m"]
直接部署:
$ kubectl apply -f test-node-local-dns.yaml
$ kubectl exec -it test-node-local-dns /bin/sh
/ # cat /etc/resolv.conf
nameserver 169.254.20.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
我们可以看到 nameserver 已经变成 169.254.20.10
了,当然对于之前的历史 Pod 要想使用 node-local-dns 则需要重建。