什么是service

service只是一个抽象概念,在逻辑上将一组pod(功能相同)给抽象出来一个统一入口。可以将他简单理解为做了一个服务的负载均衡。我们知道pod在重新部署之后ip会改变,所以一般会通过service来访问pod。core-dns会给service分配一个内部的虚拟ip(节点上根本查询不到这个ip,ping是不通的,具体是怎么访问到的继续往下看),因此内部服务可以通过这个ip或者是serviceName来访问到pod的服务。

service提供的常用type:

  • ClusterIP,也是默认方式。Service会分配一个集群内部的固定虚拟IP,实现集群内通过该IP来对POD进行访问。这个又有两类,上面说到的最普通的Service,ClusterIP还有一种是Headless Service,这种形式不会分配IP也不会通过kube-proxy做反向代理或者负载均衡,而是通过DNS提供稳定的网络ID来访问,DNS会将headless service的后端直接解析为POD的IP列表,这种主要是共StatefulSet类型使用。
  • NodePort,这种类型的Service是除了使用ClusterIP的功能外还会映射一个宿主机随机端口到service上,这样集群外部可以通过宿主机IP+随机端口来访问。这样得保证每一个node节点的该端口都可用才行,直接使用任意node节点的ip+端口就能直接访问pod。
  • HostPort,他这个和nodeport的区别是,只在某一个node节点打开端口。
  • LoadBalancer:和nodePort类似,不过除了使用ClusterIP和NodePort之外还会向使用的公有云申请一个负载均衡器,从而实现集群外部通过LB来访问服务。这个主要是依托云lb。
  • ExternalName:是Service的一种特例,此模式主要面对运行在集群外部的服务,通过它可以将外部服务映射到k8s集群,具备k8s内服务的一些特性,来为集群内部提供服务
apiVersion: v1
kind: Service
metadata:
  namespace: app
  name: eureka-server
  labels:
    name: eureka-server
spec:
  type: NodePort ##这个位置来指定service的类型
  selector:
    app: eureka-server
  ports:
    - port: 80
      targetPort: 9101
      nodePort: 31101

什么是endpoints

endpoints也是k8s的一个资源,我们在创建service的时候如果我们设置了selector选中了需要关联的pod,那么就会创建一个与service同名的endpoints。他是用来记录service对应pod的访问地址。
在这里插入图片描述

可以看上图,我的baoming服务启动了两个pod,因此endpoints中会有两个ip存储。

enpoints的维护是通过endpoint_controller

endpoint controller

endpoint controller是k8s集群控制器的其中一个组件,下面简单列举,详细的我建议大家去看下源码,go写的,我看的似懂非懂能理解一部分逻辑,简单了解干啥活就行。其功能如下:

  • 负责生成和维护所有endpoint对象的控制器
  • 负责监听service和对应pod的变化
  • 监听到service被删除,则删除和该service同名的endpoint对象
  • 监听到新的service被创建,则根据新建service信息获取相关pod列表,然后创建对应endpoint对象
  • 监听到service被更新,则根据更新后的service信息获取相关pod列表,然后更新对应endpoint对象
  • 监听到pod事件,则更新对应的service的endpoint对象,将podIp记录到endpoint中

重点来了 kube-proxy

英语过关的直接点这学霸专用通道,不行的装个有道翻译再进。

kube-proxy是对service的实现,也就是service只是用来抽象定义,真正具体化干活的是kube-proxy.它运行在每一个node节点上,负责该节点的网络代理。它是一个pod。

下面是kube-proxy三种模式:

  • Userspace模式,这种模式时最早的,不过已经不推荐使用了,效率低,因为需要在内核态和用户态多次转换,就不多做说明了。大家看图只需要看client的那条线。当一个进程在执行用户自己的代码时处于用户运行态(用户态),此时特权级最低,为3级,是普通的用户进程运行的特权级,大部分用户直接面对的程序都是运行在用户态。Ring3状态不能访问Ring0的地址空间,包括代码和数据;当一个进程因为系统调用陷入内核代码中执行时处于内核运行态(内核态),此时特权级最高,为0级。执行的内核代码会使用当前进程的内核栈,每个进程都有自己的内核栈.

  • Iptables模式(默认)。当service或者endpoints、pod发生变化时,kube-proxy会创建对应的iptables规则。那开始的时候说的service的ip就出来了,它是iptables规则中的ip,并不对应到网络设备。这种模式下kube-proxy只用来维护iptables规则,iptables作为用户态的入口,直接调用内核态Netilter,中间省去了kube-proxy的内部转换。

在这种模式下,kube-proxy监视Kubernetes控制平面以添加和删除Service和Endpoint对象。对于每个服务,它都安装iptables规则,该规则捕获到服务clusterIP和的port流量,并将该流量重定向到服务的后端集之一。对于每个Endpoint对象,它将安装iptables规则,该规则选择一个后端Pod。

默认情况下,iptables模式下的kube-proxy会随机选择一个后端。

使用iptables处理流量具有较低的系统开销,因为流量由Linux netfilter处理,而无需在用户空间和内核空间之间切换。这种方法也可能更可靠。

如果kube-proxy在iptables模式下运行,并且所选的第一个Pod没有响应,则连接失败。这与用户空间模式不同:在这种情况下,kube-proxy将检测到与第一个Pod的连接已失败,并会自动使用其他后端Pod重试。

您可以使用Pod 准备就绪状况探针 来验证后端Pod 正常运行,以便iptables模式下的kube-proxy仅将经过测试的后端视为正常。这样做意味着您避免将流量通过kube-proxy发送到已知已失败的Pod。
  • IPVS代理模式,没有仔细研究,网上查着看当service有成千上万个的时候速度上会更占优势。而且有更多的lb策略。
在ipvs模式下,kube-proxy监视Kubernetes服务和端点,调用netlink接口以相应地创建IPVS规则,并定期将IPVS规则与Kubernetes服务和端点同步。该控制循环可确保IPVS状态与所需状态匹配。访问服务时,IPVS将流量定向到后端Pod之一。

IPVS代理模式基于类似于iptables模式的netfilter挂钩函数,但是使用哈希表作为基础数据结构,并且在内核空间中工作。这意味着,与iptables模式下的kube-proxy相比,IPVS模式下的kube-proxy可以以更低的延迟来重定向流量,从而在同步代理规则时具有更好的性能。与其他代理模式相比,IPVS模式还支持更高的网络流量吞吐量。

IPVS提供了更多选项来平衡后端Pod的流量。这些是:

rr:轮循
lc:连接最少(打开的连接最少)
dh:目标哈希
sh:源哈希
sed:最短的预期延迟
nq:永不排队
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐