1. 问题现象与背景分析
最近在RKE2集群中部署NodeLocalDNS并启用网络策略后,不少用户反馈出现间歇性DNS解析失败的情况。具体表现为部分Pod无法解析集群内服务名称(如my-svc.default.svc.cluster.local),但能正常解析外部域名(如example.com)。这个问题在启用NetworkPolicy对DNS流量进行管控时尤为明显。
为什么会出现这种情况? 根本原因在于RKE2环境下NodeLocalDNS与网络策略的协同工作机制存在几个关键冲突点:
- 流量劫持机制冲突:NodeLocalDNS通过iptables规则将Pod的53端口请求重定向到本地缓存,而网络策略可能阻断这种重定向流量
- 协议支持差异:网络策略默认只放行TCP协议,但NodeLocalDNS的缓存服务同时依赖TCP和UDP
- 拓扑感知问题:当Pod被调度到与NodeLocalDNS不同的节点时,网络策略可能导致跨节点通信失败
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. NodeLocalDNS在RKE2中的工作原理
2.1 标准架构解析
在标准RKE2部署中,DNS解析的典型路径如下:
code复制Pod -> CoreDNS Service (kube-dns) -> CoreDNS Pods
启用NodeLocalDNS后,流量路径变为:
code复制Pod -> NodeLocalDNS (本地缓存) -> CoreDNS Service -> CoreDNS Pods
关键变化在于每个节点上会部署一个NodeLocalDNS缓存实例(通常监听169.254.20.10:53),并通过以下iptables规则实现流量拦截:
bash复制-A OUTPUT -d 10.96.0.10/32 -p udp -m udp --dport 53 -j DNAT --to-destination 169.254.20.10:53
-A OUTPUT -d 10.96.0.10/32 -p tcp -m tcp --dport 53 -j DNAT --to-destination 169.254.20.10:53
2.2 与网络策略的交互问题
当引入NetworkPolicy时,以下环节可能引发DNS故障:
- 出口策略限制:如果网络策略只允许Pod访问特定IP范围,可能阻断到NodeLocalDNS(169.254.20.10)的连接
- 协议限制:部分网络策略只放行TCP协议,但DNS查询默认使用UDP
- 标签匹配问题:NodeLocalDNS Pod可能不符合网络策略的PodSelector条件
3. 完整解决方案与配置示例
3.1 网络策略适配配置
创建允许DNS流量的NetworkPolicy(需根据实际安全要求调整):
yaml复制apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-access
spec:
podSelector: {} # 应用于所有Pod
egress:
- ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
to:
- ipBlock:
cidr: 169.254.20.10/32 # NodeLocalDNS IP
- ipBlock:
cidr: 10.96.0.10/32 # CoreDNS Service IP
3.2 NodeLocalDNS部署优化
使用以下Helm values.yaml配置确保兼容性:
yaml复制image:
repository: registry.k8s.io/dns/k8s-dns-node-cache
tag: 1.22.20
localDNS: 169.254.20.10
clusterDNS: 10.96.0.10
# 关键配置:强制使用TCP协议与上游通信
force_tcp: true
# 启用就绪检查确保缓存预热
healthTimeout: 5s
startupTimeout: 30s
3.3 RKE2特定配置调整
在/etc/rancher/rke2/config.yaml中添加:
yaml复制kubelet-arg:
- "cluster-dns=169.254.20.10" # 指向NodeLocalDNS
- "cluster-domain=cluster.local"
4. 故障排查与诊断方法
4.1 基础检查步骤
-
验证DNS解析路径:
bash复制kubectl run -it --rm --image=nicolaka/netshoot testpod -- dns+dig example.com -
检查iptables规则:
bash复制
iptables -t nat -L OUTPUT -n -v | grep 53 -
查看NodeLocalDNS日志:
bash复制
kubectl logs -n kube-system -l k8s-app=node-local-dns
4.2 典型错误与修复
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
NXDOMAIN响应 |
缓存未预热 | 增加startupTimeout |
| 5秒延迟后超时 | TCP连接被阻 | 检查网络策略是否放行TCP 53 |
| SERVFAIL错误 | 上游不可达 | 验证CoreDNS服务端点 |
| 随机解析失败 | 并发限制 | 调整NodeLocalDNS的burst和qps参数 |
4.3 高级调试技巧
抓包分析(在NodeLocalDNS Pod所在节点执行):
bash复制tcpdump -i any -nn -s0 port 53 -w dns.pcap
指标监控(Prometheus查询示例):
promql复制rate(node_local_dns_requests_total[1m]) # 请求量
histogram_quantile(0.99, rate(node_local_dns_duration_seconds_bucket[1m])) # 延迟
5. 性能优化建议
-
缓存调优:
yaml复制# values.yaml cache: maxItems: 5000 ttl: 300 # 秒 negativeTtl: 60 -
连接池配置:
yaml复制upstream: concurrency: 4 timeout: 5s -
资源分配:
yaml复制resources: requests: cpu: 100m memory: 70Mi limits: cpu: 250m memory: 170Mi
6. 安全加固措施
-
限制缓存污染:
yaml复制security: spoofProtection: true dropInvalid: true -
审计日志:
yaml复制logging: audit: true filter: "successful" -
网络策略细化:
yaml复制# 只允许特定命名空间访问 - namespaceSelector: matchLabels: name: kube-system pods: matchLabels: k8s-app: node-local-dns
在实际生产环境中,建议先在小规模测试集群验证配置,通过dnsutils Pod进行端到端测试后再全量部署。我们团队在迁移过程中发现,当NodeLocalDNS的force_tcp设置为false时,某些CNI插件(如Calico)会导致约5%的DNS查询超时,强制使用TCP协议后问题完全解决。
