一个Istio请求的生命周期 - 从云提供商的SLB到istio-ingressgateway

1,473 阅读7分钟

开源项目推荐

Pepper Metrics是我与同事开发的一个开源工具(github.com/zrbcool/pep…),其通过收集jedis/mybatis/httpservlet/dubbo/motan的运行性能统计,并暴露成prometheus等主流时序数据库兼容数据,通过grafana展示趋势。其插件化的架构也非常方便使用者扩展并集成其他开源组件。
请大家给个star,同时欢迎大家成为开发者提交PR一起完善项目。

简介

随着Service Mesh的概念逐渐被大家所接受,以及K8S作为一个容器管理平台已经成为事实标准,大家对于K8S下的服务治理及流量管理的需求也是日渐强烈,在Google,IBM,Lyft几个大厂的推动下,Istio + Envoy的Service Mesh及流量管理的解决方案补齐了K8S在服务治理,精细化的流量管理,灰度发布等高级特性上的不足,由于其Go加C++语言栈的优势,相比Linked的scala更节省资源,目前已经基本可以确定是Service Mesh领域的事实标准。
本篇文章,我以在阿里云创建的K8S加Istio的集群环境为例,来讲一下一个请求到底在K8S以及Istio、Envoy和微服务的Pod之间网络上是如何流转的。

基础知识说明

本文中忽略了大量网络及Kubernates中的概念及网络实现原理的说明,如果读者在阅读中遇到困难,请参考下面列出的几篇文章

  • Iptables/Netfilter(www.zsythink.net/archives/11…
  • 网桥
  • Veth设备对
  • linux网络命名空间
  • Kubernetes的cni网络插件机制及Flannel实现(阿里云使用type为Alloc的Flannel实现)
  • Kubernetes中的Service(包含LoadBalancer,NodePort,ClusterIP),Pod,Deployment等概念
  • Service Mesh的概念
  • Istio & Envoy

示例说明

新创建集群名为test,下面有两个node节点,主机名分别为work001,和work002,(通过阿里云容器服务Kubernetes版的应用市场)部署istio-ingressgateway并创建type为LoadBalancer的Service,以及部署replics为2的istio-ingressgateway POD实例,完成后可见两个POD实例分别在work001及work002上启动。
创建成功后,会在阿里云控制台上发现,LoadBalancer类型的Service会自动为我们创建好一个阿里云负载均衡(182.92.251.227)实例并将我们的两个node节点的31061端口作为阿里云负载均衡实例的后端服务,如下图所示:

根据上面描述我将信息整理成下图所示,图中红线部分是今天这篇文章要描述的请求网络流转路径

请求从外网到SLB到work001

从work001(上图中的192.168.0.17)这台机器查看iptables规则可以看到:

➜  ~ iptables -L -t nat
Chain PREROUTING (policy ACCEPT 244 packets, 14640 bytes)
 pkts bytes target     prot opt in     out     source               destination         
  32M 2003M KUBE-SERVICES  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* kubernetes service portals */

从eth0到cni0网桥

上面是一段nat表的规则,它的意思是,接受所有到来的数据,并交给KUBE-SERVICES这个规则链处理,下面我们看下KUBE-SERVICES这个规则链:

Chain KUBE-SERVICES (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 KUBE-FW-HBO2MBMQRZFAHKF2  tcp  --  *      *       0.0.0.0/0            182.92.251.227       /* default/istio-ingressgateway:http2 loadbalancer IP */ tcp dpt:80
  562 33720 KUBE-NODEPORTS  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL

可以看到目标是182.92.251.227:80的请求表要被转发给KUBE-FW-HBO2MBMQRZFAHKF2这个规则链,继续看KUBE-FW-HBO2MBMQRZFAHKF2:

Chain KUBE-FW-HBO2MBMQRZFAHKF2 (1 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 KUBE-XLB-HBO2MBMQRZFAHKF2  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/istio-ingressgateway:http2 loadbalancer IP */
    0     0 KUBE-MARK-DROP  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/istio-ingressgateway:http2 loadbalancer IP */
Chain KUBE-MARK-DROP (7 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 MARK       all  --  *      *       0.0.0.0/0            0.0.0.0/0            MARK or 0x8000

上面规则表示请求包转交给规则链KUBE-XLB-HBO2MBMQRZFAHKF2处理,如果不能匹配则丢弃(KUBE-MARK-DROP)

Chain KUBE-XLB-HBO2MBMQRZFAHKF2 (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 KUBE-SVC-HBO2MBMQRZFAHKF2  all  --  *      *       172.20.0.0/16        0.0.0.0/0            /* Redirect pods trying to reach external loadbalancer VIP to clusterIP */
   80  4800 KUBE-SEP-EW7YLHH65NRUVIFQ  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* Balancing rule 0 for default/istio-ingressgateway:http2 */

可以看到该规则链有两条规则,由于我们的请求来自于集群外,通过SLB(182.92.251.227:80)进入集群,所以匹配第二条规则KUBE-SEP-EW7YLHH65NRUVIFQ

Chain KUBE-SEP-EW7YLHH65NRUVIFQ (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 KUBE-MARK-MASQ  all  --  *      *       172.20.1.157         0.0.0.0/0            /* default/istio-ingressgateway:http2 */
   80  4800 DNAT       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/istio-ingressgateway:http2 */ tcp to:172.20.1.157:80

Chain KUBE-MARK-MASQ (102 references)
 pkts bytes target     prot opt in     out     source               destination         
  241 14460 MARK       all  --  *      *       0.0.0.0/0            0.0.0.0/0            MARK or 0x4000

我们的请求包匹配DNAT这条,DNAT将我们的请求目标地址转换为172.20.1.157:80,并交给上层协议栈处理,这样请求会走到routing decision(下面截图来自于维基百科中大牛整理的Iptables/Netfilter的流转图)

work001的路由信息如图所示

# work001
➜  ~ ip route
default via 192.168.0.253 dev eth0 
169.254.0.0/16 dev eth0 scope link metric 1002 
169.254.123.0/24 dev docker0 proto kernel scope link src 169.254.123.1 
172.20.1.128/25 dev cni0 proto kernel scope link src 172.20.1.129 
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.17

由于172.20.1.157:80满足第三条172.20.1.128/25 dev cni0 proto kernel scope link src 172.20.1.129,该请求将直接交给网桥设备cni0(ip是172.20.1.129)处理
这样请求就来到了cni0网桥

从cni0网桥到POD容器

➜  ~ kubectl get pods -o wide
NAME                                      READY     STATUS    RESTARTS   AGE       IP             NODE                                NOMINATED NODE
caf-sample-mesh-tomcat-77ff76b78f-fhgcl   2/2       Running   0          7d        172.20.1.167   cn-beijing.i-2zef83gtte2gddxlpy3q   <none>
istio-ingressgateway-84b4868f6b-9xnts     1/1       Running   0          15d       172.20.1.157   cn-beijing.i-2zef83gtte2gddxlpy3q   <none>
istio-ingressgateway-84b4868f6b-qxz5t     1/1       Running   0          22h       172.20.2.7     cn-beijing.i-2zeg4wnagly6jvlcz5kr   <none>
zookeeper-854f49888f-4zhhk                2/2       Running   0          8d        172.20.1.165   cn-beijing.i-2zef83gtte2gddxlpy3q   <none>

能够看到172.20.1.157这个IP由这个istio-ingressgateway-84b4868f6b-9xnts POD持有,进入这个POD,

➜  ~ kubectl exec -it istio-ingressgateway-84b4868f6b-9xnts bash
root@istio-ingressgateway-84b4868f6b-9xnts:/# ip route
default via 172.20.1.129 dev eth0 
172.20.0.0/16 via 172.20.1.129 dev eth0 
172.20.1.128/25 dev eth0  proto kernel  scope link  src 172.20.1.157 

我们知道K8S内的eth0网卡其实是veth设备对的一边放在了POD的网络namespace内,并且重命名成eth0,另一端在宿主机上插在网桥cni0上,从宿主机上可以查看

➜  ~ brctl show
bridge name	bridge id		STP enabled	interfaces
cni0		8000.0a58ac140181	no		veth0982819b
							veth21f3386e
							veth295aca54
							veth29c64f6d
							veth2fc83624
							veth5413f391
							veth55b5c32f
							veth5d630032
							veth5f4b9957
							veth67b85819
							veth6e5e5662
							veth6efeda57
							veth7413a550
							veth831d20f5
							veth849fecd0
							veth8dbe1d2c
							vetha7ec9173
							vetha83f9559
							vethcc9d3b42
							vethd967d407
							vethec751a37
							vethf69ed27b

这时网桥cni0其实充当了二层交换机设备,所有发往cni0的数据包会路由给相应的veth对的另一边,也就是在容器POD的网络namespace内的eth0网卡,我们的示例中是:POD istio-ingressgateway-84b4868f6b-9xnts(IP为172.20.1.157)

总结

历经重重关卡,我们的请求包终于到达istio-ingressgateway容器内部,下面来总结下:
本讲我们主要介绍了,外网(集群外)请求包如何通过阿里云的四层负载均衡设备SLB转发到我们集群内的POD(其实是istio-ingressgateway)中,也就是我们Service Mesh理念中常说的南北向流量,其实这块的网络转发全部是由阿里云的LoadBalancer(K8s的一种Service类型,由各个云提供商来实现)以及K8s的cni网络插件来实现的,目前还没Istio什么事

下一讲我们就来介绍下这个请求包在istio-ingressgateway中是如何流转,及istio如何控制这个南北向流量的,敬请期待吧