Kubernetes之Service
本文概述了Kubernetes(K8S)中的Service资源,它是实现服务发现和负载均衡的关键组件。Service为Pod提供稳定的网络访问接口,支持多种类型(如ClusterIP、NodePort、LoadBalancer),并可通过Endpoints实现Pod的发现和流量分发。内置的负载均衡器确保请求被均衡地转发到多个Pod上,提高应用的可用性和扩展性。本文为理解和使用K8S Service
目录
为什么要有Service
在kubernetes中,Pod是有生命周期的,如果Pod重启它的IP很有可能会发生变化。如果我们的服务都是将Pod的IP地址写死,Pod挂掉或者重启,和刚才重启的pod相关联的其他服务将会找不到它所关联的Pod,为了解决这个问题,在kubernetes中定义了service资源对象,Service 定义了一个服务访问的入口,客户端通过这个入口即可访问服务背后的应用集群实例,service是一组Pod的逻辑集合,这一组Pod能够被Service访问到,通常是通过Label Selector实现的
Service概述
service是一个固定接入层,客户端可以通过访问service的ip和端口访问到service关联的后端pod,这个service工作依赖于在kubernetes集群之上部署的一个附件,就是kubernetes的dns服务(不同kubernetes版本的dns默认使用的也是不一样的,1.11之前的版本使用的是kubeDNs,较新的版本使用的是coredns),service的名称解析是依赖于dns附件的,因此在部署完k8s之后需要再部署dns附件,kubernetes要想给客户端提供网络功能,需要依赖第三方的网络插件(flannel,calico等)。每个K8s节点上都有一个组件叫做kube-proxy,kube-proxy这个组件将始终监视着apiserver中有关service资源的变动信息,需要跟master之上的apiserver交互,随时连接到apiserver上获取任何一个与service资源相关的资源变动状态,这种是通过kubernetes中固有的一种请求方法watch(监视)来实现的,一旦有service资源的内容发生变动(如创建,删除),kube-proxy都会将它转化成当前节点之上的能够实现service资源调度,把我们请求调度到后端特定的pod资源之上的规则,这个规则可能是iptables,也可能是ipvs,取决于service的实现方式
Service工作原理
k8s在创建Service时,会根据标签选择器selector(lable selector)来查找Pod,据此创建与Service同名的endpoint对象,当Pod 地址发生变化时,endpoint也会随之发生变化,service接收前端client请求的时候,就会通过endpoint,找到转发到哪个Pod进行访问的地址。(至于转发到哪个节点的Pod,由负载均衡kube-proxy决定)
Kubernetes集群中有三类IP地址
Node Network(节点网络):物理节点或者虚拟节点的网络,如ens33接口上的网路地址
Pod network(pod 网络):创建的Pod具有的IP地址
Cluster Network(集群地址,也称为service network):这个地址是虚拟的地址(virtual ip),没有配置在某个接口上,只是出现在service的规则当中
Service代理
Kubernetes service只是把应用对外提供服务的方式做了抽象,真正的应用跑在Pod中的container里,我们的请求转到kubernetes nodes对应的nodePort上,那么nodePort上的请求是如何进一步转到提供后台服务的Pod的呢? 就是通过kube-proxy实现的:
kube-proxy部署在k8s的每一个Node节点上,是Kubernetes的核心组件,我们创建一个 service 的时候,kube-proxy 会在iptables中追加一些规则,为我们实现路由与负载均衡的功能。在k8s1.8之前,kube-proxy默认使用的是iptables模式,通过各个node节点上的iptables规则来实现service的负载均衡,但是随着service数量的增大,iptables模式由于线性查找匹配、全量更新等特点,其性能会显著下降。从k8s的1.8版本开始,kube-proxy引入了IPVS模式,IPVS模式与iptables同样基于Netfilter,但是采用的hash表,因此当service数量达到一定规模时,hash查表的速度优势就会显现出来,从而提高service的服务性能。
service是一组pod的服务抽象,相当于一组pod的LB,负责将请求分发给对应的pod。service会为这个LB提供一个IP,一般称为cluster IP。kube-proxy的作用主要是负责service的实现,具体来说,就是实现了内部从pod到service和外部的从node port向service的访问。
kube-proxy其实就是管理service的访问入口,包括集群内Pod到Service的访问和集群外访问service。
kube-proxy管理sevice的Endpoints,该service对外暴露一个Virtual IP,也可以称为是Cluster IP, 集群内通过访问这个Cluster IP:Port就能访问到集群内对应的serivce下的Pod。
Kube-proxy的工作模式
Kube-proxy一共有三种工作模式
Userspace方式
Client Pod要访问Server Pod时,它先将请求发给内核空间中的service iptables规则,由它再将请求转给监听在指定套接字上的kube-proxy的端口,kube-proxy处理完请求,并分发请求到指定Server Pod后,再将请求转发给内核空间中的service ip,由service iptables将请求转给各个节点中的Server Pod
这个模式有很大的问题,客户端请求先进入内核空间的,又进去用户空间访问kube-proxy,由kube-proxy封装完成后再进去内核空间的iptables,再根据iptables的规则分发给各节点的用户空间的pod。由于其需要来回在用户空间和内核空间交互通信,因此效率很差。在Kubernetes 1.1版本之前,userspace是默认的代理模型
iptables方式
客户端IP请求时,直接请求本地内核service ip,根据iptables的规则直接将请求转发到到各pod上,因为使用iptable NAT来完成转发,也存在不可忽视的性能损耗。另外,如果集群中存上万的Service/Endpoint,那么Node上的iptables rules将会非常庞大,性能还会再打折
iptables代理模式由Kubernetes 1.1版本引入,自1.2版本开始成为默认类型
ipvs方式
Kubernetes自1.9-alpha版本引入了ipvs代理模式,自1.11版本开始成为默认设置。客户端请求时到达内核空间时,根据ipvs的规则直接分发到各pod上。kube-proxy会监视Kubernetes Service对象和Endpoints,调用netlink接口以相应地创建ipvs规则并定期与Kubernetes Service对象和Endpoints对象同步ipvs规则,以确保ipvs状态与期望一致。访问服务时,流量将被重定向到其中一个后端Pod。与iptables类似,ipvs基于netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着ipvs可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs为负载均衡算法提供了更多选项
如果某个服务后端pod发生变化,标签选择器适应的pod又多一个,适应的信息会立即反映到apiserver上,而kube-proxy一定可以watch到etc中的信息变化,而将它立即转为ipvs或者iptables中的规则,这一切都是动态和实时的,删除一个pod也是同样的原理
备注
以上不论哪种,kube-proxy都通过watch的方式监控着apiserver写入etcd中关于Pod的最新状态信息,它一旦检查到一个Pod资源被删除了或新建了,它将立即将这些变化,反应再iptables 或 ipvs规则中,以便iptables和ipvs在调度Clinet Pod请求到Server Pod时,不会出现Server Pod不存在的情况。自k8s1.11以后,service默认使用ipvs规则,若ipvs没有被激活,则降级使用iptables规则。
Service服务发现
DNS是什么
DNS全称是Domain Name System:域名系统,是整个互联网的电话簿,它能够将可被人理解的域名翻译成可被机器理解IP地址,使得互联网的使用者不再需要直接接触很难阅读和理解的IP地址。域名系统在现在的互联网中非常重要,因为服务器的 IP 地址可能会经常变动,如果没有了 DNS,那么可能 IP 地址一旦发生了更改,当前服务器的客户端就没有办法连接到目标的服务器了,如果我们为 IP 地址提供一个『别名』并在其发生变动时修改别名和 IP 地址的关系,那么我们就可以保证集群对外提供的服务能够相对稳定地被其他客户端访问。DNS 其实就是一个分布式的树状命名系统,它就像一个去中心化的分布式数据库,存储着从域名到 IP 地址的映射。
什么是CoreDNS
CoreDNS 其实就是一个 DNS 服务,而 DNS 作为一种常见的服务发现手段,所以很多开源项目以及工程师都会使用 CoreDNS 为集群提供服务发现的功能,Kubernetes 就在集群中使用 CoreDNS 解决服务发现的问题。 作为一个加入 CNCF(Cloud Native Computing Foundation)的服务,CoreDNS 的实现非常简单。
Service类型
常用的Service类型有三种
ClusterIP类型
这种类型的Service只会得到虚拟IP和端口,只能在Kubernetes集群内部被访问,此模型是为默认类型。
[root@master ~]# cat service_ClusterIP.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
name: tem-nginx
labels:
app: nginx
spec:
containers:
- name: test-nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
selector:
app: nginx
[root@master ~]# kubectl apply -f service_ClusterIP.yaml
deployment.apps/nginx created
service/nginx created
[root@master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 2d2h
nginx ClusterIP 10.1.220.108 <none> 80/TCP 54s
NodePort类型
这种类型的Service除了会得到虚拟IP和端口,Kubernetes还会在所有Node节点上为其分配端口。分配端口的值可以通过spec.ports[*].nodePort指定,或由Kubernetes在配置好的区间里分配(默认为 30000-32767)即可以从 Kubernetes集群通过虚拟IP:端口访问,也可以从集群外部通过Node节点的IP:nodePort 访问。
[root@master ~]# cat service_NodePort.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
name: tem-nginx
labels:
app: nginx
spec:
containers:
- name: test-nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
selector:
app: nginx
[root@master ~]# kubectl apply -f service_NodePort.yaml
deployment.apps/nginx created
service/nginx created
[root@master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 2d2h
nginx NodePort 10.1.15.246 <none> 80:30232/TCP 5s
[root@master ~]# curl -I 192.168.207.131:30232
HTTP/1.1 200 OK
Server: nginx/1.25.3
Date: Fri, 01 Dec 2023 09:32:48 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 24 Oct 2023 13:46:47 GMT
Connection: keep-alive
ETag: "6537cac7-267"
Accept-Ranges: bytes
ExternalName类型
在Kubernetes(K8S),Service资源的ExternalName类型用于将服务映射到集群外部的名称,而不是通过Cluster IP或LoadBalancer IP。这对于将内部服务映射到外部服务的域名非常有用。ExternalName类型的Service不会创建任何Endpoint,而只是将服务的DNS记录配置为指定的外部名称。
适用于k8s集群内部容器访问外部资源,它没有selector,也没有定义任何的端口和Endpoint。以下Service 定义的是将prod名称空间中的my-service服务映射到my.database.example.com
[root@master ~]# cat service_ExternalName.yaml
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
# 创建prod命名空间
[root@master ~]# kubectl create ns prod
namespace/prod created
[root@master ~]# kubectl apply -f service_ExternalName.yaml
service/my-service created
# 查询prod命名空间的service
[root@master ~]# kubectl get svc -n prod
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service ExternalName <none> my.database.example.com <none> 8m8s
访问my-service.prod.svc.cluster.local就相当于访问my.database.example.com地址
LoadBalancer类型
这种类型的Service除了会得到虚拟的IP和端口,Kubernetes 还会在所有Node节点上为其分配端口,然后为其开通负载均衡即可以从Kubernetes集群通过虚拟IP:端口访问,也可以从集群外部通过Node节点的IP:nodePort访问,还可以通过负载均衡的IP访问。
更多推荐
所有评论(0)