# 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 中摘除，避免流量打到尚未就绪的实例。

```mermaid
graph TB
    subgraph 控制面
        EM[Endpoints Controller] -->|监听 Pod 变化| EP[Endpoints 对象]
    end
    SVC["Service<br/>ClusterIP: 10.96.10.10"] -->|selector 关联| EP
    EP -->|Pod IP 10.244.1.5| P1[Pod-A]
    EP -->|Pod IP 10.244.2.7| P2[Pod-B]
    subgraph Node-1
        KP1[kube-proxy] -->|写入转发规则| R1["iptables / IPVS"]
        CLI[客户端 Pod] -->|访问 ClusterIP| R1
        R1 -->|DNAT 转发| P1
    end
    subgraph Node-2
        KP2[kube-proxy] -->|写入转发规则| R2["iptables / IPVS"]
        R2 -->|DNAT 转发| P2
    end
```

*图 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 规则不会生效。

```mermaid
sequenceDiagram
    participant U as 外部客户端
    participant IC as Ingress Controller
    participant SVC as Service ClusterIP
    participant KP as kube-proxy 规则
    participant P as 后端 Pod
    U->>IC: HTTPS 请求 api.example.com/v1
    IC->>IC: TLS 终结 + 按 host/path 匹配规则
    IC->>SVC: 转发至对应 Service
    SVC->>KP: 命中 iptables/IPVS 规则
    KP->>KP: 负载均衡选中后端 Pod
    KP->>P: DNAT 到 Pod IP
    P-->>KP: HTTP 响应
    KP-->>SVC: 响应回传
    SVC-->>IC: 响应回传
    IC-->>U: HTTPS 响应
```

*图 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 且集群节点网络互通。

```yaml
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 未通过就绪探针。

```bash
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 发起查询。同命名空间直接用服务名即可访问，无需写全域名。

```bash
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 名称一致，否则规则不会被消费。

```yaml
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 规则即可。

```yaml
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 验证连通性变化。

```bash
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 的注入方式与安全实践。

