一文读懂Service以及实践攻略
目录
- 1 一文读懂 Kubernetes Service 以及实践攻略
- 1.1 1. 什么是 Service?
- 1.1.1 为什么需要 Service?
- 1.2 2. Service 的工作原理
- 1.2.1 核心概念
- 1.2.2 流量转发过程
- 1.3 3. Service 的几种类型及应用场景
- 1.1 1. 什么是 Service?
- 2 实践:部署和测试Service
- 2.1 示例:创建Deployment
- 2.2 创建 ClusterIP Service
- 2.2.1 示例:创建 ClusterIP Service
- 2.2.2 部署和测试
- 2.3 Headless Service 与 StatefulSet 配合
- 2.3.1 示例:创建 Headless Service
- 2.4 4.2 创建 NodePort Service
- 2.4.1 示例:创建 NodePort Service
- 2.4.2 部署和测试
- 2.5 创建 LoadBalancer Service
- 2.5.1 示例:创建 LoadBalancer Service
- 2.5.2 部署和测试
- 2.6 ExternalName
- 2.6.1 示例:创建 ExternalName Service
- 2.6.2 部署和测试
- 3 服务发现
- 3.1 环境变量
- 3.2 DNS
- 3.2.1 查询命令
- 4 总结
- 5 参考资料
1 一文读懂 Kubernetes Service 以及实践攻略
❤️ 摘要:在 Kubernetes 中,Service 是连接应用与外部世界的重要桥梁,是Kubernetes 集群中的核心资源之一。提供了稳定的访问入口,即使应用 Pod 动态变化,Service 依然保持对外的一致性。本文将从基础介绍到实践操作,深入解析 Kubernetes Service 的工作原理、类型、配置以及常见的实践应用,帮助你全面掌握 Service 的使用和最佳实践。
💯 本文关联好文:
- 《一文读懂Deployment以及实践攻略》
- 《一文读懂StatefulSet以及实践攻略》
- 《【Kubernetes笔记】为什么DNS解析会超时?》
1.1 1. 什么是 Service?
在 Kubernetes 中,Service 是一种网络抽象层,负责暴露一组 Pod,以便外部用户或内部组件能够通过一个固定的访问点与 Pod 通信。由于 Pod 的生命周期是短暂且可能频繁变动的,其 IP 地址也随之变化,Service 提供了一种稳定的访问方式,无论 Pod 如何变化,用户都可以通过 Service 一致地与后端 Pod 通信,通过抽象层将流量引导至正确的 Pod。
例如:服务(Service)作为餐厅的接待员,负责接待顾客(用户),并确保始终可以通过接待员获得稳定的服务。当Service接到顾客的订单,他会告诉后厨(pod)要完成哪些特色菜(应用程序),虽然某个厨师可能会因为休息或换班而离开,但总会把订单交到相应的厨师手上。
1.1.1 为什么需要 Service?
当某个 Pod 被删除或更新时,其 IP 地址会发生变化。如果其他服务需要与这些 Pod 通信,无法直接依赖 Pod 的 IP。因此,Service 提供了以下功能:
- 持久的网络标识:即使 Pod 重启或更换,Service 提供的 IP 和 DNS 名称仍然保持不变。Service 通过一个虚拟 IP 地址(ClusterIP)和 DNS 名称为外部提供了统一的访问方式,即使后端的 Pod 动态变化,客户端也不需要修改任何配置。
- 负载均衡:Service 会自动将流量分发到后端的一组 Pod,确保高可用性和负载均衡。
1.2 2. Service 的工作原理
❔ 说明:关于Service、EndpointSlice的关系,后续出相关文章说明。
1.2.1 核心概念
- Selector:Service 通过标签选择器(Label Selector)来定位要暴露的 Pod,Selector 允许 Service 动态绑定一组 Pod,进行流量的负载均衡。
- ClusterIP:Kubernetes 为每个 Service 分配一个虚拟 IP(ClusterIP),通过这个 IP,集群内的其他 Pod 可以稳定地访问该 Service。
- Endpoints:Service 使用 Endpoints 资源来追踪与其关联的 Pod。Endpoints 是一组 IP 地址和端口的集合,代表了实际的后端 Pod。
- EndpointSlice: 在Kubernetes v1.21版本后,EndpointSlice API 是 Endpoints 的推荐替代品,提供了更多的可伸缩性和可扩展性。
1.2.2 流量转发过程
- 当客户端(如其他 Pod)访问一个 Service 的 ClusterIP 地址时,流量会先到达 Kubernetes 的 kube-proxy。
- kube-proxy 根据该 Service 的配置,将流量通过负载均衡的方式转发到后端的 Pod 上。( 默认采用 轮询 负载均衡策略,即每个请求被依次发送到不同的 Pod。)
- Pod返回响应,完成一次完整的请求-响应过程。
❔ 说明: 对于具有更多高级需求的应用场景,还可以结合 Ingress 或 外部负载均衡器 进行更复杂的流量控制。
1.3 3. Service 的几种类型及应用场景
Kubernetes 提供了多种类型的 Service,适应不同的场景需求。常见的 Service 类型包括:
Service 类型 | 功能 | 使用场景 |
---|---|---|
ClusterIP | ClusterIP 是 Kubernetes Service 的默认类型,适用于 集群内通信。它为 Service 分配一个内部 IP,其他 Pod 可以通过该 IP 访问服务。</br> ClusterIP 只在集群内部可访问,无法被外部访问。 | 集群内部服务间的通信,如集群内部的微服务通信。 |
NodePort | NodePort 在每个节点上开放一个静态端口,外部用户可以通过 NodeIP:NodePort 的方式访问服务。 | 当你想通过集群节点的 IP 地址和端口直接访问应用时,可以使用 NodePort。 |
LoadBalancer | 为服务创建一个云提供商的外部负载均衡器(如 AWS ELB、GCP GCLB),负载均衡器将流量分发到集群中的节点,再由节点转发到相应的 Pod。自动分发流量到后端 Pod。 | 当应用需要对外暴露,并希望自动使用云提供商的负载均衡器时,可以使用 LoadBalancer 类型。适合公有云环境。 |
ExternalName | 将服务映射到 externalName 字段的内容(例如,映射到主机名 api.foo.bar.example)。 该映射将集群的 DNS 服务器配置为返回具有该外部主机名值的 CNAME 记录。 | 将 Kubernetes 外部的服务暴露给集群内的服务。 |
Headless Service | Headless Service 是一种不需要 ClusterIP 的 Service,用于 不进行负载均衡 的场景。通常与 StatefulSet 一起使用,它允许客户端直接与 Pod 通信。与普通的 Service 不同,Headless Service 不会自动创建一个 ClusterIP。 | 需要直接访问 Pod 的场景,如数据库集群或状态有依赖的应用。 |
2 实践:部署和测试Service
2.1 示例:创建Deployment
这里使用Nginx作为应用:
apiVersion: apps/v1
kind: Deployment
metadata:
# 定义Deployment的名字
name: nginx-deployment
labels:
app: nginx
spec:
# 定义副本数
replicas: 3
# 选择器指定label与pod模板的label匹配
selector:
matchLabels:
app: nginx
template:
metadata:
# 与选择器指定label匹配
labels:
app: nginx
spec:
containers:
# pod名字,可自定义
- name: nginx
# 镜像源, 这里设置私有镜像源
image: harbor.zx/hcie/nginx:1.26.1
# pod暴露端口号
ports:
- containerPort: 80
name: http
protocol: TCP
2.2 创建 ClusterIP Service
ClusterIP 是默认的 Service 类型,主要用于集群内部的通信。创建 ClusterIP 时,Kubernetes 会为 Service 分配一个集群内部的 IP 地址,其他 Pod 可以通过这个 IP 进行访问。
2.2.1 示例:创建 ClusterIP Service
---
apiVersion: v1
kind: Service
metadata:
name: my-clusterip-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
❔ 参数说明:
- selector:Service选择标签为
app: my-app
的 Pod。当创建 Service 时,确保它的selector
与目标 Pod 的标签匹配,否则流量无法正确转发到目标 Pod。 - ports:支持暴露多个端口,案例是将 ClusterIP 的 80 端口映射到 Pod 的 8080 端口。
字段 可能值 protocol
指定代理端口的协议, 默认是TCP,还支持“SCTP”、“UDP” port
服务对外暴露的端口 targetPort
服务绑定pod的端口,端口的表示方式可以是数值或名字,一般情况下,为了方便起见, targetPort 设置与 port 字段相同的值。 - clusterIP: 默认是按
service-cluster-ip-range
范围自动分配IP,如果你手动指定clusterIP,但是 IP 地址必须是为 API 服务器配置的service-cluster-ip-range
CIDR 范围内的有效 IPv4 或 IPv6 地址。如果您尝试使用无效的clusterIP
地址值创建服务,API 服务器将返回 422 HTTP 状态代码以指示存在问题。
2.2.2 部署和测试
- 部署 Service:
kubectl apply -f clusterip-service.yaml
- 查看 Service:
输出如下:kubectl get svc my-clusterip-service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE my-clusterip-service ClusterIP 10.245.81.75 <none> 80/TCP 5s
- 查看详细信息
输出如下:kubectl describe svc my-clusterip-service
Name: my-clusterip-service Namespace: default Labels: <none> Annotations: <none> # 关联pod的label Selector: app=nginx # service的类型 Type: ClusterIP IP Family Policy: SingleStack IP Families: IPv4 # Kubernetes分配的集群ip,固定不变的 IP: 10.245.81.75 IPs: 10.245.81.75 Port: <unset> 80/TCP TargetPort: 80/TCP # 与label匹配关联的pod, pod可能随便变化。 Endpoints: 172.16.126.37:80,172.16.194.100:80,172.16.224.15:80 Session Affinity: None Events: <none>
- 使用
curl
访问 Service,验证集群内通信是否正常:
输出如下:kubectl run curlpod --image=harbor.zx/hcie/curl:8.1.1 -it --rm -- curl http://my-clusterip-service
<!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
2.3 Headless Service 与 StatefulSet 配合
❤️ 关于Headless Service案例,推荐前文:《一文读懂StatefulSet以及实践攻略》
Headless Service 常与 StatefulSet 一起使用,允许客户端直接访问 Pod,而不经过负载均衡。
2.3.1 示例:创建 Headless Service
apiVersion: v1
kind: Service
metadata:
name: my-headless-service
spec:
clusterIP: None
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
❔参数说明:
- clusterIP: None:表示这是一个 Headless Service,不会创建 ClusterIP。
2.4 4.2 创建 NodePort Service
NodePort 是最简单的对外暴露服务的方式。Kubernetes 会在每个节点上开放一个指定的端口,外部流量可以通过 NodeIP:NodePort
的方式访问该服务。
2.4.1 示例:创建 NodePort Service
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30007
type: NodePort
❔参数说明:
- nodePort:手动指定30007 端口暴露给外部访问, 如果没有指定端口, Kubernetes 控制平面会从
--service-node-port-range
标志指定的范围内分配端口(默认值:30000-32767)。每个节点都会将该端口(每个节点上的相同端口号)代理到您的服务中。 - 如果你的环境有多个网络平面, 还可以将集群中的节点设置为使用特定的 IP 地址来提供节点端口服务。通过 kube-proxy 服务设置
--nodeport-addresses
标志或 kube-proxy 配置文件的nodePortAddresses
字段指定特定的 网段范围。 - 例如,如果您使用
--nodeport-addresses=127.0.0.0/8,10.0.0.0/8
标志启动 kube-proxy,则 kube-proxy 只会选择 NodePort 服务的环回接口和内网地址。--nodeport-addresses
的默认值 或nodeProtAddress: null
是空列表,这意味着 kube-proxy 应考虑 NodePort 的所有可用网络接口。
2.4.2 部署和测试
- 部署 Service:
kubectl apply -f nodeport-service.yaml
- 查看service
输出如下:kubectl get svc my-nodeport-service
查看详细信息NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE my-nodeport-service NodePort 10.245.56.79 <none> 80:30007/TCP 9s
Name: my-nodeport-service Namespace: default Labels: <none> Annotations: <none> Selector: app=nginx # service类型 Type: NodePort IP Family Policy: SingleStack IP Families: IPv4 # 分配的集群ip IP: 10.245.56.79 IPs: 10.245.56.79 Port: <unset> 80/TCP TargetPort: 80/TCP # 节点映射端口 NodePort: <unset> 30007/TCP # 映射的后端pod Endpoints: 172.16.126.37:80,172.16.194.100:80,172.16.224.15:80 Session Affinity: None External Traffic Policy: Cluster Events: <none>
- 使用任一 Node IP 和端口访问 Service:
curl http://<Node-IP>:30007
2.5 创建 LoadBalancer Service
LoadBalancer Service 通常适用云环境,它能够自动创建外部的负载均衡器(一般由云提供商提供),分发流量到后端 Pod。对于大多数云服务商,如阿里云、AWS 和 Google Cloud,LoadBalancer Service 会自动创建并配置云负载均衡。
2.5.1 示例:创建 LoadBalancer Service
apiVersion: v1
kind: Service
metadata:
name: my-loadbalancer-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
❔参数说明:
- 云环境中的 LoadBalancer:如果在本地集群中使用 LoadBalancer,需要确保集群具备与云服务的集成能力,否则 LoadBalancer 不会自动创建。
2.5.2 部署和测试
- 部署 Service:
kubectl apply -f loadbalancer-service.yaml
- 获取外部 IP 并测试:
输出如下:kubectl get svc my-loadbalancer-service
❔ 说明: Service会尝试获取外部IP,如果获取成功,可以通过负载均衡器的外部 IP 访问服务。(实验环境不是云环境,所以无法获取公网ip)NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE my-loadbalancer-service LoadBalancer 10.245.138.0 <pending> 80:24649/TCP 10s
2.6 ExternalName
ExternalName 服务类型将 Kubernetes Service 的请求映射到外部的 DNS 名称。它不涉及任何集群内部的 Pod 调度,只是通过 DNS 映射进行流量转发。
2.6.1 示例:创建 ExternalName Service
假设我们有一个外部 API 服务,域名为 api.example.com
,我们希望将其映射到 Kubernetes 中的 Service。
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
type: ExternalName
externalName: api.example.com
❔参数说明:
- 这个定义会将所有对
external-service
的请求转发到api.example.com
。
2.6.2 部署和测试
- 部署 Service:
kubectl apply -f external-name-service.yaml
- 获取外部 IP 并测试:
输出如下:kubectl get service external-service
❔ 说明: Service如果配置正确,应该能成功访问外部 API 服务,返回相应的数据。NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE external-service ExternalName <none> api.example.com <none> 1m
3 服务发现
对于在集群内运行的客户端,Kubernetes 支持两种主要的服务发现模式:环境变量和 DNS。
3.1 环境变量
当 Pod 在节点上运行时,kubelet 会为每个运行中服务添加一组环境变量。它添加了 {SVCNAME}_SERVICE_HOST
和 {SVCNAME}_SERVICE_PORT
变量,其中服务名称为大写,破折号转换为下划线。
例如,上面的服务案例会生成以下环境变量:
先删除nginx-deployment重建
kubectl delete -f nginx-deployment.yaml
kubectl apply -f nginx-deployment.yaml
打印环境变量
kubectl exec -it nginx-deployment-64db67d8bc-fcpc6 -- printenv
输出如下:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=nginx-deployment-64db67d8bc-fcpc6
NGINX_VERSION=1.26.1
NJS_VERSION=0.8.4
NJS_RELEASE=2~bookworm
PKG_RELEASE=2~bookworm
# CLUSTERIP_SERVICE
MY_CLUSTERIP_SERVICE_PORT_80_TCP_PROTO=tcp
MY_CLUSTERIP_SERVICE_PORT_80_TCP_ADDR=10.245.81.75
MY_CLUSTERIP_SERVICE_PORT_80_TCP_PORT=80
MY_CLUSTERIP_SERVICE_SERVICE_HOST=10.245.81.75
MY_CLUSTERIP_SERVICE_SERVICE_PORT=80
MY_CLUSTERIP_SERVICE_PORT_80_TCP=tcp://10.245.81.75:80
MY_CLUSTERIP_SERVICE_PORT=tcp://10.245.81.75:80
# Kubernetes集群
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.245.0.1:443
KUBERNETES_PORT_443_TCP_ADDR=10.245.0.1
KUBERNETES_PORT=tcp://10.245.0.1:443
KUBERNETES_SERVICE_HOST=10.245.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
# NODEPORT_SERVICE
MY_NODEPORT_SERVICE_PORT=tcp://10.245.56.79:80
MY_NODEPORT_SERVICE_SERVICE_HOST=10.245.56.79
MY_NODEPORT_SERVICE_SERVICE_PORT=80
MY_NODEPORT_SERVICE_PORT_80_TCP=tcp://10.245.56.79:80
MY_NODEPORT_SERVICE_PORT_80_TCP_PORT=80
MY_NODEPORT_SERVICE_PORT_80_TCP_ADDR=10.245.56.79
MY_NODEPORT_SERVICE_PORT_80_TCP_PROTO=tcp
# LOADBALANCER_SERVICE
MY_LOADBALANCER_SERVICE_SERVICE_HOST=10.245.138.0
MY_LOADBALANCER_SERVICE_SERVICE_PORT=80
MY_LOADBALANCER_SERVICE_PORT=tcp://10.245.138.0:80
MY_LOADBALANCER_SERVICE_PORT_80_TCP_PORT=80
TERM=xterm
HOME=/root
⚠️ 注意:当您有一个 Pod 需要访问某个 Service,并且您使用环境变量的方式将端口和集群 IP 发布给客户端 Pod 时,您必须在客户端 Pod 存在之前创建 Service。否则,这些客户端 Pod 将不会填充其环境变量。
3.2 DNS
❤️ 关于DNS相关知识点和案例,推荐《一文读懂StatefulSet以及实践攻略》、《【Kubernetes笔记】为什么DNS解析会超时?》
Kubernetes建议你在任何时候都应该使用插件为 Kubernetes 集群设置 DNS 服务。集群感知的 DNS 服务器(例如 CoreDNS)会监视 Kubernetes API 中的新服务,并为每个服务创建一组 DNS 记录。如果整个集群都启用了 DNS,那么所有 Pod 都应该能够自动通过其 DNS 名称解析服务。
例如,如果您在 Kubernetes 命名空间 my-ns
中有一个名为 my-service
的服务,则控制平面和 DNS 服务共同作用,为 my-service.my-ns
创建一条 DNS 记录。 my-ns
命名空间中的 Pod 应该能够通过对 my-service
进行名称查找来找到服务( my-service.my-ns
也可以)。
Kubernetes 还支持命名端口的 DNS SRV(服务)记录。如果 my-service.my-ns
服务有一个名为 http
的端口,协议设置为 TCP
,您可以对 _http._tcp.my-service.my-ns
执行 DNS SRV 查询发现 http
的端口号以及 IP 地址。
3.2.1 查询命令
$ nslookup
> set type=SRV
> _http._tcp.my-service.my-ns
或者
dig _http._tcp.my-service.my-ns SRV
4 总结
Kubernetes 中的 Service 是应用程序连接的核心。它解决了 Pod 短暂生命周期和网络不可预测的问题,为应用提供稳定的访问入口。通过 ClusterIP、NodePort、LoadBalancer 和 Headless Service 等不同的 Service 类型,Kubernetes 满足了从集群内通信到外部流量管理的多种需求。在实际应用中,你可以根据具体的业务需求选择合适的 Service 类型,构建稳定高效的应用服务网络。
5 参考资料
[1]Kubernetes官网-service
[2]Use a Service to Access an Application in a Cluster