
在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生成的集群内域名,原因有两个:
-
域名是 "集群私有" 的 :CoreDNS 只负责 K8s 集群内部的域名解析,集群外的设备(你的电脑、手机)没有配置 "用 CoreDNS 做 DNS 服务器",根本不认识
cluster.local
后缀的域名; -
网络隔离:K8s 内的 Service(尤其是默认的 ClusterIP 类型)只在集群内暴露,集群外网络无法直接访问其 IP,更别说通过域名访问了。
c.如果需要从浏览器(集群外)访问K8s里的Service,需先让Service"暴露到集群外",再用外部可访问的地址:
-
用 NodePort 类型的 Service :K8s 会在每个节点上开放一个 "节点端口",浏览器通过 "节点 IP: 节点端口" 访问(比如节点 IP 是
192.168.1.100
,节点端口是30080
,则访问http://192.168.1.100:30080
); -
用 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
):
-
创建 Service(无 selector):
apiVersion: v1
kind: Service
metadata:
name: external-mysql
spec:
type: ClusterIP
ports:- port: 3306
targetPort: 3306
- port: 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 模式
-
编辑 kube-proxy 配置文件:
kubectl edit configmap kube-proxy -n kube-system
-
修改
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 解析
-
创建测试 Pod(含
nslookup
工具):apiVersion: v1
kind: Pod
metadata:
name: dig-pod
spec:
containers:- name: dig
image: busybox:1.28
command: ["sleep", "3600"] # 保持 Pod 运行
- name: dig
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
注:点赞+关注+收藏,下期我们接着讲剩余两种工作负载,不见不散。