Skip to content

Service 与转发

Service 解决的是 Pod IP 不稳定的问题。Pod 会重建、漂移、扩缩,IP 跟着变。Service 给调用方提供稳定的名称和虚拟 IP(ClusterIP),再把流量转给后端 Ready Pod。

Service 和 EndpointSlice

Service 通过 selector 找 Pod,控制器生成 EndpointSlice 保存后端 IP 列表:

bash
kubectl get svc web -n demo
kubectl get endpointslice -n demo -l kubernetes.io/service-name=web
kubectl get pod -n demo -l app=web -o wide

Service 存在但 EndpointSlice 为空——通常是 selector 没匹配到任何 Ready Pod,或者 Pod 的 readinessProbe 没通过。

ClusterIP

ClusterIP 是集群内服务发现的基础入口,只在集群内部可达:

yaml
apiVersion: v1
kind: Service
metadata:
  name: web
  namespace: demo
spec:
  type: ClusterIP
  selector:
    app: web
  ports:
    - name: http
      port: 80        # Service 端口
      targetPort: 8080 # Pod 容器端口

访问:

bash
curl http://web.demo.svc.cluster.local

同 namespace 可以直接用短名 web。跨 namespace 用完整 DNS 名更清楚——域名里 namespace 写错是常见问题。

NodePort

NodePort 在每个节点上打开一个端口,外部通过 NodeIP:NodePort 能访问到 Service:

yaml
apiVersion: v1
kind: Service
metadata:
  name: web-nodeport
  namespace: demo
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30080  # 默认范围 30000-32767

NodePort 适合实验和裸机临时入口。生产里用 NodePort 时,前面通常还有 HAProxy、LVS、硬件负载均衡或云负载均衡做统一入口。

LoadBalancer

LoadBalancer 依赖云控制器或裸机负载均衡实现来分配外部 IP:

环境常见实现
公有云云厂商 SLB/ELB/NLB
裸机MetalLB、kube-vip、外部 LB
本地测试minikube tunnel、kind + 额外组件

裸机集群直接创建 LoadBalancer 类型的 Service,EXTERNAL-IP 会一直显示 <pending>——不是 Service 写错了,而是集群里没有能分配外部 IP 的控制器。

kube-proxy——把 Service 规则落到节点

kube-proxy 负责把 Service 规则变成节点上的转发规则。常见两种模式:

模式特点
iptables规则链转发,通用,但规则多时排查很重
IPVS内核级负载均衡,适合大规模 Service
bash
kubectl -n kube-system get cm kube-proxy -o yaml | grep mode
ipvsadm -Ln                        # IPVS 模式看转发规则
iptables-save | grep KUBE-SVC | head  # iptables 模式看转发规则

Service 转发不通时,先直连 Pod IP 测试。Pod IP 通但 Service 不通,问题在 Service、EndpointSlice、kube-proxy 或节点转发规则;Pod IP 就不通,问题更可能在 CNI。

DNS

CoreDNS 把 Service 名称解析到 ClusterIP,不负责后面 Service 到 Pod 的转发:

bash
kubectl -n kube-system get pods -l k8s-app=kube-dns

kubectl run dns-test --rm -it --image=busybox:1.36 --restart=Never -- sh
# Pod 内
nslookup kubernetes.default.svc.cluster.local
nslookup web.demo.svc.cluster.local

DNS 解析成功只说明名字能转成 ClusterIP。后端是否可用还要看 EndpointSlice 和 Pod 状态。

常见排查链路

bash
kubectl -n demo get svc web
kubectl -n demo get endpointslice -l kubernetes.io/service-name=web
kubectl -n demo get pod -l app=web -o wide

临时 Pod 验证:

bash
kubectl run curl --rm -it --image=curlimages/curl --restart=Never -- sh
# Pod 内
curl -v http://web.demo.svc.cluster.local
curl -v http://<pod-ip>:8080
现象方向
DNS 不解析CoreDNS、Pod DNS 配置、NetworkPolicy
Service 无后端selector、labels、readiness
直连 Pod 通,Service 不通kube-proxy、EndpointSlice、Service 端口
同节点通,跨节点不通CNI、节点路由、防火墙