提示:本文原创作品,良心制作,干货为主,简洁清晰,一看就会
文章目录
- 前言
- 一、Service基本知识
-
- [1.1 Service 是什么](#1.1 Service 是什么)
- [1.2 Service 核心工作机制](#1.2 Service 核心工作机制)
- [1.3 Service常用类型](#1.3 Service常用类型)
- 二、Service实例
-
- [2.1 ClusterIP](#2.1 ClusterIP)
- [2.2 NodePort](#2.2 NodePort)
- [2.3 LoadBalancer](#2.3 LoadBalancer)
- [2.4 ExternalName](#2.4 ExternalName)
- [2.5 Headless Service](#2.5 Headless Service)
- 三、Endpoint是什么
前言
在Kubernetes集群中,Pod作为应用最小运行单元,会因扩缩容、重启、节点调度频繁重建,其IP地址动态变化,无法直接作为稳定访问入口。而Service是K8s核心网络资源对象,可为一组Pod提供固定访问端点、实现负载均衡与服务发现,屏蔽后端Pod的动态变化,是集群内部及外部应用通信的关键基石
一、Service基本知识
1.1 Service 是什么
Service 是 K8S 给一组 Pod 提供的固定统一入口 + 负载均衡 + 服务发现资源对象
Pod 特点:IP 不固定、随时重启销毁、扩缩容数量变多变少,不能直接对外/互相访问
Service 作用:屏蔽 Pod 动态变化,给后端一组同类 Pod 一个固定虚拟IP(ClusterIP),客户端只访问 Service,不用关心后端 Pod 数量和IP变化;通过标签关联Pod + 固定集群IP + kube-proxy转发 + DNS服务发现,解决了Pod IP动态、访问不稳定、流量分发、服务互联四大核心问题
1.2 Service 核心工作机制
-
标签匹配 关联 Pod
Service 通过
标签Selector,匹配集群中带有对应标签的Pod,自动纳入后端端点列表(Endpoints)只要标签匹配上,新增/删除Pod,Service会自动感知,不用手动改配置
-
固定虚拟IP 稳定访问
Service 创建后会分配一个
集群内固定 ClusterIP,这个IP永久不变集群内所有服务都通过这个固定IP访问,完全感知不到后端Pod的重建和漂移
-
内置负载均衡
Service 自带四层负载均衡,请求到达 Service 后,会
轮询分发到后端健康的Pod上,自动实现流量均分 -
kube-proxy 落地转发规则
每个节点上的
kube-proxy是核心执行者监听 Service 和 Endpoints 变化,自动在节点上生成
iptables/ipvs 转发规则,把访问 Service 的流量,精准转发到后端真实Pod -
服务发现机制
集群内部:K8s 内置 CoreDNS,自动把 Service名称 解析为 ClusterIP,Pod 直接用服务名就能通信
无需手动配置IP、hosts,实现微服务自动注册与发现
1.3 Service常用类型
Kubernetes Service 包含五种常用类型
ClusterIP 为默认类型,仅分配集群内部虚拟IP,供集群内微服务间通信
NodePort 在所有节点开放固定端口,允许外部通过节点IP+端口临时访问服务
LoadBalancer 依托云厂商负载均衡能力,绑定公网IP,适用于生产环境业务公网暴露
ExternalName 无端口转发与集群IP,仅通过服务别名映射外部域名,方便集群内访问第三方外网服务
Headless 无头服务 不分配ClusterIP,DNS直接解析至后端所有Pod真实IP,多用于MySQL、Redis等有状态应用组网通信

ClusterIP:只给集群内部 Pod / 节点通信
NodePort:开节点端口,外部可通过任意节点 IP + 端口访问
LoadBalancer:云厂商在最外层加公网 SLB,统一入口、生产对外业务首选
二、Service实例
2.1 ClusterIP
ClusterIP(默认类型) :自动分配集群内部虚拟IP,仅集群内部Pod可访问,外部无法访问
适用场景:微服务之间内部调用、后端服务互相通信,是默认最常用类型
yaml
## 1. 创建pod
root@k8s-master1:/k8s-test/service# vim nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.24
ports:
- containerPort: 80
root@k8s-master1:/k8s-test/service# kubectl apply -f nginx.yaml
root@k8s-master1:/k8s-test/service# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5948f7484f-7mll8 1/1 Running 0 4s 10.244.36.115 k8s-node1 <none> <none>
nginx-5948f7484f-9vllw 1/1 Running 0 4s 10.244.169.133 k8s-node2 <none> <none>
yaml
## 2. 创建service
root@k8s-master1:/k8s-test/service# vim nginx-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
type: ClusterIP
selector: # 选择app=nginx标签的pod
app: nginx
ports:
- protocol: TCP
port: 82 # service对外提供的端口
targetPort: 80 # 代理的容器端口
root@k8s-master1:/k8s-test/service# kubectl apply -f nginx-svc.yaml
root@k8s-master1:/k8s-test/service# kubectl get svc # 可以看到ClusterIP为10.102.225.82
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 29d
nginx-svc ClusterIP 10.102.225.82 <none> 82/TCP 7s
yaml
## 3. 修改nginx访问页面
root@k8s-master1:/k8s-test/service# kubectl exec -it nginx-5948f7484f-7mll8 /bin/bash
root@nginx-5948f7484f-7mll8:/# echo 1111 > /usr/share/nginx/html/index.html
root@nginx-5948f7484f-7mll8:/# exit
exit
root@k8s-master1:/k8s-test/service# kubectl exec -it nginx-5948f7484f-9vllw /bin/bash
root@nginx-5948f7484f-9vllw:/# echo 2222 > /usr/share/nginx/html/index.html
root@nginx-5948f7484f-9vllw:/# exit
exit
访问ClusterIP:端口,发现有轮询效果

yaml
## 删除svc,ClusterIP就会被删除
root@k8s-master1:/k8s-test/service# kubectl delete -f nginx-svc.yaml
root@k8s-master1:/k8s-test/service# curl 10.102.225.82:82
^C
root@k8s-master1:/k8s-test/service# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 29d
2.2 NodePort
NodePort :在每个集群节点上开放一个固定物理端口 ,外部通过 节点IP:NodePort 即可访问服务
底层依然依赖 ClusterIP,流量先到节点端口,再转发至内部Service
适用场景:测试环境临时对外暴露服务、无需额外网关的简单外部访问
yaml
root@k8s-master1:/k8s-test/service# vim nginx-svc-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-svc-nodeport
spec:
type: NodePort
selector:
app: nginx
ports:
- protocol: TCP
port: 82 # service内部端口
targetPort: 80 # 容器端口
nodePort: 32001 # 外部访问端口,范围是30000--32767
root@k8s-master1:/k8s-test/service# kubectl apply -f nginx-svc-nodeport.yaml
root@k8s-master1:/k8s-test/service# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 29d
nginx-svc-nodeport NodePort 10.100.85.78 <none> 82:32001/TCP 2s
集群内部访问NodePort:端口

集群外部访问node ip:32001,我这里访问了k8s-node1的ip:32001和k8s-node2的ip:32001

2.3 LoadBalancer
LoadBalancer :在NodePort基础上,依托云厂商负载均衡器,分配独立公网IP ,直接暴露公网访问
适用场景:生产环境业务对外提供服务,云服务器(阿里云/腾讯云/华为云)专属类型
LoadBalancer 不是靠 YAML 单独生效,是 K8s 云控制器监听资源变化 → 调用云厂商API → 自动创建SLB、分配公网IP → 回写到Service
yaml
root@k8s-master1:/k8s-test/service# vim nginx-svc-loadbalancer.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-svc-lb
spec:
# 类型设为 LoadBalancer
type: LoadBalancer
selector:
app: nginx
ports:
- protocol: TCP
port: 82 # Service 内部端口
targetPort: 80 # Pod 容器端口
root@k8s-master1:/k8s-test/service# kubectl apply -f nginx-svc-loadbalancer.yaml
## EXTERNAL-IP是云厂商分配的公网 IP
root@k8s-master1:/k8s-test/service# kubectl get svc nginx-svc-lb
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-svc-lb LoadBalancer 10.102.120.150 120.79.xx.xx 82:30123/TCP 2m
集群内部访问curl 10.102.120.150:82
集群外部访问http://120.79.xx.xx:82
LoadBalancer 完整幕后流程:
1,用户执行
kubectl apply,向 kube-apiserver 创建LoadBalancer类型 Service 资源2,云上 K8s 集群内置云控制器 CCM ,实时监听集群中 LoadBalancer 类型 Service 的变动
3,CCM 捕获资源变更后,自动调用云厂商开放 API,申请创建负载均衡 SLB 实例
4,云厂商后台自动完成:创建 SLB、分配空闲公网 IP、配置端口监听与后端转发规则
5,CCM 获取到公网 IP 后,自动回写更新到 Service 的
EXTERNAL-IP字段,完成外部流量接入
2.4 ExternalName
ExternalName :不分配ClusterIP、不做端口转发,通过Service别名映射外部域名
适用场景:集群内Pod访问集群外第三方服务、数据库、外部接口,用服务名替代复杂外网域名
引入集群外部的服务,可以在集群内部通过别名方式访问(通过serviceName.namespaceName.svc.cluster.local访问)
yaml
## 1. 创建一个ExternalName类型的svc
root@k8s-master1:/k8s-test/service# vim nginx-svc-externalName.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-external
spec:
type: ExternalName
externalName: www.baidu.com
root@k8s-master1:/k8s-test/service# kubectl apply -f nginx-svc-externalName.yaml
root@k8s-master1:/k8s-test/service# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 29d
nginx-external ExternalName <none> www.baidu.com <none> 3s
yaml
## 2, 运行一个有ping命令的pod,然后在pod中ping百度可以访问成功
root@k8s-master1:/k8s-test/service# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
centos 1/1 Running 0 4s 10.244.36.116 k8s-node1 <none> <none>
root@k8s-master1:/k8s-test/service# kubectl exec -it centos /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
[root@centos /]# ping nginx-external.default.svc.cluster.local
PING www.a.shifen.com (36.152.44.132) 56(84) bytes of data.
64 bytes from 36.152.44.132 (36.152.44.132): icmp_seq=1 ttl=127 time=11.6 ms
64 bytes from 36.152.44.132 (36.152.44.132): icmp_seq=2 ttl=127 time=11.7 ms
^C
--- www.a.shifen.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 4005ms
rtt min/avg/max/mdev = 11.434/11.589/11.709/0.122 ms
[root@centos /]#
2.5 Headless Service
Headless Service(无头服务,ClusterIP: None) :不分配固定ClusterIP,DNS直接解析到后端所有Pod真实IP
适用场景:有状态应用(MySQL、Redis、Elasticsearch)、集群自建中间件,需要直连单个Pod的场景
问题一:普通 Service 是做什么的?
普通的 ClusterIP/NodePort Service,做的是两件事:给一组 Pod 分配一个统一的虚拟 IP(ClusterIP) ;对请求做负载均衡 ,随机转发到某一个后端 Pod
客户端只需要访问这个统一 IP,不用关心背后是哪个 Pod 在处理请求
问题二:什么时候我们不想要这种"负载均衡+统一IP"?
场景1:部署 Kafka 集群
Kafka 这类分布式中间件,客户端需要直接知道所有 Broker 的真实 IP 列表,而不是通过一个统一 IP 被随机转发。
- 客户端需要和每个节点直接通信,维护分区、副本关系
- 如果用普通 Service 的负载均衡,客户端永远只能看到一个 IP,无法感知集群拓扑,就没法正常工作
场景2:部署 MySQL 主从/双节点
这类场景通常是:
- 一个主库负责写,一个从库负责读,客户端需要自己决定发给哪个 Pod
- 不能让 Service 随机转发,否则写请求发到从库就会失败
- 客户端需要拿到两个 Pod 的真实 IP,自己控制读写路由
问题三:Headless Service 是怎么解决这些问题的?
它的核心特点就是:不给 Service 分配统一的 ClusterIP,直接把所有后端 Pod 的真实 IP 暴露给客户端。
- 配置上只要加一句:
spec.clusterIP: None - 客户端访问这个 Service 的域名时,DNS 会直接返回所有 Pod 的 IP 列表,而不是一个统一的虚拟 IP
- 客户端可以自己选择和哪个 Pod 通信,完全绕开了 K8s 的负载均衡逻辑
普通 Service 是"给客户端一个统一入口,屏蔽后端细节";Headless Service 则是"把所有后端 Pod 的真实 IP 直接暴露给客户端,让客户端自己决定怎么访问",专门解决 Kafka、MySQL 这类需要直连节点的分布式应用场景
yaml
## 1. 先创建mysql的pod
root@k8s-master1:/k8s-test/service# vim mysql.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
replicas: 2
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: "Qing@123"
root@k8s-master1:/k8s-test/service# kubectl apply -f mysql.yaml
root@k8s-master1:/k8s-test/service# kubectl get pod
NAME READY STATUS RESTARTS AGE
mysql-5c7779ff64-5sslb 1/1 Running 0 4s
mysql-5c7779ff64-9wsl2 1/1 Running 0 4s
yaml
## 2. 创建svc
root@k8s-master1:/k8s-test/service# vim mysql-headless.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
spec:
clusterIP: None # 无头服务
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
root@k8s-master1:/k8s-test/service# kubectl apply -f mysql-headless.yaml
root@k8s-master1:/k8s-test/service# kubectl get svc mysql-headless
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mysql-headless ClusterIP None <none> 3306/TCP 73s
## 3. busybox 测试DNS,直接显示所有MySQL Pod的真实IP = Headless Service生效!
root@k8s-master1:/k8s-test/service# kubectl run -it --rm --restart=Never busybox --image=busybox:1.28.4 -- nslookup mysql-headless
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: mysql-headless
Address 1: 10.244.169.145 10-244-169-145.mysql-headless.default.svc.cluster.local
Address 2: 10.244.36.122 10-244-36-122.mysql-headless.default.svc.cluster.local
pod "busybox" deleted
root@k8s-master1:/k8s-test/service#
三、Endpoint是什么
Endpoint 就是 Service 后端,实际接收流量的 Pod IP + 端口的集合
Service 负责对外暴露入口,Endpoints 负责维护后端真实 Pod 的 IP 列表,两者一一对应,共同实现服务发现与负载均衡
yaml
## 1. 创建一个控制器,然后查看其ip地址
root@k8s-master1:/k8s-test/service# vim nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.24
ports:
- containerPort: 80
root@k8s-master1:/k8s-test/service# kubectl apply -f nginx.yaml
root@k8s-master1:/k8s-test/service# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5948f7484f-65kq6 1/1 Running 0 7s 10.244.169.146 k8s-node2 <none> <none>
nginx-5948f7484f-l8bdg 1/1 Running 0 7s 10.244.36.126 k8s-node1 <none> <none>
yaml
## 2. 创建svc
root@k8s-master1:/k8s-test/service# vim nginx-svc-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-svc-nodeport
spec:
type: NodePort
selector:
app: nginx
ports:
- protocol: TCP
port: 82
targetPort: 80
nodePort: 32001
root@k8s-master1:/k8s-test/service# kubectl apply -f nginx-svc-nodeport.yaml
root@k8s-master1:/k8s-test/service# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 29d
nginx-svc-nodeport NodePort 10.109.202.98 <none> 82:32001/TCP 3s
## 3. 查看endpoints,可以看到和nginx pod的ip一致
root@k8s-master1:/k8s-test/service# kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.13.136:6443 29d
nginx-svc-nodeport 10.244.169.146:80,10.244.36.126:80 23s
## 4. 修改副本控制器数量,观察endpoints能否自动改变
root@k8s-master1:/k8s-test/service# vim nginx.yaml
replicas: 4 # 我这里修改成4了
root@k8s-master1:/k8s-test/service# kubectl apply -f nginx.yaml
root@k8s-master1:/k8s-test/service# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5948f7484f-65kq6 1/1 Running 0 108s 10.244.169.146 k8s-node2 <none> <none>
nginx-5948f7484f-664rb 1/1 Running 0 5s 10.244.169.149 k8s-node2 <none> <none>
nginx-5948f7484f-7c95k 1/1 Running 0 5s 10.244.36.123 k8s-node1 <none> <none>
nginx-5948f7484f-l8bdg 1/1 Running 0 108s 10.244.36.126 k8s-node1 <none> <none>
## 5. 可以看到endpoints会自动改变
root@k8s-master1:/k8s-test/service# kubectl describe endpoints nginx-svc-nodeport
Name: nginx-svc-nodeport
Namespace: default
Labels: <none>
Annotations: endpoints.kubernetes.io/last-change-trigger-time: 2026-05-08T04:59:05Z
Subsets:
Addresses: 10.244.169.146,10.244.169.149,10.244.36.123,10.244.36.126
NotReadyAddresses: <none>
Ports:
Name Port Protocol
---- ---- --------
<unset> 80 TCP
Events: <none>
注:
文中若有疏漏,欢迎大家指正赐教。
本文为100%原创,转载请务必标注原创作者,尊重劳动成果。
求赞、求关注、求评论!你的支持是我更新的最大动力,评论区等你~