在k8s中可以使用CoreDNS来进行集群的域名解析,但是如果在集群规模较大并发较高的情况下,可能也需要对DNS进行优化。比较典型的就是CoreDNS会出现超时5s的情况。
超时原因
在iptables模式下(默认情况),每个服务的kube-proxy在主机网络名称空间的nat表中创建一些iptables规则。比如在集群中具有两个DNS服务器实例的kube-dns服务,其相关规则大致如下所示:
(1) -A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES<...>(2) -A KUBE-SERVICES -d 10.96.0.10/32 -p udp -m comment --comment "kube-system/kube-dns:dns cluster IP" -m udp --dport 53 -j KUBE-SVC-TCOU7JCQXEZGVUNU<...>(3) -A KUBE-SVC-TCOU7JCQXEZGVUNU -m comment --comment "kube-system/kube-dns:dns" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-LLLB6FGXBLX6PZF7(4) -A KUBE-SVC-TCOU7JCQXEZGVUNU -m comment --comment "kube-system/kube-dns:dns" -j KUBE-SEP-LRVEW52VMYCOUSMZ<...>(5) -A KUBE-SEP-LLLB6FGXBLX6PZF7 -p udp -m comment --comment "kube-system/kube-dns:dns" -m udp -j DNAT --to-destination 10.32.0.6:53<...>(6) -A KUBE-SEP-LRVEW52VMYCOUSMZ -p udp -m comment --comment "kube-system/kube-dns:dns" -m udp -j DNAT --to-destination 10.32.0.7:53
每个Pod的/etc/resolv.conf文件中都有填充的nameserver 10.96.0.10这个条目。所以来自Pod的DNS查找请求将发送到10.96.0.10,这是kube-dns服务的ClusterIP地址。由于(1)请求进入KUBE-SERVER链,然后匹配规则(2),最后根据(3)的random随机模式,跳转到(5)或(6)条目,将请求UDP数据包的目标IP地址修改为DNS服务器的实际IP地址,这是通过DNAT完成的。其中10.32.0.6和10.32.0.7是集群中CoreDNS的两个Pod副本的IP地址。
内核中的DNAT
DNAT的主要职责是同时更改传出数据包的目的地址,响应数据包的源,并确保对所有后续数据包进行相同的修改。后者严重依赖于连接跟踪机制,也称为conntrack,他被实现为内核模块。conntrack会跟踪系统中正在进行的网络连接。
conntrack中的每个连接都由两个元组表示,一个元组用于原始请求(IP_CT_DIR_ORIGINAL),另一个元组用于答复(IP_CT_DIR_REPLY)。对于UDP,每个元组都由源IP地址,源端口以及目标IP和目标端口组成,答复元组包含存储在src字段中的目标的真实地址。
例如,如果IP地址为10.40.0.17的Pod向kube-dns的ClusterIP发送一个请求,该请求被转换为10.32.0.6,则将创建以下元组:
原始:src = 10.40.0.17 dst = 10.96.0.10 sport = 53378 dport = 53
回复:src = 10.32.0.6 dst = 10.40.0.17 sport = 53 dport = 53378
通过这些条目内核可以相应地修改任何相关数据包的目的地和源地址,而无需再次遍历DNAT规则,此外,它将知道如何修改回复以及应将回复发送给谁。创建conntrack条目后,将首先对其进行确认,然后如果没有已确认的conntrack条目具有相同的原始元组或回复元组,则内核将尝试确认该条目。conntrack创建和DNAT的简化流程如下所示:
