Service简介
Deployment这种工作负载能管理我们应用Pod的副本数,并实现动态的创建和销毁,所以Pod本身是临时资源(IP随时可能变化)。现在如果某组Pod A需要访问另一组Pod B,A就需要在应用的配置参数里动态跟踪并更改B的ip和端口,把这种需求放在业务端去实现显然是非常不合理的。Kubernetes给我们提供了解决方案:Kubernetes Service。
Kubernetes Service的一个关键目标是让你无需修改现有应用以使用某种不熟悉的服务发现机制。Service是Kubernetes集群中用来提供负载均衡、服务发现和通信的关键组件,能够让Pod之间以及外部流量和内部应用进行稳定通信。
Service底层工作原理
ClusterIP和kube-proxy组件
Service的核心机制依赖于Kubernetes的虚拟IP概念和kube-proxy组件(除非我们已经部署了自己的替换组件来替代kube-proxy)。每个Service都会分配一个ClusterIP,作为服务的固定入口。kube-proxy在每个节点上运行,负责维护IP关系表,将到达ClusterIP的流量转发到对应的Pod。
iptables和IPVS
kube-proxy负责设置网络规则,常用的机制有:
iptables:通过添加大量的NAT规则来进行流量转发,但当服务数量增大时,性能下降较明显。
IPVS:相比iptables,IPVS提供了更高效的流量转发机制。IPVS使用更高效的哈希表来存储转发规则,具有更高的性能,适合大规模集群中的服务发现和负载均衡。
忘记Linux中iptables和IPVS概念的小伙伴可以到这里了解:
Linux技术03-Netfilter、Iptables、Nftables、Firewalld
Linux技术04-IPVS
EndpointSlice
Service本身并不直接维护Pod的IP地址,而是通过EndpointSlice对象记录符合Service选择器的Pod列表。每当符合条件的Pod发生变更,Kubernetes控制器会更新EndpointSlice,对应的kube-proxy也会随之调整路由规则,并操作iptables或IPVS。
负载均衡
Service提供了默认的轮询负载均衡(Round Robin),即将流量均匀分配到后端Pod。具体的流量分配方式依赖于kube-proxy的转发机制,如iptables或IPVS。
Service示例
定义一个Deployment和一个Service:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app-container
image: my-app:latest
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP
- Deployment 定义了应用Pod的副本数(replicas),并通过标签选择器
app:my-app
进行匹配。 - Service 定义了一个ClusterIP类型的服务,通过标签选择器
app:my-app
将流量分发到符合条件的Pod。Service使用端口80对外提供服务,并将流量转发到Pod内部8080端口。
Service 类型
ClusterIP
仅在集群内部可以访问,提供一个虚拟IP地址供集群中的其他Pod访问,如上面的示例。
NodePort
在每个节点上暴露一个固定端口,并通过该端口访问服务。例如管理员需要访问监控组件prometheus的web ui,可以通过这种方式。
apiVersion: v1
kind: Service
metadata:
name: my-app-nodeport-service
spec:
type: NodePort
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
nodePort: 30007
示例中这个服务会在每个node节点的30007端口暴露服务,我们通过任一node的IP和30007端口即可从外部访问服务。
LoadBalancer
使用外部负载均衡器,在云服务提供商上动态创建。在支持 LoadBalancer的云环境中,这种服务类型可以自动创建负载均衡器(依赖相关的cloud-controller-manager组件),并为其分配一个外部IP地址供外部访问。
apiVersion: v1
kind: Service
metadata:
name: nginx-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb" # 使用AWS Network Load Balancer (可选)
spec:
type: LoadBalancer
selector:
app: nginx
ports:
- port: 80
targetPort: 80
protocol: TCP
ExternalName
映射到外部服务的DNS名称,而不是映射到Kubernetes内部的Pod。该映射将集群的DNS服务器配置为返回具有该外部主机名值的cname记录;集群不会为之创建任何类型代理。
例如Pod需要访问位于集群外的数据库、API 或者其他第三方服务;为外部的服务创建一个集群内部的别名,以便简化服务调用。
apiVersion: v1
kind: Service
metadata:
name: google-service
spec:
type: ExternalName
externalName: www.google.com
Headless Services(无头服务)
有时我们并不需要负载均衡,也不需要单独的Service IP。遇到这种情况,可以通过显式设置 集群 IP(spec.clusterIP
)的值为"None"
来创建无头服务(Headless Service)。
无头Service不会获得集群 IP,kube-proxy不会处理这类Service, 而且平台也不会为它们提供负载均衡或路由支持。
无头Service通过内部DNS记录报告各个Pod的端点IP地址,这些DNS记录是由集群的DNS服务所提供的。
无头Service一个常见的使用场景是StatefulSet管理的有状态服务。其他应用的Pod,需要直接访问各个节点DNS(例如kafka-0,kafka-1,kafka-2)。