当前位置: 首页 > news >正文

Kubernetes Service 全面详解:从概念到实践

前言

在 Kubernetes 中,Pod 是最小的部署单元,但 Pod 存在生命周期短、IP 动态变化等特点,直接通过 Pod IP 访问服务会面临诸多问题。Kubernetes Service 作为集群内部服务暴露和负载均衡的核心组件,解决了这些难题。本文将从概念原理、工作模式、实践操作到排错总结,全面讲解 Kubernetes Service,帮助读者深入理解并掌握其使用方法。

1.Service 概念原理

1.1 概述

Kubernetes Service 定义了一种抽象:一个 Pod 的逻辑分组,以及访问它们的策略(通常称为微服务)。这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector 进行关联。

简单来说,Service 就像一个 “中介”,它为一组具有相同标签的 Pod 提供一个稳定的访问入口,并实现负载均衡功能。无论后端 Pod 如何动态变化(创建、删除、IP 变更等),客户端只需通过 Service 提供的固定地址即可访问服务。

1.2为什么需要 Service

在 Kubernetes 集群中,我们可以通过 Deployment 等控制器创建多个副本的 Pod 来提供服务。例如,创建 100 个 myapp 的 Pod 给用户提供访问。但此时存在一个问题:如何为用户提供一个统一的接口并实现负载均衡?

如果不使用 Service,直接通过 Pod IP 配置负载均衡(如 Nginx 的 upstream)会存在以下问题:

  • 当 Pod 发生故障重建时,其 IP 会发生变化,而 Nginx 配置中的 IP 不会自动更新,导致访问失败。
  • 即使 Nginx 有存活探测功能,能剔除已死亡的 Pod,但无法自动添加新创建的 Pod。
  • 需要编写额外的脚本从 apiserver 抓取 Pod 变化并更新配置,操作复杂。

Service 恰好解决了这些问题:

  • Service 会为一组 Pod 提供一个固定的虚拟 IP(VIP)。
  • 通过 Label Selector 动态关联符合条件的 Pod,当 Pod 发生变化时,Service 会自动更新关联的 Pod 列表。
  • 实现了前后端解耦,客户端只需访问 Service 的 VIP,无需关心后端 Pod 的具体信息。

 

1.3 核心迭代

Kubernetes Service 的实现依赖于每个 Node 上运行的 kube-proxy 进程,kube-proxy 负责为 Service 实现虚拟 IP(VIP)的形式。其迭代过程如下:

  • Kubernetes v1.0 版本:代理完全在 userspace(用户空间)。
  • Kubernetes v1.1 版本:新增了 iptables 代理,但不是默认模式。
  • 从 Kubernetes v1.2 起:默认使用 iptables 代理。
  • Kubernetes v1.8.0beta.0 中:添加了 ipvs 代理。

目前,最新的 Kubernetes 集群默认仍使用 iptables 方式实现负载均衡。

验证当前代理模式的小实验

  1. 删除所有 cronjob(清理环境):

    kubectl delete cronjob --all
    
     

    该命令用于删除集群中所有的 CronJob 资源,避免对后续实验产生干扰。

  2. 创建一个 deployment 控制器:

    kubectl create deployment myapp --image=harbor.registry.com/library/myapp:1.0
    
     
    • kubectl create deployment:创建 Deployment 资源。
    • myapp:Deployment 的名称。
    • --image=harbor.registry.com/library/myapp:1.0:指定创建 Pod 所使用的镜像。
  3. 查看创建的 Pod:

    kubectl get pod
    
     

    输出类似:

    NAME READY STATUS RESTARTS AGE
    myapp-5846867694-lhn5v 1/1 Running 0 14s
    
     

    该命令用于查看当前命名空间下的 Pod 状态,确认 Pod 已成功运行。

  4. 扩容 deployment:

    kubectl scale deployment myapp --replicas=10
    
     
    • kubectl scale deployment:用于扩容或缩容 Deployment 管理的 Pod 数量。
    • myapp:要扩容的 Deployment 名称。
    • --replicas=10:指定目标副本数为 10。
  5. 再次查看 Pod,确认已扩容到 10 个:

    kubectl get pod
    
  6. 创建 service:

    kubectl create svc clusterip myapp --tcp=80:80
    
     
    • kubectl create svc clusterip:创建 ClusterIP 类型的 Service。
    • myapp:Service 的名称。
    • --tcp=80:80:指定 TCP 端口映射,前面的 80 是 Service 暴露的端口,后面的 80 是 Pod 中容器的端口。
  7. 查看 ipvs 规则:

    ipvsadm -Ln
    
     

    若没有对应的 ipvs 规则,则说明当前使用的是 iptables 代理模式。

1.3.1 userspace 模式

在 userspace 模式下,包含 kube-apiServer 和 kube-proxy 两个组件:

  • 每台机器上的 kube-proxy 监听 kube-apiServer,获取 Service 对象的变化。
  • 客户端 Pod 访问本地的防火墙规则,防火墙规则将流量转发至当前节点或其他节点的 kube-proxy。
  • kube-proxy 再代理请求到后端的 Server Pod,并将结果返回给客户端 Pod,实现负载均衡。

这种模式下,kube-proxy 既负责修改防火墙规则,又负责代理请求,当请求量较大时,kube-proxy 可能成为性能瓶颈。

1.3.2 iptables 模式

iptables 模式同样包含 kube-apiServer 和 kube-proxy 组件:

  • kube-proxy 监听 kube-apiServer 后,仅将结果写入当前节点的 iptables 规则。
  • 客户端的访问完全由 iptables 转发给本地或远程的 Server Pod,kube-proxy 不再参与代理功能。

这种模式实现了解耦,kube-proxy 仅负责更新规则,不参与请求转发,稳定性更高,不会因请求量增大而给 kube-proxy 带来压力。

1.3.3 ipvs 模式

ipvs 模式的工作方式与 iptables 类似,唯一的区别是将底层的 iptables 替换为四层负载均衡器 ipvs(也叫 LVS)。

ipvs 在四层负载均衡领域更专业,性能优于 iptables。但默认情况下 Kubernetes 不使用 ipvs,因为需要内核开启 ipvs 模块,官方担心部分机器未启用该模块导致服务无法正常工作。

修改 Kubernetes 集群配置以支持 ipvs 工作方式

  1. 编辑 kube-proxy 的 configmap:

    kubectl edit configmap kube-proxy -n kube-system
    
     
    • kubectl edit configmap:编辑 ConfigMap 资源。
    • kube-proxy:要编辑的 ConfigMap 名称。
    • -n kube-system:指定命名空间为 kube-system,因为 kube-proxy 运行在该命名空间下。
  2. 在编辑页面中,找到 mode 配置项,将其值改为 ipvs

    mode: "ipvs"
    
  3. 保存退出后,删除 kube-proxy 的 Pod 使其重建,以应用新配置:

    kubectl delete pod -n kube-system -l k8s-app=kube-proxy
    
     
    • kubectl delete pod:删除 Pod。
    • -n kube-system:指定命名空间。
    • -l k8s-app=kube-proxy:通过标签选择器删除所有 k8s-app=kube-proxy 的 Pod。
  4. 查看 kube-proxy 的 Pod 是否重建成功:

    kubectl get pod -A
    
  5. 再次查看 ipvs 规则,确认已生效:

    ipvsadm -Ln
    
     

    此时可以看到与 Service 相关的 ipvs 规则。

2. Service 工作模式及使用

2.1 Service 类型

Kubernetes Service 主要有以下几种类型:

  • ClusterIP:默认类型,自动分配一个仅集群内部可以访问的虚拟 IP。
  • NodePort:在 ClusterIP 基础上,为 Service 在每台机器上绑定一个端口,可通过 <NodeIP>:NodePort 访问服务。
  • LoadBalancer:在 NodePort 的基础上,借助云服务提供商创建外部负载均衡器,将请求转发到 <NodeIP>:NodePort
  • ExternalName:将集群外部的服务引入到集群内部,通过域名别名实现,无需创建代理。仅 Kubernetes 1.7 或更高版本的 kube-dns 支持。

2.2 组件协同

当管理员通过 kubectl 命令创建 Service 时,流程如下:

  1. 请求发送到 ApiServer,并将数据存储到 etcd 数据库中。
  2. kube-proxy 监听 ApiServer 中 Service 的变化。
  3. kube-proxy 将 Service 信息转换为对应节点的 IPVS 或 iptables 规则(取决于配置的代理模式)。

2.3 ClusterIP

2.3.1 模式结构

ClusterIP 是默认的 Service 类型,用于为集群内部的 Pod 提供访问服务的固定虚拟 IP。其结构如下:

  • 前端的 Pod(如 Nginx)通过 Service 访问后端的多个 Pod(如 WebApp)。
  • 当后端 Pod 的 IP 发生变化时,Service 会自动发现并更新,对前端 Pod 透明。

ClusterIP 类型的 Service 资源清单示例

apiVersion: v1  # 接口组版本为核心组 v1 版
kind: Service   # 资源类型为 Service
metadata:       # 定义元数据name: myapp-clusterip  # 当前 service 的名字namespace: default     # 放在 default 名字空间下
spec:           # 定义期望状态type: ClusterIP  # 工作模式为 ClusterIP,默认模式selector:        # 标签选择器,需与 pod 的标签匹配app: myapprelease: stabelsvc: clusteripports:           # 定义负载均衡集群的端口- name: http     # 端口的名称port: 80       # 集群暴露的端口targetPort: 80 # 后端 Pod 中容器的端口

IPVS 支持三种工作模式,Kubernetes 中默认使用 NAT 模式:

  • NAT 模式:地址转换模式,后端端口与集群端口可以不同;但入站和出站流量都必须经过 IPVS 调度器。
  • DR 模式:直接路由模式,回程流量不经过 IPVS,压力更小;但后端端口和集群端口必须一致,且可能需要配置 ARP 响应。
  • TUN 模式:隧道模式,可跨公网组建集群;但需要对数据报文二次封装,性能较低。

在 Kubernetes 中选择 NAT 模式,是因为每个节点的 ipvs 规则仅被当前节点的客户端使用,压力较小,且集群端口与后端端口可以不同,灵活性更高。

2.3.2 模式演示

实验目的:演示 ClusterIP 类型的 Service 如何关联 Pod 并实现负载均衡。

实验步骤

  1. 清理环境,删除之前创建的 deployment 和 service:

    kubectl delete deployment --all
    kubectl delete svc myapp
    
     
    • kubectl delete deployment --all:删除所有 Deployment。
    • kubectl delete svc myapp:删除名为 myapp 的 Service。
  2. 创建一个目录用于存放资源清单文件,并进入该目录:

    mkdir 6
    cd 6
    
  3. 创建 Deployment 资源清单文件 1.deployment.yaml

    apiVersion: apps/v1
    kind: Deployment
    metadata:name: myapp-clusterip-deploynamespace: default
    spec:replicas: 3selector:matchLabels:app: myapprelease: stabelsvc: clusteriptemplate:metadata:labels:  # 定义 pod 的标签app: myapprelease: stabelenv: testsvc: clusteripspec:containers:- name: myapp-containerimage: harbor.registry.com/library/myapp:1.0imagePullPolicy: IfNotPresentports:- name: httpcontainerPort: 80readinessProbe:  # 就绪探测httpGet:port: 80path: /index1.htmlinitialDelaySeconds: 1  # 延迟 1 秒开始探测periodSeconds: 3        # 每 3 秒探测一次
    
     

    该 Deployment 定义了 3 个副本的 Pod,每个 Pod 包含一个 myapp 容器,并配置了就绪探测,探测 80 端口的 index1.html 页面。

  4. 创建 Deployment 资源:

    kubectl apply -f 1.deployment.yaml
    
     

    kubectl apply -f:根据资源清单文件创建资源。

  5. 查看 Pod 状态:

    kubectl get pod
    
     

    此时 Pod 处于 Running 状态,但 READY 为 0/1,因为就绪探测失败(镜像中不存在 index1.html 文件)。

  6. 创建 Service 资源清单文件 2.svc.yaml

    apiVersion: v1
    kind: Service
    metadata:name: myapp-clusteripnamespace: default
    spec:type: ClusterIPselector:app: myapprelease: stabelsvc: clusteripports:- name: httpport: 80targetPort: 80
    
     

    该 Service 为 ClusterIP 类型,通过标签选择器关联具有 app: myapprelease: stabelsvc: clusterip 标签的 Pod。

  7. 创建 Service 资源:

    kubectl apply -f 2.svc.yaml
    
  8. 查看 Service 信息:

    kubectl get svc
    
     

    可以看到 myapp-clusterip 已创建,并分配了 ClusterIP。

  9. 尝试访问 Service 的 ClusterIP:

    curl 10.3.124.142  # 替换为实际的 ClusterIP
    
     

    访问失败,因为 Pod 未通过就绪探测,Service 未关联任何 Pod。

  10. 进入其中一个 Pod,创建 index1.html 文件,使就绪探测通过:

    kubectl exec -it myapp-clusterip-deploy-77c59649fb-6vzxx -- /bin/bash
    
     
    • kubectl exec -it:交互式进入 Pod 中的容器。
    • myapp-clusterip-deploy-77c59649fb-6vzxx:Pod 的名称。
    • -- /bin/bash:在容器中执行 bash 命令。

    在容器内部执行:

    cd /usr/local/nginx/html/
    date > index1.html
    exit
    
  11. 再次查看 Pod 状态,确认该 Pod 已就绪(READY 为 1/1):

    kubectl get pod
    
  12. 再次访问 Service 的 ClusterIP:

    curl 10.3.124.142  # 替换为实际的 ClusterIP
    
     

    此时可以访问成功,返回 nginx 页面内容。

  13. 查看访问的是哪个 Pod:

    curl 10.3.124.142/hostname.html  # 替换为实际的 ClusterIP
    
     

    返回 Pod 的名称。

  14. 按照步骤 10 的方法,使另外两个 Pod 也通过就绪探测。

  15. 多次访问 Service 的 ClusterIP,验证负载均衡:

    curl 10.3.124.142/hostname.html
    curl 10.3.124.142/hostname.html
    curl 10.3.124.142/hostname.html
    
     

    可以看到请求被分发到不同的 Pod。

  16. 查看 ipvs 规则,确认已包含三个 Pod 的 IP:

    ipvsadm -Ln
    
2.3.3 域名解析

Kubernetes 集群中的每个 Service 创建后,都会在 DNS 插件中注册一个域名,格式为:Service名称.所在名字空间.svc.cluster.local。集群内部的 Pod 可以通过该域名访问 Service。

实验目的:验证 Service 的域名解析功能。

实验步骤

  1. 安装 bind-utils 工具包(包含 dig 等 DNS 工具):
    dnf install bind-utils -y
  1. 该命令用于安装 DNS 解析工具,方便后续测试域名解析。

  2. 查看 Service 名称:

    kubectl get svc
    
     

    确认 myapp-clusterip 服务的名称及所在命名空间(默认是 default)。

  3. 查看 DNS 插件的 IP:

    kubectl get pod -n kube-system -o wide | grep dns
    
     

    输出结果中包含 coredns 插件的 Pod 及对应的 IP 地址(如 10.244.85.208、10.244.85.210),这些是集群内部的 DNS 服务器地址。

  4. 解析 Service 域名:

    dig -t A myapp-clusterip.default.svc.cluster.local @10.0.0.10
    
     
    • -t A:指定查询 A 记录(IP 地址)。
    • myapp-clusterip.default.svc.cluster.local:Service 的完整域名,格式为 “Service 名称。命名空间.svc.cluster.local”。
    • @10.0.0.10:指定 DNS 服务器地址(集群 DNS 服务的虚拟 IP)。

    解析结果中,ANSWER 部分会显示该域名对应的 ClusterIP(如 10.3.124.142),说明域名解析成功。

  5. 在 Pod 内部验证域名访问:
    创建一个用于测试的 Pod 资源清单 3.dnstest.yaml

    apiVersion: v1
    kind: Pod
    metadata:name: pod-demonamespace: defaultlabels:app: myapp
    spec:containers:- name: busybox-1image: harbor.registry.com/library/busybox:1.0command: ["/bin/sh", "-c", "sleep 3600"]
    
     

    该 Pod 运行一个 busybox 容器,主要用于测试集群内部的网络访问。

    创建 Pod:

    kubectl create -f 3.dnstest.yaml
    
     

    进入 Pod 内部,通过域名访问 Service:

    kubectl exec -it pod-demo -- /bin/sh
    / # wget http://myapp-clusterip.default.svc.cluster.local/hostname.html && cat hostname.html && rm -rf hostname.html
    
     

    输出结果会显示访问到的 Pod 名称,说明在 Pod 内部可通过域名访问 Service,且实现了负载均衡。

2.3.4 internalTrafficPolicy

internalTrafficPolicy 用于定义节点如何分发通过 ClusterIP 接收的服务流量,有以下两种取值:

  • Cluster(默认):将流量路由到所有端点(跨节点的 Pod)。
  • Local:仅将流量路由到与客户端 Pod 同一节点上的端点,若没有本地端点则丢弃流量。

实验目的:对比 Cluster 和 Local 模式的区别。

实验步骤

  1. 查看当前 Service 的 internalTrafficPolicy 配置:

    kubectl get svc myapp-clusterip -o yaml | grep internalTrafficPolicy
    
     

    默认值为 Cluster

  2. 在 Cluster 模式下测试访问:

    • 通过 ClusterIP 访问:curl 10.3.124.142(成功)。
    • 在 Pod 内部通过域名访问:wget http://myapp-clusterip.default.svc.cluster.local(成功)。
  3. 修改为 Local 模式:

    kubectl edit svc myapp-clusterip
    
     

    在编辑页面中将 internalTrafficPolicy: Cluster 改为 internalTrafficPolicy: Local,保存退出。

  4. 在 Local 模式下测试访问:

    • 通过 ClusterIP 访问:curl 10.3.124.142(失败,连接被拒绝)。
    • 在 Pod 内部通过域名访问:wget http://myapp-clusterip.default.svc.cluster.local(成功)。

    结论:Local 模式仅允许集群内部的 Pod 通过域名访问,不允许通过 ClusterIP 直接访问,可减少跨节点流量,提高性能。

2.3.5 会话保持(IPVS 持久化连接)

通过配置 sessionAffinity 可实现会话保持,即同一客户端的请求在一段时间内定向到同一后端 Pod。sessionAffinity 有以下取值:

  • None(默认):无会话保持,请求按负载均衡算法分发。
  • ClientIP:基于客户端 IP 进行会话保持,默认超时时间为 3 小时(10800 秒)。

实验目的:验证 ClientIP 模式的会话保持功能。

实验步骤

  1. 查看当前 Service 的 sessionAffinity 配置:

    kubectl explain svc.spec.sessionAffinity
    
     

    默认值为 None

  2. 在 None 模式下测试访问:

    curl 10.3.124.142/hostname.html
    curl 10.3.124.142/hostname.html
    
     

    多次访问会负载到不同的 Pod。

  3. 修改为 ClientIP 模式:

    kubectl edit svc myapp-clusterip
    
     

    在编辑页面中添加 sessionAffinity: ClientIP,保存退出。

  4. 查看持久化时间配置:

    kubectl explain svc.spec.sessionAffinityConfig.clientIP
    
     

    默认超时时间为 10800 秒(3 小时),可通过 timeoutSeconds 自定义(0 < 取值 ≤ 86400 秒)。

  5. 在 ClientIP 模式下测试访问:

    curl 10.3.124.142/hostname.html
    curl 10.3.124.142/hostname.html
    
     

    多次访问会定向到同一 Pod,验证了会话保持功能。

  6. 查看 ipvs 规则:

    ipvsadm -Ln
    
     

    规则中会显示 persistent 10800,表示开启了 3 小时的持久化连接。

2.4 NodePort

NodePort 类型在 ClusterIP 基础上,为 Service 在每个节点的物理网卡绑定一个端口(默认范围 30000-32767),可通过 <NodeIP>:NodePort 从集群外部访问服务。

2.4.1 模式结构
  • 底层通过 Deployment 创建 Pod,关联一个 ClusterIP 类型的 Service。
  • 再创建一个 NodePort 类型的 Service,匹配目标 Pod,并在每个节点绑定一个物理端口(如 30010)。
  • 外部客户端通过任一节点的 IP 和 NodePort 访问服务,流量经节点的 IPVS 规则转发到后端 Pod。

 

2.4.2 模式演示

实验目的:演示 NodePort 类型的 Service 如何暴露服务到集群外部。

实验步骤

  1. 创建 Deployment 资源清单 5.deployment.yaml

    apiVersion: apps/v1
    kind: Deployment
    metadata:name: myapp-nodeport-deploynamespace: default
    spec:replicas: 3selector:matchLabels:app: myapprelease: stabelsvc: nodeporttemplate:metadata:labels:app: myapprelease: stabelenv: testsvc: nodeportspec:containers:- name: myapp-containerimage: harbor.registry.com/library/myapp:1.0imagePullPolicy: IfNotPresentports:- name: httpcontainerPort: 80
    
     

    创建 3 个副本的 Pod,标签为 app: myapprelease: stabelsvc: nodeport

    创建 Deployment:

    kubectl apply -f 5.deployment.yaml
    
  2. 创建 NodePort 类型的 Service 资源清单 6.nodeport.yaml

    apiVersion: v1
    kind: Service
    metadata:name: myapp-nodeportnamespace: default
    spec:type: NodePortselector:app: myapprelease: stabelsvc: nodeportports:- name: httpport: 80          # 集群内部访问端口targetPort: 80    # 后端 Pod 端口nodePort: 30010   # 集群外部访问端口(需在默认范围)
    
     

    创建 Service:

    kubectl apply -f 6.nodeport.yaml
    
  3. 查看 Service 信息:

    kubectl get svc myapp-nodeport
    
     

    输出中 PORT(S) 列显示 80:30010/TCP,表示集群内部通过 80 端口访问,外部通过 30010 端口访问。

  4. 集群内部访问测试:

    curl 10.1.69.228:80/hostname.html  # 替换为实际的 ClusterIP
    
     

    成功访问并实现负载均衡。

  5. 集群外部访问测试:
    在集群外部的机器上,通过节点 IP 和 NodePort 访问:

    curl 192.168.10.11:30010/hostname.html  # 替换为实际的节点 IP
    curl 192.168.10.12:30010/hostname.html
    
     

    所有节点的 30010 端口均可访问,验证了 NodePort 暴露服务的功能。

2.4.3 externalTrafficPolicy

externalTrafficPolicy 用于定义节点如何分发通过 NodePort、ExternalIPs 或 LoadBalancer IP 接收的外部流量,取值如下:

  • Cluster(默认):将流量路由到所有端点(跨节点的 Pod)。
  • Local:仅将流量路由到接收流量的节点上的本地端点,保留客户端源 IP,若没有本地端点则丢弃流量。

实验目的:验证 Local 模式对外部访问的限制。

实验步骤

  1. 修改 NodePort 服务的 externalTrafficPolicy 为 Local:

    kubectl edit svc myapp-nodeport
    
     

    在编辑页面中添加 externalTrafficPolicy: Local,保存退出。

  2. 集群内部节点访问测试:

    curl 192.168.10.11:30010  # 节点自身 IP 访问,成功
    
  3. 集群外部机器访问测试:

    curl 192.168.10.11:30010  # 失败,无法访问
    
     

    结论:Local 模式仅允许集群节点通过物理 IP:NodePort 访问,外部机器无法访问,适用于需要限制外部访问来源的场景。

2.5 LoadBalancer

LoadBalancer 类型在 NodePort 基础上,借助云服务提供商(如阿里云、AWS)的负载均衡服务,自动创建外部负载均衡器,将流量转发到节点的 NodePort。该类型仅在云环境中可用,解决了 NodePort 模式下外部访问的高可用问题(避免单节点故障导致服务不可用)。

资源清单示例(阿里云):

apiVersion: v1
kind: Service
metadata:annotations:service.beta.kubernetes.io/alibaba-cloud-loadbalancer-id: ${YOUR_LB_ID}service.beta.kubernetes.io/alicloud-loadbalancer-force-override-listeners: 'true'labels:app: nginxname: my-nginx-svcnamespace: default
spec:ports:- port: 80protocol: TCPtargetPort: 80selector:app: nginxtype: LoadBalancer

  • annotations:云厂商特定的负载均衡器配置(如负载均衡器 ID)。
  • 云厂商会自动将负载均衡器的流量转发到集群节点的 NodePort,实现外部访问的高可用。

2.6 ExternalName

ExternalName 类型用于将集群外部的服务引入集群内部,通过 DNS 别名实现,无需选择器或端口配置。适用于访问集群外部的固定服务(如外部数据库)。

2.6.1 模式结构
  • 无底层 IPVS 参与,仅通过 CoreDNS 实现域名别名映射。
  • 集群内部的 Pod 通过 Service 域名访问,CoreDNS 将其解析为外部服务的域名,再由客户端解析外部域名获取 IP。

2.6.2 模式演示

实验目的:将外部服务(如 www.baidu.com)通过 ExternalName 引入集群内部。

实验步骤

  1. 创建 ExternalName 类型的 Service 资源清单 7.ensvc.yaml

    apiVersion: v1
    kind: Service
    metadata:name: my-service-1namespace: default
    spec:type: ExternalNameexternalName: www.baidu.com
    
     
    • externalName: www.baidu.com:指定外部服务的域名。

    创建 Service:

    kubectl apply -f 7.ensvc.yaml
    
  2. 查看 Service 信息:

    kubectl get svc my-service-1
    
     

    输出中 EXTERNAL-IP 列为 www.baidu.com,无 ClusterIP。

  3. 在 Pod 内部验证访问:

    kubectl exec -it pod-demo -- /bin/sh
    / # ping my-service-1.default.svc.cluster.local
    
     

    解析结果为百度的 IP(如 183.240.99.58),说明成功通过别名访问外部服务。

2.6.3 实用案例:访问外部 MySQL

创建一个关联外部 MySQL 的 ExternalName 服务:

  1. 资源清单 8.mysql-external-svc.yaml

    apiVersion: v1
    kind: Service
    metadata:name: mysql-servicenamespace: default
    spec:type: ExternalNameexternalName: mysql.prod.company.comports:- name: mysqlport: 3306targetPort: 3306
    
     
    • 关联外部 MySQL 服务 mysql.prod.company.com:3306
  2. 应用程序配置中通过 Service 域名访问:

    spring.datasource.url=jdbc:mysql://mysql-service:3306/dbname
    
     

    当外部 MySQL 地址变更时,只需修改 externalName 即可,无需修改所有应用配置。

3. Endpoints

Endpoints 是 Service 的底层模型,记录了 Service 关联的后端端点(Pod IP 和端口)。Service 通过 Label Selector 自动关联 Pod 并生成同名 Endpoints,也可手动创建 Endpoints 关联外部服务。

3.1 关联关系

  • Service:定义逻辑分组和访问策略,通过 Label Selector 匹配 Pod。
  • Endpoints:存储实际的服务端点(IP:Port),与 Service 同名,由 Service 自动创建或手动定义。
  • Pod:后端服务的实际运行单元,需满足标签匹配和就绪状态才会被加入 Endpoints。

3.2 自动关联(配置 Selector)

当 Service 配置 Label Selector 时,会自动监测符合条件的 Pod,将其 IP 和端口添加到同名 Endpoints 中,kube-proxy 再将 Endpoints 同步到 IPVS 规则。

实验验证

  1. 创建带就绪探测的 Deployment 和匹配的 Service(参考 6.2.3.2 步骤)。
  2. 查看 Endpoints:

    bash

    kubectl get endpoints myapp-clusterip
    

    当 Pod 就绪后,Endpoints 会显示 Pod 的 IP:Port(如 10.244.58.193:80)。

3.3 手动关联(无 Selector)

若 Service 未配置 Selector,需手动创建同名 Endpoints 定义后端端点(可关联集群外部服务)。

实验目的:手动关联外部 Nginx 服务。

实验步骤

  1. 创建无 Selector 的 Service 资源清单 8.svc.yaml
apiVersion: v1
kind: Service
metadata:name: nginx-noselects
spec:ports:- protocol: TCPport: 6666  # Service 暴露端口targetPort: 80  # 后端服务端口

创建 Service:

kubectl create -f 8.svc.yaml

  • 该 Service 未配置 selector,因此不会自动生成 Endpoints。

  1. 查看 Service 和 Endpoints:

    kubectl get svc nginx-noselects  # 确认 Service 已创建
    kubectl get endpoints nginx-noselects  # 无结果,未自动生成
    
  2. 手动创建 Endpoints 资源清单 9.ep.yaml

    apiVersion: v1
    kind: Endpoints
    metadata:name: nginx-noselects  # 必须与 Service 同名
    subsets:
    - addresses:- ip: 192.168.10.12  # 外部服务 IP(如节点 node01 的 IP)ports:- port: 80  # 外部服务端口
    
     

    创建 Endpoints:

    kubectl create -f 9.ep.yaml
    
  3. 在节点 192.168.10.12 上启动 Nginx 服务:

    docker run --name nginx -d -p 80:80 harbor.registry.com/library/myapp:1.0
    
  4. 访问 Service 验证:

    curl 10.9.126.145:6666  # 替换为 Service 的 ClusterIP
    
     

    成功返回 Nginx 页面,说明手动关联的外部服务可通过 Service 访问。

4. publishNotReadyAddresses

默认情况下,Service 仅关联就绪状态且标签匹配的 Pod。若需将未就绪的 Pod 也纳入负载均衡(如特殊调试场景),可开启 publishNotReadyAddresses: true

实验目的:验证未就绪 Pod 能否被 Service 关联。

实验步骤

  1. 创建带就绪探测的 Pod 资源清单 10.pod.yaml

    apiVersion: v1
    kind: Pod
    metadata:name: readiness-httpget-podnamespace: defaultlabels:app: myappenv: test
    spec:containers:- name: readiness-httpget-containerimage: harbor.registry.com/library/myapp:1.0readinessProbe:httpGet:port: 80path: /index1.html  # 镜像中无此文件,就绪探测失败initialDelaySeconds: 1periodSeconds: 3
    
     

    创建 Pod:

    kubectl create -f 10.pod.yaml
    
  2. 查看 Pod 状态:

    kubectl get pod readiness-httpget-pod  # 状态为 Running,但 READY 0/1
    
  3. 创建匹配的 Service:

    kubectl create svc clusterip myapp --tcp=80:80
    
  4. 访问 Service(失败,因 Pod 未就绪):

    curl 10.14.121.157  # 替换为 Service 的 ClusterIP,连接被拒绝
    
  5. 开启 publishNotReadyAddresses

    kubectl patch service myapp -p '{"spec": {"publishNotReadyAddresses": true}}'
    
  6. 再次访问 Service(成功,未就绪 Pod 被关联):

    curl 10.14.121.157  # 返回 Nginx 页面
    

5. 排错指南

  1. Service 无法访问后端 Pod

    • 检查 Pod 标签是否与 Service 的 selector 匹配:kubectl get pod --show-labels 对比 kubectl get svc <name> -o yaml | grep selector
    • 检查 Pod 是否就绪:kubectl get pod 确保 READY 1/1,就绪探测是否通过。
    • 检查网络策略是否阻止流量:kubectl get networkpolicy
  2. NodePort 外部无法访问

    • 检查 NodePort 是否在默认范围(30000-32767):kubectl get svc <name>
    • 检查节点防火墙是否开放 NodePort:firewall-cmd --list-ports(如 30010/tcp)。
    • 检查 externalTrafficPolicy 是否为 Local 导致外部访问被拒:修改为 Cluster 重试。
  3. 域名解析失败

    • 检查 Service 域名格式是否正确:Service名称.命名空间.svc.cluster.local
    • 检查 CoreDNS 插件是否正常运行:kubectl get pod -n kube-system | grep coredns
    • 在 Pod 内部执行 nslookup <域名> 排查 DNS 配置。
  4. ipvs 规则未生成

    • 检查 kube-proxy 模式是否为 ipvs:kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode
    • 重启 kube-proxy 使配置生效:kubectl delete pod -n kube-system -l k8s-app=kube-proxy

6. 总结

Kubernetes Service 是集群内部服务发现和负载均衡的核心组件,通过抽象 Pod 逻辑分组,提供稳定的访问入口。本文详细讲解了:

  1. 核心概念:Service 定义 Pod 逻辑分组及访问策略,解决 Pod IP 动态变化问题。
  2. 工作模式
    • ClusterIP:集群内部访问,提供虚拟 IP。
    • NodePort:暴露服务到集群外部,绑定节点端口。
    • LoadBalancer:云环境专用,结合外部负载均衡器。
    • ExternalName:通过域名别名关联外部服务。
  3. 底层实现:kube-proxy 基于 userspace、iptables 或 ipvs 实现负载均衡,ipvs 性能更优。
  4. Endpoints:记录后端端点,支持自动或手动关联,是 Service 与 Pod 关联的桥梁。
  5. 高级配置:会话保持、流量策略、未就绪地址暴露等功能,满足复杂场景需求。

掌握 Service 的使用和原理,能有效提升 Kubernetes 集群中服务的可用性和可维护性。

7. 思考与答案

思考:在生产环境中,如何选择 Service 类型?

答案

  • 仅集群内部访问:优先选择 ClusterIP,性能最优且资源开销小。
  • 需要外部访问且无云环境:使用 NodePort,配合外部负载均衡器(如 Nginx、F5)实现高可用。
  • 云环境外部访问:使用 LoadBalancer,利用云厂商的负载均衡服务,简化高可用配置。
  • 访问集群外部服务:使用 ExternalName,通过域名别名解耦,避免直接依赖外部 IP。

根据实际场景选择合适的类型,平衡可用性、性能和维护成本。

 

 

http://www.lryc.cn/news/608200.html

相关文章:

  • 具身智能VLA困于“数据泥潭”,人类活动视频数据是否是“破局之钥”?
  • 北京-4年功能测试2年空窗-报培训班学测开-今天来聊聊我的痛苦
  • 对于考研数学的理解
  • Linux 系统管理-15-OpenSSH 服务管理
  • 【Pytorch✨】LSTM 入门
  • Next.js 怎么使用 Chakra UI
  • 洛谷做题4:P5713 【深基3.例5】洛谷团队系统
  • OAuth 2.0 详解:现代授权的核心协议
  • 知识随记-----Qt 实战教程:使用 QNetworkAccessManager 发送 HTTP POST
  • Web前端实现银河粒子流动特效的3种技术方案对比与实践
  • C#中的除法
  • 【Web】CCF智能汽车大赛-CTF遴选赛 wp
  • LVGL代码框架简介
  • 苹果MAC 安卓模拟器
  • 计算机网络:任播和负载均衡的区别
  • 【QT】Qt信号与槽机制详解信号和槽的本质自定义信号和槽带参数的信号和槽
  • 【Python修仙编程】(二) Python3灵源初探(11)
  • linux中pthread_t 的值与top -Hp中线程id值的区别
  • 知识随记-----用 Qt 打造优雅的密码输入框:添加右侧眼睛图标切换显示
  • Autosar Nm-网管报文PNC停发后无法休眠问题排查
  • Antlr4在Windows环境下的配置
  • 涉水救援机器人cad【12张】三维图+设计书明说
  • Vue 服务端渲染 Nuxt 使用详解
  • AI Agent开发学习系列 - LangGraph(6): 有多个节点的Sequential Graph(练习解答)
  • 深入理解C++中的Lazy Evaluation:延迟计算的艺术
  • LangGraph认知篇-Command函数
  • UDP通信中BIND端口号的作用解析,LOCALPORT的关系解析
  • 搜索与图论(最小生成树 二分图)
  • 【Django】-5- ORM的其他用法
  • 企业级单点登录(SSO)技术详解:从原理到安全实践