当Nginx在K8s里‘找不到’服务:一次完整的CoreDNS服务发现排错与优化记录

张开发
2026/4/21 4:43:23 15 分钟阅读

分享文章

当Nginx在K8s里‘找不到’服务:一次完整的CoreDNS服务发现排错与优化记录
当Nginx在K8s里‘找不到’服务一次完整的CoreDNS服务发现排错与优化记录那天凌晨三点手机突然震动起来——监控系统触发了no resolver defined to resolve *****-web.****-namespace的告警。作为一个常年与Kubernetes打交道的SRE我立刻意识到这又是一个经典的DNS解析问题。但这次不同的是它发生在Nginx与CoreDNS的交互场景中而解决方案最终引导我们重新思考Kubernetes服务发现的本质。1. 故障初现Nginx的no resolver defined之谜我们的生产环境运行着一个典型的微服务架构前端Nginx作为API网关通过服务名反向代理到后端的多个服务。当用户报告502 Bad Gateway时查看Nginx错误日志发现了关键线索2024/02/20 03:12:45 [error] 158#158: *142 no resolver defined to resolve backend-service.dev-namespace这个错误看似简单实则暗藏玄机。Nginx默认不会使用系统的DNS解析器而是要求显式配置resolver指令。但在Kubernetes环境中事情变得更加复杂普通Service的DNS特性K8s会为每个Service创建DNS记录如service.namespace.svc.cluster.localClusterIP的局限默认Service的DNS解析到ClusterIP再由kube-proxy实现负载均衡Nginx的特殊性作为反向代理它需要直接解析到Pod IP以实现更精细的控制关键发现通过kubectl run -it --rm debug --imagebusybox --restartNever -- nslookup backend-service.dev-namespace测试发现DNS解析本身工作正常问题出在Nginx的配置机制上。2. 深入CoreDNS理解K8s的DNS解析机制要彻底解决这个问题必须理解CoreDNS在Kubernetes中的工作方式。我们集群的CoreDNS配置如下通过kubectl get configmap coredns -n kube-system -o yaml获取apiVersion: v1 data: Corefile: | .:53 { errors health { lameduck 5s } ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa } prometheus :9153 forward . /etc/resolv.conf cache 30 loop reload loadbalance }几个关键参数决定了服务发现的行为参数作用对Nginx的影响pods insecure允许直接解析Pod DNS需要Headless Service配合loadbalanceDNS轮询负载均衡影响Nginx的upstream选择cache 30DNS缓存时间需与Nginx的valid参数协调通过dig命令进一步验证解析行为# 查询普通Service dig 10.96.0.10 backend-service.dev-namespace.svc.cluster.local short 10.96.152.63 # 查询Headless Service dig 10.96.0.10 headless-service.dev-namespace.svc.cluster.local short 10.244.1.23 10.244.2.453. Nginx配置的艺术resolver与缓存策略正确的Nginx配置需要解决三个核心问题DNS解析器指定必须指向K8s的DNS服务IP缓存时效控制平衡解析开销与Pod变化频率负载均衡策略与K8s Service机制协同工作最佳实践的Nginx配置示例如下http { # 核心配置 resolver 10.96.0.10 valid2s ipv6off; server { listen 80; location /api { set $backend backend-service.dev-namespace.svc.cluster.local; proxy_pass http://$backend:8080; # 高级重试策略 proxy_next_upstream error timeout http_502; proxy_next_upstream_timeout 2s; proxy_next_upstream_tries 3; } } }关键参数解释valid2sDNS记录缓存时间建议2-5秒以适应Pod变化ipv6off禁用IPv6避免不必要的解析延迟set $backend变量存储服务名实现动态解析4. Headless Service当普通Service不再适用在某些场景下将Service改为Headless模式是更优解。我们通过修改Service定义实现apiVersion: v1 kind: Service metadata: name: backend-service spec: clusterIP: None # 关键配置 ports: - port: 8080 targetPort: 8080 selector: app: backendHeadless Service带来的优势直接解析到Pod IP绕过kube-proxy实现更高效的流量路由自定义负载均衡Nginx可以自主控制流量分发策略服务网格友好与Istio等方案协同性更好但需要注意端口必须严格匹配Service端口与Pod容器端口一致需要自行处理Pod变化带来的影响监控复杂度增加需要监控所有Pod实例5. 验证与优化构建健壮的服务发现机制完整的验证流程应该包括基础功能测试kubectl exec -it nginx-pod -- curl -v http://backend-service:8080/health性能基准测试# 使用wrk测试不同配置下的性能 wrk -t4 -c100 -d60s http://nginx-service/benchmark故障注入验证随机删除Pod验证自动恢复模拟CoreDNS宕机测试降级能力我们最终采用的优化方案组合场景方案优点缺点常规微服务普通Service resolver配置简单可靠依赖kube-proxy高性能网关Headless Service Nginx负载极致性能维护成本高混合架构部分Headless DNS缓存调优平衡取舍配置复杂在实施这些优化后系统的服务发现延迟从平均120ms降低到45ms同时502错误率下降了99.8%。更重要的是这次排错过程让我们建立了完整的K8s服务发现知识图谱为后续的架构演进打下了坚实基础。

更多文章