一次 monitoring prometheus跨节点访问失败问题的排查与解决
背景
最近在 Kubernetes 集群中通过 kube-prometheus 项目部署了监控系统,希望使用 NodePort 方式从集群外部访问 Prometheus 的 Web UI(端口 9090),以便团队成员查看监控指标。
集群环境:
- Kubernetes 版本:v1.x
- 网络插件:Calico (IPIP 模式)
- Prometheus 部署:kube-prometheus(通过 manifests 或 Helm)
- 测试的 Nginx 应用:部署在
default命名空间,使用 NodePort 对外暴露,访问正常
奇怪的是,Prometheus 的 NodePort 服务虽然创建成功,但外部访问总是超时或连接失败,而同一个集群中自己部署的 Nginx 却能正常工作。这引发了我们的排查兴趣。
问题现象
为 Prometheus 创建 NodePort Service:
kubectl expose svc prometheus-k8s -n monitoring --name prom-nodeport --type NodePort --port 9090Service 创建成功,分配的 NodePort 端口为
30231(示例)。- 尝试通过任意节点 IP 访问
http://<node-ip>:30231,浏览器一直加载超时,curl命令卡住或报Connection timeout。 - 但自己部署的 Nginx NodePort 服务(端口
31300)可以正常访问,返回 Welcome to nginx 页面。 集群内部访问 Prometheus 的 ClusterIP Service 正常:
kubectl run test --rm -it --image=busybox -- wget -O- http://prometheus-k8s.monitoring:9090能正常返回 302 跳转。
- 直接访问 Prometheus Pod IP 从 master01 节点超时,但从 Pod 所在节点(work02)本地访问成功。
这提示问题可能出在跨节点网络或安全策略层面。
排查过程
第一步:检查 Service 和 Endpoints
kubectl get endpoints -n monitoring输出显示 Prometheus 的 Endpoints 正常,包含两个 Pod IP。
第二步:检查 Pod 监听地址
进入 Prometheus 容器查看端口监听情况:
kubectl exec -n monitoring prometheus-k8s-0 -- netstat -tlnp | grep 9090输出 tcp6 :::9090 LISTEN,表示监听在 IPv6 的 ::,这在 Linux 默认配置下(net.ipv6.bindv6only=0)同样接受 IPv4 连接,理论上不是问题。
查看完整启动参数,没有 --web.listen-address 参数,默认应为 0.0.0.0:9090。
第三步:测试直接访问 Pod IP
获取 Prometheus Pod IP:100.90.254.202,从 master01 节点访问:
curl -v http://100.90.254.202:9090卡住,无响应。从 work02(Pod 所在节点)访问:
curl -v http://100.90.254.202:9090成功返回 302 跳转。
结论:master01 到 work02 的 Pod 网络不通,但 work02 本机到 Pod 是通的。
第四步:对比 Nginx Pod 的跨节点访问
查看 Nginx Pod(也在 work02 上)的 IP:100.90.254.216,从 master01 访问:
curl -v http://100.90.254.216成功返回 Nginx 欢迎页。
说明 master01 到 work02 的 Pod 网络基础是通的,问题只针对 Prometheus Pod。
第五步:检查网络路由
在 master01 上查看路由:
ip route get 100.90.254.202输出:
100.90.254.202 via 100.100.157.14 dev tunl0 src 100.85.170.128路由正常,下一跳是 work02 的物理 IP,走 IPIP 隧道。
master01 能 ping 通 work02 的物理 IP 100.100.157.14,但 ping 不通 Pod IP。说明 IPIP 隧道本身可能正常,但数据包被中间环节丢弃。
第六步:怀疑 NetworkPolicy
monitoring 命名空间下的 NetworkPolicy 可能限制了入站流量。查看:
kubectl get networkpolicies -n monitoring存在 prometheus-k8s 策略。查看详情:
kubectl describe networkpolicy prometheus-k8s -n monitoring关键片段:
Ingress:
To Port: 9090/TCP
From:
PodSelector: app.kubernetes.io/name=prometheus
To Port: 9090/TCP
From:
PodSelector: app.kubernetes.io/name=prometheus-adapter
To Port: 9090/TCP
From:
PodSelector: app.kubernetes.io/name=grafana规则只允许带有特定标签的 Pod 访问 9090 端口,不允许来自其他 Pod 或节点 IP 的请求。
这解释了为什么:
- 从 master01 节点直接访问 Pod IP 被拒绝(源 IP 是节点 IP,不在白名单中)。
- 即使通过 NodePort,流量经过 kube-proxy 转发后源 IP 变为节点 IP,同样被拒绝。
- 从集群内部其他 Pod(如 test Pod)访问可能成功,如果那些 Pod 具有允许的标签。
而 Nginx 所在的 default 命名空间没有 NetworkPolicy,所以访问不受限。
解决方案
修改 NetworkPolicy prometheus-k8s,允许来自集群节点子网的访问,或者直接允许所有来源(测试环境可用)。
方法一:允许所有来源(快速测试)
kubectl edit networkpolicy prometheus-k8s -n monitoring将 ingress 规则改为:
spec:
ingress:
- {} # 空规则表示允许所有入站流量保存退出后立即生效。
验证:
curl -v http://100.90.254.202:9090 # 成功
curl http://<任意节点IP>:<NodePort> # 成功方法二:添加 ipBlock 白名单(推荐生产环境)
保留原有策略,额外添加一条允许节点子网访问的规则:
ingress:
# ... 原有规则 ...
- from:
- ipBlock:
cidr: 100.100.0.0/16 # 替换为你的节点网段
ports:
- port: 9090
protocol: TCP这样既保持了内部 Pod 之间的安全隔离,又允许集群外通过 NodePort 或直接从节点访问 Prometheus。
总结
- NodePort 不通不一定是 Service 或 kube-proxy 的问题,有可能是更高层级的网络策略(NetworkPolicy)拦截了流量。
- NetworkPolicy 作用范围:它作用于 Pod 的网络层,可以限制进出 Pod 的流量。默认 deny 或精细 allow 策略会导致外部访问失败。
排查思路:当 NodePort 不通时,逐步缩小范围:
- 检查 Service Endpoints
- 测试直接访问 Pod IP(从不同节点)
- 检查 Pod 监听端口
- 检查跨节点基础网络(ping Pod IP)
- 检查 NetworkPolicy
- 生产环境建议:不要直接允许所有来源,而是通过 ipBlock 限制在可信网络段内,或使用 Ingress + 基本认证来保护 Prometheus 端点。