service

1、Service资源定义

root@VM-26-198-ubuntu:/usr/local/src/study# cat service.yaml 
apiVersion: v1
kind: Service
metadata:
 labels:
   k8s-app: kube-dns
   kubernetes.io/cluster-service: "true"
   kubernetes.io/name: CoreDNS name: kubernetes
 namespace: test
spec:
 ports: # Service 端口配置
 - name: dns # Service 端口名字
   port: 53 # Service 端口
   protocol: UDP # 代理协议
   targetPort: 53 # 目标端口,程序端口
 - name: dns-tcp
   port: 53
   protocol: TCP
   targetPort: 53
 selector: # 代理到哪些 Pod
   k8s-app: kube-dns
 sessionAffinity: None # 会话保持配置
 type: ClusterIP # Service 类型

Service 支持将一个接收端口映射到任意的 targetPort,如果 targetPort 为空,targetPort 将被设置为与 Port 字段相同的值。targetPort 可以设置为一个字符串,引用 Pod 的一个端口的名称,这样的话即使更改了 Pod 的端口,也不会对 Service 的访问造成影响。

Kubernetes Service 能够支持 TCP、UDP、SCTP 等协议,默认为 TCP 协议。

2、Service类型

1. ClusterIP
  • 作用
    为服务分配一个集群内部 IP,只能在 Kubernetes 集群内访问。
  • 适用场景
    • 内部服务间通信(如前端服务访问后端 API)。
    • 不直接对外暴露的服务。
apiVersion: v1
kind: Service
metadata:
  name: my-internal-service
spec:
  type: ClusterIP  # 可省略(默认类型)
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80      # Service 端口
      targetPort: 8080  # Pod 端口
2. NodePort
  • 作用
    ClusterIP** 基础上**,在每个节点的指定端口(默认范围 30000-32767)暴露服务,允许通过 <节点IP>:<端口> 从外部访问。*
  • 适用场景
    • 开发/测试环境临时访问。
    • 需要绕过云平台负载均衡器的场景。
apiVersion: v1
kind: Service
metadata:
  name: my-nodeport-service
spec:
  type: NodePort
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 31000  # 手动指定端口(可选)
3. ExternalName
  • 作用
    Service 映射到外部服务的 DNS 名称(如数据库、第三方 API),不创建任何代理或端口暴露。
  • 适用场景
    • 集成集群外部的服务(如云数据库 RDS)。
    • 统一内部和外部服务的访问方式。
apiVersion: v1
kind: Service
metadata:
  name: my-external-service
spec:
  type: ExternalName
  externalName: my-database.example.com  # 外部服务域名
4. LoadBalancer
  • 作用
    NodePort** 基础上,通过云服务商(如 AWS、阿里云)自动创建外部负载均衡器**,直接将流量路由到 Service
  • 适用场景
    • 生产环境对外暴露服务。
    • 需要高可用和自动流量分发的场景。
5. Headless Service(无头服务)
  • 作用
    不分配 ClusterIP,直接返回**Pod IP **列表,适用于需要直接访问 Pod 的场景(如 StatefulSet)。
  • 适用场景
    • 有状态应用(如数据库集群)。
    • 需要自定义负载均衡策略。
apiVersion: v1
kind: Service
metadata:
  name: my-headless-service
spec:
  clusterIP: None  # 关键标识
  selector:
    app: my-stateful-app
  ports:
    - port: 80
      targetPort: 8080

3、ExternalName 代理域名

3.1 核心作用
  • DNS别名映射:将k8s集群内的服务名称(如my-external-service)映射到外部服务的域名(如api.external-provider.com
  • 无代理转发:不创建任何负载均衡或端口代理,仅仅通过DNS CNAME 记录实现透明解析。
  • 环境抽象:屏蔽外部服务的真实地址,简化应用配置(如开发、测试、生产环境使用不同的外部端点)。
3.2 配置实例
apiVersion: v1
kind: Service
metadata:
  name: payment-gateway  # 集群内服务名称
spec:
  type: ExternalName
  externalName: api.payment-provider.com  # 外部服务域名
3.3 生产环境案例:统一访问云数据库

场景描述

  • 应用部署在 Kubernetes 集群中,需访问 AWS RDS 上的 MySQL 数据库。
  • RDS 实例的端点可能因环境(开发/生产)或故障转移而变化。

使用 ExternalName 的优势

  • 配置解耦:应用代码中只需连接 mysql-service,无需硬编码 RDS 地址。
  • 灵活切换:通过修改 externalName 切换不同环境的数据库(如开发环境用测试库,生产环境用高可用库)。

操作步骤:

  1. 定义ExternalName Service:

生产环境对应的service

# 生产环境配置
apiVersion: v1
kind: Service
metadata:
  name: mysql-service
  namespace: app-prod  # 生产环境
spec:
  type: ExternalName
  externalName: prod-db.cluster-abc123.us-west-1.rds.amazonaws.com

测试环境对应的service

# 生产环境配置
apiVersion: v1
kind: Service
metadata:
  name: mysql-service
  namespace: app-test  # test环境
spec:
  type: ExternalName
  externalName: test-db.cluster-abc123.us-west-1.rds.amazonaws.com
  1. 应用连接数据:

应用代码中使用统一端点 mysql-service(Kubernetes DNS 解析为外部域名):

# Python 示例
import mysql.connector
db = mysql.connector.connect(
  host="mysql-service",  # Kubernetes 服务名称
  user="admin",
  password="password"
)

这样无需修改应用代码,仅仅需要更新service中定义的externalName 就可以实现多环境同使用同一个service名字就能自动连接到对应的环境中。

其他典型生产场景

  1. 集成第三方 API
  • payment-service 映射到第三方支付网关(如 PayPal、Stripe),便于更换供应商或切换沙箱/生产环境。
  1. 混合云架构
  • 集群内服务需访问本地数据中心的老旧系统(如 legacy-api.internal-company.com),通过 ExternalName 统一入口。
  1. 跨集群服务发现
  • 在多个 Kubernetes 集群间共享服务名称,如集群 A 的服务通过 ExternalName 指向集群 B 的服务端点。

4、使用Service代理k8s外部服务

使用场景:

  • 希望在生产环境中使用某个固定的名称而非 IP 地址访问外部的中间件服务;
  • 希望 Service 指向另一个 Namespace 中或其他集群中的服务;
  • 正在将工作负载转移到 Kubernetes 集群,但是一部分服务仍运行在 Kubernetes 集群之外的 backend。
root@VM-26-198-ubuntu:/usr/local/src/study# cat waibu.yaml
apiVersion: v1
kind: Service
metadata:
 labels:
   app: nginx-svc-external
 name: nginx-svc-external
spec:
  ports:
  - name: http
    port: 80  # service 端口
    protocol: TCP
    targetPort: 80  # 外部服务实际端口
  sessionAffinity: None
  type: ClusterIP
---
# cat nginx-ep-external.yaml 
apiVersion: v1
kind: Endpoints
metadata:
  labels:
    app: nginx-svc-external # 必须与 Service 同名
  name: nginx-svc-external # 必须与 Service 同名
subsets:
 - addresses:
   - ip: 120.52.51.107  # 外部服务 IP
   ports:
   - name: http
     port: 80   # 必须与 Service 的 targetPort 一致
     protocol: TCP
  • 需手动维护 Endpoints 的 IP 列表。
  • 支持 TCP/UDP,但不支持 HTTP 路径路由等高级特性。

5、会话保持

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: ClusterIP
  sessionAffinity: ClientIP  # 可选值:None(默认)、ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800  # 会话保持时间(默认 10800 秒,即 3 小时)
  ports:
    - port: 80
      targetPort: 8080
  selector:
    app: my-app
  • 同一客户端 IP 的请求在 timeoutSeconds 内始终转发到同一 Pod。
  • 若 Pod 重启或缩容,客户端 IP 的哈希结果可能变化,导致会话中断。
局限性(弊端)
  • IP 哈希的缺陷
    • NAT 问题:若客户端通过 NAT 网关(如企业内网或移动网络),多个用户可能共享同一出口 IP,导致流量集中到同一 Pod。
    • IP 变化:客户端 IP 动态变化(如移动网络切换)会导致会话丢失。
  • Pod 变化不感知
    • Pod 扩缩容、重启或故障时,哈希结果重新计算,无法保证会话持久化。
  • 负载不均衡
    • 某些 IP 的流量较大时,可能导致目标 Pod 负载不均。

6、Headless

Headless Service 是 Kubernetes 中一种特殊的 Service 类型,不分配 *ClusterIP,直接暴露 Pod 的 IP 地址和 DNS 记录。它适用于需要*直接访问 Pod 实例的场景,尤其与 StatefulSet 结合使用时,为有状态应用提供稳定的网络标识。

  1. 无 ClusterIP
    普通 Service 会分配一个虚拟 IP(ClusterIP),流量通过该 IP 负载均衡到后端 Pod。 Headless Service 省略此步骤,直接返回 Pod IP 列表。
  2. DNS 解析行为
    • 普通 Service:DNS 查询返回 ClusterIP。
    • Headless Service:DNS 查询返回 所有 Pod 的 IP 地址(当有 Selector 时)或配置的 Endpoints(当无 Selector 时)。
    • StatefulSet 增强:为每个 Pod 生成稳定的 DNS 记录(如 pod-name.service-name.namespace.svc.cluster.local)。
  3. 负载均衡策略
    • 无内置负载均衡,客户端需自行处理流量分发(如轮询、哈希等)。
apiVersion: v1
kind: Service
metadata:
  name: mysql-headless
spec:
  clusterIP: None  # 关键标识,声明为 Headless Service
  selector:
    app: mysql     # 必须匹配 StatefulSet 的 Pod 标签
  ports:
    - port: 3306
      targetPort: 3306
使用场景

####### 1. 有状态应用(StatefulSet)
场景需求

  • 每个 Pod 需要稳定的唯一标识(如数据库主从节点)。
  • 客户端需直接连接到特定 Pod(如写请求到主节点,读请求到从节点)。

结合 StatefulSet 的 DNS 记录

  • StatefulSet 创建的 Pod 名称遵循固定模式:<statefulset-name>-<ordinal>(如 mysql-0mysql-1)。
  • Headless Service 为每个 Pod 生成独立 DNS 记录:
# Pod 的 DNS 记录格式
mysql-0.mysql-headless.default.svc.cluster.local
mysql-1.mysql-headless.default.svc.cluster.local

####### 2.自定义服务发现与负载均衡
场景需求

  • 应用需要获取所有后端 Pod 的 IP 列表,自行实现负载均衡逻辑(如一致性哈希)。
  • 需避免 Kubernetes 默认的随机负载均衡策略。

操作方式

  • 查询 Headless Service 的 DNS 记录,获取所有 Pod IP:
# 解析 Service 名称返回所有 Pod IP
dig +short mysql-headless.default.svc.cluster.local
# 输出示例
10.244.1.5
10.244.2.10
10.244.3.15

####### 守护进程集(Statefulset)直连
场景需求

  • 每个节点部署一个 Pod(如日志收集器、监控代理)。
  • 需要直接访问某节点上的特定 Pod。

配置方式

  • 为 Statefulset创建 Headless Service:
root@VM-26-198-ubuntu:~# kubectl get svc -n middleware  elasticsearch-logging  -o yaml 
apiVersion: v1
kind: Service
metadata:
  annotations:
  labels:
    addonmanager.kubernetes.io/mode: Reconcile
    k8s-app: elasticsearch-logging
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: Elasticsearch
  name: elasticsearch-logging
  namespace: middleware
spec:
  clusterIP: None
  clusterIPs:
  - None
  ports:
  - name: db
    port: 9200
    protocol: TCP
    targetPort: 9200
  - name: transport
    port: 9300
    protocol: TCP
    targetPort: 9300
  - name: metrics
    port: 9114
    protocol: TCP
    targetPort: 9114
  publishNotReadyAddresses: true
  selector:
    k8s-app: elasticsearch-logging
  sessionAffinity: None
  type: ClusterIP

通过节点名称访问 Pod:

root@VM-26-198-ubuntu:~# kubectl exec -it cluster-test-65ff7fcc95-n2vzf bash 
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
(7 08:47 cluster-test-65ff7fcc95-n2vzf:/) curl elasticsearch-logging-0.elasticsearch-logging.middleware:9200
{"error":{"root_cause":[{"type":"security_exception","reason":"missing authentication credentials for REST request [/]","header":{"WWW-Authenticate":"Basic realm=\"security\" charset=\"UTF-8\""}}],"type":"security_exception","reason":"missing authentication credentials for REST request [/]","header":{"WWW-Authenticate":"Basic realm=\"security\" charset=\"UTF-8\""}},"status":401}(08:48 cluster-test-65ff7fcc95-n2vzf:/) 
Headless Service 注意事项
  1. DNS 解析延迟
    Pod IP 变化后,DNS 记录更新可能有延迟(默认 TTL 为 5 分钟)。

解决方案:在客户端实现重试机制或使用 watch API 监听 Endpoints 变化。

  1. 客户端负载均衡
    需自行实现流量分发逻辑(如使用客户端库或 Sidecar 代理)。
  2. 无 ClusterIP 的局限性
    不支持 Service Mesh 的 ClusterIP 层级流量拦截,需依赖 Pod 直连。

7、Service代理模式

7.1 iptables

Iptables 是 Linux 原生提供的一个功能强大的防火墙工具,可以用来设置、维护和检查 IPv4数据包,并且支持源目地址转换等规则。在 iptables 代理模式下,kube-proxy 通过监听 KubernetesAPI Server 中 Service 和 Endpoint 对象的变化,动态地更新节点上的 iptables 规则,以实现请求的转发。

工作原理:

kube-proxy监听serviceEndpoint变化,为每个service创建iptables规则链。当流量命中Service的ClusterIP和端口时,通过DNAT(目标地址转换)转发到后端PodIP。

示例规则

# 查看 iptables 规则(简化示例)
-A KUBE-SVC-XXXXXX -m statistic --mode random --probability 0.3333 -j KUBE-SEP-AAAAAA
-A KUBE-SVC-XXXXXX -m statistic --mode random --probability 0.5000 -j KUBE-SEP-BBBBBB
-A KUBE-SVC-XXXXXX -j KUBE-SEP-CCCCCC
  • 优点
    • 兼容性广泛,几乎所有 Linux 内核均支持。
    • 实现简单,适合小规模集群。
  • 缺点
    • 规则链线性匹配,时间复杂度为 O(n),大规模集群性能下降明显。
    • 负载均衡算法单一(仅随机概率均衡)。
    • 规则膨胀问题(Service 数量多时,规则数呈指数级增长)。
7.2 ipvs模式

原理:

  1. 虚拟服务创建
    kube-proxy 使用 IPVS 为每个 Service 创建虚拟服务器(Virtual Server),绑定 ClusterIP 和端口。
  2. 后端绑定
    将后端 Pod IP 添加到虚拟服务器的Real Server列表,并指定负载均衡算法。
  3. 内核级转发
    流量直接由内核的 IPVS 模块处理,通过哈希表快速匹配目标 Real Server
# 查看 IPVS 虚拟服务和 Real Server
ipvsadm -Ln
# 输出示例
TCP  10.96.0.1:443 rr
  -> 192.168.1.10:6443     Masq    1      0          0
  -> 192.168.1.11:6443     Masq    1      0          0
  • 优点
    • 基于哈希表查询,时间复杂度 O(1),性能远高于 iptables。
    • 支持多种负载均衡算法(如轮询、最少连接等)。
    • 规则简洁,扩展性强,适合大规模集群。
  • 缺点
    • 需 Linux 内核 ≥ 4.4 并启用 IPVS 模块。
    • 旧版本 Kubernetes(<1.11)支持不完善。
生产环境选型建议
  1. 优先选择 ipvs
    • 集群规模 ≥ 100 节点或 Service 数量 ≥ 1000。
    • 需要高性能负载均衡(如高并发 API 网关)。
    • 需灵活选择负载均衡算法(如最少连接优化长链接场景)。
  2. 2. 使用 iptables 的场景
    • 旧版本 Kubernetes(<1.11)或内核不支持 IPVS。
    • 小规模测试环境,简化调试。
ipvs 支持的算法
算法 名称 适用场景
rr 轮询 默认算法,适用于Pod性能均匀的场景
wrr 加权轮询 根据Pod权重分配流量(如Pod配置不同资源)
lc 最少连接 长连接服务(如数据库),优先选择活跃连接少的 Pod。
wlc 加权最少连接 结合权重和连接数,优化资源利用率。
sh 源地址哈希 会话保持(同一客户端始终访问同一 Pod)。
dh 目标地址哈希 特定用途(如缓存服务器)。
nq 无需队列等待 如果后端 Pod 的队列为空,则直接选择该 Pod;如果所有 Pod 的队列都非空,则采用其他策略(如轮询或最少连接)来选择 Pod
sed 最短期望延迟 sed,考虑 Pod 的当前连接数和连接请求的平均处理时间,选择预计处理时间最短的 Pod 来接收新请求

更改service代理模式

查看当前的代理模式:

# curl 127.0.0.1:10249/proxyMode
iptables

更改 proxy 的代理模式为 ipvs:

# kubectl edit cm kube-proxy  -n kube-system (二进制安装方式配置文件在每个机器上)

image

重启 kube-proxy 生效(二进制安装方式使用 systemctl restart kube-proxy):

kubectl patch daemonset kube-proxy -p "{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"date\":\"`date +'%s'`\"}}}}}" -n kube-system

在机器上查看 ipvs 规则:

# yum install ipvsadm -y
# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.32.128:32000 rr
 -> 172.16.85.217:8443 Masq 1 0 0 
TCP 172.16.32.128:32001 rr
 -> 192.168.181.141:80 Masq 1 0 0

更改代理算法为最小连接数:

kubectl edit cm kube-proxy -n kube-system

image

重启Proxy

# kubectl patch daemonset kube-proxy -p "{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"date\":\"`date +'%s'`\"}}}}}" -n kube-system

查看 ipvs 算法:


# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.32.128:32000 lc
 -> 172.16.85.217:8443 Masq 1 0 0 
TCP 172.16.32.128:32001 lc
 -> 192.168.181.141:80 Masq 1 0 0