亚马逊AWS官方博客
如何优化Amazon EKS集群DNS性能
背景介绍
CoreDNS是一个灵活的、可扩展的DNS服务器,可以用作Kubernetes集群DNS。当您启动至少有一个节点的Amazon EKS集群时,默认情况下会部署CoreDNS image的两个副本,而不管集群中部署了多少节点。CoreDNS pod为EKS集群中的所有pod提供名称解析。EKS集群中默认部署的CoreDNS在DNS QPS较高场景下可能会出现DNS解析时延高、解析超时、解析失败等问题。本文介绍如何优化EKS集群中DNS性能。
前提条件
在具有 Kubernetes 1.16 或更高版本的Amazon EKS 集群上支持 CoreDNS
有关如何在Amazon EKS上安装和升级CoreDNS,请参考https://docs.aws.amazon.com/zh_cn/eks/latest/userguide/coredns.html
优化方案
增加CoreDNS副本数
默认部署的Amazon EKS集群,CoreDNS的副本数是2,当集群规模较大和DNS查询并发较高时,可能会出现2个Pods的CoreDNS不足以及时处理DNS查询请求从而影响DNS查询效率,我们可以通过直接增加CoreDNS副本数量的方式提升DNS查询性能。
执行以下命令调整CoreDNS的副本数到合理值,N时目标副本数
kubectl scale --replicas=N deployment/coredns -n kube-system
增加CoreDNS的Cache时间
在有大量外部域名解析请求的场景中,适当增加CoreDNS对条目的缓存时间可以有效的提高CoreDNS的性能,CoreDNS默认配置的缓存时间是30s,增大cache时间对域名解析TTL敏感型的应用会有一定的影响,会延缓应用感知域名解析配置变更的时间。
kubectl edit configmap coredns -n kube-system
apiVersion: v1
data:
Corefile: |
.:53 {
errors
log
health
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
forward . /etc/resolv.conf
cache 60
loop
reload
loadbalance
}
例如配置文件中cache 30调整为60。
调整ndots数值
Pod在使用DNS的时候有四种策略,默认是ClusterFirst策略,在ClusterFirst模式下,Pod内/etc/resolv.conf配置的DNS服务地址是集群DNS服务的地址kube-dns
nameserver 10.100.0.10
search default.svc.cluster.local svc.cluster.local cluster.local ap-southeast-1.compute.internal
options ndots:5
在Amazon EKS中CoreDNS的service对应的cluster ip地址默认是10.100.0.10,在ClusterFirst模式下存在一个问题,当集群内部查询外部域名的时候,2次(1次IPv4,1次IPv6)集群外部域名DNS查询会产生10次(5次IPv4,5次IPv6)查询请求。例如解析www.amazon.com域名,会先分别携带四个/etc/resolv.conf 中search对应的搜索域后缀,产生八次无效查询请求,这样会导致集群DNS QPS放大四倍(例如如下日志中显示为NXDOMAIN的请求全部为无效请求)
[INFO] 192.168.91.232:38354 - 53881 "AAAA IN www.amazon.com.rproxy.goskope.com.default.svc.cluster.local. udp 58 false 512" NXDOMAIN qr,aa,rd 151 0.000166861s
[INFO] 192.168.91.232:38354 - 44145 "A IN www.amazon.com.rproxy.goskope.com.default.svc.cluster.local. udp 58 false 512" NXDOMAIN qr,aa,rd 151 0.000228392s
[INFO] 192.168.91.232:41643 - 16970 "AAAA IN www.amazon.com.rproxy.goskope.com.svc.cluster.local. udp 50 false 512" NXDOMAIN qr,aa,rd 143 0.000084297s
[INFO] 192.168.91.232:41643 - 65092 "A IN www.amazon.com.rproxy.goskope.com.svc.cluster.local. udp 50 false 512" NXDOMAIN qr,aa,rd 143 0.000074557s
[INFO] 192.168.91.232:36820 - 54794 "AAAA IN www.amazon.com.rproxy.goskope.com.cluster.local. udp 46 false 512" NXDOMAIN qr,aa,rd 139 0.000100624s
[INFO] 192.168.91.232:36820 - 11271 "A IN www.amazon.com.rproxy.goskope.com.cluster.local. udp 46 false 512" NXDOMAIN qr,aa,rd 139 0.000098021s
[INFO] 192.168.91.232:33623 - 47179 "AAAA IN www.amazon.com.rproxy.goskope.com.ap-southeast-1.compute.internal. udp 64 false 512" NXDOMAIN qr,rd,ra 187 0.009907116s
[INFO] 192.168.91.232:33623 - 58950 "A IN www.amazon.com.rproxy.goskope.com.ap-southeast-1.compute.internal. udp 64 false 512" NXDOMAIN qr,rd,ra 187 0.010631084s
[INFO] 192.168.91.232:48515 - 56834 "A IN www.amazon.com. udp 32 false 512" NOERROR qr,rd,ra 212 0.001209964s
[INFO] 192.168.91.232:48515 - 16391 "AAAA IN www.amazon.com. udp 32 false 512" NOERROR qr,rd,ra 318 0.001528659s
在进行域名解析时,操作系统先判断其是否是一个 FQDN(Fully qualified domain name,即完整域名,指以.结尾的),如果是则会直接查询 DNS 服务器;如果不是则就要根据 search 和 ndots 的设置进行 FQDN 的拼接再将其发到 DNS 服务器进行解析。
ndots 表示的是完整域名中必须出现的.的个数,如果域名中的.的个数不小于 ndots,则该域名会被认为是一个 FQDN,操作系统会直接将其发给 DNS 服务器进行查询;否则,操作系统会在 search 搜索域中依次查询。
默认情况下ndots 为 5,查询的域名 www.amazon.com 不以.结尾,且.的个数少于 5,因此操作系统会依次在 default.svc.cluster.local svc.cluster.local cluster.local ap-southeast-1.compute.internal
四个域中进行搜索。如果集群内只会用到同 namespace 下的 Service和跨 namespace 下 Service的访问, 将ndots 的默认值设为 2 便可满足业务需求并避免大量无效的解析请求。
修改pod的ndots值的方法如下
apiVersion: apps/v1
kind: Deployment
metadata:
name: "centos"
spec:
selector:
matchLabels:
app: "centos"
replicas: 1
template:
metadata:
labels:
app: "centos"
spec:
containers:
- image: centos
imagePullPolicy: Always
name: "centos"
args:
- /bin/sh
- -c
- sleep 10; touch /tmp/healthy; sleep 30000
readinessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 10
periodSeconds: 5
dnsConfig:
options:
- name: ndots
value: "2"
修改后测试解析DNS域名,CoreDNS日志如下:
[INFO] 192.168.67.216:39187 - 41028 "A IN www.amazon.com. udp 32 false 512" NOERROR qr,rd,ra 212 0.001406832s
[INFO] 192.168.67.216:39187 - 22603 "AAAA IN www.amazon.com. udp 32 false 512" NOERROR qr,rd,ra 280 0.002514496s
可以看到只有两次解析,无效解析已经消失。
使用autopath插件
autopath是一个可选的优化插件,可以提高集群外部名称查询的性能(例如amazon.com)。启用autopath插件需要CoreDNS使用更多的内存来存储有关Pod的信息。Autopath的原理是在第一次域名查询失败尝试找到正确的域名,这样共需要2次(1次IPv4,1次IPv6)查询就可以获取到正确的结果, 减少客户端在查找外部名称时进行的DNS查询次数。
启用autopath的方式如下
kubectl edit configmap coredns -n kube-system
apiVersion: v1
data:
Corefile: |
.:53 {
errors
log
health
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods verified
fallthrough in-addr.arpa ip6.arpa
}
autopath @kubernetes
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
使用NodeLocal DNSCache
NodeLocal DNSCache 通过在集群节点上作为 DaemonSet 运行 DNS 缓存代理来提高集群 DNS 性能,通过这种架构,可以让pod直接查询运行在同一节点上的DNS缓存代理,避免DNS查询请求报文经过iptables NAT和conntrack从而可能触发瓶颈。
关于在集群上部署NodeLocal DNSCache,可以参考
https://kubernetes.io/docs/tasks/administer-cluster/nodelocaldns/
使用Amazon Route53 resolver作为外部DNS服务器
默认情况下,Amazon EKS的CoreDNS部署后使用节点中/etc/resolv.conf中配置的nameserver作为外部DNS解析服务器。在VPC中单个EC2的网卡的域名解析并发为1024个数据包,并且无法提高此限制,这样就导致单个CoreDNS的pod最大的外部DNS解析能力是1024 QPS,除了前文提到的增加更多的CoreDNS pod的方法外,我们还可以利用Amazon Route53 resolver提供的Inbound endpoints作为CoreDNS外部解析地址,突破1024 QPS的限制(每个Amazon Route53 resolver终端节点中单个 IP 地址的每秒查询数为10000,可以增加更多的IP提高QPS)
apiVersion: v1
data:
Corefile: |
.:53 {
errors
log
health
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
forward . 192.168.56.21 192.168.1.68
cache 30
loop
reload
loadbalance
}
其中192.168.56.21和192.168.1.68是Amazon Route53 resolver的入栈终端节点
(备注:此方案会额外产生Amazon Route53 resolver费用)
性能测试
我们可以使用dnsperf工具对CoreDNS进行测试,我们先创建一个Pod,在Pod中安装dnsperf,模拟DNS查询客户端。安装命令如下
(备注:根据操作系统下载对应的版本):
rpm -ivh https://download-ib01.fedoraproject.org/pub/epel/8/Everything/x86_64/Packages/e/epel-release-8-11.el8.noarch.rpm
yum install dnsperf
准备好压测的DNS列表,dns.txt格式如下:
执行压测命令:
dnsperf -l 30 -s 10.100.0.10 -d dns.txt
(备注:其中-l代表压测时长,-s代表域名服务器地址,-d 指向压测域名的配置文件)
压测环境:
使用3台c5.xlarge,压测的dnsperf Pod和CoreDNS Pod分布在不同的Node上,CoreDNS使用默认资源配置
压测结果:
Dnsperf Pod个数 | CoreDNS pod个数 | QPS | 平均延迟 | CoreDNS Cache设置 | 测试域名 | 丢包率 | 备注 |
1 | 1 | 26706 | 3ms | 30s | 外部域名 | 0% | |
1 | 1 | 38571 | 2ms | 30s | 内部域名 | 0% | |
2 | 1 | 33991 | 4ms | 30s | 外部域名 | 0.03% | |
2 | 1 | 41830 | 3ms | 30s | 内部域名 | 0.03% | |
1 | 1 | 534 | 187ms | 0s | 外部域名 | 0% | |
1 | 1 | 30620 | 3ms | 0s | 内部域名 | 0% | |
1 | 1 | 8515 | 11ms | 0s | 外部域名 | 0% | 使用route53 resolver |
2 | 1 | 811 | 216ms | 0s | 外部域名 | 0.03% | |
2 | 1 | 30886 | 4ms | 0s | 内部域名 | 0.03% | |
2 | 2 | 1060 | 188ms | 0s | 外部域名 | 0% | |
2 | 2 | 50152 | 3ms | 0s | 内部域名 | 0% |
(备注:因为测试域名只有一个,未必能反应实际业务场景,实际数据会随着节点性能、CoreDNS的资源分配而有差异,其中Cache设置为0是为了测试缓存未命中的情况下CoreDNS的实际性能)
结果分析:
CoreDNS cache对外部域名查询性能影响很大
增加CoreDNS pod个数基本可以线性增加QPS
使用Route53 resolver可以提升单个CoreDNS Pod对外查询的QPS
总结
通过以上方法,在大规模EKS集群中在遇到DNS查询性能问题的时候,我们根据不同的场景可以采取不同的解决办法,能够有效的提升EKS集群处理大并发域名解析的能力。对应的场景和方案选取如下(几种优化措施可以同时启用)。
- 增加CoreDNS pod个数和使用Route 53 resolver的方式可以突破EC2单网卡查询请求DNS最大1024 QPS的限制,在单个CoreDNS达到QPS上限的时候可以使用。
- 使用NodeLocal DNSCache和增加CoreDNS cache的方式对可以命中缓存的查询可以有效的提升DNS查询QPS和性能。需要注意的是增加cache都会增加DNS的缓存时间,如果对DNS TTL敏感的应用需要平衡考量。
- 修改Ndots和启用autopath都是为了降低外部域名查询的时候的无效请求,Ndots的方式操作简单但可能会影响跨集群的应用调用,autopath会增加CoreDNS的内存占用,一般情况下选择一种配置即可。
参考链接
https://docs.aws.amazon.com/zh_cn/eks/latest/userguide/coredns.html
https://github.com/kubernetes/kubernetes/issues/33554#issuecomment-266251056
https://github.com/kubernetes/perf-tests/tree/master/dns