服务与网络¶
Pod 的 IP 是临时的,Service 才是稳定的访问入口。再往上,Ingress 负责七层路由,NetworkPolicy 负责「谁能访问谁」。这一章把三层网络讲清楚。
你将学到¶
- Kubernetes 的 Pod 网络模型与 CNI 的职责
- ClusterIP、NodePort、LoadBalancer、ExternalName、Headless 的差异
- Service、Endpoints、EndpointSlice 的关系
- CoreDNS 的解析规则与常见 DNS 问题
- Ingress 的路由与 TLS
- NetworkPolicy 的默认拒绝与精确放行
Pod 网络模型¶
Kubernetes 对网络有三条基本要求:
- 每个 Pod 有独立 IP;
- 所有 Pod 之间可以直接通信,不需要 NAT;
- 节点上的进程可以直接访问所有 Pod。
这套「扁平网络」由 CNI 插件实现。CNI 负责:
- 给 Pod 分配 IP;
- 配置 veth、路由、网桥;
- 实现跨节点通信(VXLAN / IPIP / BGP / 原生路由);
- 部分 CNI 还实现 NetworkPolicy。
flowchart LR
subgraph Node1
P1[Pod A<br/>10.244.1.10] --> V1[veth]
V1 --> B1[cni0 / 路由]
end
subgraph Node2
P2[Pod B<br/>10.244.2.20] --> V2[veth]
V2 --> B2[cni0 / 路由]
end
B1 <-->|VXLAN / BGP / 路由| B2
图:跨节点 Pod 通信由 CNI 负责;具体封装或路由方式取决于插件配置。
Service¶
为什么需要 Service¶
Pod 会被重建,IP 会变化。Service 提供:
- 稳定的 ClusterIP 和 DNS 名称;
- 基于标签选择器自动维护后端;
- 负载均衡(iptables / IPVS / eBPF)。
apiVersion: v1
kind: Service
metadata:
name: demoapp-svc
spec:
type: ClusterIP
selector:
app: demoapp
ports:
- name: http
protocol: TCP
port: 80
targetPort: http
| 字段 | 含义 |
|---|---|
port |
Service 暴露的端口 |
targetPort |
后端 Pod 的端口(可以是名字) |
selector |
选中哪些 Pod |
type |
ClusterIP / NodePort / LoadBalancer / ExternalName |
四种类型¶
| 类型 | 访问范围 | 实现 | 适用 |
|---|---|---|---|
| ClusterIP | 集群内 | 虚拟 IP + kube-proxy | 内部服务 |
| NodePort | 集群外(节点 IP + 端口) | 每个节点开一个端口 | 实验、临时暴露 |
| LoadBalancer | 集群外 | 云 LB 或 MetalLB | 生产入口 |
| ExternalName | 集群内 | CNAME 到外部域名 | 访问外部服务 |
apiVersion: v1
kind: Service
metadata:
name: demoapp-nodeport
spec:
type: NodePort
selector:
app: demoapp
ports:
- port: 80
targetPort: 80
nodePort: 30080
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
type: ExternalName
externalName: db.example.com
Headless Service¶
clusterIP: None 不为 Service 分配虚拟 IP,DNS 直接返回所有 Pod IP。StatefulSet 依赖它提供稳定身份。
apiVersion: v1
kind: Service
metadata:
name: demoapp-headless
spec:
clusterIP: None
selector:
app: demoapp
ports:
- port: 80
targetPort: 80
预期返回多个 Pod IP,而不是一个 ClusterIP。
Endpoints 与 EndpointSlice¶
Service 通过 selector 找到 Pod,把就绪的 Pod IP 写入 EndpointSlice。kube-proxy 根据 EndpointSlice 编程转发规则。
kubectl get endpoints demoapp-svc
kubectl get endpointslice -l kubernetes.io/service-name=demoapp-svc
如果 Endpoints 为空:
- selector 不匹配;
- Pod 没有 Ready(readiness 失败);
- Pod 在别的 namespace。
kube-proxy 模式¶
| 模式 | 原理 | 特点 |
|---|---|---|
| iptables | 规则匹配 + DNAT | 成熟,规则多时性能下降 |
| IPVS | 内核四层负载均衡 | 大规模性能好,需要 ipvs 模块 |
| eBPF | Cilium 替换 kube-proxy | 性能与可观测性强,复杂度高 |
DNS¶
CoreDNS 为 Service 提供 DNS:
| 名称 | 解析结果 |
|---|---|
demoapp-svc |
同 namespace 的 ClusterIP |
demoapp-svc.demo |
指定 namespace |
demoapp-svc.demo.svc |
加上 svc |
demoapp-svc.demo.svc.cluster.local |
FQDN |
demoapp-headless |
所有 Pod IP(Headless) |
demoapp-0.demoapp-headless.demo.svc.cluster.local |
StatefulSet 单 Pod |
Pod 的 /etc/resolv.conf:
ndots:5 意味着「名字里少于 5 个点就优先尝试 search 域」,这也是跨 namespace 访问时偶尔多一次查询的原因。
测试:
kubectl run -it --rm dns --image=busybox:1.36 --restart=Never -- sh
# 容器内
nslookup kubernetes.default
nslookup demoapp-svc.demo
cat /etc/resolv.conf
Ingress¶
Service 是四层(TCP/UDP),Ingress 是七层(HTTP/HTTPS),提供基于域名和路径的路由。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demoapp
spec:
ingressClassName: nginx
tls:
- hosts:
- demo.lab
secretName: demo-tls
rules:
- host: demo.lab
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80
| pathType | 行为 |
|---|---|
Exact |
精确匹配 |
Prefix |
按路径段前缀匹配 |
ImplementationSpecific |
由 Ingress Controller 决定 |
Ingress 本身只是规则,真正生效需要 Ingress Controller(如 ingress-nginx)。没有 Controller 时,Ingress 对象不会产生任何流量。
NetworkPolicy¶
默认情况下,Pod 之间全部互通。NetworkPolicy 用来实现「默认拒绝 + 按需放行」。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: demo
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
这条策略选中 namespace 内所有 Pod,拒绝所有入站和出站流量。然后按需放行:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-web
namespace: demo
spec:
podSelector:
matchLabels:
app: demoapp
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 80
NetworkPolicy 需要 CNI 支持
Flannel 默认不支持 NetworkPolicy;Calico、Cilium 支持。写了策略但没有生效,先确认 CNI 是否实现了 networking.k8s.io/v1 的 NetworkPolicy。
NetworkPolicy 是「允许列表」的叠加:一旦某个 Pod 被至少一条策略选中,未被允许的流量默认拒绝。
实验:Service 与 NetworkPolicy¶
1. 部署两个后端¶
kubectl create namespace demo --dry-run=client -o yaml | kubectl apply -f -
kubectl create deploy web --image=nginx:1.27 --replicas=2 -n demo
kubectl expose deploy web --port=80 --name=web -n demo
kubectl get endpoints web -n demo
2. 验证 ClusterIP¶
kubectl run curl --image=curlimages/curl:8.10.1 --rm -it --restart=Never -n demo -- \
curl -s http://web/
3. 验证 Headless¶
kubectl expose deploy web --port=80 --name=web-headless --clusterip=None -n demo
kubectl run dns --image=busybox:1.36 --rm -it --restart=Never -n demo -- \
nslookup web-headless
4. 默认拒绝¶
kubectl apply -f deny-all.yaml
kubectl run curl --image=curlimages/curl:8.10.1 --rm -it --restart=Never -n demo -- \
curl -m 3 -s http://web/
预期超时。然后应用 allow-web.yaml,再次访问。注意:策略中的 namespaceSelector 要匹配实际来源 namespace。
5. 观察 EndpointSlice¶
删除一个 Pod,观察 EndpointSlice 如何更新。
排错表¶
| 现象 | 原因 | 处理 |
|---|---|---|
| Service 访问超时 | 没有 Endpoints / 网络策略 / CNI 异常 | get endpoints、describe svc |
curl 返回 503 |
后端未 Ready | get pods、readiness 探针 |
| DNS 解析失败 | CoreDNS 异常 / 网络策略阻断 | kubectl get pods -n kube-system |
| 跨 namespace 解析失败 | 用了短名 | 用 svc.ns 或 FQDN |
| NodePort 访问不通 | 防火墙 / externalTrafficPolicy |
检查节点端口与安全组 |
| Ingress 404 | host/path 不匹配 | curl -H 'Host: ...',检查 pathType |
| Ingress 502 | 后端端口错 / 未 Ready | get endpoints,检查 targetPort |
| NetworkPolicy 不生效 | CNI 不支持 | 换 Calico/Cilium |
| 源 IP 丢失 | SNAT / externalTrafficPolicy: Cluster |
改 Local 或用 X-Forwarded-For |
进阶挑战¶
- 用
externalTrafficPolicy: Local部署 NodePort,观察源 IP 变化。 - 给 StatefulSet 配置 Headless Service,验证
pod-0.svc的 DNS。 - 写一条只允许
frontend访问api的 NetworkPolicy,并用kubectl exec验证。 - 用
kubectl get endpointslice -o yaml观察endpoints与ports结构。 - 把 CoreDNS 的 ConfigMap 改错,观察集群 DNS 故障,再恢复。
小结¶
- Pod 网络由 CNI 实现,Service 提供稳定入口,Ingress 提供七层路由。
- Service 的 selector 与 Pod 标签不匹配,Endpoints 就为空。
- DNS 名称有固定格式:
svc.namespace.svc.cluster.local。 - NetworkPolicy 必须由支持的 CNI 实现,默认拒绝要配合精确放行。
延伸阅读¶
- Service
- Ingress
- NetworkPolicy
- 原仓库示例:examples/services
- 下一章:认证与安全