Kubernetes里的Service究竟是如何工作

        Service是Kubernetes接入层的一种抽象资源,它为我们提供了一种固定的、统一的访问接口地址和负载均衡能力,这时可能会想到,当时使用docker-compose的时候,不存在Service概念,不也运行起来了吗?是的,在Kubernetes集群内部Pod ip也是互通的,但是Pod的ip会经常因为扩容、重建而导致客户端访问错误,pod访问无法提供负载均衡的能力,而Service通过选择一组Pod的label就直接可以访问到Pod,而且可以使用万年不变的域名,所以就选择Service了。

Service是怎么产生的,在集群内部是如何存在的呢

        在kubernetes当中所谓的Service是kube-proxy生成iptables或ipvs规则,它会产生一组虚拟地址,在集群环境下有效。Service不能直接到达Pod内部,中间会间隔EndPoints,这是一组ip和port的组合。默认类型是ClusterIP它仅能接收集群中pod客户端程序的访问请求。这也是最常用的一种类型,另外还有NodePort、LoadBalancer、ExternalName。

iptables或ipvs,到底是iptables还是ipvs呢?

一个Service对象就是工作节点上的一些iptables或ipvs规则,用于将到达Service对象IP地址的流量调度转发至相应的Endpoints对象指向的IP地址和端口之上。

这句话我们经常看到,如何理解呢?

        Kubernetes1.1之前是基于userspace实现,这种模型之下,每次请求流量要先到达内核空间,经有套接字转发到kube-proxy,然后再由它送回到内核空间,之后调度到后端pod之上,可以看出请求在用户空间和内核空间来回转发,效率必然不高。但是当pod无响应时,能够自动重定向到其它pod。

        在Kubernetes1.1版本开始引入iptables规则,Kubernetes1.2开始成为默认类型。即在创建Service资源时,集群上每个节点的kube-proxy都会收到通知,并且创建iptables规则,用于转发到此Service ClusterIP的流量。工作TCP/IP的传输层,高效稳定。

但是这种方式有如下缺点:

  • 1、iptables代理模型挑中的pod无响应时,不能自动重定向到集群内部其它pod资源对象之上。
  • 2、kube-proxy通过iptables处理Service和pod的交互,每产生一个计算节点或者产生大量的pod就需要产生相应大量的iptables规则,不难想象,这些iptables规则每次需要刷新匹配保证正确性,就需要占用大量的CPU资源,所以基于iptables的Service实现就成了制约Kubernetes承载更多pod的瓶颈。

        在Kubernetes1.11开始默认使用ipvs模型,在这种模型下kube-proxy会跟踪APIServer上Service和endpoints对象变化,调用netlink创建ipvs规则,请求调度流量功能由ipvs实现,运行于netfilter之上的钩子函数,具有流量转发速度快,规则性能同步好的特点,支持众多调度算法,剩下仍然由iptables完成。说到这里我们就大概明白了iptables和ipvs的作用和关系了。

Kubernetes的服务发现是通过dns实现,那么为什么会出现四种类型的服务暴露方式呢?

说到Service不得不介绍kubernetes网络模型和通信方式
        一个完整的Kubernetes集群应该包含三层网络,首先第一层是mater和node节点之间的网络,这个网络需要在部署kubernetes集群之前配置完成;

        第二层网络是pod的网络通过kubelet或者cni插件实现,用于pod之间或者内部的通信,集群中的所有pod均处在同一个网络平面空间内,可以直接通信;

        第三层网络是Service资源的网络,是一个虚拟网络,用于为Kubernetes集群配置IP地址,但此地址并不配置于任何主机或者容器的网络接口之上,而是通过kubeproxy配置为iptables规则,将发往该地址的所有流量调度至后端的pod之上。

通信方式分为以下四种:

  • 同一个pod的内部通信;
  • 各个pod彼此通信;
  • pod和service的通信;
  • 集群外部流向service的通信。

所以Service为了满足这些通信方式就出现了如下类型:
        ClusterIP:为集群内部ip地址暴露服务,仅在集群内可达,外部ip无法访问,默认Service类型;

        NodePort:这种类型建立在clusterIp之上,为节点的IP地址暴NodePort服务,外部节点可以通过NodeIP:NodePort直接访问;

        LoadBalancer:这种类型构建在NodePort之上,它可以关联到集群外部的某个负载均衡设备。Kubernetes没有为私有化集群提供LoadBalancer的支持。如果在私有化集群使用需要自建负载均衡器;

        ExternalName:其通过将Service映射至由externalName字段的内容指定的主机名来暴露服务,此主机名需要被DNS服务解析至CNAME类型的记录。这句话怎么理解呢?

        举个例子,你所有的服务都在集群内部,但是你有个数据库是mongodb,没有实现容器化,更没有部署在Kubernetes内部,当然你可以通过在ConfigMap中添加配置访问这个外部服务,但是当你的环境发生变化,比如从开发环境和生产环境的数据地址不同,这个时候你需要修改和重建ConfigMap。

        这个时候可以使用Kubernetes ExternalName内置服务发现机制运用于集群外部运行的服务,像使用集群内的服务一样使用外部服务!通过这种方式,您可以在开发环境和生产环境中实现相同的功能,如果您最终将服务移入集群内,则不需要更改任何代码和配置。

1
2
3
4
5
6
7
8

kind: Service
apiVersion: v1
metadata:
name: mongo
spec:
type: ExternalName
externalName: mango123456.com

你只需要访问:mongodb://:@mongo:就可以自动访问外部服务。

Service本身有端口、Pod也有端口、容器也有端口,之间有什么关系呢?

        containerPort:一个信息性数据,为集群提供一个可以快速了解相关pod可以访问端口的途径,而且显式指定容器端口,无论你是否指定都不影响其他节点上的客户端pod对其进行访问;

        port:服务提供端口,用于kubernetes集群内部服务访问;

        targetPort:pod目标端口,如果不设置使用默认port端口,port和nodePort的数据通过这个端口进入到Pod内部,Pod里面的containers的端口映射到这个端口,提供服务;

nodePort:Kubernetes集群外部用户访问端口;

总结

        本文主要总结了Service的工作原理和机制。只有明白Service这些概念,才能灵活使用Service,当我们服务出现故障的时候,根据它的原理去分析问题到底出在什么地方,进而快速解决问题。

来源: 微信群分享
原文: https://mp.weixin.qq.com/s/ucwnfpXgHFNBR3CHd5JLMQ
版权: 本文版权归原作者所有

坚持原创技术分享,您的支持将鼓励我继续创作!
0%