Service介绍及使用案例
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
切换不同环境的数据库(如开发环境用测试库,生产环境用高可用库)。
操作步骤:
- 定义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
- 应用连接数据:
应用代码中使用统一端点 mysql-service
(Kubernetes DNS 解析为外部域名):
# Python 示例
import mysql.connector
db = mysql.connector.connect(
host="mysql-service", # Kubernetes 服务名称
user="admin",
password="password"
)
这样无需修改应用代码,仅仅需要更新service中定义的externalName
就可以实现多环境同使用同一个service名字就能自动连接到对应的环境中。
其他典型生产场景
- 集成第三方 API:
- 将
payment-service
映射到第三方支付网关(如 PayPal、Stripe),便于更换供应商或切换沙箱/生产环境。
- 混合云架构:
- 集群内服务需访问本地数据中心的老旧系统(如
legacy-api.internal-company.com
),通过 ExternalName 统一入口。
- 跨集群服务发现:
- 在多个 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 结合使用时,为有状态应用提供稳定的网络标识。
- 无 ClusterIP:
普通 Service 会分配一个虚拟 IP(ClusterIP),流量通过该 IP 负载均衡到后端 Pod。 Headless Service 省略此步骤,直接返回 Pod IP 列表。 - DNS 解析行为:
- 普通 Service:DNS 查询返回 ClusterIP。
- Headless Service:DNS 查询返回 所有 Pod 的 IP 地址(当有 Selector 时)或配置的 Endpoints(当无 Selector 时)。
- StatefulSet 增强:为每个 Pod 生成稳定的 DNS 记录(如
pod-name.service-name.namespace.svc.cluster.local
)。
- 负载均衡策略:
- 无内置负载均衡,客户端需自行处理流量分发(如轮询、哈希等)。
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-0
,mysql-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 注意事项
- DNS 解析延迟:
Pod IP 变化后,DNS 记录更新可能有延迟(默认 TTL 为 5 分钟)。
解决方案:在客户端实现重试机制或使用 watch API 监听 Endpoints 变化。
- 客户端负载均衡:
需自行实现流量分发逻辑(如使用客户端库或 Sidecar 代理)。 - 无 ClusterIP 的局限性:
不支持 Service Mesh 的 ClusterIP 层级流量拦截,需依赖 Pod 直连。
7、Service代理模式
7.1 iptables
Iptables 是 Linux 原生提供的一个功能强大的防火墙工具,可以用来设置、维护和检查 IPv4数据包,并且支持源目地址转换等规则。在 iptables 代理模式下,kube-proxy 通过监听 KubernetesAPI Server 中 Service 和 Endpoint 对象的变化,动态地更新节点上的 iptables 规则,以实现请求的转发。
工作原理:
kube-proxy
监听service
和Endpoint
变化,为每个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模式
原理:
- 虚拟服务创建:
kube-proxy 使用IPVS
为每个Service
创建虚拟服务器(Virtual Server
),绑定ClusterIP
和端口。 - 后端绑定:
将后端Pod IP
添加到虚拟服务器的Real Server
列表,并指定负载均衡算法。 - 内核级转发:
流量直接由内核的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)支持不完善。
生产环境选型建议
- 优先选择 ipvs:
- 集群规模 ≥ 100 节点或 Service 数量 ≥ 1000。
- 需要高性能负载均衡(如高并发 API 网关)。
- 需灵活选择负载均衡算法(如最少连接优化长链接场景)。
- 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 (二进制安装方式配置文件在每个机器上)
重启 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
重启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