返回首页

K8s 服务访问排查 — 从 Pod、Service 到 Ingress 十步工作流

📅 创建于 2026-05-11 🔄 更新于 2026-05-12 📝 2173 字

K8s 服务访问排查 — 从 Pod、Service 到 Ingress 十步工作流

排查核心:分层定位,逐层排除,始终从 Pod 层开始,往上排查。

记住口诀:先 Pod 再 Svc,EP 不为空则网络通;Ingress 有问题先看日志,DNS 不通查 CoreDNS。

K8s 服务访问路径全景

在动手排查之前,必须清楚流量从客户端到后端 Pod 的完整路径。不同类型的服务,路径不同。

场景一:集群内部 Pod 访问 Service(最常见)

PodA → ClusterIP:ServicePort → kube-proxy (iptables/ipvs) → EndpointIP:ContainerPort → PodB

Pod 内进程访问 http://service-name.namespace.svc.cluster.local:port → CoreDNS 解析为 Service IP → kube-proxy 拦截并 DNAT 转发到后端某个 Endpoint IP。

场景二:集群外部通过 NodePort 访问

外部客户端 → NodeIP:NodePort → kube-proxy → EndpointIP:ContainerPort → PodB

外部请求到达任意节点的 NodePort,kube-proxy 将流量转发到后端 Pod(不一定是 Pod 所在节点)。

场景三:通过 Ingress 访问

外部客户端 → Ingress Controller Pod → Ingress 规则匹配 → Service:NodePort → kube-proxy → PodB

流量先打到 Ingress Controller,Ingress Controller 根据 Host 和 Path 规则找到对应的 Backend Service,再通过 Service 访问到后端 Pod。


排查优先级

第一优先级(5 分钟内定位)

  • Pod 是否 Running、Ready、日志是否正常
  • Service 的 Endpoints 是否有关联的 Pod IP
  • 从 Pod 内直接访问 Service IP(绕过 Ingress 和 NodePort)

第二优先级(5-15 分钟)

  • kube-proxy 的 iptables/ipvs 规则是否正确生成
  • CNI 插件是否正常(flannel.1 / calico* 接口是否存在)
  • Ingress Controller 是否 Running、日志是否有 502/503 错误

第三优先级(15 分钟以上)

  • DNS 解析是否正常(CoreDNS 日志、ndots 配置)
  • NetworkPolicy 是否阻止了合法流量
  • 跨节点网络路由是否正确(VPC/物理网络层)

第一步:确认问题范围和现象

1.1 确认问题是否真实存在

# 确认目标 Service 存在
kubectl get svc -n <namespace> <service-name>

# 确认 Pod 是否存在且 Running
kubectl get pods -n <namespace> -l app=<app-label>

# 如果 Pod 不在 Running,先解决 Pod 问题

1.2 从业务角度确认问题现象

# 测试外部能否访问(用 NodePort 或 LoadBalancer IP)
curl -v http://<external-ip>:<port>/<health-path>
# 典型 502: HTTP/1.1 502 Bad Gateway
# 典型超时: curl: (7) Failed to connect to ... Connection timed out

1.3 确认问题影响范围

# 查看该 Service 有多少个健康的 Endpoint
kubectl get endpoints -n <namespace> <service-name>
# 如果 endpoints 列表为空,说明 Service 没有关联到健康的 Pod
# 如果 endpoints 数量正常,问题可能在 Ingress 或更上层

# 查看 Pod 分布
kubectl get pods -n <namespace> -o wide | grep <service-name>

第二步:从 Pod 层开始排查

Pod 是流量的最终目的地。如果 Pod 本身不正常,前面查什么都不重要。

2.1 确认 Pod 是否存在且 Running

kubectl get pods -n <namespace> -o wide
# 确认所有副本都在 Running
kubectl get pods -n <namespace> --field-selector=status.phase!=Running

2.2 查看 Pod 事件

kubectl describe pod <pod-name> -n <namespace> | tail -20
# 常见问题线索:
# - ImagePullBackOff: 镜像拉取失败
# - CrashLoopBackOff: 容器启动后立即退出
# - CreateContainerConfigError: 配置错误(ConfigMap/Secret)
# - OOMKilled: 内存不足被杀
# - Evicted: 被驱逐

2.3 查看容器内部日志

# 当前日志
kubectl logs <pod-name> -n <namespace>
# 上次运行的日志(启动后立即崩溃)
kubectl logs <pod-name> -n <namespace> --previous
# 多容器 Pod 指定容器名
kubectl logs <pod-name> -n <namespace> -c <container-name>

2.4 登录 Pod 内部排查网络连通性

kubectl exec -it <pod-name> -n <namespace> -- /bin/sh

# 确认进程是否监听在预期端口
ss -tlnp  # 或 netstat -tlnp

# 测试 localhost 端口
wget -qO- http://127.0.0.1:<container-port>/healthz
# 或 curl -s http://127.0.0.1:<container-port>/healthz

如果 localhost 访问正常,但通过 Service 访问有问题 → 问题在 Service 层。

2.5 检查 Pod 资源限制

# 查看资源请求和限制
kubectl get pod <pod-name> -n <namespace> \
  -o jsonpath='{.spec.containers[*].resources}'

# 查看实际资源使用
kubectl top pod <pod-name> -n <namespace>

资源限制不当会导致 OOMKill 或 CPU Throttle → 请求超时。


第三步:排查 Service 层

3.1 确认 Service 配置正确

kubectl get svc <service-name> -n <namespace> -o yaml
# 重点检查:
# - spec.selector: 是否正确关联到目标 Pod 的 label
# - spec.ports: 端口号、目标端口、协议
# - spec.type: ClusterIP / NodePort / LoadBalancer

常见配置错误一:selector 写错

# 确认 Pod 实际的 label
kubectl get pods -n <namespace> --show-labels
# 验证 selector 是否能匹配到 Pod
kubectl get pods -n <namespace> -l app=web-frontend
# 如果没有输出,说明 selector 写错了

常见配置错误二:端口配置错误

  • targetPort 写成了 named port,但容器没有定义该 named port
  • targetPort 使用了数字端口,但与容器实际监听端口不一致

3.2 确认 Endpoints 存在且健康

kubectl get endpoints <service-name> -n <namespace>
# 正常输出:my-service  10.244.1.15:8080,10.244.2.23:8080
# 为空:<none>

Endpoints 为空的排查方法:

# 1. 确认 selector 是否匹配到 Pod
kubectl get pods -n <namespace> --show-labels

# 2. 确认 Pod 的 Ready 状态
kubectl get pods -n <namespace> -o wide
# 关注 Ready 列:应该是 1/1、2/2 等

# 3. 查看 Pod 的 readinessProbe 配置
kubectl get pod <pod-name> -n <namespace> \
  -o jsonpath='{.spec.containers[*].readinessProbe}'

readinessProbe 配置错误的常见场景:HTTP 路径返回 404,但应用实际路径不同。

3.3 从集群内部测试访问 Service

# 创建临时测试 Pod
kubectl run -n <namespace> testpod --image=busybox:1.36 \
  --restart=Never -it --rm -- sh

# 在 Pod 内部执行:
# 1. DNS 解析测试
nslookup <service-name>
# 正常输出:Name: <service-name>  Address: <ClusterIP>

# 2. 通过 Service IP 访问
wget -qO- http://<ClusterIP>:<port>/healthz

# 3. 通过 DNS 名称访问
wget -qO- http://<service-name>.<namespace>.svc.cluster.local:<port>/healthz

DNS 名称能解析但连接超时 → kube-proxy 没有正确转发流量。 Connection refused → 容器内端口没监听或 targetPort 配置错误。

3.4 测试 NodePort 类型 Service

# 查看 NodePort
kubectl get svc <service-name> -n <namespace> | grep NodePort

# 从集群外部测试
curl -v http://<任意节点IP>:<NodePort>/<path>

# 如果某个节点能访问、另一个不能 → 检查不能访问节点上的 kube-proxy

第四步:排查 kube-proxy 层

kube-proxy 运行在每个节点上,监听 Service 和 Endpoints 的变化,动态更新本地的 iptables 或 ipvs 规则。

4.1 检查 kube-proxy 是否运行

# kube-proxy 以 DaemonSet 形式运行
kubectl get pods -n kube-system -l k8s-app=kube-proxy

# 查看日志
kubectl logs -n kube-system -l k8s-app=kube-proxy --tail=50

4.2 检查 kube-proxy 的转发模式

# 查看 kube-proxy 的启动参数
kubectl get configmap -n kube-system kube-proxy -o yaml | grep mode
  • iptables 模式:默认,性能稍差,但稳定
  • ipvs 模式:性能更好,但配置复杂,某些内核版本支持不好

4.3 iptables 模式排查

# SSH 到节点查看 iptables 规则
# 1. 查看 NAT 表中关于某个 Service 的规则
ssh <node> "sudo iptables -t nat -L -n | grep <service-name>"

# 输出示例:
# KUBE-SVC-XXXXXXXX tcp -- anywhere 10.109.12.34 tcp dpt:80
# KUBE-SEP-XXXXXXXX tcp -- anywhere 10.244.1.15 tcp dpt:8080

# 2. 查看 KUBE-SERVICES 链
ssh <node> "sudo iptables -t nat -L KUBE-SERVICES -n | grep <service-name>"

# 3. 查看 FORWARD 链
ssh <node> "sudo iptables -t filter -L FORWARD -n | grep KUBE"

如果没有 KUBE-SVC/KUBE-SEP 规则 → kube-proxy 没有为这个 Service 生成规则。

4.4 ipvs 模式排查

# 查看 ipvs 规则
ssh <node> "sudo ipvsadm -L -n"
# 正常输出:
# TCP  <ServiceIP>:80 rr
#   -> <EndpointIP>:8081 Masq 1 0 0

# 检查 ipvs 模块是否加载
ssh <node> "lsmod | grep ip_vs"

如果 ipvsadm 没有安装或 ip_vs 模块未加载,kube-proxy 会回退到 iptables。

4.5 kube-proxy 日志分析

kubectl logs -n kube-system -l k8s-app=kube-proxy | grep -i error
# 常见错误:
# - "Failed to delete service" - 删除 Service 时清理规则失败
# - "Failed to sync iptables" - 同步规则失败,可能是权限问题
# - "ipvs struct not found" - ipvs 模式下连接状态异常

第五步:排查 CNI 插件层

CNI 插件选型与底层技术对比参见 cni-comparison(Flannel vs Calico vs Cilium 深度对比)。

CNI 负责 Pod IP 分配和 Pod 之间的网络连通性。如果 Service 层和 kube-proxy 都正常,但 Pod 之间还是不通,问题可能在 CNI 层。

5.1 确认 Pod 网络是否正常

# 在 Pod 内测试到另一个 Pod 的连通性
kubectl exec -it <pod-name> -n <namespace> -- /bin/sh
ping -c 3 <另一个Pod的IP>
# 查看默认网关
ip route

如果 ping 不通:确认两边的 Pod 是否在同一个网段;确认 CNI 插件创建的接口是否存在。

5.2 常见 CNI 插件排查

Flannel 排查:

# 查看 flannel 是否为所有节点创建了网络
kubectl get pods -n kube-system -l app=flannel

# 在节点上查看 flannel.1 接口
ip addr show flannel.1

# 查看网络范围(Pod CIDR)
kubectl get cm -n kube-system kube-flannel-cfg -o yaml | grep -A 3 "net-conf.json"

典型问题:flannel.1 没有 UP → 该节点的 CNI 网络没有正常初始化。

Calico 排查:

# 查看 calico-node 是否 Running
kubectl get pods -n kube-system -l k8s-app=calico-node

# 查看日志
kubectl logs -n kube-system -l k8s-app=calico-node --tail=100

# 查看 IP 池(需要 calicoctl)
calicoctl get ippool -o wide
calicoctl get workloadendpoints -o wide

5.3 跨节点 Pod 通信测试

# 获取两个不同节点上 Pod 的 IP
kubectl get pods -o wide --all-namespaces | grep Running

# 测试跨节点连通性
kubectl exec -it <源Pod> -n <namespace> -- ping -c 5 <目标PodIP>

# 测试特定端口
kubectl exec -it <源Pod> -n <namespace> -- nc -zv <目标PodIP> <端口>

同节点 Pod 通信正常,跨节点不通 → 检查源节点到目标节点的路由、CNI 隧道(flannel VXLAN/Calico BGP)、防火墙规则(flannel UDP 8472/Calico IP-in-IP 4)。


第六步:排查 Ingress 层

6.1 确认 Ingress Controller 正常运行

# Ingress Controller 通常部署在 kube-system
kubectl get pods -n kube-system | grep -E "ingress|nginx"

# 查看日志
kubectl logs -n kube-system <ingress-controller-pod-name> -f --tail=100

6.2 查看 Ingress 规则配置

# 查看所有 Ingress 资源
kubectl get ingress -A

# 查看具体配置
kubectl get ingress <ingress-name> -n <namespace> -o yaml
# 重点检查:spec.rules(Host/Path)、spec.tls(HTTPS)、spec.backend(默认后端)

6.3 确认 Ingress 和 Service 的关联

# 检查 backend.service.name 是否和目标 Service 名称一致
kubectl get ingress <ingress-name> -n <namespace> \
  -o jsonpath='{.spec.rules[*].http.paths[*].backend.service}'

# 检查端口是否一致
kubectl get ingress <ingress-name> -n <namespace> \
  -o jsonpath='{.spec.rules[*].http.paths[*].backend.service.port}'

6.4 Ingress 日志分析

# 搜索 502 / 503 / 504 错误
kubectl logs -n kube-system <ingress-controller-pod-name> | grep " 502 \| 503 \| 504 "
# 搜索特定 Host 的访问
kubectl logs -n kube-system <ingress-controller-pod-name> | grep "Host: <domain>"

如果没有日志输出 → 请求根本没到 Ingress Controller,检查 DNS 解析、LB 配置、NodePort。

6.5 逐层排查 Ingress 流量路径

层级一:外部 → DNS 解析 → Ingress Controller 的外部 IP/NodePort
层级二:Ingress Controller → Ingress 规则匹配 → Backend Service
层级三:Backend Service → kube-proxy → Endpoint → Pod

层级一:确认外部能访问到 Ingress Controller

# 获取 Ingress Controller 的 NodePort 或 LoadBalancer IP
kubectl get svc -n kube-system | grep -E "ingress|nginx"

# 测试从外部直接访问 NodePort(绕过 DNS)
curl -v http://<任意节点IP>:<NodePort>/<path>
# NodePort 能访问但域名不行 → DNS 解析问题

层级二:确认 Ingress 规则被正确加载

# 查看 nginx.conf
kubectl exec -n kube-system <ingress-controller-pod> -- cat /etc/nginx/nginx.conf
# 搜索对应的 server {} 块
kubectl exec -n kube-system <ingress-controller-pod> -- grep -A 20 "server_name <domain>"
# 确认 upstream {} 中的 server 列表包含正确 Endpoint IP
kubectl exec -n kube-system <ingress-controller-pod> -- grep -A 10 "upstream <backend-service>"

6.6 TLS/HTTPS 相关问题

# 查看 TLS 配置
kubectl get ingress <ingress-name> -n <namespace> -o yaml | grep -A 10 tls

# 常见 TLS 问题:
# 1. Secret 不存在
kubectl get secret <secret-name> -n <namespace>
# 2. Secret 类型不是 kubernetes.io/tls
# 3. 证书过期(Let's Encrypt 有效期 90 天)
# 4. 证书和域名不匹配

# 测试 HTTPS(跳过证书验证排查连接问题)
curl -v https://<domain>/<path> --insecure
# --insecure 能通 → 证书配置问题

第七步:排查 DNS 解析问题

DNS 问题是导致"服务访问不通"的重灾区。Pod 访问 Service 通过 DNS 名称,如果 DNS 解析失败,所有依赖 Service 名称的调用都会失败。

7.1 测试 DNS 解析

# 在 Pod 内测试
kubectl exec -it <pod-name> -n <namespace> -- nslookup kubernetes.default
kubectl exec -it <pod-name> -n <namespace> -- nslookup <service-name>.<namespace>.svc.cluster.local

# 如果 nslookup 不存在,用 dig
kubectl exec -it <pod-name> -n <namespace> -- dig +short kubernetes.default.svc.cluster.local

# 用 getent 测试
kubectl exec -it <pod-name> -n <namespace> -- getent hosts <service-name>.<namespace>.svc.cluster.local

7.2 查看 CoreDNS 状态

# CoreDNS 以 Deployment 运行在 kube-system
kubectl get pods -n kube-system -l k8s-app=kube-dns

# 查看日志
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=100

# 查看配置
kubectl get configmap -n kube-system coredns -o yaml

7.3 DNS 解析慢的常见原因

  • DNS 查询并发限制:Linux 内核对每个进程/容器的 DNS 并发查询有限制
  • ndots 配置过高:K8s 默认 ndots=5,任何带 5 个以下点的名称都会先查集群 DNS 再查外部 DNS

优化 ndots 配置:

spec:
  dnsConfig:
    options:
      - name: ndots
        value: "2"           # 默认 5,改成 2 减少集群 DNS 查询
      - name: timeout
        value: "2"
      - name: attempts
        value: "2"

第八步:排查 NetworkPolicy

NetworkPolicy 问题隐蔽性最强,排查到最后才考虑。

8.1 查看命名空间的 NetworkPolicy

# 查看是否应用了 NetworkPolicy
kubectl get networkpolicy -n <namespace>

# 查看具体规则
kubectl get networkpolicy <policy-name> -n <namespace> -o yaml

8.2 临时测试:放行所有流量

# 如果确认是 NetworkPolicy 问题,临时放行验证
kubectl apply -f - <<'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all
  namespace: <namespace>
spec:
  podSelector: {}
  ingress:
    - {}
EOF

第九步:综合故障案例

案例一:Service 有 Endpoint 但返回 502

现象: Service 的 Endpoints 列表正常(有 IP 有端口),但通过 Ingress 访问返回 502。

排查过程:

  1. kubectl get svc my-service -n default → Endpoints 数量和 Pod 数量一致 ✅
  2. 从 Pod 内直接访问 Service(绕过 Ingress)→ 能正常返回 ✅
  3. 通过 Ingress 访问返回 502 ❌
  4. 查看 Ingress Controller 日志 → "upstream timed out (110: Connection timed out)"
  5. 根因: 应用的重试机制导致请求携带无效的 Host 头,Ingress Controller 的 upstream server_name 和请求头中的 Host 不匹配

修复: 调整 Ingress 的 host 字段或应用的 Host 头。

案例二:Pod 之间通过 Headless Service 无法互相发现

现象: 部署了 MySQL 主从使用 Headless Service,但 Pod 之间无法通过 DNS 互相发现。

排查过程:

  1. 确认 Headless Service 配置:clusterIP: None
  2. DNS 记录为空 nslookup mysql-headless.database.svc.cluster.local
  3. 根因: StatefulSet 的 serviceName 和 Headless Service 名称不一致

修复: StatefulSet 的 serviceName 必须和 Headless Service 名称完全一致,否则 Pod 的 DNS 记录不会生成。

# 检查 StatefulSet 的 serviceName
kubectl get statefulset mysql -n database \
  -o jsonpath='{.spec.serviceName}'

第十步:预防措施

10.1 服务可达性巡检脚本

#!/bin/bash
# k8s_service_health_check.sh
NAMESPACES=$(kubectl get ns -o jsonpath='{.items[*].metadata.name}' | tr ' ' '\n' | grep -v kube-system)
for NS in $NAMESPACES; do
  SERVICES=$(kubectl get svc -n $NS -o json | jq -r '.items[] | select(.spec.clusterIP != "None") | select(.spec.clusterIP != "") | .metadata.name')
  for SVC in $SERVICES; do
    EP_COUNT=$(kubectl get endpoints $SVC -n $NS -o json | jq '.subsets | length')
    if [ "$EP_COUNT" == "0" ] || [ "$EP_COUNT" == "null" ]; then
      echo "[ALERT] Service $SVC in namespace $NS has NO endpoints"
    fi
  done
done
echo "巡检完成"

10.2 Ingress 和 Service 关联性校验

#!/bin/bash
for INGRESS in $(kubectl get ingress -A -o jsonpath='{.items[*].metadata.name}'); do
  NS=$(kubectl get ingress $INGRESS -A -o jsonpath='{.items[0].metadata.namespace}')
  SVC=$(kubectl get ingress $INGRESS -n $NS -o jsonpath='{.spec.rules[0].http.paths[0].backend.service.name}')
  EP_COUNT=$(kubectl get endpoints $SVC -n $NS -o json 2>/dev/null | jq '.subsets | map(.addresses) | flatten | length')
  if [ "$EP_COUNT" == "0" ] || [ "$EP_COUNT" == "null" ]; then
    echo "[WARNING] Ingress $INGRESS → Service $SVC has NO endpoints!"
  fi
done

10.3 Pod readinessProbe 配置规范

Spring Boot 应用:

readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10
  timeoutSeconds: 5
  successThreshold: 1
  failureThreshold: 3

Golang 应用:

readinessProbe:
  tcpSocket:
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10
  failureThreshold: 3

排查顺序口诀

先 Pod 再 Svc,EP 不为空则网络通;
Ingress 有问题先看日志,DNS 不通查 CoreDNS。

关联页面

页面关联点
k8s-coredns-custom-domain-resolutionCoreDNS 自定义域名解析(DNS 是服务访问十步排查的第一步)