Kubernetes 系列【16】服务:ClusterIP 核心原理与演示案例

文章目录

  • [1. 概述](#1. 概述)
  • [2. 工作原理](#2. 工作原理)
    • [2.1 分配虚拟 IP (CIDR)](#2.1 分配虚拟 IP (CIDR))
    • [2.2 流量转发(kube-proxy)](#2.2 流量转发(kube-proxy))
    • [2.3 完整流量链路](#2.3 完整流量链路)
    • [2.4 外部网络访问限制](#2.4 外部网络访问限制)
  • [3. 演示案例](#3. 演示案例)
    • [3.1 定义工作负载](#3.1 定义工作负载)
    • [3.2 定义 ClusterIP 服务](#3.2 定义 ClusterIP 服务)
      • [3.2.1 基本信息](#3.2.1 基本信息)
      • [3.2.2 标签选择器](#3.2.2 标签选择器)
      • [3.2.3 端口映射](#3.2.3 端口映射)
      • [3.2.4 其他可选配置](#3.2.4 其他可选配置)
      • [3.2.5 完整清单](#3.2.5 完整清单)
    • [3.3 验证测试](#3.3 验证测试)
    • [3.4 多端口 Service](#3.4 多端口 Service)

1. 概述

Kubernetes 里,Pod临时性、可销毁 的资源。Deployment 会根据副本数动态创建和销毁 Pod,滚动更新、扩缩容、节点故障都会导致 Pod IP 发生变化。

这就带来一个核心问题:

前端业务要调用后端服务,如果后端 PodIP 一直在变,前端该找谁?怎么保证不会因为 Pod 重建就调用失败?

Service 就是来解决这个问题的,可以把 Service 理解成「Pod 集群的统一入口 + 内部负载均衡器」:

  • 它给一组 Pod 分配一个固定不变 的虚拟 IP(集群 IP
  • 客户端只需要访问这个固定地址,流量会自动转发到健康的后端 Pod
  • 后端 Pod 增减、重建、更新,对客户端完全透明,无需修改任何配置

Service 的核心设计目标是业务无侵入 :无论你的应用是云原生新程序,还是老旧系统容器化改造,都不需要修改代码适配服务发现,直接用 Service 即可完成网络暴露。

Service 通过 type 字段指定暴露方式,共四种,能力逐层递进:

类型 访问范围 典型场景
ClusterIP 仅集群内部 微服务内部调用、数据库、缓存等后端服务
NodePort 集群外通过节点IP+端口访问 测试环境、自建负载均衡、临时暴露
LoadBalancer 公网/私有网络通过云负载均衡访问 生产环境对外提供服务
ExternalName 集群内部 DNS 别名 映射外部服务到集群内部域名

ClusterIPService 的默认类型,仅分配一个集群内部可达的虚拟 IP只能在集群内部访问 。它是最基础、最常用的 Service 类型。

很多新手会混淆这三者:

  • Service :四层(TCP/UDP)负载均衡,负责集群内 Pod 的网络抽象与访问
  • Ingress :七层(HTTP/HTTPS)路由入口,负责把外部流量按域名/路径分发到不同 Service
  • Gateway APIIngress 的升级替代方案,功能更丰富、扩展性更强,通过 CRD 实现更灵活的流量治理

2. 工作原理

2.1 分配虚拟 IP (CIDR)

K8s 安装时会单独划分一段虚拟 IP 地址池 ,专门给 Service 使用,这个地址段就叫 Service CIDR,这段 IP 不会分配给任何服务器、容器 Pod,只用来做 Service 的虚拟入口。

新建 ClusterIP Service 时,集群控制平面apiserver + etcd)会自动从这个池里挑一个没人用的 IP 绑定给当前 Service。这个 IP 不是真实网卡 IP ,是集群内部逻辑虚拟 IP ,外部网络路由表里不存在这条地址。

示例 :集群 Service CIDR = 10.96.0.0/12 ,创建后端服务 Service ,自动分配虚拟 IP10.96.12.34,这个 IP 只代表服务入口,不对应任何一台真实机器。


2.2 流量转发(kube-proxy)

每个 K8s 节点都会运行一个守护进程 kube-proxy,本身不直接转发数据包,负责实时监听所有 Service 和后端 Pod 变化,自动在节点内核生成转发规则

底层基于 Linux 内核网络防火墙 / 流量过滤工具,比如:

  • iptables:默认稳定模式
  • ipvs:传统高性能模式
  • nftables:新一代高性能模式

Service 资源创建后对象会保存到 etcd 中,每个节点的 kube-proxy 会持续长轮询监听 apiserver ,一旦检测到新增 / 修改 Service、后端 Pod 上下线,立刻触发规则更新。

比如,iptables 模式中,节点上的 kube-proxy 检测到新 Service 时,会创建一套以 KUBE- 开头的专属链,分层匹配:

  • KUBE-SERVICES:全局总入口,匹配 ClusterIP 虚拟地址并跳转对应服务链。
  • KUBE-SVC-xxx:单服务负载均衡链,按随机概率分流至不同后端 Pod 规则。
  • KUBE-SEP-xxx:后端端点专属链,执行 DNAT(目标网络地址转换) 将虚拟 IP 替换为 Pod 真实 IP

关键点:虚拟 IP 本身不接收、不存储数据,只是一个路由路标


2.3 完整流量链路

标准 iptables 模式分步详细解析:

  1. 集群节点 NodeA 上的客户端 Pod,业务进程发起访问请求:curl 10.96.12.34:8080ClusterIP
  2. 流量进入 NodeA 本机内核,被 iptables 规则拦截:
    • 到达内核 nat 表的 KUBE-SERVICES 全局入口链;
    • 匹配条件:目标 IP = 10.96.12.34、目标端口 = 8080
    • 匹配成功后,自动跳转当前服务专属链 KUBE-SVC-XXXX
  3. 进入 KUBE-SVC-XXXX 服务负载均衡链:
    • kube-proxy 预先根据后端 3Pod 端点,生成概率随机分流规则,自动实现负载均衡;
    • 按权重随机选择后端 Pod1 / Pod2 / Pod3
    • 流量进入对应 KUBE-SEP-XXX 端点链
  4. KUBE-SEP 链执行 DNAT 地址转换:
    • 将数据包目标地址 10.96.12.34:8080(虚拟 IP)替换为 后端真实 Pod 容器 IP
    • 示例:10.96.12.3410.244.x.x(真实 PodIP
  5. DNAT 改写完成后,内核重新根据最新目标 IP 路由:
    • 若后端 Pod 在当前节点:直接本机投递
    • 若后端 Pod 在其他节点:通过集群 CNI 网络跨节点转发
    • 最终流量精准抵达后端真实业务 Pod

核心总结:

  1. ClusterIP 只是逻辑入口:无网卡、无实体,仅存在于 iptables 规则中;
  2. 所有转发决策在请求发起节点完成:负载均衡 + DNAT 全部在本地执行;
  3. 三层链分工明确:KUBE-SERVICES(匹配VIP)→ KUBE-SVC(负载均衡)→ KUBE-SEPDNAT真实IP
  4. 客户端全程无感后端真实 Pod,实现服务透明、弹性扩容、无感切换。

2.4 外部网络访问限制

两层网络隔离限制:

  1. 路由层面隔离
    • 服务器宿主机、机房外网网关、公网路由器,都没有配置 Service CIDR10.96.0.0/12)这段地址的路由规则。
    • 外网机器发请求到 10.96.x.x,数据包一出节点就直接丢弃,找不到转发路径。
  2. 转发规则仅集群内生效iptables/ipvs 转发规则只作用于集群内部网卡流量(容器网桥、节点内部互通网卡),节点物理网卡接收的外部流量不会触发这套转发规则。

场景区分:

  • ✅ 能访问:集群内任意 Pod、集群任意节点服务器 ssh 登录后 curl

  • ❌ 不能访问:本机电脑、公网服务器、集群外其他机器


3. 演示案例

理论落地必须结合实操,通过 原生 YAML 从零编写 的方式,完成工作负载定义、单端口 ClusterIP 服务搭建、服务核心配置解析、多端口 Service 实战。全程采用 Nginx 官方镜像 ,兼容性极强、适配 x86/ARM 所有架构、无启动报错、无需特殊启动参数,是 Kubernetes 入门实验最稳定通用的业务镜像。


3.1 定义工作负载

Service 是一组 Pod 的统一访问入口与负载均衡抽象,所有流量转发、服务发现的前提是存在一组可被绑定的业务 Pod 集群。

我们创建 nginx-deploy.yaml,定义 3 副本 Deployment 模拟业务集群,Nginx 默认监听 80 端口 ,同时配合 Service 实现单端口、多端口流量转发实战。

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-web
  namespace: default
spec:
  # 启动3个Pod副本,用于负载均衡演示
  replicas: 3
  selector:
    matchLabels:
      app: nginx-web
  template:
    metadata:
      labels:
        # 核心标签:用于Service标签选择器精准匹配绑定
        app: nginx-web
    spec:
      containers:
      - name: nginx
        # 官方轻量镜像,兼容x86/ARM架构,稳定不崩溃
        image: nginx:latest
        ports:
        # Nginx默认业务监听端口
        - containerPort: 80

执行部署命令,快速拉起 Nginx 业务集群,等待所有 Pod 就绪:

bash 复制代码
# 部署Nginx工作负载集群
kubectl apply -f nginx-deploy.yaml

# 查看Pod运行状态,确保全部就绪Ready
kubectl get pods -o wide

正常输出可看到 3 个状态为 RunningNginx Pod,每个 Pod 拥有独立内网 IP,且统一携带标签 app: nginx-web,为后续 Service 动态绑定、负载均衡提供核心匹配依据。

bash 复制代码
nginx-web-55ff4cbb5c-4pwpn   1/1     Running   0          29s   100.99.1.177   iz2ze60mcbemirrld91q6rz   <none>           <none>
nginx-web-55ff4cbb5c-rl647   1/1     Running   0          31s   100.84.1.200   iz2ze60mcbemirrld91q6sz   <none>           <none>
nginx-web-55ff4cbb5c-sbjl8   1/1     Running   0          19s   100.84.1.203   iz2ze60mcbemirrld91q6sz   <none>           <none>

3.2 定义 ClusterIP 服务

基于已部署的 Nginx Pod 集群,我们创建默认类型的 ClusterIP Service,为动态可变的 Pod 提供固定虚拟 IP、四层负载均衡能力。下面拆解 Service 所有核心配置项,逐一讲解字段作用。

3.2.1 基本信息

基础元数据用于标识资源身份,包含资源类型、服务名称、命名空间,是集群资源管理、DNS 自动解析的基础。

创建 nginx-svc.yaml,基础配置如下:

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  namespace: default

核心特性Service 名称会自动注册到集群 CoreDNS,集群内所有 Pod 可通过固定域名 nginx-svc.default.svc.cluster.local 访问服务,无需感知 Pod 真实 IPClusterIP

3.2.2 标签选择器

标签选择器是 ServicePod 关联的 核心唯一规则Service 不绑定固定 Pod IP,而是通过标签动态匹配后端 Pod 集群。

yaml 复制代码
spec:
  selector:
    app: nginx-web

工作机制 :每个节点的 kube-proxy 持续监听集群资源变化,只要 Pod 带有 app: nginx-web 标签,会自动加入当前 Service 的后端端点池;Pod 扩缩容、重建、删除时,转发规则自动更新,业务访问完全无感知。

3.2.3 端口映射

端口映射定义 Service 虚拟端口与后端 Pod 真实端口的转发规则,是流量通信的核心,包含三个核心字段:

yaml 复制代码
ports:
  - name: http-web
    port: 8080
    targetPort: 80
    protocol: TCP

字段详解

  • portService 对外暴露的虚拟端口,集群内客户端通过 ClusterIP:port 访问服务

  • targetPort:后端 Pod 容器真实监听端口,Nginx 默认监听 80 端口

  • protocol:传输层协议,默认 TCP,支持 UDPSCTP

1)port

Service 这个「虚拟负载均衡器」对外暴露的端口,是集群内部访问服务的唯一入口。只要 Service 不删除、不修改,这个端口就永远不变,和后端 Pod 怎么变化无关。

访问方式 :集群内的客户端 Pod 通过 服务名:port 或者 ClusterIP:port 访问服务。例如:curl http://my-service:80,这里的 80 就是 port

关键特性

  • 纯虚拟概念,不存在真实的进程监听这个端口,由 kube-proxy 通过 iptables/ipvs 规则实现转发;
  • 不同 Service 可以使用相同的 port,互不影响,因为每个 Service 有独立的 ClusterIP
  • 是四种 Service 类型都具备的基础字段。

新手常见误区 :误以为 port 必须和后端容器端口一致。实际上两者完全解耦,比如 Service80 端口,后端容器可以用 93768080 等任意端口。

2)targetPort

核心作用 :流量最终要送达的容器端口,也就是你业务代码、中间件实际启动时监听的端口。Service 收到流量后,最终会转发到 Pod 的这个端口上。

两种写法

  1. 写具体端口号:targetPort: 9376,直接指定容器端口;
  2. 写端口名称:targetPort: http-web-svc,引用 Pod 定义里的端口名称(生产推荐,兼容性更强)。

关键特性

  • 必须和容器内程序真实监听的端口完全一致,否则会出现连接超时、连接拒绝,服务不通;
  • 一组后端 Pod 可以有不同的容器端口,只要端口名称一致,Service 就能统一适配,非常适合多版本灰度、业务迭代场景。

新手常见误区 :漏写 targetPort。如果不写,K8s 默认让 targetPortport 取值相同;如果容器端口和 port 不一致,会直接导致服务不可用。


3.2.4 其他可选配置

ClusterIP 支持拓展配置,可实现会话保持等进阶能力,适配特殊业务场景:

yaml 复制代码
type: ClusterIP
  # 基于客户端IP实现会话保持
  sessionAffinity: ClientIP
  # 会话保持超时时间
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 3600

适用场景:需要会话粘性的业务,保证同一客户端流量始终转发至同一个后端 Pod。普通无状态微服务无需开启。

3.2.5 完整清单

整合所有配置,完整可直接运行的单端口 ClusterIP 服务 YAML

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  namespace: default
spec:
  type: ClusterIP
  selector:
    app: nginx-web
  ports:
  - name: http-web
    port: 8080
    targetPort: 80
    protocol: TCP
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 3600

部署服务:

bash 复制代码
kubectl apply -f nginx-svc.yaml

3.3 验证测试

查看 Service 虚拟 IP 与端口:

bash 复制代码
kubectl get svc nginx-svc

输出示例:

bash 复制代码
NAME        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
nginx-svc   ClusterIP   10.98.17.136   <none>        8080/TCP   7s

查看后端绑定的 Pod 端点,验证匹配成功:

bash 复制代码
kubectl get endpoints nginx-svc

正常输出会展示 3 个后端 Pod 真实 IP,证明 Service 已成功关联全部业务副本:

bash 复制代码
NAME        ENDPOINTS                                            AGE
nginx-svc   100.84.42.200:80,100.84.42.203:80,100.99.87.177:80   2m19s

在集群内部,通过虚拟 IP:port 正常访问到真实服务:

bash 复制代码
[root@iZ2ze60mcbemirrld91q6qZ td]# curl 10.98.17.136:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

3.4 多端口 Service

很多应用需要同时暴露多个端口(比如 HTTP + HTTPS、业务端口 + 监控端口),Service 支持多端口配置,每个端口必须指定名称避免歧义:

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 8080
      targetPort: 80
    - name: https
      protocol: TCP
      port: 443
      targetPort: 443

命名规范:端口名只能包含小写字母、数字和横杠,且必须以字母数字开头和结尾。例如 123-abcweb 合法,123_abc-web 不合法。

相关推荐
Zhu7588 小时前
在k8s环境部署Apache Kafka 集群架构,combined模式,无SSL配置
kafka·kubernetes·apache
天狼啸月19908 小时前
RagSEDE复现: Docker and Ragflow 关系
docker·容器·ragflow·ragsede
C137的本贾尼9 小时前
第十篇:Docker & K8s——标准化应用的“集装箱“和“码头调度中心“
docker·容器·kubernetes
增量星球9 小时前
《持续交付2.0系列一》从技术交付到业务引领
java·docker·容器·devops
Zhu7589 小时前
在k8s环境部署Apache kafka4.3.1
容器·kubernetes·apache
Demon_Hao10 小时前
Spring Boot 4、JDK 25 与 GraalVM:Java 云原生进化,能否撼动 Go 的地位?
java·spring boot·云原生
江畔柳前堤19 小时前
GO01-Go 语言与主流编程语言深度对比
开发语言·人工智能·后端·微服务·云原生·golang·go
探索云原生1 天前
终于搞懂 Kueue:5 个核心对象一次讲透
linux·docker·ai·云原生·kubernetes
众人皆醒我独醉1 天前
etcd,其实不是你以为的"数据库"——Kubernetes 大脑的六个反直觉真相
面试·kubernetes
月落星还在1 天前
Spring MVC 与 Spring Boot:从“手动挡”到“自动驾驶”的进化论,兼谈前后端分离的哲学
spring boot·spring·云原生·mvc