Node Local DNS Cache。这是一款非常酷的软件,它通过将大多数响应缓存在节点本地DNS上来帮助DNS加载,并解决了Linux conntrack
争用问题,这会导致某些DNS请求出现5s的间歇性延迟。你可以阅读更多有关contrack问题5S的DNS间歇延迟发布和活跃的跟踪连接和DNS查找超时
默认情况下,Kubernetes中的Pod将请求发送到kube-dns服务。在内部,这是通过将pod的/etc/resolv.conf
nameserver
字段设置为kube-dns服务svc IP
(10.244.0.3
默认情况下)来完成的。 kube-proxy管理这些服务IP,并通过iptables或IPVS技术将它们转换为kube-dns的endpoint IP
。使用Node Local DNS Cache
,pod可以直接请求Node Local DNS Cache
的svc IP
,而无需任何连接跟踪。如果请求在pod的缓存中,那么我们可以直接返回响应。否则,Node Local DNS Cache
会通过TCP请求上游kube-dns的svc IP
,从而避免conntrack错误。
部署Node Local DNS Cache
,您将必须选择本地链接地址段,。本地链接地址段是IP地址的特殊类别,范围为169.254.0.1至169.254.255.254。定义在 RFC3927 。重要的是,路由器不转发带有本地链接地址段的数据包,因为不能保证该范围在其网段之外是唯一的。示例部署使用169.254.25.10
默认的本地链接地址。
集群正在ipvs
模式下运行kube-proxy,而kube-dns的svc IP
是10.244.0.3
并且使用169.254.25.10
作为本地链接地址。
首先,因为部署Node Local DNS Cache
是通过Daemonset
的形式,并且是配置为hostNetwork: true
然后在每个主机本地创建一个nodelocaldns
的网络接口
4: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
link/ether fe:43:bf:53:02:89 brd ff:ff:ff:ff:ff:ff
...
inet 10.244.0.3/32 brd 10.244.0.3 scope global kube-ipvs0
valid_lft forever preferred_lft forever
...
12: nodelocaldns: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
link/ether e2:13:08:c8:65:24 brd ff:ff:ff:ff:ff:ff
inet 169.254.25.10/32 brd 169.254.25.10 scope global nodelocaldns
valid_lft forever preferred_lft forever
...
节点本地DNS将同步iptables
规则。你可以通过执行看到这些规则iptables-save
,寻找169.254.25.10
IP相关的规则。这是我在一个节点上看到的
*raw
...
-A PREROUTING -d 169.254.25.10/32 -p udp -m udp --dport 53 -j NOTRACK
-A PREROUTING -d 169.254.25.10/32 -p tcp -m tcp --dport 53 -j NOTRACK
...
-A OUTPUT -s 169.254.25.10/32 -p tcp -m tcp --sport 8080 -j NOTRACK
-A OUTPUT -d 169.254.25.10/32 -p tcp -m tcp --dport 8080 -j NOTRACK
-A OUTPUT -d 169.254.25.10/32 -p udp -m udp --dport 53 -j NOTRACK
-A OUTPUT -d 169.254.25.10/32 -p tcp -m tcp --dport 53 -j NOTRACK
-A OUTPUT -s 169.254.25.10/32 -p udp -m udp --sport 53 -j NOTRACK
-A OUTPUT -s 169.254.25.10/32 -p tcp -m tcp --sport 53 -j NOTRACK
...
*filter
...
-A INPUT -d 169.254.25.10/32 -p udp -m udp --dport 53 -j ACCEPT
-A INPUT -d 169.254.25.10/32 -p tcp -m tcp --dport 53 -j ACCEPT
...
-A OUTPUT -s 169.254.25.10/32 -p udp -m udp --sport 53 -j ACCEPT
-A OUTPUT -s 169.254.25.10/32 -p tcp -m tcp --sport 53 -j ACCEPT
...
ipvs转发规则
root@node1:~/k8s_manifests/busybox# kubectl describe ep coredns -n kube-system
Name: coredns
Namespace: kube-system
Labels: addonmanager.kubernetes.io/mode=Reconcile
k8s-app=kube-dns
kubernetes.io/name=coredns
Annotations: <none>
Subsets:
Addresses: 172.20.0.228,172.20.1.223
NotReadyAddresses: <none>
Ports:
Name Port Protocol
---- ---- --------
dns 53 UDP
dns-tcp 53 TCP
metrics 9153 TCP
Events: <none>
# ipvsadm -L
UDP node1:domain rr
-> 172.20.0.228:domain Masq 1 0 0
-> 172.20.1.223:domain Masq 1 0 0
TCP node1:9153 rr
-> 172.20.0.228:9153 Masq 1 0 0
-> 172.20.1.223:9153 Masq 1 0 0
任何kube-proxy规则之前添加这些规则,以便首先对其进行评估。-j NOTRACK
使发送到本地169.254.25.10
地址的TCP和UDP数据包被conntrack取消跟踪。这就是节点本地DNS缓存避免conntrack错误并接管数据包以发送到kube-dns IP的方式
# kubectl get configmap nodelocaldns -n kube-system -o yaml
...
Corefile: |
cluster.local:53 {
errors
cache {
success 9984 30
denial 9984 5
}
reload
loop
bind 169.254.25.10
forward . 10.244.0.3 {
force_tcp
}
prometheus :9253
health 169.254.25.10:9254
}
in-addr.arpa:53 {
errors
cache 30
reload
loop
bind 169.254.25.10
forward . 10.244.0.3 {
force_tcp
}
prometheus :9253
}
ip6.arpa:53 {
errors
cache 30
reload
loop
bind 169.254.25.10
forward . 10.244.0.3 {
force_tcp
}
prometheus :9253
}
.:53 {
errors
cache 30
reload
loop
bind 169.254.25.10
forward . 10.244.154.167 {
force_tcp
}
prometheus :9253
}
...
这部分配置中cluster.local
区域,负责处理kubernetes集群内部的DNS解析,在此区域中,可以看到配置了
缓存,成功和拒绝的记录限制都为9984条,成功有效生存时间30s,拒绝的有效生存时间为5s 。如果请求未命中缓存,就强制使用TCP(避免conntrack race错误)将请求发送到10.244.0.3
这个__PILLAR__CLUSTER__DNS
地址。
还有一个.:53
区域,该区域处理如果解析请求不是针对Kubernetes内部运行的服务的情况。我们缓存请求并转发到__PILLAR__UPSTREAM__SERVERS
上游DNS服务器。节点本地DNS __PILLAR__UPSTREAM__SERVERS
从kube-dns configmap 查找值。该示例部署未设置它,因此默认为/etc/resolv.conf
。请注意,节点本地DNS使用dnsPolicy: Default
,这/etc/resolv.conf
与节点上的相同。
为了避免 coredns的 hosts和rewrite配置不生效,得在nodelocaldns里 forward . /etc/resolv.conf 修改成 forward . __PILLAR__UPSTREAM__SERVERS
piVersion: v1
kind: Service
metadata:
name: kube-dns-upstream
namespace: kube-system
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
kubernetes.io/name: "KubeDNSUpstream"
spec:
ports:
- name: dns
port: 53
protocol: UDP
targetPort: 53
- name: dns-tcp
port: 53
protocol: TCP
targetPort: 53
selector:
k8s-app: kube-dns
获取kube-dns-upstream
的service IP
kubectl get svc -n kube-system | grep kube-dns-upstream
这部分高可用性取决于Node Local DNS Cache
的pod对于信号的处理,如果Pod是正常终止,Pod将删除nodelocaldns这个虚拟网络地址和iptables的规则,然后将请求的流量从自身切换到集群中的kube-dns的Pod上,不影响正常的DNS解析,如果是强行OOM被kill掉的话,则不会删除iptables规则,这样的话将影响正常的DNS解析。
为了避免不必要的麻烦,默认的Daemonset没有设置memory/CPU限制,以避免Pod因内存不足或者CPU节流被非正常kill掉,此外如果pod的annotation被标记为system-node-critical
使得它们几乎可以保证在节点资源不足时首先进行调度,而不太可能被驱逐。
同时为了 安全起见,建议将Daemonset的更新策略更改为OnDelete 通过手动额外删除Node Local DNS pod 来进行维护/升级
https://povilasv.me/kubernetes-node-local-dns-cache/#
https://github.com/kubernetes/enhancements/blob/master/keps/sig-network/20190424-NodeLocalDNS-beta-proposal.md