前言
Pod 会生会死,IP 说变就变——“怎么找到服务"是分布式系统的第一问题。K8s 的答案是 Service(四层)+ Ingress(七层) 双层体系。这篇把流量链路彻底打通。
一、为什么需要 Service
Deployment v1: Pod 10.244.1.5 ──┐ 滚动更新 ┌─ Pod 10.244.2.9
Pod 10.244.2.3 ──┼──────► 期间 IP 漂移 ─►
Pod 10.244.1.8 ──┘ └─ Pod 10.244.1.12
- Pod IP 是临时的(重建就变)
- 扩缩容时 Pod 列表动态变化
- 需要稳定的名字 + 虚拟 IP + 负载均衡 → Service
二、Service 工作原理
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector: {app: api} # 圈定后端 Pod(靠 label 匹配)
ports:
- port: 80 # Service 端口
targetPort: 8080 # 容器端口
幕后发生的事:
1. 创建时: 分配稳定 ClusterIP(如 10.96.10.5, 全集群虚拟网段)
2. Endpoints 控制器: 把 selector 匹配到的 Pod IP 写入 Endpoints 对象
(Pod 增减, Endpoints 实时更新)
3. kube-proxy(每个节点): 监听 Service/Endpoints 变化, 在节点上写
iptables/IPVS 规则 → ClusterIP:80 的流量被 DNAT 到某个 PodIP:8080
4. 流量到达: 按规则负载均衡(默认随机); 连接级别的会话保持可用
kubectl get svc api
kubectl get endpoints api # 后端真实 IP 列表
kubectl exec debug-pod -- curl http://10.96.10.5/
三、三种类型 + Headless
ClusterIP(默认):集群内访问
spec:
type: ClusterIP
访问方式——CoreDNS 域名:
<service>.<namespace>.svc.cluster.local
api.default.svc.cluster.local
同 namespace 内直接: http://api
跨 namespace: http://api.prod.svc
kubectl run dnstest --rm -it --image=busybox -- nslookup api
NodePort:每个节点开高位端口
spec:
type: NodePort
ports:
- port: 80
nodePort: 30080 # 可不指定自动分配(30000-32767)
访问: 任意节点IP:30080 → (经 Service 规则) → Pod
缺点: 端口难看、暴露所有节点、无 TLS/域名路由 → 只适合临时/内网
LoadBalancer:云上正式入口
spec:
type: LoadBalancer
云厂商自动创建一个公网 LB 指向 Service(EXTERNAL-IP 列出现公网 IP)。每个 LB 一个服务成本高——正经做法是一个 LB + Ingress 路由所有服务。
Headless Service:不要 VIP,给我 Pod 列表
spec:
clusterIP: None # 不分配虚拟 IP
DNS 查询直接返回所有 Pod IP(而不是 VIP)——用于:
- 有状态服务直连(StatefulSet + 固定域名
pod-0.svc-name) - 客户端自己做负载均衡(如 gRPC 长连接场景)
四、Ingress:七层的大门
NodePort/LB 是四层转发,域名路由、路径路由、TLS、重写这些七层能力靠 Ingress:
┌─ Ingress Controller(真正干活的 Pod, 如 nginx-ingress)
用户 ─► LoadBalancer ────►│ 读取 Ingress 规则, 转成 nginx.conf
│
│ antidebug.cn/ → blog-svc:80
│ antidebug.cn/api/ → api-svc:8080
│ www.antidebug.cn → 301 → antidebug.cn
└─ TLS 证书统一在这里终结
先装 Controller(Ingress 对象本身只是规则说明书,没 Controller 不生效):
# 以 ingress-nginx 为例
kubectl apply -f https://ghfast.top/https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml
kubectl -n ingress-nginx get svc # 等待 EXTERNAL-IP 就绪
Ingress 规则:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: main
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
spec:
ingressClassName: nginx
tls:
- hosts: [antidebug.cn, www.antidebug.cn]
secretName: blog-tls # 证书存 Secret
rules:
- host: antidebug.cn
http:
paths:
- path: /
pathType: Prefix
backend: {service: {name: blog, port: {number: 80}}}
- path: /api
pathType: Prefix
backend: {service: {name: api, port: {number: 8080}}}
- host: www.antidebug.cn
http:
paths:
- path: /
pathType: Prefix
backend: {service: {name: blog, port: {number: 80}}}
kubectl apply -f ingress.yaml
curl -H "Host: antidebug.cn" http://<LB_IP>/
五、流量全链路图(背下来)
外部用户
│ DNS 解析 → LB 公网 IP
▼
LoadBalancer Service ──► Ingress Controller Pod(nginx)
│ │ 读 Ingress 规则: 域名/路径/TLS
▼ ▼
Service(ClusterIP, CoreDNS 名字解析)
│ │ Endpoints → 健康 Pod 列表
▼ ▼
Pod(readiness 通过才在列表里)
一句话版本:Ingress 管七层路由,Service 管四层均衡,CoreDNS 管名字,Endpoints 管名单。
六、排障速查
# Service 不通四步定位
# 1. Endpoints 空? → selector 与 Pod label 不匹配(最常见!)
kubectl get endpoints api
kubectl get pods -l app=api --show-labels
# 2. Pod 自身好吗?
kubectl exec -it <pod> -- curl localhost:8080/healthz
# 3. Service VIP 通吗?
kubectl run t --rm -it --image=busybox -- wget -qO- http://api
# 4. DNS 解析对吗?
kubectl run t --rm -it --image=busybox -- nslookup api.default.svc.cluster.local
# Ingress 排查
kubectl describe ingress main # 看 Backend 列表
kubectl -n ingress-nginx logs -l app.kubernetes.io/name=ingress-nginx --tail=50
小结
| 组件 | 层 | 职责 |
|---|---|---|
| Service ClusterIP | 四层 | 稳定 VIP + 均衡(集群内) |
| NodePort | 四层 | 节点高位端口暴露 |
| LoadBalancer | 四层 | 云 LB 公网入口 |
| Headless | — | 返回 Pod IP 列表 |
| Ingress + Controller | 七层 | 域名/路径/TLS 路由 |
| CoreDNS | — | 服务名 → VIP |
本文是「云原生」系列第 8 篇。