本文主要介绍一下,在k8s集群中west-east/north-south 的流量路径
内部服务间的通信:
Cluster IP
在集群内部访问Service时,主要采用Cluster IP,Kube-proxy负责将发送到Cluster IP的请求转发到后端的Pod上。
Kube-proxy是一个运行在每个节点上的go应用程序,支持三种工作模式:
- userspace 该模式下kube-proxy会为每一个Service创建一个监听端口。发向Cluster IP的请求被Iptables规则重定向到Kube-proxy监听的端口上,Kube-proxy根据LB算法选择一个提供服务的Pod并和其建立链接,以将请求转发到Pod上。 该模式下,Kube-proxy充当了一个四层Load balancer的角色。由于kube-proxy运行在userspace中,在进行转发处理时会增加两次内核和用户空间之间的数据拷贝,效率较另外两种模式低一些;好处是当后端的Pod不可用时,kube-proxy可以重试其他Pod。
- iptables 为了避免增加内核和用户空间的数据拷贝操作,提高转发效率,Kube-proxy提供了iptables模式。在该模式下,Kube-proxy为service后端的每个Pod创建对应的iptables规则,直接将发向Cluster IP的请求重定向到一个Pod IP。 该模式下Kube-proxy不承担四层代理的角色,只负责创建iptables规则。该模式的优点是较userspace模式效率更高,但不能提供灵活的LB策略,当后端Pod不可用时也无法进行重试。
- ipvs 该模式和iptables类似,kube-proxy监控Pod的变化并创建相应的ipvs rules。ipvs也是在kernel模式下通过netfilter实现的,但采用了hash table来存储规则,因此在规则较多的情况下,Ipvs相对iptables转发效率更高。除此以外,ipvs支持更多的LB算法。如果要设置kube-proxy为ipvs模式,必须在操作系统中安装IPVS内核模块。
注意:
通过kube-proxy 访问service的流量都会在源host上做SNAT,DNAT,所以实际流量就变成了Node与pod之间的通信
Istio Sidecar Proxy
在Kubernetes中部署Istio后,Istio通过iptables和Sidecar Proxy接管服务之间的通信,服务间的相互通信不再通过Kube-proxy,而是通过Istio的Sidecar Proxy进行。请求流程是这样的:Client发起的请求被iptables重定向到Sidecar Proxy,Sidecar Proxy根据从控制面获取的服务发现信息和路由规则,选择一个后端的Server Pod创建链接,代理并转发Client的请求
Istio Sidecar Proxy和Kube-proxy的userspace模式的工作机制类似,都是通过在用户空间的一个代理来实现客户端请求的转发和后端多个Pod之间的负载均衡。两者的不同点是:Kube-Proxy工作在四层,而Sidecar Proxy则是一个七层代理,可以针对HTTP,GRPS等应用层的语义进行处理和转发,因此功能更为强大,可以配合控制面实现更为灵活的路由规则和服务管控功能。
如何从外部网络访问
NodePort
NodePort在集群中的主机节点上为Service提供一个代理端口,以允许从主机网络上对Service进行访问。
注意:
通过kube-proxy 访问service的流量都会在源host上做SNAT,DNAT,所以实际流量就变成了Node与pod之间的通信
LoadBalancer
主机节点不稳定,性能瓶颈。为了解决这些问题,kubernetes支持将Service定义为LoadBalancer类型。
备注:LoadBalancer类型需要云服务提供商的支持,Service中的定义只是在Kubernetes配置文件中提出了一个要求,即为该Service创建Load Balancer,至于如何创建则是由Google Cloud或Amazon Cloud等云服务商提供的,创建的Load Balancer的过程不在Kubernetes Cluster的管理范围中。
Ingress
LoadBalancer类型的Service提供的是四层负载均衡器,当只需要向外暴露一个服务的时候,采用这种方式是没有问题的。但当一个应用需要对外提供多个服务时,采用该方式则要求为每一个四层服务(IP+Port)都创建一个外部load balancer。
一般来说,同一个应用的多个服务/资源会放在同一个域名下,在这种情况下,创建多个Load balancer是完全没有必要的,反而带来了额外的开销和管理成本。另外直接将服务暴露给外部用户也会导致了前端和后端的耦合,影响了后端架构的灵活性,如果以后由于业务需求对服务进行调整会直接影响到客户端。
在这种情况下,我们可以通过使用Kubernetes Ingress来统一网络入口。Kubernetes Ingress声明了一个应用层(OSI七层)的负载均衡器,可以根据HTTP请求的内容将来自同一个TCP端口的请求分发到不同的Kubernetes Service,其功能包括:
- 按HTTP请求的URL进行路由 同一个TCP端口进来的流量可以根据URL路由到Cluster中的不同服务,如下图所示:
- 按HTTP请求的Host进行路由 同一个IP进来的流量可以根据HTTP请求的Host路由到Cluster中的不同服务,如下图所示:
ingress 规则定义了对七层网关的要求,包括URL分发规则,基于不同域名的虚拟主机,SSL证书等。Kubernetes使用Ingress Controller 来监控Ingress规则,并通过一个七层网关来实现这些要求,一般可以使用Nginx,HAProxy,Envoy等。
虽然Ingress Controller通过七层网关为后端的多个Service提供了统一的入口,但由于其部署在集群中,因此并不能直接对外提供服务。实际上Ingress需要配合NodePort和LoadBalancer才能提供对外的流量入口,如下图所示:
上图描述了如何采用Ingress配合NodePort和Load Balancer为集群提供外部流量入口,从该拓扑图中可以看到该架构的伸缩性非常好,在NodePort,Ingress,Pod等不同的接入层面都可以对系统进行水平扩展,以应对不同的外部流量要求
下图是具体实现:
流量从外部网络到达Pod的完整路径如下:
- 外部请求先通过四层Load Balancer进入内部网络
- Load Balancer将流量分发到后端多个主机节点上的NodePort (userspace转发)
- 请求从NodePort进入到Ingress Controller (iptabes规则,Ingress Controller本身是一个NodePort类型的Service)
- Ingress Controller根据Ingress rule进行七层分发,根据HTTP的URL和Host将请求分发给不同的Service (userspace转发)
- Service将请求最终导入到后端提供服务的Pod中 (iptabes规则)
从前面的介绍可以看到,K8S Ingress提供了一个基础的七层网关功能的抽象定义,其作用是对外提供一个七层服务的统一入口,并根据URL/HOST将请求路由到集群内部不同的服务上
参考:
https://zhaohuabing.com/post/2019-03-29-how-to-choose-ingress-for-service-mesh/