最佳实践:Kubernetes 集群中 DNS 故障的可观测性与根因诊断

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
应用实时监控服务-可观测链路OpenTelemetry版,每月50GB免费额度
应用实时监控服务-用户体验监控,每月100OCU免费额度
简介: 本文介绍了 CoreDNS 服务器、客户端侧的常见 DNS 异常、故障根因,异常观测方案和故障处理流程,希望对大家的问题诊断有所帮助。DNS 服务对于 Kubernetes 集群是至关重要的,除了观测异常之外,我们在架构设计之初就应充分考虑 DNS 服务的稳定性,采纳一些例如 DNS 本地缓存之类的最佳实践。

作者:谢于宁(予栖)


前言


本议题主要介绍如何在 Kubernetes 集群中实现 DNS 故障的可观测性以及疑难问题的诊断,我会将从以下几方面展开:


  • Kubernetes 集群中 DNS 如何工作?
  • 常见的 DNS 故障原因
  • 如何诊断 DNS 服务端异常 - CoreDNS 内置的可观测性能力
  • 如何诊断客户端异常 - 基于 BPF 的客户端 DNS 异常定位
  • 如何处理一起真实的 DNS 故障?


Kubernetes 中 DNS 如何工作?


Kubernetes 集群中 DNS 无处不在:


  • 当你的微服务架构中一个 APP,想要跟另外一个 APP 进行通信的时候,它依赖于 DNS 做服务发现。


  • 当你部署了一个分布式数据库,数据库节点之前利用 DNS 发现彼此。


  • 当你的 Kubernetes 集群中日志组件、监控组件需要去跟 APIServer 进行通信、更新数据的时候,DNS 告诉它 APIServer 是哪个 IP 地址。


1.png

image.gif

我们以一个 web APP 应用想要访问 database 数据库为例,表面上看起来就是一个数据库链接被建立了,但底下发生了许多事:


  1. 应用被配置了 database:6379 作为数据库链接串
  2. 应用启动后,通过 DNS Server 查询 database 的实际位置,即 IP 地址
  3. DNS Server 返回 database 这个 Kubernetes Service 的 IP 地址
  4. 应用访问 Service IP 地址,负载均衡 IPVS 模块对 IP 地址做了目的地址转换 DNAT,流量被转发到了真实的数据库 Pod IP 172.20.1.7


image.gif2.jpeg


那么,web APP 应用是如何找到 DNS Server 的呢?我们观察 Pod 容器中 /etc/resolv.conf 文件可以发现里面配置了一个 DNS Server 的 IP 地址,172.21.0.10。这个 IP 地址其实就是集群中 kube-system 命名空间下的 kube-dns ClusterIP 服务的 IP 地址,集群中的 CoreDNS 通过这个 ClusterIP 服务提供 DNS 解析的服务。同时从图中可以看到,集群中其实部署有多个 CoreDNS 的容器副本,这些副本彼此地位相同,客户端访问这些副本时,也是通过 IPVS 做负载均衡的。


除了 IP 地址之外,/etc/resolv.conf 还通常配置有 search path,它由多个域名后缀组成。在请求一个域名时,如果域名包含的 dots(点)的数量小于 ndots,就会被拼接上 search path,组成一个完整的域名(FQDN)再发出域名查询。本例子中,web app 请求 database 时,由于 database 包含的点数量(ndots)为 0,小于设定的 ndots 5,就会被拼接上 default.svc.cluster.local 这个后缀,组成 database.default.svc.cluster.local 这个完整域名(FQDN)再发出。search path 和 ndots 的设置需要非常小心,不合理的配置可能会让应用的解析域名耗时成倍的增加。


在真实的 Kubernetes 集群中,我们的应用经常会访问一些外部域名,以 www.aliyun.com 为例,应用请求这个域名的 IP 地址的过程中,实际会依次请求 www.aliyun.com.default.svc.cluster.local、www.aliyun.com.svc.default.cluster.local、www.aliyun.com.cluster.local、www.aliyun.com,前三个域名通通不存在,CoreDNS 均会返回域名不存在(NXDOMAIN)的报错,直到最后这一次请求 CoreDNS 才会返回正确(NOERROR)的结果。


除此之外,大部分的现代应用都支持 IPv6 了,其解析的时候都会并发地请求 A 和 AAAA(IPv6)记录,因此对于一个集群外部域名,Pod 容器内往往需要花费 8 次 DNS 解析。这带来了成倍的 DNS 解析压力。


常见的 DNS 故障根因


DNS 历史悠久,协议简单可靠,但从上面我们发现 Kubernetes 中 DNS 服务发现并非那么简单:


  • 爆炸半径大:几乎所有数据面、管控平面都会依赖于 DNS
  • 请求链路长:作为 Kubernetes Service,访问 DNS 涉及内核 IPVS、iptables 多个模块
  • 请求 QPS 高:应用本身请求域名就会非常频繁,加上 search 搜索域的设计,这个 QPS 会翻倍


我们在日常的运维和工单处理过程中,我们发现 DNS 故障经常发生,且每次的原因并不一样。


CPU 限制


CoreDNS Server 能提供的 QPS 是与 CPU 正相关的,当 CPU 明显出现瓶颈或业务高峰期来临时,CoreDNS 在响应 DNS 请求时会出现一些明显的延迟或者解析失败。因此,我们应维持副本数在一个合适的水位。一般建议每个副本的峰值 CPU 使用量小于一核,如果其在业务峰值期间占用 CPU 大于一核,建议对 CoreDNS 进行副本扩容。默认情况下,可以采用副本数和集群节点数一比八的比值来部署,即每扩容八个集群节点,增加一个 CoreDNS 副本。


Conntrack 表项限制


另外一个常见的异常是 Conntrack Table 表项满。默认情况下,Linux 中每个 TCP、UDP 类型的链接都会占用掉一个 Conntrack 表项,这个表的数目是有限制的。当业务使用短链接请求特别多的时候,很容易把表打满,报出 Conntrack table full 的异常。针对这种异常,可以扩大 Conntrack Table 或者尽可能使用一些长链接请求。除了 Conntrack 限制以外,ARP Table、Socket 数目限制都会导致类似的问题。


Conntrack 中源端口复用竞态问题


还有一些场景是由于冲突导致的。通常我们在高 QPS 场景下,客户端会复用源端口请求 DNS。比如当我们去使用 Alpine 作为容器基础镜像时,其内置的 Musl 运行库会并发地采用同一个源端口请求 A 和 AAAA 域名记录。恰巧这种并发触发了内核中 Conntrack 的 Race Condition 的缺陷,这个缺陷会导致后进来的 DNS 报文被直接丢弃。


IPVS 后端变更后,报文源端口复用导致丢包


我们从内核网络栈的设计来看,DNS 由于 UDP 的天然设计的问题,就是往往特别容易丢包。再举一个例子就是 IPVS 的场景,当集群使用 IPVS 作为负载均衡时,如果 CoreDNS 有单个副本重启、或者副本缩容了,此时如果有一个报文采用了一个几分钟前刚用过的源端口发起请求,这个报文会被丢弃,导致域名解析的延迟。以上两个源端口冲突的问题,都可以通过 Kernel 升级来解决。


CoreDNS 软件 Bug


早期版本的 CoreDNS 存在诸多问题,包含 APIServer 断连时 Panic 重启、AutoPath 插件异常退出等问题,通常会导致一些偶现的解析失败问题,但如果 APIServer 持续不能恢复可能会带来整个集群的域名完全无法解析。想要避免这些问题,建议将 CoreDNS 升级到最新的稳定版本。


CoreDNS 内置的可观测性能力


前面我们知道了 Kubernetes 集群中 DNS 故障的常见根因,如何去发现他们呢?值得庆幸的是,CoreDNS 是一款天生插件化的 DNS 服务器软件,它内置了非常丰富的可观测性能力。以下我们介绍几个常用的插件,以及使用方式。


Log


image.gif3.jpeg

4.png

image.gif

CoreDNS 每接收到一个请求并完成响应时,会打印一行包含请求域名内容、来源 IP、响应状态码在内的日志,这一行日志类似于 Nginx 的 Access Log,可以帮我们我们快速地定位一些问题发生的位置,完成问题的整个定界过程。


此外我们可以将日志上传到云端,做日志持久化、绘出趋势图等等,甚至我们可以做一些域名访问的审计,例如识别到集群内某一个 Pod 访问了非法的域名等等。在右图的这个例子中采用了 SLS 日志服务的仪表盘,可以将每天的趋势,CoreDNS 响应状态码的以可视化的方式展示出来,方便问题的分析。当然,除了可视化以外,日志仪表盘支持告警规则配置[1]


Dump


Dump 插件和 Log 插件类似,也会打印一行日志,差异在于其会在 CoreDNS 接收到客户端请求的时候就开始打印。如果你确认解析请求到了 CoreDNS 但 Log 日志没有输出时,可以打开 Dump 插件排查。


Debug


5.jpeg


image.gifDebug 插件顾名思义就是用于排错。当网络链路异常或上游 DNS 服务器返回异常时,CoreDNS 可能会收到一个异常的报文,此时 Debug 插件就可以将报文以完整 16 进展的方式打印出来。后期就可以用 Wireshark 之类的工具分析这个报文,看下报文在哪里出了问题。


DNSTap


6.jpeg

image.gif7.jpeg


image.gifDNSTap 是一个非常有意思的插件。DNSTap 本身是一种灵活的二进制日志的协议,专门为 DNS 报文所设计的。左图中共有四条报文链路:


  1. 客户端请求 CoreDNS
  2. CoreDNS 请求上游 DNS 服务器
  3. 上游 DNS 服务器响应 CoreDNS
  4. CoreDNS 响应客户端


这四条报文均可以被 CoreDNS dnstap 插件以该二进制日志的方式投递到远程的 dnstap server 中,dnstap server 可以完成存储、分析、上报异常等动作。dnstap 在发送这些报文的过程中采用了异步 IO 设计,避免了本地磁盘日志写入和后续处理过程中报文反序列化流程,可以实现低性能开销、高效的报文采集和异常诊断流程。


那么我们如何在 dnstap server 中实现报文的异常诊断呢?dnstap server 是可以解析出原始的 DNS 报文的,从图中的 DNS 报文的 RCODE 状态码字段可以提取出 DNS 解析请求的响应状态码。不同的状态码提现了不同的异常类型,在实际问题排查过程中可以根据右表进行问题定位。


8.jpeg


除了报文本身的 RCODE 字段外,我们可以根据 dnstap 报文的 Message Type 做一些异常判断。当一个相同的 DNS Message ID 的报文仅产生了 ClientQuery 和 ForwarderQuery 时,说明上游并没有响应该 DNS 请求,CoreDNS 也没有响应给客户端。针对这样不同 Message Type 出现的组合,可以诊断出不同的场景。我们利用这个思路,在阿里云 ACK 也提供了一个开箱即用的方案,可以快速的做一些问题的定界[2]


Trace


9.jpeg


Trace 插件实现了 OpenTracing 的能力,可以将一个请求从接收,到逐个插件何时开始处理、何时结束处理,都记录在一个外部的 OpenTracing 服务器中。通常我们怀疑某个 DNS 请求耗时太久,想要去定位延迟点位、优化耗时的话,可以使用这个插件快速发现瓶颈所在。


Prometheus


image.gif10.jpeg

11.png

image.gif

Prometheus 插件就提供了暴露 CoreDNS 服务端侧各种运行指标的能力。通常我们需要将 metrics 接口配置到 Prometheus 集群的采集对象列表中,每隔一定时间,Prometheus 就可以将 CoreDNS 的数据取走并存到时序数据库中。利用 Grafana 仪表盘可以将这些指标展示出来,我们可以从图表中观察到不同 RCODE 状态码的返回趋势、请求的 QPS 趋势等。针对这些趋势设置合理的阈值就可以做实时告警了。右图时在阿里云 ACK 集群使用 ARMS Prometheus 服务默认配置的 CoreDNS 监控仪表盘示例。


基于 BPF 的客户端 DNS 异常定位


image.gif12.jpeg


很多 DNS 异常发生在客户端这一侧,CoreDNS 根本没有接收到请求。这类问题根因通常会较难定位,除了观察内核日志、CPU 负载情况等等,我们还经常使用 tcpdump 抓包的方式分析报文的流向,在哪个地方产生了丢失。当一个 DNS 异常发生的概率比较低,tcpdump 抓包这种方式的成本就变得非常高。


好消息是,我们可以利用 BPF 工具去观测一些常见的内核侧的报文异常。图中是一个 BPF 示例工具 trace_dns_drops.bt[3],这个工具启动后会监测常见的两个 DNS 报文丢失的内核函数,根据函数的返回值判断该报文是否会丢失,如果丢失则打印出当前访问的报文源 IP 源端口。在这个例子里,我们模拟了 IPVS 后端变更时源端口冲突导致的报文丢失和错误的 iptables 把 DNS 报文丢弃的两个场景。除了源端口和源 IP 以外,我们也可以再对工具做一些改写,从报文内提取 DNS 报文的字段,例如记录类型、请求的域名等。


BPF 技术可以帮我们精准定位一个报文在内核的流转流程,通过提取时间、报文五元组、经过的函数,就可以知晓报文具体的丢包原因,继而进一步优化配置。当前开源社区也有一些非常好用的工具可以辅助定位,例如 BCC 的 gethostlatency.py、Cilium 的 pwru 项目等。


如何应对一个 DNS 故障


上面我们介绍了如何观测 DNS 服务端或客户端侧的异常,但当一个真实的线上环境中遇到了 DNS 故障,我们应该怎么处理呢?


明确问题


  1. 不要过早地下定论:Kubernetes 中 DNS 解析链路很长,不要盲目地认定某一侧出了问题
  2. 明确报错原因:不同客户端在 DNS 异常时有不同类型的报错,有时候是连不上 DNS 服务器,有时候是连上了 DNS 服务器但域名不存在
  3. 域名是否拼写正确,Pod DNS 配置和 CoreDNS 配置是否符合预期
  4. 最近有没有做过任何变更,比如扩容机器了以后,新的机器不一定在一个安全组


收集信息


  1. 向客户端、客户端所在集群节点、CoreDNS 所在集群节点、CoreDNS 服务端要日志,看异常时间内请求是否有相关明显异常的日志
  2. 查询 Kubernetes 事件、集群节点负载监控等等,有无异常事件、异常流量峰值等


验证猜想


  1. 从客户端、客户端所在集群节点、同集群节点其它容器分别手动发起 dig 测试,可以测试到 kube-dns ClusterIP 的 IP、或者不同 CoreDNS 副本的 IP,验证网络上有无问题
  2. 启用 CoreDNS 的可观测性插件,或是运行 BPF、tcpdump 等工具,验证报文是否异常


修复问题


  1. 针对找到的根因,寻求解决方案
  2. 逐步地执行修复,并做好回滚措施
  3. 执行验证问题恢复的测试流程,通过各类仪表盘全面地观察修复效果


以上步骤中,从收集完信息到验证猜想之间,我们往往需要问自己几个问题,例如:


  1. 异常出现的频次如何?一直出现/高峰时期/时不时的
  2. 异常出现的范围如何?整个集群/固定的几个 Pod/随机 Pod/随机节点上任意 Pod


这些问题的答案指向了非常多可能的异常原因。我们在阿里云 ACK 产品文档中提供了一系列  Kubernetes 域名解析异常问题排查[4]的文档,可供辅助排查。


总结


本文介绍了 CoreDNS 服务器、客户端侧的常见 DNS 异常、故障根因,异常观测方案和故障处理流程,希望对大家的问题诊断有所帮助。DNS 服务对于 Kubernetes 集群是至关重要的,除了观测异常之外,我们在架构设计之初就应充分考虑 DNS 服务的稳定性,采纳一些例如 DNS 本地缓存之类的最佳实践。谢谢大家!


参考文档


[1] DNS 最佳实践

https://help.aliyun.com/document_detail/172339.html


[2] 本议题 PPT 下载地址

https://kccncosschn21.sched.com/event/pcag/huan-daepkubernetes-zhong-shi-dns-dun-zha-qi-tizhong-best-practice-dns-failure-observability-and-diagnosis-in-kubernetes-yuning-xie-alibaba


[3] Linux /etc/resolv.conf 配置文档

https://man7.org/linux/man-pages/man5/resolv.conf.5.html


相关链接


[1]日志仪表盘

https://help.aliyun.com/document_detail/213461.html


[2]问题的定界

 https://help.aliyun.com/document_detail/268638.html


[3]BPF 示例工具 trace_dns_drops.bt 

https://gist.github.com/xh4n3/61d8081b834d7e21bff723614e07777c


[4]异常问题排查

https://help.aliyun.com/document_detail/404754.html


点击文末“此处”,即可查看容器服务 ACK 产品详情!




#云原生与云未来的新可能#


复制并前往下方链接,即可免费下载电子书

https://developer.aliyun.com/topic/download?id=8265


13.png


相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
1月前
|
Kubernetes 监控 开发者
掌握容器化:Docker与Kubernetes的最佳实践
【10月更文挑战第26天】本文深入探讨了Docker和Kubernetes的最佳实践,涵盖Dockerfile优化、数据卷管理、网络配置、Pod设计、服务发现与负载均衡、声明式更新等内容。同时介绍了容器化现有应用、自动化部署、监控与日志等开发技巧,以及Docker Compose和Helm等实用工具。旨在帮助开发者提高开发效率和系统稳定性,构建现代、高效、可扩展的应用。
|
12天前
|
Kubernetes 算法 调度
阿里云 ACK FinOps成本优化最佳实践
本文源自2024云栖大会梁成昊演讲,讨论了成本优化策略的选择与实施。文章首先介绍了成本优化的基本思路,包括优化购买方式、调整资源配置等基础策略,以及使用弹性、资源混部等高级策略。接着,文章详细探讨了集群优化和应用优化的具体方法,如使用抢占式实例降低成本、通过资源画像识别并优化资源配置,以及利用智能应用弹性策略提高资源利用效率。
|
12天前
|
Kubernetes 容灾 调度
阿里云 ACK 高可用稳定性最佳实践
本文整理自2024云栖大会刘佳旭的演讲,主题为《ACK高可用稳定性最佳实践》。文章探讨了云原生高可用架构的重要性,通过Kubernetes的高可用案例分析,介绍了ACK在单集群高可用架构设计、产品能力和最佳实践方面的方法,包括控制面和数据面的高可用策略、工作负载高可用配置、企业版容器镜像服务高可用配置等内容,旨在帮助企业构建更加可靠和高效的应用运行环境。
|
1月前
|
存储 运维 Kubernetes
K8s业务迁移最佳实践: 灵活管理资源备份与调整策略,实现高效简便的应用恢复
在当今快速变化的云原生领域,Kubernetes(K8s)集群的运维面临着诸多挑战,其中灾备与业务迁移尤为关键。ACK备份中心支持丰富的资源调整策略,在数据恢复阶段即可自动适配目标集群环境,确保业务无缝重启。
|
1月前
|
Kubernetes 监控 API
深入解析Kubernetes及其在生产环境中的最佳实践
深入解析Kubernetes及其在生产环境中的最佳实践
48 1
|
1月前
|
运维 Kubernetes Cloud Native
Kubernetes云原生架构深度解析与实践指南####
本文深入探讨了Kubernetes作为领先的云原生应用编排平台,其设计理念、核心组件及高级特性。通过剖析Kubernetes的工作原理,结合具体案例分析,为读者呈现如何在实际项目中高效部署、管理和扩展容器化应用的策略与技巧。文章还涵盖了服务发现、负载均衡、配置管理、自动化伸缩等关键议题,旨在帮助开发者和运维人员掌握利用Kubernetes构建健壮、可伸缩的云原生生态系统的能力。 ####
|
1月前
|
人工智能
深度解析AI在医疗诊断中的最新应用与挑战
深度解析AI在医疗诊断中的最新应用与挑战
|
1月前
|
存储 Kubernetes 调度
深度解析Kubernetes中的Pod生命周期管理
深度解析Kubernetes中的Pod生命周期管理
|
2月前
|
存储 缓存 监控
深入解析:Elasticsearch集群性能调优策略与最佳实践
【10月更文挑战第8天】Elasticsearch 是一个分布式的、基于 RESTful 风格的搜索和数据分析引擎,它能够快速地存储、搜索和分析大量数据。随着企业对实时数据处理需求的增长,Elasticsearch 被广泛应用于日志分析、全文搜索、安全信息和事件管理(SIEM)等领域。然而,为了确保 Elasticsearch 集群能够高效运行并满足业务需求,需要进行一系列的性能调优工作。
162 3
|
2月前
|
存储 Kubernetes 监控
深度解析Kubernetes在微服务架构中的应用与优化
【10月更文挑战第18天】深度解析Kubernetes在微服务架构中的应用与优化
137 0

相关产品

  • 容器服务Kubernetes版