首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

  • 24-12-05 23:25
  • 3962
  • 8598
juejin.cn

最近在边缘部署的单机 K8s 环境中出现了间歇性的 DNS 解析失败,服务跑一段时间就出现了。问题的原因之前没有出现过,因此做了一个记录。

部署架构

  • 单节点 Kubernetes 集群
  • CoreDNS 以主机网络模式运行
  • 通过 iptables 规则将 DNS 查询请求转发至虚拟 IP (10.43.0.10:53)

coredns-arch

问题现象

在排查过程中发现了一个有趣的现象:

  • 正常解析场景:使用 dig 命令手动发送 DNS 查询包均能获得正确响应
  • 异常解析场景:Redisson 客户端的 DNSMonitor 代码(基于 Netty 实现)每 5 秒进行的域名解析会出现失败(用来判断 DNS 解析是否失效)

将 Netty 发送的 DNS 数据包保存下来,使用 nc 工具重放相同的 payload 能够成功获得响应。测试命令如下:

代码解读
复制代码
nc -u 10.43.0.10 53 < dns_pkt

这就有点奇怪了,几乎所有的条件都一致。后面发现一个奇怪的地方,出问题的时候,在 DNS 解析失败的情况下,Netty 客户端始终使用同一个源端口进行 DNS 查询。这个特征与正常情况下应该随机选择源端口的行为不符。

通过检查 conntrack NAT 表项,conntrack 表项的反向 SRC 和 DST 地址都是不符合预期的(后面会分析),又由于 Redisson 的 DNSMonitor 组件配置了 5 秒的监控间隔,这导致:DNS 查询请求每 5 秒发送一次 UDP,conntrack 表项的默认过期时间为 30 秒,频繁的 DNS 查询导致 conntrack 表项无法正常老化和更新。

css
代码解读
复制代码
udp 17 173 src=10.42.0.129 dst=10.43.0.10 sport=52421 dport=53 src=10.43.0.10 dst=172.20.112.199 sport=53 dport=52421 [ASSURED] mark=0 use=1

当我手动把这个记录删除时,解析立马可以恢复正常。

为什么 conntrack 记录会跑着跑着就不对了。我写了一个脚本监控 conntrack 异常记录出现的时间,

DNS 解析异常与凌晨的定时任务存在关联。运维定时任务导致 CoreDNS 发生非预期的每日重启。

为什么 coredns 重启会导致 conntrack 异常

在 CoreDNS 重启过程中会出现一个临时状态,NAT 规则变更:

  • CoreDNS Pod 终止时,相关的 NAT 规则会被删除
  • 新的 CoreDNS Pod 启动后,会重新添加 NAT 规则

在 NAT 规则被删除到重新添加的这段时间内,如果容器与 CoreDNS(10.43.0.10)发生通信,由于特定的 NAT 规则缺失,流量会被错误地导向默认 NAT 规则处理。

我做了一个重启期间的 iptable 的 NAT 表变化记录,重启操作前的 iptables 的 NAT 表规则:

sql
代码解读
复制代码
Chain KUBE-SERVICES (2 references) pkts bytes target prot opt in out source destination 2838 262K KUBE-SVC-TCOU7JCQXEZGVUNU udp -- any any anywhere 10.43.0.10 /* kube-system/kube-dns:dns cluster IP */ udp dpt:domain 0 0 KUBE-NODEPORTS all -- any any anywhere anywhere /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL Chain KUBE-SVC-TCOU7JCQXEZGVUNU (1 references) pkts bytes target prot opt in out source destination 2838 262K KUBE-SEP-2NOFLIOVOWN7BA6Y all -- any any anywhere anywhere /* kube-system/kube-dns:dns */ Chain KUBE-SEP-2NOFLIOVOWN7BA6Y (1 references) pkts bytes target prot opt in out source destination 162 15174 KUBE-MARK-MASQ all -- any any seewo-ac-server anywhere /* kube-system/kube-dns:dns */ 2838 262K DNAT udp -- any any anywhere anywhere /* kube-system/kube-dns:dns */ udp to:172.20.112.199:53

在重启过程中,这几条规则被删除了。

conntrack 前后对比如下:

css
代码解读
复制代码
$ conntrack -L # 异常 udp 17 177 src=10.42.0.33 dst=10.43.0.10 sport=52421 dport=53 src=10.43.0.10 dst=172.20.112.199 sport=53 dport=52421 [ASSURED] mark=0 use=1 # 正常 udp 17 28 src=10.42.0.33 dst=10.43.0.10 sport=59515 dport=53 src=172.20.112.199 dst=10.42.0.33 sport=53 dport=59515 mark=0 use=1

NAT 规则不存在时:

当 NAT 规则存在时:

错误的 NAT 规则会导致 conntrack 表项记录异常,这些异常的 conntrack 记录会持续存在,直到 UDP conntrack 表项自然过期(默认 30 秒),影响后续的 DNS 查询请求的正常处理。

如何复现

写一段 Go 程序,使用同一个端口,不停的与 10.43.0.10 的 53 端口通信

go
代码解读
复制代码
package main import ( "fmt" "github.com/miekg/dns" "net" "os" "strconv" "time" ) const DnsAddr = "10.43.0.10:53" func main() { if len(os.Args) < 2 { fmt.Println("Please provide domain name") return } domain := os.Args[1] sleepTime := 1000 if len(os.Args) > 2 { if t, err := strconv.Atoi(os.Args[2]); err == nil { sleepTime = t } } fmt.Printf("dns_fk!, dns: %s, domain: %s, sleep: %dms\n", DnsAddr, domain, sleepTime) conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 52001}) if err != nil { fmt.Printf("Failed to bind: %v\n", err) return } defer conn.Close() dnsAddr, err := net.ResolveUDPAddr("udp", DnsAddr) if err != nil { fmt.Printf("Failed to resolve DNS address: %v\n", err) return } packet := buildDNSPacket(domain) count := 0 lastPrint := time.Now() for { count++ if time.Since(lastPrint) > 3*time.Second { fmt.Printf("send count: %d\n", count) lastPrint = time.Now() } _, err := conn.WriteToUDP(packet, dnsAddr) if err != nil { fmt.Printf("Failed to send packet: %v\n", err) continue } if sleepTime > 0 { time.Sleep(time.Duration(sleepTime) * time.Millisecond) } } } func buildDNSPacket(domain string) []byte { m := new(dns.Msg) m.SetQuestion(dns.Fqdn(domain), dns.TypeA) m.RecursionDesired = false m.Id = dns.Id() data, _ := m.Pack() return data }
bash
代码解读
复制代码
./udp_fk rocketmq-nameserver.xeewo-edge-base.svc.cluster.local 10

此时会创建一条 conntrack 记录

css
代码解读
复制代码
$ conntrack -E -s 10.42.0.33 -p udp [NEW] udp 17 30 src=10.42.0.33 dst=10.43.0.10 sport=52001 dport=53 [UNREPLIED] src=172.20.112.199 dst=10.42.0.33 sport=53 dport=52001 [UPDATE] udp 17 30 src=10.42.0.33 dst=10.43.0.10 sport=52001 dport=53 src=172.20.112.199 dst=10.42.0.33 sport=53 dport=52001 [UPDATE] udp 17 120 src=10.42.0.33 dst=10.43.0.10 sport=52001 dport=53 src=172.20.112.199 dst=10.42.0.33 sport=53 dport=52001 [ASSURED]

然后重启 coredns

perl
代码解读
复制代码
kubectl delete pod coredns-65b5854f7-lkwlf -n kube-system

观察 conntrack 变化过程:

scss
代码解读
复制代码
$ conntrack -E -s 10.42.0.33 -p udp // 重启前 [NEW] udp 17 30 src=10.42.0.33 dst=10.43.0.10 sport=52001 dport=53 [UNREPLIED] src=172.20.112.199 dst=10.42.0.33 sport=53 dport=52001 [UPDATE] udp 17 30 src=10.42.0.33 dst=10.43.0.10 sport=52001 dport=53 src=172.20.112.199 dst=10.42.0.33 sport=53 dport=52001 [UPDATE] udp 17 120 src=10.42.0.33 dst=10.43.0.10 sport=52001 dport=53 src=172.20.112.199 dst=10.42.0.33 sport=53 dport=52001 [ASSURED] // 重启后 // 旧的 conntrack 记录被删除 [DESTROY] udp 17 src=10.42.0.33 dst=10.43.0.10 sport=52001 dport=53 src=172.20.112.199 dst=10.42.0.33 sport=53 dport=52001 [ASSURED] // 新增了一条新的 conntrack 记录(异常) [NEW] udp 17 30 src=10.42.0.33 dst=10.43.0.10 sport=52001 dport=53 [UNREPLIED] src=10.43.0.10 dst=172.20.112.199 sport=53 dport=42285

通过抓包就可以发现,此时的 DNS 通信就是失败的。

k8s 的问题?

事后,经过查看 k8s 的 issue,还真这是一个老 bug 了(kube-proxy/iptables, client continuous to send packets, udp server pod restart cause UDP conntrack entry error )链接地址 github.com/kubernetes/…

k8s 的新版本可以缓解这一问题。

修复

k8s 版本是升不动的,改业务代码倒是比较容易,本质问题是 redisson 的 DNSMonitor 特性,目前看是使用同一个 UDP 端口去查询 DNS,解决一下就可以了,具体的措施就不展开。

小结

这次问题出现有两个条件:

  • coredns 每天定期重启,加速了问题的出现
  • redisson 使用固定的端口,持续的请求 coredns,导致错误的 conntrack 记录无法过期清理

正经人谁用 redisson。。。

注:本文转载自juejin.cn的的文章"https://juejin.cn/post/7444202714316914740"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

103
后端
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top