Kubernetes(通常简写为K8s)中的Service是一种核心资源对象,它用于将一组Pod(容器组)抽象为一个单一的服务,使得这些Pod能够对外提供稳定的服务入口。
一、作用
- 提供稳定的网络终点:Service为Pod提供了一个稳定的IP地址和DNS名称,无论Pod如何变化(例如被删除、重新调度等),Service的IP地址和DNS名称都不会改变,确保了服务的稳定性。
- 实现负载均衡:Service内部实现了负载均衡机制,它可以将进入的请求均匀地分配给后端的Pod副本,提高了系统的可扩展性和可靠性。
- 实现故障隔离:当某个Pod发生故障时,Service会自动将该Pod从服务池中剔除,保证请求不会被故障的Pod处理,从而实现了故障隔离。
- 实现服务发现:Service允许前端的应用程序通过Label Selector来找到提供特定服务的Pod,从而实现了服务的自动发现。
二、类型
Kubernetes支持以下几种类型的Service:
- ClusterIP:这是最常见的Service类型,也是默认类型。它为Service在集群内部提供一个固定的虚拟IP(ClusterIP),只有集群内部的客户端可以访问此服务。ClusterIP Service适用于内部服务通信,对外部不可见。
- NodePort:这种类型的Service允许将服务公开到集群节点上的某个端口上(NodePort),从而可以从集群外部访问服务。它会在每个节点上监听相同的端口,并将流量转发到Service中的Pod。NodePort Service适用于需要从集群外部访问服务的情况,通常用于测试和开发环境。
- LoadBalancer:在NodePort的基础上,LoadBalancer类型的Service通过云服务提供商(如AWS、GCP、Azure)的负载均衡器来公开服务,可以将流量分布到多个节点上的Pod。这允许外部流量访问服务,同时实现负载均衡。LoadBalancer Service适用于需要在云环境中公开服务,并且需要负载均衡的情况,常用于生产环境。
- ExternalName:这种类型的Service将Kubernetes Service映射到外部服务的DNS名称,不通过kube-proxy进行代理。它通常用于将现有的外部服务映射到Kubernetes内部服务,实现平滑迁移。
- Headless Service:Headless Service不分配ClusterIP,而是为每个选择的Pod创建DNS记录。这意味着每个Pod都有其独立的DNS记录,通常用于服务发现或需要直接访问每个Pod的场景,如数据库复制等。
三、类型详解
1、ClusterIP
ClusterIP 是 Service 类型的一种,它是默认的服务类型。当创建一个类型为 ClusterIP 的 Service 时,Kubernetes 会为该 Service 分配一个集群内部的虚拟IP地址(Cluster IP),这个地址仅在集群内部可见和可访问。这个地址的分配和管理是由 Kubernetes 集群中的 kube-proxy 组件和底层的网络插件(如 Calico、Flannel、Weave 等)共同完成的。
(1)特点
- 内部访问:ClusterIP Service 主要用于集群内部的Pod之间的相互通信。外部网络无法直接访问通过 ClusterIP 暴露的服务,除非通过其他方式(如 NodePort、LoadBalancer 或 Ingress)将服务暴露到集群外部。
- 虚拟IP:每个 ClusterIP Service 都会获得一个虚拟IP地址,这个地址在集群内部是唯一的,并且会根据集群的网络配置和大小来分配。Pod 可以使用 Service 的 ClusterIP 地址和端口来访问背后的后端Pod集合。
- 负载均衡:Service 的 ClusterIP 地址实际上是一个逻辑上的IP,而不是直接绑定到某个物理或虚拟网络接口上的。当集群内部的Pod通过 ClusterIP 访问 Service 时,kube-proxy 会将请求转发到 Service 后面的一个或多个 Pod 上,从而实现负载均衡。具体的负载均衡算法(如轮询、最少连接等)取决于 kube-proxy 的配置和使用的网络插件。
- DNS 支持:Kubernetes 的 DNS 系统(CoreDNS 或 kube-dns)会为每个 Service 创建一个 DNS 记录,这样 Pod 就可以通过 Service 的名称(而不是 IP 地址)来访问它。这使得服务发现变得更加简单和可靠。
- Service 的定义:在定义 ClusterIP Service 时,需要指定 Service 的名称、Selector(用于选择一组Pod)、端口信息(包括服务的端口和Pod上对应的端口)等。Selector 是一个标签选择器,用于指定哪些Pod应该被包括在这个Service中。
(2)示例
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80 # Service端口
targetPort: 9376 # Pod端口
type: ClusterIP
在这个例子中,创建了一个名为 my-service 的 ClusterIP Service,它选择所有具有 app=my-app 标签的Pod,并将这些Pod的9376端口映射到Service的80端口上。Service的类型被明确指定为 ClusterIP。
总之,ClusterIP Service 是 Kubernetes 中用于在集群内部提供稳定网络终点的关键资源对象,它通过分配一个集群内部的虚拟IP地址和端口,并结合 kube-proxy 的负载均衡功能,为Pod之间的通信提供了极大的便利。
2、NodePort
NodePort 类型的Service是一种将服务暴露到集群外部的方式。当创建一个NodePort Service时,Kubernetes不仅会在集群内部为Service分配一个ClusterIP(就像ClusterIP类型的Service那样),还会在每个节点上选择一个特定的端口(NodePort)来代理Service的流量。这个NodePort是静态的,范围在Kubernetes集群的配置中指定(通常是30000-32767),并且整个集群内是唯一的。
(1)特点
- 外部访问:通过NodePort,可以从集群外部访问Service。只需要访问任意集群节点的IP地址加上指定的NodePort端口,流量就会被转发到后端的Pod上。
- 内部访问:尽管NodePort主要用于外部访问,但也可以从集群内部通过ClusterIP或NodePort来访问Service。
- 端口范围:NodePort的范围是有限的,通常在30000-32767之间,这意味着每个NodePort Service都需要在这个范围内选择一个未被占用的端口。
- 负载均衡:与ClusterIP Service类似,NodePort Service也提供了负载均衡功能。当外部流量通过NodePort进入集群时,kube-proxy会将请求分发到Service背后的Pod上。
- 服务发现:与ClusterIP Service一样,NodePort Service也支持服务发现,即Pod可以通过Service的名称来访问它,而无需知道具体的IP地址或端口号。
(2)示例
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
type: NodePort
selector:
app: my-app
ports:
- protocol: TCP
port: 80 # Service端口
targetPort: 9376 # Pod端口
nodePort: 30007 # 可选的,如果不指定,Kubernetes将自动分配一个端口
在这个例子中,创建了一个名为my-nodeport-service的NodePort Service,它选择所有具有app=my-app标签的Pod,并将这些Pod的9376端口映射到Service的80端口上。同时,指定了一个NodePort端口30007(这是可选的,如果不指定,Kubernetes会自动选择一个可用的端口)。
(3)访问
要从集群外部访问NodePort Service,可以使用任何集群节点的IP地址加上指定的NodePort端口。例如,如果集群的一个节点IP是192.168.1.100,并且指定的NodePort是30007,那么可以通过http://192.168.1.100:30007来访问Service。
需要注意的是,由于NodePort将Service暴露到了集群的每个节点上,因此它可能会带来安全风险,因为任何能够访问集群节点的人都可以访问到该Service。为了更安全地暴露服务,可以考虑使用LoadBalancer Service(如果云提供商支持)或Ingress资源。
3、LoadBalancer
LoadBalancer 类型的Service是一种将服务暴露到集群外部的高级方式,它通常依赖于云提供商的负载均衡器来实现。当创建一个LoadBalancer Service时,Kubernetes会首先为Service分配一个ClusterIP(就像ClusterIP和NodePort类型的Service那样),然后它还会在云提供商的负载均衡器上配置一个外部IP地址,以便从集群外部访问Service。
(1)特点
- 外部访问:LoadBalancer Service通过云提供商的负载均衡器来提供外部访问。这意味着可以从集群外部通过负载均衡器的IP地址和端口来访问Service。
- 动态IP分配:与NodePort Service不同,LoadBalancer Service的外部IP地址是由云提供商动态分配的。这意味着不需要手动指定IP地址,但这也意味着每次创建LoadBalancer Service时,IP地址都可能会改变(尽管在实际使用中,很多云提供商都会尝试保留同一个IP地址,直到显式释放它)。
- 负载均衡和故障转移:云提供商的负载均衡器负责将外部流量分发到集群中的多个节点上,并进一步由kube-proxy将流量转发到Service背后的Pod上。负载均衡器还负责处理故障转移,即当某个节点或Pod不可用时,自动将流量重定向到其他健康的节点或Pod上。
- 成本:与NodePort Service相比,LoadBalancer Service通常会产生额外的成本,因为需要为云提供商的负载均衡器付费。
- 服务发现:与ClusterIP和NodePort Service一样,LoadBalancer Service也支持服务发现,即Pod可以通过Service的名称来访问它,而无需知道具体的IP地址或端口号。
(2)示例
apiVersion: v1
kind: Service
metadata:
name: my-loadbalancer-service
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- protocol: TCP
port: 80 # Service端口
targetPort: 9376 # Pod端口
在这个例子中,创建了一个名为my-loadbalancer-service的LoadBalancer Service,它选择所有具有app=my-app标签的Pod,并将这些Pod的9376端口映射到Service的80端口上。由于我们没有指定nodePort或externalIPs字段,Kubernetes将使用云提供商的负载均衡器来动态分配一个外部IP地址。
(3)访问
要从集群外部访问LoadBalancer Service,可以使用云提供商提供的负载均衡器的IP地址和端口(在这个例子中是80端口)。通常可以在Service的YAML文件中查看或更新后通过kubectl get svc命令查看负载均衡器的外部IP地址。
需要注意的是,并非所有Kubernetes环境都支持LoadBalancer Service。它主要适用于那些由云提供商托管的Kubernetes集群,如AWS EKS、GCP GKE、Azure AKS等。如果正在使用一个裸机Kubernetes集群或自托管集群,并且没有云提供商的负载均衡器支持,那么可能需要使用其他方法来暴露服务,如NodePort Service结合外部负载均衡器或Ingress资源。
4、 ExternalName
ExternalName 类型的Service是一种特殊的服务,它不提供集群内部的Pod的负载均衡和发现,而是将Service名称解析为外部资源的CNAME记录。这意呀着,当尝试访问这个Service时,Kubernetes的DNS系统将返回配置在Service中的外部资源的地址,而不是集群内部Pod的地址。
(1)特点
- 外部资源访问:通过ExternalName Service,可以为集群内部的Pod提供一个简单的方式来访问外部资源,如另一个Kubernetes集群中的服务、外部数据库或API等。
- CNAME记录:当创建一个ExternalName Service时,需要指定一个externalName字段,该字段包含了外部资源的CNAME。Kubernetes的DNS系统将这个Service名称解析为这个CNAME,从而允许Pod通过Service名称来访问外部资源。
- 无负载均衡:与ClusterIP、NodePort和LoadBalancer类型的Service不同,ExternalName Service不提供负载均衡功能。它只是将Service名称解析为外部资源的地址。
- 无端口映射:由于ExternalName Service不直接连接到集群内部的Pod,因此它也不需要端口映射。访问的是外部资源的原始端口。
(2)示例
apiVersion: v1
kind: Service
metadata:
name: my-external-service
spec:
type: ExternalName
externalName: my.database.example.com
在这个例子中,创建了一个名为my-external-service的ExternalName Service,并将其externalName字段设置为my.database.example.com。当集群内部的Pod尝试访问my-external-service时,Kubernetes的DNS系统将返回my.database.example.com的CNAME记录,Pod随后可以使用这个地址来访问外部数据库。
(3)访问
由于ExternalName Service只是将Service名称解析为外部资源的CNAME记录,因此实际上是在直接访问外部资源。这意味着需要知道如何与这个外部资源通信(例如,使用正确的协议、端口和认证凭据)。当尝试访问my-external-service时,实际上是在访问my.database.example.com。
需要注意的是,ExternalName Service主要用于解决集群内部Pod访问外部资源的需求,而不是将服务暴露到集群外部。如果需要将服务暴露到集群外部,应该考虑使用NodePort、LoadBalancer或Ingress等类型的Service。
5、 Headless Service
Headless Service(无头服务)是一种特殊类型的Service,它主要用于需要直接访问Pod IP地址的场景,而不是通过Service的虚拟IP地址进行负载均衡。
(1)特点
- 无ClusterIP:Headless Service的spec.clusterIP字段被显式设置为None,因此Kubernetes不会为该服务分配一个虚拟IP(ClusterIP)地址。
- DNS解析:Kubernetes的DNS系统为无头服务生成一条特殊的DNS记录,该记录列出所有与该服务关联的Pod的IP地址。客户端可以通过域名解析直接获得Pod列表,进而实现自定义的负载均衡或服务发现逻辑。
- 直接访问Pod:客户端可以直接通过Pod的具体IP地址进行通信,而不是通过Service的ClusterIP地址。
(2)示例
apiVersion: v1
kind: Service
metadata:
name: my-app-headless-service
spec:
clusterIP: None # 设置为None以创建无头服务
selector:
app: my-app
ports:
- port: 80
targetPort: 80
这个Headless Service选择了所有具有app: my-app标签的Pod,并监听80端口(这里targetPort也是80,意味着Service将流量转发到Pod的80端口)。由于clusterIP被设置为None,因此Kubernetes不会为这个Service分配一个ClusterIP。
(3)访问
由于现在有了Pod的IP地址,可以直接通过IP地址和端口(在这个例子中是80端口)来访问Pod。然而,请注意,直接访问Pod通常不是推荐的做法,因为它绕过了Service提供的抽象和可能的负载均衡、故障转移等特性。在实际应用中,可能会根据Pod的IP地址列表实现自定义的负载均衡逻辑。
三、工作原理
Service的工作原理主要涉及kube-proxy和相关的网络插件。当创建Service时,kube-proxy会基于监听的机制发现Service的变动,并将最新的Service信息转换成对应的访问规则。这些规则可以是iptables规则或ipvs规则,具体取决于kube-proxy的工作模式(userspace、iptables、ipvs)。通过这些规则,kube-proxy能够实现请求的转发、负载均衡和故障隔离等功能。
(1)创建
- 定义Service资源:在Kubernetes中,通过定义Service资源来创建一个Service。这通常涉及到编写一个YAML或JSON文件,其中指定了Service的名称、类型、标签选择器(Selector)以及端口等信息。
- Cluster IP:Service在集群内部可以通过Cluster IP访问,这是一个虚拟的IP地址,由Kubernetes系统自动分配。这个IP地址在集群内部是唯一的,并且在Service的整个生命周期内保持不变。
- 标签选择器(Selector):通过标签选择器,Service可以与一组具有相同标签的Pod关联起来。这允许Service将请求转发到这些Pod上。
(1)发现
- Endpoints:Kubernetes使用Endpoints对象来记录与Service关联的所有Pod的IP地址和端口信息。当Pod启动或终止时,它们的IP地址和端口可能会发生变化。Endpoints对象会实时更新这些变化,以确保Service能够始终将请求转发到正确的Pod。
- DNS服务:Kubernetes还提供了DNS服务(如kube-dns),为Service创建DNS记录。这样,客户端就可以通过Service的名称(而不是IP地址)来访问Service,进一步简化了服务发现的过程。
(1)路由
- kube-proxy:在Kubernetes集群中,每个节点都部署了一个名为kube-proxy的组件。kube-proxy负责监听Service和Endpoints的变化,并根据这些变化更新节点上的网络规则(如iptables或IPVS规则)。
- 负载均衡:当请求到达Service的Cluster IP时,kube-proxy会根据配置的负载均衡算法(如轮询、最少连接数等)将请求转发到后端的Pod上。这样,Service就实现了请求的负载均衡分发。
- 转发流程:
- 客户端发送请求到Kubernetes集群中的Service IP。
- kube-proxy根据Service的配置规则和iptables或IPVS等内核技术将请求转发到正确的Pod。
- 请求被Pod中的应用程序处理,并将响应发送回kube-proxy。
- kube-proxy将响应返回给客户端。
Endpoint
Endpoint是一个关键组件,它负责将服务的网络地址(IP地址和端口号)与后端容器或节点上的实际服务进行关联,从而实现服务的访问和负载均衡。
主要功能
- 关联Service与后端Pod
- 存储Pod信息:Endpoint对象存储了一组IP地址和端口号的列表,这些IP地址和端口号对应着提供相同服务的Pod实例。这样,Endpoint就建立了Service与后端Pod之间的关联。
- 自动创建与更新:当Service被创建时,Kubernetes会自动为其关联一个Endpoint资源对象,并将该Service所选择的Pod的IP地址和端口信息填充到Endpoint中。当后端Pod的数量发生变化、新的Pod加入或旧的Pod删除时,Kubernetes会及时更新Endpoint中的信息。
- 实现负载均衡
- 流量路由:Endpoint是Service实现负载均衡的关键。当有请求发送给Service时,Kubernetes会根据Service的配置和Endpoint中的信息,将请求转发到对应的Pod实例上。通过这种方式,Endpoint帮助Service实现了请求的负载均衡分发。
- 支持多种负载均衡算法:虽然Endpoint本身不直接实现负载均衡算法,但它与kube-proxy等组件配合,可以支持多种负载均衡算法(如轮询、最少连接数等),以满足不同的需求。
- 服务发现
- 提供稳定的访问点:Endpoint为Service提供了一个稳定的虚拟访问点(尽管Endpoint本身并不直接提供虚拟IP,但它是实现Service虚拟IP访问的关键一环)。客户端可以通过Service的名称和端口来访问服务,而无需关心后端Pod的具体IP地址和端口号。
- 动态更新服务信息:由于Endpoint会根据Service和Pod的变化自动更新信息,因此它能够实现服务的动态发现。当新的Pod加入或旧的Pod退出时,客户端无需重新配置即可继续访问服务。
- 支持多端口和复杂服务
- 多端口服务:Endpoint可以支持一个Service对应多个端口的情况。在Endpoint对象中,可以通过定义多个端口号来实现这一点。
- 复杂服务场景:对于需要多个后端Pod实例共同提供服务的复杂场景(如微服务架构中的服务链调用),Endpoint可以通过将多个Pod的IP地址和端口号关联到同一个Service上,来简化服务的访问和管理。
- 安全性与隔离性
- 内部使用:需要注意的是,Endpoint主要在Kubernetes集群内部使用,用于实现集群内部的服务发现和负载均衡。它并不直接对外暴露服务,从而保证了集群内部服务的安全性。
- 隔离性:通过Endpoint的隔离性设计,可以确保不同Service之间的请求不会相互干扰,从而提高了服务的隔离性和稳定性。
kube-proxy
kube-proxy 是一个至关重要的组件,它负责实现服务的网络代理和负载均衡功能。
- 角色
- 负载均衡器:kube-proxy 在每个节点上充当一个分布式的负载均衡器,负责将访问到某个 Service 的请求转发到该 Service 对应的真实后端 Pod 上。
- 网络代理:kube-proxy 通过监听 API Server 中服务和 Endpoints 资源的变化,并根据这些变化来更新节点上的网络规则,以实现请求的转发和负载均衡。
- 原理
- 监听 API Server
kube-proxy 通过与 API Server 通信,实时监听集群中服务和 Endpoints 资源的变化。当这些资源发生变化时,kube-proxy 会接收到相应的通知。 - 更新网络规则
根据服务定义和 Endpoints 的变化,kube-proxy 会更新节点上的网络规则。这些网络规则可能是 iptables 规则或 IPVS 规则,具体取决于 Kubernetes 集群的配置。 - 流量转发
当请求到达节点时,kube-proxy 会根据网络规则将请求转发到后端的 Pod 上。这个过程是自动的,用户无需手动干预。
- 工作模式
kube-proxy 支持多种工作模式,以适应不同的使用场景和需求:
- userspace 模式:不太常用,主要问题在于效率不高,因为请求需要在用户空间和内核空间之间多次传递。
- iptables 模式:Kubernetes 早期版本的默认模式。该模式使用 iptables 规则来实现请求的转发和负载均衡。然而,当服务数量较多时,iptables 规则会变得非常庞大,从而影响性能。
- IPVS 模式:为解决 iptables 模式的性能问题,Kubernetes 引入了 IPVS 模式。IPVS 是一种基于内核的负载均衡器,它支持增量式更新和多种负载均衡算法(如轮询、最小连接数等)。IPVS 模式可以显著提高 Service 的性能和可扩展性。