【K8s Service 基础知识、五大类型应用机制及Endpoint 深度解析】

提示:本文原创作品,良心制作,干货为主,简洁清晰,一看就会

文章目录

  • 前言
  • 一、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 核心工作机制

  1. 标签匹配 关联 Pod

    Service 通过标签Selector,匹配集群中带有对应标签的Pod,自动纳入后端端点列表(Endpoints)

    只要标签匹配上,新增/删除Pod,Service会自动感知,不用手动改配置

  2. 固定虚拟IP 稳定访问

    Service 创建后会分配一个集群内固定 ClusterIP,这个IP永久不变

    集群内所有服务都通过这个固定IP访问,完全感知不到后端Pod的重建和漂移

  3. 内置负载均衡

    Service 自带四层负载均衡,请求到达 Service 后,会轮询分发到后端健康的Pod上,自动实现流量均分

  4. kube-proxy 落地转发规则

    每个节点上的 kube-proxy 是核心执行者

    监听 Service 和 Endpoints 变化,自动在节点上生成iptables/ipvs 转发规则,把访问 Service 的流量,精准转发到后端真实Pod

  5. 服务发现机制

    集群内部: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%原创,转载请务必标注原创作者,尊重劳动成果。

求赞、求关注、求评论!你的支持是我更新的最大动力,评论区等你~

相关推荐
老卢聊运维1 小时前
K8s 资源一直 Terminating?kubectl 强制删除完整实操手册
云原生·容器·kubernetes
眷蓝天1 小时前
Kubernetes Ingress 资源对象
云原生·容器·kubernetes
Nice_Fold2 小时前
Kubernetes Ingress 七层负载均衡与Nginx实现
nginx·kubernetes·负载均衡
步步为营DotNet2 小时前
NET 11 中 C# 14 新特性在云原生微服务架构的深度实践
云原生·架构·c#
ん贤2 小时前
Kubernetes入门
云原生·容器·kubernetes
赵优秀一一2 小时前
Docker1: 安装、镜像和容器概念
运维·docker·容器
shizhan_cloud2 小时前
K8S部署LNMP架构 ECShop
kubernetes
milo.qu11 小时前
RockyLinux9.7 docker部署Jisti Meet
linux·docker·容器
梦想与想象-广州大智汇13 小时前
自建docker加速镜像,使用 Cloudflare Workers/Pages 部署加速教程
运维·docker·容器