k8s 服务发现与网络

导读
Pod 的 IP 随销毁重建而变化,直接以 Pod IP 通信在滚动更新等场景下会立即断裂。Service 在 Pod 之上提供稳定的虚拟 IP 和 DNS 名称,将流量分发到一组后端 Pod,是 K8s 内部服务间通信的基石。本篇拆解 Service 四种类型、kube-proxy 转发链路、Ingress 七层路由与 NetworkPolicy 隔离策略,并给出可直接复制的实操 YAML。
核心概念
ClusterIP 是 Service 的默认类型,在集群内部暴露一个稳定的虚拟 IP,仅集群内可访问,适合内部服务间调用。NodePort 在 ClusterIP 基础上于每个节点开一个高位端口(默认 30000-32767),外部可通过 节点IP:节点端口 访问,适合测试或自建负载均衡的接入层。LoadBalancer 依赖云厂商的负载均衡器,自动创建一个外部 IP 并将流量引到 NodePort,常用于云上生产入口。ExternalName 通过 CNAME 把 Service 映射到一个外部域名,不分配 ClusterIP,也不生成 Endpoints,纯粹做 DNS 别名。
Service 对象本身不转发流量,真正维护后端 Pod 列表的是 Endpoints(自定义资源则用 EndpointSlice)。控制面的 Endpoints Controller 持续监听 Pod 变化,把符合 selector 的 Pod IP 写入 Endpoints 对象。当 Pod 扩缩容或重建时,Endpoints 随之更新,Service 始终指向健康的后端。就绪探针未通过的 Pod 会被从 Endpoints 中摘除,避免流量打到尚未就绪的实例。
图 1:Service → Endpoints → Pod 转发模型与 kube-proxy 角色
图 1 展示了 Service 到 Pod 的完整转发模型。客户端 Pod 访问 ClusterIP 时,请求被节点上的 iptables/IPVS 规则拦截,规则由 kube-proxy 写入并持续同步。kube-proxy 有两种主流模式:iptables 模式基于链式规则做随机或轮询,规则量大时遍历开销随线性增长;IPVS 模式基于内核哈希表,支持 rr/lc/sh 等多种调度算法,在大规模集群下转发性能更优。
DNS 发现由集群内的 CoreDNS 提供。每创建一个 Service,CoreDNS 会生成一条 服务名.命名空间.svc.cluster.local 的 A 记录指向 ClusterIP。Pod 内 /etc/resolv.conf 的 search 域由 kubelet 写入,因此在同命名空间内可直接用服务名访问,跨命名空间用 服务名.命名空间 即可。Headless Service(clusterIP: None)不分配虚拟 IP,DNS 直接返回所有后端 Pod IP,适合 StatefulSet 等需要直连 Pod 的场景。
图解原理
Ingress 在四层 Service 之上提供七层 HTTP/HTTPS 路由能力,可按域名、路径把流量分发到不同 Service,并在此处做 TLS 终结。Ingress 本身只是 API 资源声明规则,真正监听入口并执行转发的是 Ingress Controller,社区主流实现有 ingress-nginx、Traefik、HAProxy Ingress 等。没有部署 Ingress Controller,Ingress 规则不会生效。
图 2:外部请求经 Ingress Controller 到后端 Pod 的转发时序
图 2 描述了外部请求从入口到后端 Pod 的完整转发链路。Ingress Controller 先终结 TLS 证书,再依据 Ingress 规则的 host 和 path 把明文请求转给目标 Service 的 ClusterIP,随后由节点上的 kube-proxy 规则完成 DNAT 到具体 Pod。整条链路中,七层路由与四层负载各司其职,Ingress 只负责入口分发,到 Pod 的最后一跳仍走 Service 机制。
NetworkPolicy 是 K8s 内置的 L3/L4 网络隔离资源,通过 label selector 限定 Pod 的入站(Ingress)和出站(Egress)流量来源与目标。默认情况下集群所有 Pod 网络互通,NetworkPolicy 一旦命中某个 Pod,未显式放行的流量即被丢弃。它依赖 CNI 插件实现,Calico、Cilium 等均原生支持。
CNI 插件负责 Pod 网络的 IP 分配与跨节点连通。Calico 基于 BGP 在节点间同步路由,支持 NetworkPolicy,部署简单、生态成熟,是多数自建集群的默认选择。Cilium 基于 eBPF 在内核态做转发与策略过滤,绕过 iptables,在高吞吐和可观测性场景表现突出,并扩展了 L7 策略与服务网格能力。
动手实操
创建一个三副本 Nginx 应用并暴露 ClusterIP Service,随后叠加 Ingress 路由与 NetworkPolicy 隔离,验证从 DNS 解析到流量转发的完整链路。所有 YAML 可直接保存为文件依次 apply。操作前确认已安装 CoreDNS 且集群节点网络互通。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: web
spec:
type: ClusterIP
selector:
app: web
ports:
- port: 80
targetPort: 80应用后查看 Service 和 Endpoints,确认控制器已自动写入后端 Pod 地址。EndpointSlice 是 Endpoints 的分片版本,在大规模 Service 下查询更高效。若 Endpoints 为空,通常是 selector 与 Pod label 不匹配或 Pod 未通过就绪探针。
kubectl apply -f web.yaml
kubectl get svc web
kubectl get endpoints web
kubectl get endpointslice -l kubernetes.io/service-name=web用临时 Pod 在集群内验证 DNS 解析与服务连通性。busybox 的 nslookup 会向 Pod 的 resolv.conf 指定的 CoreDNS 发起查询。同命名空间直接用服务名即可访问,无需写全域名。
kubectl run dns-test --image=busybox:1.36 -it --rm --restart=Never -- sh
# 容器内执行
nslookup web
wget -qO- http://web部署 ingress-nginx Controller 后,创建一条基于域名和路径的路由规则,并在同命名空间准备名为 api-tls 的 TLS Secret。rewrite-target 注解把 /v1 前缀剥离后再转发到后端。ingressClassName 必须与集群中实际运行的 IngressClass 名称一致,否则规则不会被消费。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-tls
rules:
- host: api.example.com
http:
paths:
- path: /v1
pathType: Prefix
backend:
service:
name: web
port:
number: 80创建一条 NetworkPolicy,仅允许带有 app: gateway 标签的 Pod 访问 web 的 80 端口,其余入站流量全部拒绝。policyTypes 列出 Ingress 表示只管控入站方向。若同时需要限制出站,追加 Egress 规则即可。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: web-allow-gateway
namespace: default
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: gateway
ports:
- protocol: TCP
port: 80查看策略命中情况,describe 输出会列出允许的来源 selector 与端口。若 CNI 不支持 NetworkPolicy,规则虽能创建但不会生效。可用带不同 label 的 Pod 验证连通性变化。
kubectl get networkpolicy
kubectl describe networkpolicy web-allow-gateway常见问题与避坑
生产环境优先选 IPVS 模式。iptables 模式在 Service 数量过千时,每次规则变更都要全量重写,转发延迟随规模劣化。通过 --mode=ipvs 配置 kube-proxy,并在 kube-proxy configmap 中确认 scheduler: rr。
NodePort 不要直接对外暴露生产服务。节点端口暴露的是所有节点,单一节点故障会导致入口不可用,且端口范围受限、缺乏 TLS 与七层路由能力。正确做法是在前端接入 Ingress 或云负载均衡。
Ingress 规则写完没生效先查 Controller。Ingress 资源只是声明,没有对应的 Ingress Controller 运行,规则不会被任何组件消费。执行 kubectl get ingressclass 确认默认类,kubectl logs -n ingress-nginx 查看控制器是否报错。
小结与进阶
Service 提供稳定的访问入口与负载均衡,Ingress 在其上叠加七层路由与 TLS 终结,NetworkPolicy 守住 Pod 间的访问边界,三者共同构成 K8s 的网络通信骨架。理解 kube-proxy 的转发模式与 CoreDNS 的解析链路,是排查集群内连通性问题的前提。下一篇将进入配置与密钥管理,拆解 ConfigMap 与 Secret 的注入方式与安全实践。