亚马逊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.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.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.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.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.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.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.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.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格式如下:

www.test.com A

执行压测命令:

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://docs.aws.amazon.com/zh_cn/Route53/latest/DeveloperGuide/resolver-getting-started.html

https://docs.aws.amazon.com/zh_cn/vpc/latest/userguide/vpc-dns.html#vpc-dns-limits

https://github.com/kubernetes/perf-tests/tree/master/dns

本篇作者

柳向全

AWS 解决方案架构师,负责基于AWS的云计算方案架构的咨询和设计,目前主要专注于容器和大数据技术领域研究和AWS云服务在国内和全球的应用和推广。