Kubernetes Serivce是一组具有相同label Pod集合的抽象(可以简单的理解为集群内的LB),集群内外的各个服务可以通过Service进行互相通信。
发现机制
Service创建
当用户在kubernetes集群中创建了含有label的Service之后,同时会在集群中创建出一个同名的Endpoints对象,用于存储该Service下的Pod IP,最终Service和Endpoint信息都会存入ETCD。它们的关系如下图所示:
Service发现
每个运行在Node节点的kube-proxy通过apiservice监听ETC中的Services和Endpoints,如果感知到Services和Endpoints的变化之后,会在各自的Node节点设置相关的iptables或IPVS规则,用于之后用户通过Service的ClusterIP去访问该Service下的服务。如下图所示1,2步骤:
Service类型
K8S中Service分为四类,分别是ClusterIP,NodePort,LoadBalancer以及ExternalName。下面一张图描述了他们之间的关系以及服务类型:
其中绿色的代表从外向内的访问模式;蓝色的代表从内向外的访问模式,黄色代表集群内部的访问模式。可以看到,除了ExternalName类型之外,其余三种类型都是逐层封装而来的。下面就分类讲一讲这四种类型:
ClusterIP
这是K8S默认的服务类型,只能在K8S中进行服务通信。在ClientIP中,K8S会在Service创建完毕后提供一个内部IP作为ClientIP属性,K8S内部服务可以通过ClientIP或者ServiceName来访问该服务。
yaml
apiVersion: v1
kind: Service
metadata:
name: service-python
spec:
ports:
- port: 3000
protocol: TCP
targetPort: 443
selector:
run: pod-python
type: ClusterIP
NodePort
我们的场景不全是集群内访问,也需要集群外业务访问。那么ClusterIP就满足不了了。NodePort在ClusterIP基础上为Service在每台机器上绑定一个端口。如下图,我们可以通过http://4.4.4.1:30080
对pod-python访问。
yaml文件
apiVersion: v1
kind: Service
metadata:
name: service-python
spec:
ports:
- port: 3000
protocol: TCP
targetPort: 443
nodePort: 30080
selector:
run: pod-python
type: NodePort
LoadBalancer
在NodePort的基础上,借助Cloud Provider创建一个外部负载均衡器,并将请求转发到NodePort。
yaml文件
apiVersion: v1
kind: Service
metadata:
name: service-python
spec:
ports:
- port: 3000
protocol: TCP
targetPort: 443
nodePort: 30080
selector:
run: pod-python
type: LoadBalancer
使用 kuebctl get svc
:
可以看到external-ip。我们就可以通过该ip来访问了。
ExternalName
把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有 Kubernetes 1.7或更高版本的kube-dns才支持。
yaml文件
kind: Service
apiVersion: v1
metadata:
name: test-service
spec:
ports:
- port: 3000
protocol: TCP
targetPort: 443
type: ExternalName
externalName: test.database.example.com
场景:需要把应用迁移到k8s集群,但是数据库还是保留在AWS RDS,但您还希望能够将MySQL容器用于测试环境。
解决:将Web应用程序配置为使用URL测试服务访问数据库,但是在生产集群上,数据库位于AWS RDS上,并且具有以下URL test.database.example.com
。创建ExternalName service 并且你的Web Pod尝试访问test-service上的数据库之后,Kubernetes DNS服务器将返回值为test.database.example.com
的CNAME记录。问题解决了 。
代理模式
userspace 代理模式
userspace 模式在 k8s v1.2
后就已经被淘汰了,userspace 的作用就是在 proxy 的用户空间监听一个端口,所有的 svc 都转到这个端口,然后 proxy 内部应用层对其进行转发。proxy 会为每一个 svc 随机监听一个端口,并增加一个 iptables 规则。
从客户端到 ClusterIP:Port
的报文都会通过 iptables 规则被重定向到 Proxy Port
,Kube-Proxy 收到报文后,然后分发给对应的 Pod。
缺点:
流量的转发主要是在用户空间下完成的,上面提到了客户端的请求需要借助于 iptables 规则找到对应的 Proxy Port
,因为 iptables 是在内核空间,这里就会请求就会有一次从用户态到内核态再返回到用户态的传递过程, 一定程度降低了服务性能。所以就会认为这种方式会有一定的性能损耗。访问pod时多了一次kube-proxy的转发。
iptables 代理模式
在 kubernetes v1.2
之后 iptables 成为默认代理模式,这种模式下,kube-proxy 会监视 Kubernetes master
对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会安装 iptables 规则,从而捕获到达该 Service 的 clusterIP(虚拟 IP)和端口的请求,进而将请求重定向到 Service 的一组 backend 中的某个pod上面。因为流量转发都是在内核进行的,所以性能更高更加可靠。
缺点:
因为 iptables 的底层实现是链表,对路由规则的增删查改都需要遍历一次链表。iptables 模式最主要的问题是在 service 数量大的时候会产生太多的 iptables 规则,使用非增量式更新会引入一定的时延,大规模情况下有明显的性能问题
ipvs 模式
在 kubernetes v1.2
之后 ipvs 成为kube-proxy的默认代理模式。ipvs 是 LVS 的负载均衡模块,与 iptables 比较像的是,ipvs 的实现虽然也基于 netfilter 的钩子函数,但是它却使用哈希表作为底层的数据结构并且工作在内核态,也就是说 ipvs 在重定向流量和同步代理规则有着更好的性能,几乎允许无限的规模扩张。
ipvs 支持三种负载均衡模式:
- DR模式(Direct Routing);
- NAT 模式(Network Address Translation);
- Tunneling(也称 ipip 模式)。
三种模式中只有 NAT 支持端口映射,所以 ipvs 使用 NAT 模式。linux 内核原生的 ipvs 只支持 DNAT,当在数据包过滤,SNAT 和支持 NodePort 类型的服务这几个场景中ipvs 还是会使用 iptables。文章来源:https://www.toymoban.com/news/detail-603730.html
ipvs 也支持更多的负载均衡算法:文章来源地址https://www.toymoban.com/news/detail-603730.html
- rr:round-robin/轮询;
- lc:least connection/最少连接;
- dh:destination hashing/目标哈希;
- sh:source hashing/源哈希;
- sed:shortest expected delay/预计延迟时间最短;
- nq:never queue/从不排队
到了这里,关于k8s之Service详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!