mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1717 字
5 分钟
Kubernetes 服务发现与网络基础
2023-07-05

Pod 的 IP 是临时的——每次重建 Pod 都会分配新的 IP。如果前端应用直接写死后端 Pod 的 IP,一旦 Pod 重建,连接就会断开。Kubernetes 通过 Service 解决这个问题:Service 为一组 Pod 提供稳定的虚拟 IP 和 DNS 名称,无论后端 Pod 如何变化,客户端始终通过 Service 访问。

本章从 Service 的四种类型出发,逐步深入 Endpoints、Ingress、DNS、kube-proxy 和 NetworkPolicy,构建完整的 Kubernetes 网络知识体系。

一、Service:Pod 的稳定访问入口#

Service 的核心机制是标签选择器(Label Selector)——它通过 Selector 筛选匹配的 Pod,将流量转发到这些 Pod 上。Pod 增减、重建、IP 变化,Service 自动更新后端列表,客户端无感知。

1.1 Service 的工作原理#

graph LR Client["客户端"] --> SVC["Service<br/>ClusterIP: 10.96.0.100<br/>Port: 80"] SVC --> EP["Endpoints<br/>10.244.1.5:8080<br/>10.244.2.3:8080<br/>10.244.3.7:8080"] EP --> P1["Pod A"] EP --> P2["Pod B"] EP --> P3["Pod C"] style SVC fill:#e3f2fd,stroke:#1565c0 style EP fill:#fff3e0,stroke:#e65100
  1. Service 根据 selector 找到匹配的 Pod
  2. 将 Pod 的 IP + targetPort 写入 Endpoints 对象
  3. kube-proxy 监听 Endpoints 变化,在节点上配置转发规则
  4. 访问 Service IP 的流量被转发到后端 Pod

1.2 四种 Service 类型#

ClusterIP(默认)#

分配一个集群内部可达的虚拟 IP,只能在集群内部访问。

apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
type: ClusterIP # 默认类型
selector:
app: nginx
ports:
- port: 80 # Service 端口
targetPort: 8080 # Pod 容器端口
# 集群内访问
curl http://nginx-svc:80
curl http://10.96.0.100:80

NodePort#

在 ClusterIP 基础上,在每个 Node 上开放一个端口(默认范围 30000-32767),外部可以通过 NodeIP:NodePort 访问。

spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30080 # 指定 NodePort(可选,不指定则自动分配)
graph LR Ext["外部客户端<br/>NodeIP:30080"] --> NP["NodePort<br/>30080"] NP --> SVC["Service<br/>ClusterIP:80"] SVC --> P1["Pod A:8080"] SVC --> P2["Pod B:8080"] style NP fill:#e8f5e9,stroke:#2e7d32
Warning

NodePort 的端口范围有限(30000-32767),不适合直接对外暴露服务。生产环境通常在 NodePort 前面再加一层 LoadBalancer 或 Ingress。

LoadBalancer#

在 NodePort 基础上,向云厂商请求一个外部负载均衡器(如 AWS ELB、GCP Load Balancer),将外部流量导入集群。

spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
# 查看外部 IP
kubectl get svc nginx-svc
# NAME TYPE EXTERNAL-IP PORT(S)
# nginx-svc LoadBalancer 203.0.113.100 80:30080/TCP

LoadBalancer 类型依赖云厂商的 Cloud Controller Manager。在本地集群(如 minikube、kind)中,外部 IP 通常显示为 <pending>

ExternalName#

将 Service 映射到外部 DNS 名称,不创建 Endpoints,不代理流量,仅在集群 DNS 中创建 CNAME 记录。

spec:
type: ExternalName
externalName: my.database.aws.com
# 集群内访问 my-db.default.svc.cluster.local
# DNS 返回 CNAME -> my.database.aws.com

1.3 Service 类型选择#

场景推荐类型原因
集群内部通信ClusterIP安全,不暴露到外部
开发/测试环境外部访问NodePort简单直接
生产环境对外暴露LoadBalancer + Ingress云厂商负载均衡 + 路由管理
访问集群外部服务ExternalName统一 DNS 命名

二、Endpoints 与 EndpointSlice#

2.1 Endpoints#

每个 Service 自动创建一个同名的 Endpoints 对象,存储匹配 Pod 的 IP 和端口:

apiVersion: v1
kind: Endpoints
metadata:
name: nginx-svc # 与 Service 同名
subsets:
- addresses:
- ip: 10.244.1.5
- ip: 10.244.2.3
- ip: 10.244.3.7
ports:
- port: 8080

当 Pod 的 Readiness Probe 失败时,该 Pod 的 IP 会从 Endpoints 中移除,Service 不再将流量转发给它——这就是 Readiness Probe 与 Service 的协作机制。

2.2 EndpointSlice#

当 Service 后端 Pod 数量很大时(数千个),单个 Endpoints 对象会变得非常大,每次变更都需要全量同步。EndpointSlice 将后端 Pod 拆分为多个切片,每个切片最多 100 个端点:

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: nginx-svc-abc123
labels:
kubernetes.io/service-name: nginx-svc
addressType: IPv4
endpoints:
- addresses:
- 10.244.1.5
conditions:
ready: true
- addresses:
- 10.244.2.3
conditions:
ready: true
ports:
- port: 8080
Note

Kubernetes 1.21 起 EndpointSlice 成为默认特性。对于大规模集群(1000+ Pod/Service),EndpointSlice 显著降低了 API Server 和 kube-proxy 的负载。

三、Ingress 与 Ingress Controller#

Service 的 LoadBalancer 类型有一个问题:每个 Service 需要一个外部负载均衡器,成本高且 IP 资源浪费。Ingress 提供了更优雅的方案——一个入口点,基于域名和路径路由到不同 Service。

3.1 Ingress 工作模型#

graph TB Client["客户端"] --> IC["Ingress Controller<br/>Nginx/Traefik/..."] IC -->|"host: api.example.com"| SVC1["Service: api-svc"] IC -->|"host: web.example.com<br/>path: /"| SVC2["Service: web-svc"] IC -->|"host: web.example.com<br/>path: /api"| SVC3["Service: api-svc"] SVC1 --> P1["Pod: API"] SVC2 --> P2["Pod: Web"] SVC3 --> P1 style IC fill:#e3f2fd,stroke:#1565c0

3.2 Ingress 配置示例#

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx # 指定 Ingress Controller
tls:
- hosts:
- api.example.com
- web.example.com
secretName: tls-secret
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80

3.3 Ingress Controller 选型#

Controller特点适用场景
NGINX Ingress最成熟,社区最大,配置丰富通用场景
Traefik自动服务发现,原生支持 CRD云原生、动态配置
Kong插件生态丰富,API 网关能力API 管理、限流认证
Envoy (Contour/Gloo)高性能,xDS 动态配置大规模、服务网格
Note

Ingress 只是 API 对象(定义路由规则),Ingress Controller 才是实际执行转发的组件。集群必须部署至少一个 Ingress Controller,Ingress 规则才会生效。

四、集群 DNS 服务发现#

Kubernetes 集群内置 DNS 服务(CoreDNS),为每个 Service 自动创建 DNS 记录。

4.1 DNS 记录规则#

对象DNS 名称解析结果
Service(ClusterIP)my-svc.my-ns.svc.cluster.localService ClusterIP
Service(Headless)my-svc.my-ns.svc.cluster.local所有 Pod IP(轮询)
Pod(StatefulSet)pod-0.my-svc.my-ns.svc.cluster.localPod IP
Pod(普通)10-244-1-5.my-ns.pod.cluster.localPod IP
# 在集群内查询 Service DNS
nslookup nginx-svc
# Server: 10.96.0.10
# Name: nginx-svc.default.svc.cluster.local
# Address: 10.96.0.100
# 同 Namespace 下可以简写
curl http://nginx-svc:80
# 跨 Namespace 需要全限定名
curl http://nginx-svc.production.svc:80

4.2 CoreDNS 配置#

CoreDNS 通过 ConfigMap 配置,支持自定义 DNS 转发和解析:

apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}

五、kube-proxy:Service 背后的流量转发#

kube-proxy 运行在每个 Node 上,负责实现 Service 的负载均衡。它监听 API Server 的 Service 和 Endpoints 变化,在节点上配置转发规则。

5.1 iptables 模式(默认)#

iptables 模式使用 Linux netfilter 规则实现流量转发:

# kube-proxy 创建的 iptables 规则(简化)
-A KUBE-SERVICES -d 10.96.0.100/32 -j KUBE-SVC-NGINX
-A KUBE-SVC-NGINX -m statistic --mode random --probability 0.333 -j KUBE-SEP-POD1
-A KUBE-SVC-NGINX -m statistic --mode random --probability 0.500 -j KUBE-SEP-POD2
-A KUBE-SVC-NGINX -j KUBE-SEP-POD3
-A KUBE-SEP-POD1 -s 10.244.1.5/32 -j MARK --set-xmark 0x4000/0x4000
-A KUBE-SEP-POD1 -p tcp -m tcp -j DNAT --to-destination 10.244.1.5:8080

iptables 模式的特点:

  • 随机概率实现负载均衡(--probability 参数)
  • 规则链式匹配,Service 数量多时性能下降(O(n) 复杂度)
  • 无法平滑切换后端 Pod(规则更新是原子替换)

5.2 IPVS 模式#

IPVS(IP Virtual Server)是 Linux 内核的 L4 负载均衡器,专为大规模负载均衡设计:

# kube-proxy 创建的 IPVS 规则
ipvsadm -Ln
# TCP 10.96.0.100:80 rr
# -> 10.244.1.5:8080 Masq 1 0 0
# -> 10.244.2.3:8080 Masq 1 0 0
# -> 10.244.3.7:8080 Masq 1 0 0

IPVS 模式的优势:

维度iptablesIPVS
负载均衡算法随机概率rr/wrr/lc/wlc/sh/dh/sed/nq 等
规则查找O(n) 线性O(1) 哈希
大规模性能1000+ Service 时延迟明显万级 Service 仍稳定
会话保持支持支持(sessionAffinity
Tip

生产环境建议使用 IPVS 模式。需要在所有 Node 上加载 IPVS 内核模块:ip_vs, ip_vs_rr, ip_vs_wrr, ip_vs_sh, nf_conntrack

5.3 模式切换#

# 查看 kube-proxy 当前模式
kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode
# 切换到 IPVS 模式
kubectl edit configmap kube-proxy -n kube-system
# 将 mode: "" 改为 mode: "ipvs"
# 重启 kube-proxy
kubectl rollout restart daemonset kube-proxy -n kube-system

六、NetworkPolicy:网络隔离#

默认情况下,Kubernetes 集群中所有 Pod 可以互相通信。NetworkPolicy 提供基于 Label 的网络访问控制。

graph TB subgraph 允许["允许的流量"] FE["Frontend Pod<br/>app=web"] -->|"NetworkPolicy 允许"| API["API Pod<br/>app=api"] end subgraph 拒绝["拒绝的流量"] OTHER["其他 Namespace Pod"] -->|"NetworkPolicy 拒绝"| API EXT["外部 Pod"] -->|"NetworkPolicy 拒绝"| API end style 允许 fill:#c8e6c9,stroke:#2e7d32 style 拒绝 fill:#ffcdd2,stroke:#c62828

6.1 NetworkPolicy 示例#

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-policy
namespace: production
spec:
podSelector:
matchLabels:
app: api # 策略应用到 app=api 的 Pod
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector: # 允许来自 app=web 的 Pod
matchLabels:
app: web
ports:
- port: 8080
- port: 443
- from:
- namespaceSelector: # 允许来自 monitoring Namespace
matchLabels:
name: monitoring
ports:
- port: 9090 # 监控端口
egress:
- to:
- podSelector: # 允许访问 app=mysql 的 Pod
matchLabels:
app: mysql
ports:
- port: 3306
- to: # 允许 DNS 解析
- namespaceSelector: {}
ports:
- port: 53
protocol: UDP

6.2 NetworkPolicy 的关键规则#

规则说明
podSelector策略应用到哪些 Pod(空表示 Namespace 内所有 Pod)
policyTypes指定 Ingress/Egress 方向(未指定方向默认全部允许)
ingress.from允许的入站来源(podSelector + namespaceSelector 是 AND 关系,同一 from 列表项内;不同列表项是 OR 关系)
egress.to允许的出站目标
Warning

NetworkPolicy 由 CNI 插件实现,不是 kube-proxy。如果 CNI 不支持 NetworkPolicy(如 Flannel),配置了也不会生效。支持 NetworkPolicy 的 CNI:Calico、Cilium、Weave Net。

总结#

概念核心要点
Service ClusterIP集群内部虚拟 IP,默认类型
Service NodePort在 Node 上开放端口,外部可访问
Service LoadBalancer云厂商负载均衡器,生产环境首选
Service ExternalNameDNS CNAME 映射,访问外部服务
Endpoints/EndpointSliceService 后端 Pod 列表
Ingress基于域名/路径的 HTTP 路由,统一入口
CoreDNS集群内 DNS 服务发现
kube-proxy iptables默认模式,规则链匹配,小规模够用
kube-proxy IPVS哈希查找,多算法,大规模首选
NetworkPolicy基于 Label 的网络隔离,CNI 实现

下一章将深入 Kubernetes 的应用管理——Helm 包管理与 Operator 模式。

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

Kubernetes 服务发现与网络基础
https://blog.souloss.com/posts/kubernetes/k8s-service-networking/
作者
Souloss
发布于
2023-07-05
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时