文章目录
- [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 发生变化。
这就带来一个核心问题:
前端业务要调用后端服务,如果后端
Pod的IP一直在变,前端该找谁?怎么保证不会因为Pod重建就调用失败?
Service 就是来解决这个问题的,可以把 Service 理解成「Pod 集群的统一入口 + 内部负载均衡器」:
- 它给一组
Pod分配一个固定不变 的虚拟IP(集群IP) - 客户端只需要访问这个固定地址,流量会自动转发到健康的后端
Pod - 后端
Pod增减、重建、更新,对客户端完全透明,无需修改任何配置
Service 的核心设计目标是业务无侵入 :无论你的应用是云原生新程序,还是老旧系统容器化改造,都不需要修改代码适配服务发现,直接用 Service 即可完成网络暴露。
Service 通过 type 字段指定暴露方式,共四种,能力逐层递进:
| 类型 | 访问范围 | 典型场景 |
|---|---|---|
| ClusterIP | 仅集群内部 | 微服务内部调用、数据库、缓存等后端服务 |
| NodePort | 集群外通过节点IP+端口访问 | 测试环境、自建负载均衡、临时暴露 |
| LoadBalancer | 公网/私有网络通过云负载均衡访问 | 生产环境对外提供服务 |
| ExternalName | 集群内部 DNS 别名 | 映射外部服务到集群内部域名 |
ClusterIP 是 Service 的默认类型,仅分配一个集群内部可达的虚拟 IP,只能在集群内部访问 。它是最基础、最常用的 Service 类型。
很多新手会混淆这三者:
- Service :四层(
TCP/UDP)负载均衡,负责集群内Pod的网络抽象与访问 - Ingress :七层(
HTTP/HTTPS)路由入口,负责把外部流量按域名/路径分发到不同Service - Gateway API :
Ingress的升级替代方案,功能更丰富、扩展性更强,通过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 ,自动分配虚拟 IP:10.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 模式分步详细解析:
- 集群节点
NodeA上的客户端Pod,业务进程发起访问请求:curl 10.96.12.34:8080(ClusterIP) - 流量进入
NodeA本机内核,被iptables规则拦截:- 到达内核
nat表的KUBE-SERVICES全局入口链; - 匹配条件:目标
IP=10.96.12.34、目标端口 =8080; - 匹配成功后,自动跳转当前服务专属链
KUBE-SVC-XXXX;
- 到达内核
- 进入
KUBE-SVC-XXXX服务负载均衡链:kube-proxy预先根据后端3个Pod端点,生成概率随机分流规则,自动实现负载均衡;- 按权重随机选择后端
Pod1/Pod2/Pod3; - 流量进入对应
KUBE-SEP-XXX端点链
KUBE-SEP链执行DNAT地址转换:- 将数据包目标地址
10.96.12.34:8080(虚拟IP)替换为 后端真实Pod容器IP - 示例:
10.96.12.34→10.244.x.x(真实PodIP)
- 将数据包目标地址
DNAT改写完成后,内核重新根据最新目标IP路由:- 若后端
Pod在当前节点:直接本机投递 - 若后端
Pod在其他节点:通过集群CNI网络跨节点转发 - 最终流量精准抵达后端真实业务
Pod。
- 若后端

核心总结:
ClusterIP只是逻辑入口:无网卡、无实体,仅存在于iptables规则中;- 所有转发决策在请求发起节点完成:负载均衡 +
DNAT全部在本地执行; - 三层链分工明确:
KUBE-SERVICES(匹配VIP)→KUBE-SVC(负载均衡)→KUBE-SEP(DNAT真实IP) - 客户端全程无感后端真实
Pod,实现服务透明、弹性扩容、无感切换。
2.4 外部网络访问限制
两层网络隔离限制:
- 路由层面隔离 :
- 服务器宿主机、机房外网网关、公网路由器,都没有配置
Service CIDR(10.96.0.0/12)这段地址的路由规则。 - 外网机器发请求到
10.96.x.x,数据包一出节点就直接丢弃,找不到转发路径。
- 服务器宿主机、机房外网网关、公网路由器,都没有配置
- 转发规则仅集群内生效 :
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 个状态为 Running 的 Nginx 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 真实 IP 和 ClusterIP。
3.2.2 标签选择器
标签选择器是 Service 与 Pod 关联的 核心唯一规则 ,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
字段详解:
-
port:Service对外暴露的虚拟端口,集群内客户端通过ClusterIP:port访问服务 -
targetPort:后端Pod容器真实监听端口,Nginx默认监听80端口 -
protocol:传输层协议,默认TCP,支持UDP、SCTP等
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 必须和后端容器端口一致。实际上两者完全解耦,比如 Service 用 80 端口,后端容器可以用 9376、8080 等任意端口。
2)targetPort
核心作用 :流量最终要送达的容器端口,也就是你业务代码、中间件实际启动时监听的端口。Service 收到流量后,最终会转发到 Pod 的这个端口上。
两种写法:
- 写具体端口号:
targetPort: 9376,直接指定容器端口; - 写端口名称:
targetPort: http-web-svc,引用Pod定义里的端口名称(生产推荐,兼容性更强)。
关键特性:
- 必须和容器内程序真实监听的端口完全一致,否则会出现连接超时、连接拒绝,服务不通;
- 一组后端
Pod可以有不同的容器端口,只要端口名称一致,Service就能统一适配,非常适合多版本灰度、业务迭代场景。
新手常见误区 :漏写 targetPort。如果不写,K8s 默认让 targetPort 和 port 取值相同;如果容器端口和 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-abc、web合法,123_abc、-web不合法。