作者:InfraCloud 技术领导、开源贡献者 Sanket Sudake。客座文章最初在InfraCloud 的博客[1]上发表。
我们增加了对托管在 Kubernetes 集群上的一个应用程序的 HTTP 请求,这导致了 5xx 错误的激增。该应用程序是一个 GraphQL 服务器,调用大量外部 API,然后返回聚合响应。我们最初的反应,是增加应用程序的副本数量,看看它是否提高了性能,并减少了错误。当我们与应用程序开发人员进一步深入时,发现了大多数与 DNS 解析相关的失败。这就是我们在 Kubernetes 开始深入研究 DNS 解析的地方。
这篇文章强调了我们在故障排除过程中对 CoreDNS 做了深入的研究和了解。
DNS 服务器在其数据库中存储记录,并使用数据库回答域名查询。如果 DNS 服务器没有此数据,它将尝试从其他 DNS 服务器找到解决方案。
CoreDNS 成为 Kubernetes 1.13+之后的默认 DNS 服务[2]。现在,当你使用托管 Kubernetes 集群或为应用程序工作负载自我管理集群时,你通常关注于调整应用程序,而不是 Kubernetes 提供的服务或如何利用它们。DNS 解析是任何应用程序的基本要求,因此你需要确保它能够正常工作。我们建议查看dns 调试解析[3]故障排除指南,并确保你的 CoreDNS 已正确配置和运行。
默认情况下,当你提供一个集群时,你应该有一个仪表板来观察关键的 CoreDNS 指标。为了获得 CoreDNS 指标,你应该启用Prometheus 插件[4]作为 CoreDNS 配置的一部分。
下面的配置示例使用 prometheus 插件从 CoreDNS 实例中启用指标集合。
.:53 {
errors
health
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods verified
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
以下是关键指标,我们建议在你的仪表板中有。如果你正在使用 Prometheus、DataDog、Kibana 等,你可能会发现来自社区/提供商已经准备好供使用的仪表板模板了。
我们使用 DataDog 来监控特定的应用程序。下面是我用 DataDog 构建的用于分析的示例仪表板。
当我们开始深入研究应用程序如何向 CoreDNS 发出请求时,我们观察到大多数出站请求都是通过应用程序向外部 API 服务器发出的。
这通常是 resolv.conf 在应用程序部 deployment pod 中的样子。
nameserver 10.100.0.10
search kube-namespace.svc.cluster.local svc.cluster.local cluster.local us-west-2.compute.internal
options ndots:5
如果你了解 Kubernetes 如何尝试解析一个 FQDN——它会尝试在不同的级别上进行 DNS 查找。
考虑到上述 DNS 配置,当 DNS 解析器向 CoreDNS 服务器发送查询时,会根据搜索路径尝试搜索域。
如果我们在寻找一个 boktube.io 域。它将执行以下查询,并在最后一个查询中接收成功的响应。
botkube.io.kube-namespace.svc.cluster.local <= NXDomain
botkube.io.svc.cluster.local <= NXDomain
boktube.io.cluster.local <= NXDomain
botkube.io.us-west-2.compute.internal <= NXDomain
botkube.io <= NoERROR
由于我们进行了太多的外部查找,我们得到了大量的 DNS 搜索的 NXDomain 响应。为了优化这一点,我们在 Deployment 对象中定制了 spec.template.spec.dnsConfig。以下是改变的样子:
dnsPolicy: ClusterFirst
dnsConfig:
options:
- name: ndots
value: "1"
通过上面的改变,pod 上的 resolve.conf 也改变了。只对外部域执行搜索。这减少了对 DNS 服务器的查询数量。这也有助于减少应用程序的 5xx 错误。在下面的图中可以看出 NXDomain 响应次数的不同。
一个更好的解决方案是 Kubernetes 1.18+引入的节点级缓存[6]。
我们可以使用插件定制 CoreDNS。Kubernetes 支持不同类型的工作负载,而标准的 CoreDNS 配置可能无法满足你的所有需求。CoreDNS 有不少树内插件和外部插件。根据你在集群上运行的工作负载类型,假设应用程序之间相互通信,或者在 Kubernetes 集群外部交互的独立应用程序,试图解析的 FQDN 类型可能会有所不同。我们应该试着调整 CoreDNS 的设定。假设你在一个特定的公共/私有云中运行 Kubernetes,并且大多数 DNS 支持的应用程序都在同一个云中。在这种情况下,CoreDNS 还提供了特定的云相关或通用的插件,可以用来扩展 DNS 区域记录。
如果你对根据需求定制 DNS 行为感兴趣,我们建议你阅读 Cricket Liu 和 John Belamaric 合著的《Learning CoreDNS》一书。该本书提供了不同 CoreDNS 插件及其用例的详细概述。它还深入介绍了 CoreDNS + Kubernetes 的集成。
是否在 Kubernetes 集群中运行适当数量的 CoreDNS 实例是关键决定之一。建议至少运行两个 CoreDNS 服务器实例,以更好地保证 DNS 请求得到服务。根据服务的请求数量、请求的性质、集群上运行的工作负载数量和集群的大小,你可能需要为集群添加额外的 CoreDNS 实例或配置 HPA(Horizontal Pod Autoscaler)。
这篇博文试图强调 Kubernetes 中 DNS 请求循环的重要性,很多时候你会以“这不是 DNS 问题”开始,但最终会以“这总是 DNS 问题!”结束。所以要小心这些地雷。
[1]
InfraCloud 的博客: https://www.infracloud.io/blogs/using-coredns-effectively-kubernetes/
[2]
默认 DNS 服务: https://kubernetes.io/blog/2018/12/03/kubernetes-1-13-release-announcement/#coredns-is-now-the-default-dns-server-for-kubernetes
[3]
dns 调试解析: https://kubernetes.io/docs/tasks/administer-cluster/dns-debugging-resolution/
[4]
Prometheus 插件: https://coredns.io/plugins/metrics/
[5]
错误码: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6
[6]
节点级缓存: https://kubernetes.io/docs/tasks/administer-cluster/nodelocaldns/