Kubernetes Service与服务发现

Kubernetes Service与服务发现

_

一、Service基本概念

1. Pod的特征

Pod拥有独立的IP,属于临时性容器资源,可以灵活创建与销毁。集群扩缩容过程中,Pod实例数量会动态增减。如果Pod发生故障,ReplicaSet会自动生成新实例替换。面对Pod频繁启停、扩容缩容、故障重建的场景,如何保障业务访问持续稳定不中断。

2. Service概述

Kubernetes Service是为动态变化的Pod提供固定统一访问入口的核心资源,它通过标签匹配关联后端一组业务Pod,依靠内置负载均衡机制转发流量,屏蔽Pod重建、销毁、扩缩容带来的IP变动问题。既实现集群内外便捷的服务发现,又能保障后端实例频繁更新迭代,业务访问始终稳定不中断。

2.1 Service解决的的核心问题

  • Pod随时创建、删除、漂移、重建,IP地址不固定,无法直接用来访问业务

  • 多个相同业务Pod需要负载均衡分发请求

  • 实现集群内外统一访问,解耦客户端和后端Pod

2.2 Service的实现类型

  • ClusterIP:通过集群的内部IP暴露服务,选择该值时服务只能够在集群内部访问,这是默认的Service类型。

  • NodePort:通过每个节点上的IP和静态端口(NodePort)暴露服务。NodePort服务会路由到自动创建的ClusterIP服务。通过请求<节点IP>:<节点端口>,你可以从集群的外部访问一个NodePort服务。

  • LoadBalancer:使用云厂商的负载均衡器对外暴露服务,外部负载均衡可以将流量路由到自动创建的NodePort服务和ClusterIP服务上。

  • ExternalName:通过返回CNAME和对应值,可以将服务映射到externalName字段的内容,无需创建任何类型的代理。

3. Servic访问解耦方案

3.1 问题的根源

  • Pod是临时性容器:Pod会被创建、销毁、扩缩容,IP地址会频繁变化。

  • 客户端直连:客户端需要知道所有Pod的真实IP,一旦Pod重建/扩容,客户端就得更新地址列表,无法稳定访问。

  • 缺少负载均衡:客户端自己做负载均衡不仅麻烦,还容易出现连接不均、故障节点无法自动剔除。

3.2 解决方案:引入Service逻辑对象,解决上述问题

  • 稳定访问入口:Service会分配一个固定的虚拟IP和DNS名称,客户端只需要访问这个固定地址,再也不用关心Pod IP的变化。

  • 服务发现与负载均衡:Service会自动发现后端所有健康的Pod,并将客户端的请求分发到这些Pod上,实现内置的负载均衡。

  • 解耦客户端与后端:客户端只需要和Service交互,后端Pod的扩缩容、故障重建对客户端来说完全透明,业务访问因此稳定无中断。

4. CoreDNS

CoreDNS是轻量化集群DNS服务组件,以插件化架构部署于Kubernetes集群,核心提供域名解析或服务发现能力。集群内既可以通过IP地址访问业务资源,也可以使用域名便捷访问各类服务。从Kubernetes1.13版本起,CoreDNS正式替代原有kubeDNS,成为集群默认内置的DNS解析组件。

[Tricks] 查看Kubernetes集群组件,能够看到CoreDNS相关Pod实例。

root@k8s-master:~# kubectl get pods -n kube-system

5. Headless无头服务

Headless即Kubernetes无头服务,通过配置“clusterIP: None”进行定义。该服务不会分配集群虚拟IP,不具备流量转发与负载均衡能力,kube-proxy也不会对其做路由处理。

依托集群CoreDNS实现服务发现,域名解析时直接返回匹配标签后端Pod的真实IP,客户端可以跳过代理层,直接与单个Pod建立通信。如果服务配置标签选择器,系统会自动生成对应端点记录,域名请求将直接转发至后端Pod。

无头服务无法适配依赖集群IP的会话保持类配置,适用于无需负载均衡、需要直连实例的场景,常用于MySQL、Redis等有状态集群部署,满足固定节点访问需求。相同命名空间下,客户端可以直接使用服务名进行访问。

[Step1] Headless服务的完整DNS记录名称,解析该DNS记录能够返回所有匹配该服务的Pod真实IP列表。

[Headless服务名称].[命名空间].svc.cluster.local

[Step2] Deployment中Pod的DNS记录名称,解析该DNS记录只会返回单个Pod的IP。

[Pod的IP].[Headless服务名称].[命名空间].svc.cluster.local

6. iptables代理模式

在iptables代理模式下,kube-proxy实时监听集群内Service与Endpoints资源的增减变动。针对每一个Service,自动配置iptables规则,拦截访问集群IP与端口的请求,并将流量转发至对应的后端Pod。通顺一点说,配置iptables规则捕获请求,然后重定向到后端Pod。

同时依据Endpoints信息完成后端节点匹配,该模式默认采用随机调度策略分发请求,依托Linux内核的netfilter机制转发流量,无需进行用户态和内核态的数据切换,系统资源开销更低,运行稳定性也更强。

7. IPVS代理模式

在IPVS代理模式下,kube-proxy持续监听集群内Service与Endpoints资源变更,通过netlink接口自动创建并维护IPVS转发规则,同时定期完成规则同步,保障IPVS运行状态与集群服务配置始终一致。客户端访问服务时,IPVS可自动将流量转发至后端任意可用Pod。

IPVS模式同样依托netfilter机制实现流量转发,底层采用哈希表存储数据,全程在内核态完成处理。相较于iptables模式,IPVS有效降低流量转发延迟,大幅提升代理规则同步效率,具备更高的网络吞吐能力,更适配高并发业务场景。

4.1 IPVS与iptables比较

维度

iptables 模式

IPVS 模式

设计目标

通用防火墙/NAT

专用四层负载均衡

查找方式

线性规则匹配(O (n))

哈希表(O (1))

性能

随规则增多明显下降

高并发下稳定

调度算法

仅随机/轮询

多种(rr/wrr/lc/wlc…)

适用场景

小规模集群

中大规模、高并发集群

8. Endpoint Controller

端点控制器(Endpoint Controller)用于创建并维护集群内所有Endpoint资源,持续监听Service与Pod的状态变动。

  • 监听到Service删除时,同步移除同名Endpoint资源

  • 监听到新建Service时,匹配关联Pod清单,自动创建对应Endpoint

  • 监听到Service配置更新时,重新匹配Pod列表,完成Endpoint信息更新

  • 监听到Pod状态发生变更时,同步更新所属Service的Endpoint,完成Pod IP信息录入维护

9. Kubernetes服务类型总结

Service类型

访问范围

是否暴露外部

应用场景

说明

ClusterIP

仅集群内部

集群内微服务互相调用(默认类型)

k8s默认服务类型,仅集群内部可访问,不对外提供访问入口

NodePort

集群的全部节点

临时调试、简单外部访问

在所有节点开放固定端口,外网可以通过【节点IP+端口】访问服务

Headless

仅集群内部

有状态服务、服务直连Pod

不分配ClusterIP,DNS直接解析为Pod真实IP,常配合StatefulSet使用

LoadBalancer

外部网络

公有云、本地负载均衡(MetalLB)

自动分配独立外网IP,依赖云厂商负载均衡或本地MetalLB组件

Ingress

外网、集群

多服务统一入口、基础HTTP路由

传统七层流量入口,基于域名、路径转发请求,需要部署Ingress Controller

Gateway API

外网、集群

企业级流量治理、复杂网关场景

Ingress下一代标准,协议支持更广、权限与路由策略更精细,扩展性更强

二、Service基础案例

1. 命令行生成NodePort类型的Service

[Step1] 本地新建YAML文件,创建一个deployment。

root@k8s-master:~# vim nginx-deployment01.yml
​
# 写入下列内容
apiVersion: apps/v1     # 固定配置
kind: Deployment        # 资源类型
metadata:
  name: nginx-deployment01
  labels:                   # 定义标签
    app: nginx-deployment01 # 自定义标签(键和值均可自定义)
spec:
  replicas: 4                   # 预期数量为4
  selector:
    matchLabels:                # 标签匹配模式:精确匹配
      app: nginx-deployment01   # 匹配的标签值
  template:         # 根据此模板来新建Pod
    metadata:
      labels:       # 给Pod加上标签,需要和selector定义一致
        app: nginx-deployment01
    spec:
      containers:
        - name: nginx
          image: nginx:latest
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80

[Step2] 根据YAML文件向Kubernetes集群声明式地创建Deployment资源。

root@k8s-master:~# kubectl create -f nginx-deployment01.yml

[Step3] 验证:查看当前集群中的所有Deployment资源。

root@k8s-master:~# kubectl get deployments.apps

[Step4] 通过命令行的方式创建Kubernetes Service。

root@k8s-master:~# kubectl expose deployment nginx-deployment01 --port=8080 --name=httpd-nodeport-service01 --target-port=80 --type=NodePort
  • kubectl expose:创建Service,将一个工作负载(Deployment/Pod)暴露成可访问的服务

  • deployment nginx-deployment01:指定要暴露的类型的Deployment,名称为nginx-deployment

  • --port=8080:指定ClusterIP端口

  • --name=httpd-nodeport-service01:指定Service的名称

  • --target-port=80:后端Deployment中Pod真正监听的端口

  • --type=NodePort:Service类型为NodePort

[Step5] 验证:查看当前命名空间下的所有Service资源。整体转发逻辑其实是当集群外部客户端通过集群任意节点主机IP+32604端口发起访问请求,请求先抵达节点开放的NodePort端口,再经由节点上kube-proxy组件转发至nginx-service服务的ClusterIP+8080端口再通过服务的端口映射规则转发至后端四个正常运行的NginxPod内部80端口。

root@k8s-master:~# kubectl get service
  • CLUSTER-IP:Service在集群内部固定的虚拟IP

  • PORT(S):8080为Service内部端口,32604为节点对外开放的端口

[Step6] 验证:先查看当前主机的IP地址,然后访问Deployment管理的Nginx Pod测试页面,能够正常访问。

root@k8s-master:~# ip add show ens33
root@k8s-master:~# curl 192.168.8.3:32604

[Step7] 验证:使用集群的任意节点IP地址访问Deployment管理的Nginx Pod测试页面,同样能够正常访问。

root@k8s-master:~# curl 192.168.8.4:32604
root@k8s-master:~# curl 192.168.8.5:32604

2. 声明式生成NodePort类型的Service

[Step1] 本地新建YAML文件,创建一个deployment。

root@k8s-master:~# vim nginx-deployment02.yml
​
# 写入下列内容
apiVersion: apps/v1     # 固定配置
kind: Deployment        # 资源类型
metadata:
  name: nginx-deployment02
  labels:                   # 定义标签
    app: nginx-deployment02 # 自定义标签(键和值均可自定义)
spec:
  replicas: 4                   # 预期数量为4
  selector:
    matchLabels:                # 标签匹配模式:精确匹配
      app: nginx-deployment02   # 匹配的标签值
  template:         # 根据此模板来新建Pod
    metadata:
      labels:       # 给Pod加上标签,需要和selector定义一致
        app: nginx-deployment02
    spec:
      containers:
        - name: nginx
          image: nginx:latest
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80

[Step2] 根据YAML文件向Kubernetes集群声明式地创建Deployment资源。

root@k8s-master:~# kubectl create -f nginx-deployment02.yml

[Step3] 验证:查看当前集群中的所有Deployment资源。

root@k8s-master:~# kubectl get deployments.apps

[Step4] 本地新建YAML文件,创建NodePort类型的Service。

root@k8s-master:~# vim httpd-nodeport-service02.yml
​
# 写入下列内容
apiVersion: v1      # 固定配置
kind: Service       # 资源类型
metadata:
  name: httpd-nodeport-service02
spec:
  type: NodePort    # Service类型
  selector:         # 标签选择器,匹配后端Pod
    app: nginx-deployment02 # 匹配后端Pod的标签
  ports:
    - protocol: TCP     # 使用TCP协议
      port: 8081        # Service自己暴露的端口
      targetPort: 80    # 后端Pod实际监听的端口
      nodePort: 31803   # 指定Node节点对外开放的端口,不指定则随机分配

[Step5] 根据YAML文件向Kubernetes集群声明式地创建Service资源。

root@k8s-master:~# kubectl create -f httpd-nodeport-service02.yml

[Step6] 验证:查看当前命名空间下的所有Service资源。

root@k8s-master:~# kubectl get service

[Step7] 验证:先查看当前主机的IP地址,然后访问Deployment管理的Nginx Pod测试页面,能够正常访问。

root@k8s-master:~# ip add show ens33
root@k8s-master:~# curl 192.168.8.3:31803

[Step8] 验证:使用集群的任意节点IP地址访问Deployment管理的Nginx Pod测试页面,同样能够正常访问。

root@k8s-master:~# curl 192.168.8.4:31803
root@k8s-master:~# curl 192.168.8.5:31803

3. 声明式生成ClusterIP类型的Service

[Step1] 本地新建YAML文件,创建一个deployment。

root@k8s-master:~# vim httpd-deployment01.yml
​
# 写入下列内容
apiVersion: apps/v1     # 固定配置
kind: Deployment        # 资源类型
metadata:
  name: httpd-deployment01
  labels:               # 定义标签
    app: httpd-deployment01 # 自定义标签(键和值均可自定义)
spec:
  replicas: 4                   # 预期数量为4
  selector:
    matchLabels:                # 标签匹配模式:精确匹配
      app: httpd-deployment01   # 匹配的标签值
  template:         # 根据此模板来新建Pod
    metadata:
      labels:       # 给Pod加上标签,需要和selector定义一致
        app: httpd-deployment01
    spec:
      containers:
        - name: httpd
          image: httpd:latest
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80

[Step2] 根据YAML文件向Kubernetes集群声明式地创建Deployment资源。

root@k8s-master:~# kubectl create -f httpd-deployment01.yml

[Step3] 验证:查看当前集群中的所有Deployment资源。

root@k8s-master:~# kubectl get deployments.apps

[Step4] 本地新建YAML文件,创建ClusterIP类型的Service。

root@k8s-master:~# vim httpd-clusterip-service.yml
​
# 写入下列内容
apiVersion: v1      # 固定配置
kind: Service       # 资源类型
metadata:
  name: httpd-clusterip-service
spec:
  type: ClusterIP   # Service类型,不写的话默认为ClusterIP
  selector:         # 标签选择器,匹配后端Pod
    app: httpd-deployment01 # 匹配后端Pod的标签
  ports:
    - protocol: TCP     # 使用TCP协议
      port: 8082        # Service自己暴露的端口
      targetPort: 80    # 后端Pod实际监听的端口

[Step5] 根据YAML文件向Kubernetes集群声明式地创建Service资源。

root@k8s-master:~# kubectl create -f httpd-clusterip-service.yml

[Step6] 验证:查看当前命名空间下的所有Service资源。

root@k8s-master:~# kubectl get service

[Step7] 验证:查看Service关联的后端Pod IP与端口信息。

root@k8s-master:~# kubectl get endpoints

[Step8] 验证:使用ClusterIP加上端口号访问后端服务。

root@k8s-master:~# curl http://10.100.229.189:8082

4. 声明式生成Headless类型的Service

[Step1] 本地新建YAML文件,创建一个Deployment。

root@k8s-master:~# vim busybox-headless-deployment.yml
​
# 写入下列内容
apiVersion: apps/v1     # 固定配置
kind: Deployment        # 资源类型
metadata:
  name: busybox-headless-deployment
  labels:               # 定义标签
    app: busybox-headless-deployment    # 自定义标签(键和值均可自定义)
spec:
  replicas: 4           # 预期数量为4
  selector:
    matchLabels:        # 标签匹配模式:精确匹配
      app: busybox-headless-deployment  # 匹配的标签值
  template:             # 根据此模板来新建Pod
    metadata:
      labels:           # 给Pod加上标签,需要和selector定义一致
        app: busybox-headless-deployment
    spec:
      containers:
        - name: busybox
          image: busybox:latest
          imagePullPolicy: IfNotPresent
          command: ['sh','-c','echo "Hello World!" && sleep 3600']

[Step2] 根据YAML文件向Kubernetes集群声明式地创建Deployment资源。

root@k8s-master:~# kubectl create -f busybox-headless-deployment.yml

[Step3] 验证:查看当前集群中的所有Deployment资源。

root@k8s-master:~# kubectl get deployments.apps

[Step4] 本地新建YAML文件,创建Headless类型的Service。

root@k8s-master:~# vim busybox-headless-service.yml
​
# 写入下列内容
apiVersion: v1      # 固定配置
kind: Service       # 资源类型
metadata:
  name: busybox-headless-service
spec:
  clusterIP: None   # 声明这是一个Headless服务
  selector:         # 标签选择器,匹配后端Pod
    app: busybox-headless-deployment    # 匹配后端Pod的标签
  ports:            # 必须要写,这里busybox其实用不上端口号,随便写一个
    - port: 8083

[Step5] 根据YAML文件向Kubernetes集群声明式地创建Service资源。警告信息表示无头服务将自动忽略“spec.sessionAffinity”配置。因为无头服务不存在“ClusterIP”,不具备负载均衡与IP代理能力,而“sessionAffinity(会话保持)”功能依托ClusterIP实现流量代理,因此识别到该项配置后,集群会自动弃用并触发提示。

root@k8s-master:~# kubectl create -f busybox-headless-service.yml

[Step6] 验证:查看当前命名空间下的所有Service资源,可以看到无头服务不存在ClusterIP。

root@k8s-master:~# kubectl get service

[Step7] 验证:显示当前命名空间下的所有Pod。

root@k8s-master:~# kubectl get pods -o wide

[Step8] 验证:登录Pod容器内部命令行终端,测试Headless服务的DNS域名解析,能够查看所有后端Pod的IP地址列表。

root@k8s-master:~# kubectl exec -it busybox-headless-deployment-65cfbf5f7-6cwns -- /bin/sh
/ # nslookup busybox-headless-service.default.svc.cluster.local


Kubernetes Deployment管理与使用 2026-08-26
Kubernetes DaemonSet、Job与CronJob控制器原理 2026-08-30

评论区