K8s服务发现避坑指南:当Nginx遇上CoreDNS,为什么你的Service名解析总失败?
K8s服务发现避坑指南:当Nginx遇上CoreDNS,为什么你的Service名解析总失败?
深夜的告警铃声总是格外刺耳。当运维工程师发现Nginx代理的微服务A无法调用服务B,错误日志里赫然写着"no resolver defined to resolve..."时,一场关于K8s服务发现的深度排查就此展开。这不是简单的配置错误,而是Kubernetes网络模型中那些鲜为人知的"暗坑"在作祟。
1. 故障现场还原:当Nginx遇到CoreDNS
让我们从一个真实的故障场景开始。某电商平台的订单服务(Service A)通过Nginx反向代理调用库存服务(Service B),突然开始频繁报错。查看Nginx错误日志,会发现类似这样的关键信息:
2024/03/15 02:34:56 [error] 15#15: *176 inventory-service.default.svc.cluster.local could not be resolved (3: Host not found)核心矛盾点在于:同样的Service名称,为什么有些Pod能解析,Nginx却不行?这涉及到K8s服务发现的三个关键组件协同问题:
- CoreDNS的工作机制:默认情况下,CoreDNS会为每个Service创建DNS记录,格式为
<service>.<namespace>.svc.cluster.local - Nginx的解析特性:Nginx不会自动使用宿主机的DNS配置,必须显式指定resolver
- K8s网络策略:Pod到Service的通信与实际Service类型密切相关
提示:快速验证DNS是否正常的方法是在任意Pod内执行
nslookup inventory-service.default.svc.cluster.local,如果返回IP但Nginx仍报错,问题很可能出在Nginx配置层。
2. 深度解剖:Service DNS解析的三种模式
Kubernetes中的Service DNS解析并非表面看起来那么简单。根据Service类型不同,CoreDNS会生成完全不同的DNS记录:
| Service类型 | DNS记录类型 | 解析结果 | 适用场景 |
|---|---|---|---|
| ClusterIP(默认) | A记录 | 返回Service的ClusterIP | 常规服务间通信 |
| Headless Service | A记录 | 返回所有Pod IP列表 | 需要直接访问Pod的场景 |
| ExternalName | CNAME记录 | 返回配置的外部域名 | 集成外部服务 |
关键差异点在于Headless Service(无头服务)。当我们将Service的clusterIP设置为None时:
apiVersion: v1 kind: Service metadata: name: inventory-service spec: clusterIP: None selector: app: inventory ports: - protocol: TCP port: 8080 targetPort: 8080此时,CoreDNS会直接返回该Service后端所有Pod的IP地址,而不是虚拟IP。这解释了为什么有些场景下改为Headless Service就能解决问题——它绕过了kube-proxy的iptables/NFTables规则,直接进行Pod级通信。
3. Nginx的特殊配置之道
Nginx在K8s环境中的配置有其特殊性。要让Nginx正确解析Service名称,必须注意以下要点:
必须配置项:
- 显式指定resolver:
resolver 10.96.0.10 valid=1s; # CoreDNS的ClusterIP - 在proxy_pass中使用变量触发动态解析:
set $upstream http://inventory-service.default.svc.cluster.local; proxy_pass $upstream;
常见陷阱:
- 直接使用域名而不设置resolver
- 忘记设置
valid参数导致DNS缓存时间过长(Pod IP变化时无法及时更新) - 在proxy_pass中直接使用静态域名而非变量
注意:获取CoreDNS ClusterIP的命令是
kubectl get svc -n kube-system | grep dns,不同K8s发行版中服务名可能是kube-dns或coredns。
4. 高级排查:当基础方案失效时
如果按照上述配置仍然失败,就需要深入网络层排查。以下是进阶排查路线图:
检查CoreDNS健康状态:
kubectl -n kube-system logs -l k8s-app=kube-dns kubectl -n kube-system describe pod -l k8s-app=kube-dns验证网络策略:
kubectl describe networkpolicy检查kube-proxy工作模式:
kubectl logs -n kube-system -l k8s-app=kube-proxy测试跨节点通信:
kubectl run -it --rm debug --image=nicolaka/netshoot -- bash curl -v http://inventory-service.default.svc.cluster.local:8080
特别关注点:当集群使用Calico等CNI插件时,可能需要检查GlobalNetworkPolicy是否阻止了DNS查询流量。
5. 架构视角:服务发现的最佳实践
经过上述深度排查后,我们可以总结出K8s服务发现的黄金法则:
Nginx配置规范:
- 始终配置resolver指向CoreDNS
- 设置合理的DNS缓存时间(通常1-5s)
- 使用变量触发动态解析
Service类型选择策略:
- 需要负载均衡 → ClusterIP
- 需要直接访问Pod → Headless Service
- 需要会话保持 → Headless Service + 客户端负载均衡
命名空间管理:
set $upstream http://service.namespace.svc.cluster.local;监控指标:
- CoreDNS的DNS查询延迟
- DNS查询错误率
- Nginx的upstream响应时间
在微服务架构中,这些配置差异可能意味着99.9%和99.99%的SLA区别。某金融科技团队在将关键服务改为Headless Service后,延迟从平均120ms降到了45ms,这正是理解了服务发现底层机制带来的直接收益。
