Kubernetes 四层负载均衡:Service核心原理与实战指南

在Kudernetes(K8s)集群中,Pod存在生命周期短,IP动态变化的特性,直接通过Pod IP访问服务会导致"服务不可达"问题。Service(服务)作为K8s核心的四层负载均衡组件,通过抽象固定访问入口、关联后端Pod集群,实现流量分发,解决了Pod动态性带来的服务访问难题。本文将从概念、原理、配置实战、核心组件(kube-proxy、CoreDNS)等维度,全面解读Service知识点。

一、Service 核心概念:为什么需要 Service?

service是K8s中定义的"逻辑服务抽象",本质是一组具有相同标签的Pod的负载均衡入口,其核心价值在于解决以下两大问题:

1.解决Pod IP动态变化问题

Pod因重启、重建、调度等原因,IP会动态变更。若直接依赖Pod IP访问服务,Pod异常后服务会中断。Service为后端Pod提供固定的虚拟IP(Cluster IP),客户端只需访问Service IP,无需关心后端Pod的IP变化 ----Service会自动通过标签选择器(Lable Selector)关联最新的Pod

2.实现集群内外服务访问

  • 集群内访问:Service 提供 ClusterIP,供集群内其他 Pod(如前端服务访问后端 API)通过固定地址访问;

  • 集群外访问:通过 NodePort、LoadBalancer 等 Service 类型,将内部服务暴露到集群外,支持外部客户端(如浏览器、手机 App)访问。

注:什么是API?

API(应用程序编程接口)是不同软件 / 系统间遵循统一规则沟通的 "桥梁",定义了数据请求与返回的方式,能实现功能复用、降低开发成本且保障交互安全,像餐厅服务员般帮软件间传递需求与结果,无需暴露彼此内部逻辑。

二、Service 工作原理:从请求到 Pod 的完整链路

Service的工作依赖标签选择器(Label Selector)、Endpoint、kube-proxy、CoreDNS四大组件的协同,完整链路如下:

1. 核心组件协作流程

**1.标签关联Pod:**Service通过spec.selector匹配具有相同标签的Pod(如run:my-nginx);

**2.自动创建EndPoint:**K8s会自动创建与Service同名的Endpoint对象,记录所有匹配的Pod的IP和端口(如:10.244.187.102:80)

**3.kube-proxy配置转发规则:**每个节点上的kube-proxy组件监听APIServer,实时同步Service和Endpoint变化,在节点上生成IPVS/iptables转发规则;

**5.DNS服务发现:**CoreDNS为Service提供域名解析(如my-nginx.default.svc.cluster.loacl),客户端通过域名访问Service,无需记忆IP

注:1.这里就很抽象先理清K8s里的"客户端"是谁?2.浏览器能直接访问这个域名吗?3.想在集群外访问怎么办?

a.这里的"客户端"不是我们日常用的浏览器,而是K8s集群内部的Pod(比如前端Pod访问后端服务Pod),或集群内的其他服务组件(如监控组件访问数据库Service)。

🙋‍♀️🌰:你在K8s里部署了"客户端" ----它不用记nginx Service的IP,直接用域名my-nginx.default.svc.cluster.local就能访问。

b.日常用的浏览器(比如Chrome、Edge)不在K8s集群内部,无法解析CoreDNS生成的集群内域名,原因有两个:

  1. 域名是 "集群私有" 的 :CoreDNS 只负责 K8s 集群内部的域名解析,集群外的设备(你的电脑、手机)没有配置 "用 CoreDNS 做 DNS 服务器",根本不认识cluster.local后缀的域名;

  2. 网络隔离:K8s 内的 Service(尤其是默认的 ClusterIP 类型)只在集群内暴露,集群外网络无法直接访问其 IP,更别说通过域名访问了。

c.如果需要从浏览器(集群外)访问K8s里的Service,需先让Service"暴露到集群外",再用外部可访问的地址:

  1. 用 NodePort 类型的 Service :K8s 会在每个节点上开放一个 "节点端口",浏览器通过 "节点 IP: 节点端口" 访问(比如节点 IP 是192.168.1.100,节点端口是30080,则访问http://192.168.1.100:30080);

  2. 用 Ingress :通过 Ingress 控制器(如 Nginx Ingress)为 Service 绑定 "外部域名"(比如nginx.example.com),浏览器直接访问这个外部域名即可(需提前把外部域名解析到 Ingress 控制器的 IP)。

一句话总结:K8s 里的 Service 域名(如my-nginx.default.svc.cluster.local)是 CoreDNS 按 "Service 名 + 命名空间" 自动生成的,集群内的 Pod(客户端)直接按规则写域名就能访问,不用主动获取;日常浏览器在集群外,无法解析这个私有域名,需通过 NodePort 或 Ingress 暴露 Service 后,用外部地址访问。

2. K8s 中的三类 IP 地址

Service 工作依赖集群中三类不同角色的 IP,需明确区分:

IP 类型 作用 示例 特点
Node IP(节点 IP) 物理 / 虚拟节点的实际 IP 192.168.1.63 配置在节点网卡(如 ens33),真实可路由
Pod IP(Pod 网络) 每个 Pod 的独立 IP 10.244.187.76 由网络插件(如 Flannel、Calico)分配,仅集群内可达
Cluster IP(服务 IP) Service 的虚拟 IP 10.99.198.177 无实际网卡绑定,仅存在于转发规则中

三、Service 核心配置:类型、端口与 YAML 示例

Service 的核心配置集中在 spec 字段,包括 类型(type)、端口(ports)、标签选择器(selector),以下是详细解析。

1. Service 的四种类型

K8s 支持四种 Service 类型,对应不同的访问场景:

类型 作用 适用场景 访问方式
ClusterIP 集群内暴露服务,仅集群内可访问 内部服务通信(如前端→后端 API) ClusterIP:Port(如 10.99.198.177:80
NodePort 集群外暴露服务,在每个节点开放固定端口 测试环境外部访问(如浏览器访问) NodeIP:NodePort(如 192.168.1.63:30380
LoadBalancer 结合云厂商负载均衡器(如 AWS ELB) 生产环境外部访问(高可用) 云厂商提供的外部 IP(如 10.0.0.1
ExternalName 将 Service 映射到外部域名 访问集群外服务(如外部 MySQL) 域名(如 my.database.example.com

注:1.service的FQDN(全质量域名) 是: <service_name>.<namespace>.svc.cluster.local

my-service.prod. svc.cluster.local
2. NodePort:通过每个Node节点上的IP和静态端口暴露
k 8s 集群内部的 服务。通过请求<NodeIP>:<NodePort> 可以把请求代理到内部的pod。 Client----->NodeIP:NodePort-----> Service Ip :ServicePort----->PodIP:ContainerPort。

2. Service 端口配置:port、targetPort、nodePort

Service 的端口配置是流量转发的关键,需明确三个端口的区别:

端口字段 作用 示例 说明
port Service 对外暴露的端口(集群内访问) 80 客户端访问 Service 时使用的端口
targetPort 后端 Pod 的实际端口 80 需与 Pod 容器暴露的端口(如 Dockerfile 的 EXPOSE)一致
nodePort 节点上开放的端口(仅 NodePort 类型) 30380 范围默认 30000-32767,外部客户端通过 NodeIP:nodePort 访问

3. 四种 Service 类型的 YAML 实战示例

示例 1:ClusterIP(默认类型,集群内访问)

适用于集群内服务通信(如前端 Pod 访问后端 Nginx 服务):

复制代码
apiVersion: v1
kind: Service
metadata:
  name: my-nginx        #Service名称
  labels:
    run: my-nginx       #自定义标签
spec:
  type: ClusterIP       #类型为ClusterIP
  selector:
    run: my-nginx       #匹配标签为 run: my-nignx的Pod
  ports:
  - port: 80            #Service端口(集群内访问用)
    targetPort: 80     #Pod容器端口
    protocol: TCP       #协议(默认 TCP)

验证命令

复制代码
# 创建 Service
kubectl apply -f service-clusterip.yaml
# 查看 Service(获取 ClusterIP)
kubectl get svc my-nginx   # 输出:10.99.198.177:80
# 集群内访问(如在控制节点执行)
curl 10.99.198.177:80  # 返回 Nginx 欢迎页
示例 2:NodePort(集群外访问)

适用于测试环境,外部客户端通过节点 IP + 固定端口访问:

复制代码
apiVersion: v1
kind: Service
metadata:
  name: my-nginx-nodeport
spec:
  type: NodePort  # 类型为 NodePort
  selector:
    run: my-nginx-nodeport  # 匹配对应 Pod 标签
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30380  # 节点开放的端口(可选,默认自动分配)

验证命令

复制代码
# 创建 Service
kubectl apply -f service-nodeport.yaml
# 查看 Service(确认 NodePort)
kubectl get svc my-nginx-nodeport  # 输出:80:30380/TCP
# 集群外访问(如本地浏览器)
curl 192.168.1.63:30380  # 192.168.1.63 是节点 IP
示例 3:ExternalName(映射外部域名)

适用于访问集群外服务(如外部 MySQL 数据库),无需关联 Pod:

复制代码
apiVersion: v1
kind: Service
metadata:
  name: external-mysql
  namespace: prod
spec:
  type: ExternalName  # 类型为 ExternalName
  externalName: my.database.example.com  # 外部域名
  ports:
  - port: 3306
    targetPort: 3306

验证命令

复制代码
# 集群内 Pod 访问外部域名
kubectl exec -it client-pod -- curl external-mysql.prod.svc.cluster.local:3306
示例 4:映射外部服务(手动配置 Endpoint)

若需访问集群外的固定 IP 服务(如节点上的 MySQL),可手动创建 Endpoint 关联 Service(无需 selector):

  1. 创建 Service(无 selector):

    apiVersion: v1
    kind: Service
    metadata:
    name: external-mysql
    spec:
    type: ClusterIP
    ports:

    • port: 3306
      targetPort: 3306

2.手动创建 Endpoint(关联外部 IP):

复制代码
apiVersion: v1
kind: Endpoints
metadata:
  name: external-mysql  # 必须与 Service 同名
subsets:
- addresses:
  - ip: 192.168.1.13  # 外部服务 IP(如节点上的 MySQL)
  ports:
  - port: 3306

验证命令

复制代码
# 查看 Endpoint(确认外部 IP 已关联)
kubectl get ep external-mysql  # 输出:192.168.1.13:3306
# 集群内访问外部 MySQL
kubectl exec -it client-pod -- mysql -h external-mysql -P 3306 -u root

四、kube-proxy:Service 转发的核心实现

kube-proxy 是每个节点上的核心组件,负责将 Service 流量转发到后端 Pod,支持 Userspace、iptables、IPVS 三种工作模式,目前主流为 IPVS 模式。

1. 三种工作模式对比

模式 原理 性能 特点 适用版本
Userspace 流量在用户空间与内核空间来回切换 早期模式,已淘汰 K8s 1.1 之前
iptables 基于内核 Netfilter 规则转发 规则数量随 Service 增多线性增长,性能下降 K8s 1.2 - 1.10
IPVS 基于内核 IPVS 模块(哈希表)转发 支持轮询、最小连接等多种负载均衡算法 K8s 1.11+(默认模式)

1、Userspace方式:

Client Pod要访问Server Pod时,它先将请求发给内核空间中的service iptables规则,由它再将请求转给监听在指定套接字上的kube-proxy的端口,kube-proxy处理完请求,并分发请求到指定Server Pod后,再将请求转发给内核空间中的service ip,由service iptables将请求转给各个节点中的Server Pod。

这个模式有很大的问题,客户端请求先进入内核空间的,又进去用户空间访问kube-proxy,由kube-proxy封装完成后再进去内核空间的iptables,再根据iptables的规则分发给各节点的用户空间的pod。 由于其需要来回在用户空间和内核空间交互通信,因此效率很差。在Kubernetes 1.1版本之前,userspace是默认的代理模型。

2 、iptables方式:

请求时到达内核空间时,根据ipvs的规则直接分发到各pod上。kube-proxy会监视Kubernetes Service对象和Endpoints,调用netlink接口以相应地创建ipvs规则并定期与Kubernetes Service对象和Endpoints对象同步ipvs规则,以确保ipvs状态与期望一致。访问服务时,流量将被重定向到其中一个后端Pod。与iptables类似,ipvs基于netfilter 的 hook 功能,但使用 哈希表 作为底层数据结构并在内核空间中工作。这意味着ipvs可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs为负载均衡算法提供了更多选项,例如:

rr:轮询调度

lc:最小连接数

dh:目标哈希

sh:源哈希

sed:最短期望延迟

nq:不排队调度

如果某个服务后端pod发生变化,标签选择器适应的pod 多一个,适应的信息会立即反映到apiserver上,而kube-proxy一定可以watch到etc中的信息变化,而将它立即转为ipvs或者iptables中的规则,这一切都是动态和实时的,删除一个pod也是同样的原理。如图:

注: 无论 kube-proxy 采用 Userspace、iptables 还是 IPVS 哪种工作模式,其核心运行逻辑始终围绕 "实时同步集群资源状态" 展开:它会通过 K8s 原生的 Watch 机制,持续监听 APIServer 推送的 Pod 状态变更(如 Pod 新建、删除、重启等)------ 这些状态最终会被 APIServer 持久化到 etcd 中。

一旦感知到 Pod 资源的变化(比如某个后端 Pod 因故障被删除,或新 Pod 完成调度启动),kube-proxy 会立即触发本地转发规则的更新:若当前启用的是 IPVS 模式(K8s 1.11 及以上版本默认),则同步更新内核中的 IPVS 哈希表规则;若 IPVS 模块未激活(如节点内核未加载相关模块),则自动降级为 iptables 模式,通过修改 Netfilter 规则来调整流量分发目标。

这种 "实时感知 - 即时更新" 的机制,能确保客户端请求转发时,始终指向集群中存活的后端 Pod,从根本上避免因 Pod 动态变化导致的 "请求路由到不存在的 Pod" 问题,保障服务访问的可用性。

有可能模式不是ipvs,需要修改为ipvs模式

2. IPVS 模式配置与验证

2.1 开启 IPVS 模式
  1. 编辑 kube-proxy 配置文件:

    kubectl edit configmap kube-proxy -n kube-system

  2. 修改 mode: "ipvs"(默认可能为 iptables):

    data:
    config.conf: |-
    mode: ipvs

3.重启 kube-proxy Pod(使配置生效):

复制代码
kubectl delete pod -l k8s-app=kube-proxy -n kube-system
2.2 验证 IPVS 规则

IPVS 规则会将 Service IP 映射到后端 Pod IP,可通过 ipvsadm 查看:

复制代码
# 安装 ipvsadm(CentOS)
yum install ipvsadm -y
# 查看 Service(如 my-nginx 的 ClusterIP 为 10.99.198.177)
ipvsadm -Ln | grep 10.99.198.177

输出示例(轮询模式 rr):

复制代码
TCP  10.99.198.177:80 rr
  -> 10.244.187.102:80            Masq    1      0          0         
  -> 10.244.209.149:80            Masq    1      0          0         

五、CoreDNS:Service 的 DNS 服务发现

CoreDNS 是 K8s 集群的默认 DNS 组件,为 Service 提供 域名解析,客户端可通过域名访问 Service,无需记忆 ClusterIP。

1. Service 域名规则

Service 的默认全限定域名(FQDN)格式为:
{service-name}.{namespace}.svc.cluster.local

  • service-name:Service 名称;
  • namespace:Service 所在命名空间;
  • svc.cluster.local:K8s 集群默认域名后缀。

示例:my-nginx.default.svc.cluster.local 对应 default 命名空间下的 my-nginx Service。

2. 验证 CoreDNS 解析

  1. 创建测试 Pod(含 nslookup 工具):

    apiVersion: v1
    kind: Pod
    metadata:
    name: dig-pod
    spec:
    containers:

    • name: dig
      image: busybox:1.28
      command: ["sleep", "3600"] # 保持 Pod 运行

2.执行域名解析:

复制代码
# 进入 Pod
kubectl exec -it dig-pod -- /bin/sh
# 解析 Service 域名(如 kubernetes 服务)
nslookup kubernetes.default.svc.cluster.local

3.成功输出示例:

复制代码
Server:    10.96.0.10  # CoreDNS IP
Address 1: 10.96.0.10  # CoreDNS IP
Name:      kubernetes.default.svc.cluster.local
Address 1: 10.96.0.1   # kubernetes Service 的 ClusterIP

注:点赞+关注+收藏,下期我们接着讲剩余两种工作负载,不见不散。

相关推荐
半梦半醒*2 小时前
haproxy负载均衡
linux·运维·服务器·centos·负载均衡
鼠鼠我捏,要死了捏2 小时前
Java Stream API性能优化实践指南
java·performance·stream api
王嘉俊9252 小时前
Java面试宝典:核心基础知识精讲
java·开发语言·面试·java基础·八股文
ZNineSun2 小时前
第二章:Java到Go的思维转变
java·golang
白鲸开源2 小时前
(二)3.1.9 生产“稳”担当:Apache DolphinScheduler Worker 服务源码全方位解析
java·大数据·开源
Joan_Vivian2 小时前
旧项目适配Android15
android·java
华仔啊3 小时前
SpringBoot 中的 7 种耗时统计方式,你用过几种?
java·后端
小蒜学长3 小时前
springboot宠物领养救助平台的开发与设计(代码+数据库+LW)
java·数据库·spring boot·后端·宠物
fendouweiqian3 小时前
pom.xml 不在根目录,idea无法识别项目处理方案
xml·java·intellij-idea