背景介绍

service mesh 和 istio概述

Service Mesh是专用的基础设施层,轻量级高性能网络代理。提供安全的、快速的、可靠地服务间通讯,与实际应用部署一起,但对应用透明。

为了帮助理解, 下图展示了服务网格的典型边车部署方式:

istio loki 是干嘛的_微服务

图中应用作为服务的发起方,只需要用最简单的方式将请求发送给本地的服务网格代理,然后网格代理会进行后续的操作,如服务发现,负载均衡,最后将请求转发给目标服务。当有大量服务相互调用时,它们之间的服务调用关系就会形成网格,如下图所示:

istio loki 是干嘛的_运维_02

Istio——一个用来连接、管理和保护微服务的开放service mesh平台。Istio提供一种简单的方式来建立已部署服务网络,具备负载均衡、服务间认证、监控等功能,而不需要改动任何服务代码。想要为服务增加对Istio的支持,您只需要在环境中部署一个特殊的边车(sidecar),使用Istio控制面板功能配置和管理代理,拦截微服务之间的所有网络通信。

istio结构如下图所示:

istio loki 是干嘛的_istio loki 是干嘛的_03

kubernetes网络概述

pods间通信

为了解决docker容器间跨主机通信的问题,k8s引入了flannel等overlay网络通信机制。

flannel是CoreOS提供用于解决Docker集群跨主机通讯的覆盖网络工具。它的主要思路是:预先留出一个网段,每个主机使用其中一部分,然后每个容器被分配不同的ip;让所有的容器认为大家在同一个直连的网络,底层通过UDP/VxLAN等进行报文的封装和转发。

istio loki 是干嘛的_网络_04

flannel底层采用了vxlan机制作为跨网段转发基础。

vxlan作用:

istio loki 是干嘛的_网络_05

vxlan主要用于在不同网段上构建局域网,通过udp隧道机制,在三层网络上组建一个二层vlan。
vxlan报文结构:

istio loki 是干嘛的_运维_06

k8s的service机制 && kube-proxy

Pod的IP是在docker0网段动态分配的,当发生重启,扩容等操作时,IP地址会随之变化。当某个Pod(frontend)需要去访问其依赖的另外一组Pod(backend)时,如果backend的IP发生变化时,如何保证fronted到backend的正常通信变的非常重要。由此,引出了Service的概念。
service对外暴露一个Virtual IP,也成为Cluster IP, 集群内通过访问这个Cluster IP:Port就能访问到集群内对应的serivce下的Pod。
service是通过Selector选择的一组Pods的服务抽象,其实就是一个微服务,提供了服务的LB和反向代理的能力。
service另外一个重要作用是,一个服务后端的Pods可能会随着生存灭亡而发生IP的改变,service的出现,给服务提供了一个固定的IP,而无视后端Endpoint的变化。

在实际生产环境中,对Service的访问可能会有两种来源:Kubernetes集群内部的程序(Pod)和Kubernetes集群外部,为了满足上述的场景,Kubernetes service有以下三种类型:

ClusterIP:提供一个集群内部的虚拟IP以供Pod访问。
NodePort:在每个Node上打开一个端口以供外部访问。
LoadBalancer:通过外部的负载均衡器来访问。

cluster ip

此模式用于集群内部的互相访问,会提供一个集群内部的虚拟IP(与Pod不在同一网段),以供集群内部的pod之间通信使用。

istio loki 是干嘛的_istio loki 是干嘛的_07

nodeport

此模式用于集群外网络访问集群内网络,Kubernetes将会在每个Node上打开一个端口并且每个Node的端口都是一样的,通过:NodePort的方式Kubernetes集群外部的程序可以访问Service。

kube-proxy实现

kube-proxy其实就是管理service的访问入口,包括集群内Pod到Service的访问和集群外访问service。
kube-proxy管理sevice的Endpoints,负责service的实现。
kube-proxy有两种实现方式:userspace和iptables。其中userspace mode是v1.0及之前版本的默认模式,从v1.1版本中开始增加了iptables mode,在v1.2版本中正式替代userspace模式成为默认模式。

userspace mode

userspace是在用户空间,通过kube-proxy来实现service的代理服务。废话不多说,其原理如下如图所示:

istio loki 是干嘛的_后端_08


可见,这种mode最大的问题是,service的请求会先从用户空间进入内核iptables,然后再回到用户空间,由kube-proxy完成后端Endpoints的选择和代理工作,这样流量从用户空间进出内核带来性能损耗。

而ServiceMesh正式基于这种机制,并极大增强了kube-proxy的功能,比如流量限制,流量灰度,流量追踪。另一种mode是iptables,它完全利用内核iptables来实现service的代理和LB。是v1.2及之后版本默认模式,其原理图如下所示:

istio loki 是干嘛的_istio loki 是干嘛的_09


如果集群中存在上万的Service/Endpoint,那么Node上的iptables rules将会非常庞大,会打折扣。

kube-dns

kube-dns可以解决Service的发现问题,k8s将Service的名称当做域名注册到kube-dns中,通过Service的名称就可以访问其提供的服务。

istio loki 是干嘛的_istio loki 是干嘛的_10


SkyDNS是用于服务发现的开源框架,构建于etcd之上。作用是为k8s集群中的Pod提供DNS查询接口。kube2sky是k8s实现的一个适配程序,它通过名为kubernetes的Service(通过kubectl get svc可以查看到该Service,由集群自动创建)调用k8s的list和watch API来监听k8s Service资源的变更,从而修改etcd中的SkyDNS记录。

通过示例程序,分析istio网络转发过程

示例程序部署环境

示例程序bookinfo结构:

istio loki 是干嘛的_istio loki 是干嘛的_11


在本示例中,我们将部署一个简单的应用程序,显示书籍的信息,类似于网上书店的书籍条目。在页面上有书籍的描述、详细信息(ISBN、页数等)和书评。

BookInfo 应用程序包括四个独立的微服务:

productpage:productpage(产品页面)微服务,调用 details 和 reviews 微服务来填充页面。
details:details 微服务包含书籍的详细信息。
reviews:reviews 微服务包含书籍的点评。它也调用 ratings 微服务。
ratings:ratings 微服务包含随书评一起出现的评分信息。

开发环境的k8s集群部署在两个Node上:

istio loki 是干嘛的_微服务_12

master node 和 other node

示例程序包括如下四个服务:

istio loki 是干嘛的_微服务_13

和如下一些POD:

istio loki 是干嘛的_运维_14

PODS网段为10.244.0.0/16

Ingress流程(流量外部入口)

查看外部端口

$sudo kubectl get svc -n istio-system | grep ingress

istio loki 是干嘛的_istio loki 是干嘛的_15


通过此命令得知服务监听30701端口,通过kubernetes的LoadBalance方式部署,因为没有外部负载均衡,无法获取外部ip,所以只能通过NodePort的方式转发(在任意节点上,发送

到这个端口的流量都被转发到istio-ingress服务中)

那么我们就可以通过此地址访问服务 http://(your-node-ip):30701/productpage

然后我们逐步分析,请求数据包是怎么传递,响应数据包是怎么返回给浏览器的。
首先思路在iptables,因为k8s支持2种proxy模式,userspace和iptables。 从v1.2版本开始,默认采用iptables proxy。
 
kube-proxy: https://ieevee.com/tech/2017/01/20/k8s-service.htmliptables详解: 

在任意node上查看30701端口转发规则

$sudo iptables-save | grep -i 30701

istio loki 是干嘛的_后端_16


得知流量被转发到KUBE-SVC-JSIH6CCNAROIS6ON服务中

继续跟踪

istio loki 是干嘛的_运维_17


最终流量被转发到10.244.1.150:80查看该ip对应的pod

istio loki 是干嘛的_后端_18

得知流量被转发到istio-ingress-84c7ddcb7f-kx7gn pod中

查看istio-ingress服务内部转发逻辑

$sudo kubectl exec istio-ingress-84c7ddcb7f-kx7gn -n istio-system -i -t -- /bin/bash
进入此pod

root@istio-ingress-84c7ddcb7f-kx7gn:/# ps -efw

istio loki 是干嘛的_istio loki 是干嘛的_19


发现pod中运行了envoy转发服务root@istio-ingress-84c7ddcb7f-kx7gn:/# netstat -anop | head

istio loki 是干嘛的_网络_20


80端口也确实被enovy监听查看envoy路由规则

istio loki 是干嘛的_后端_21


/productpage路径下的流量被转发到out.productpage.default.svc.cluster.local cluster中对应的k8s service为productpage.default.svc.cluster.localroot@istio-ingress-84c7ddcb7f-kx7gn:/# ping productpage.default.svc.cluster.local

istio loki 是干嘛的_后端_22


对应的ip为10.109.223.13

istio loki 是干嘛的_微服务_23


该ip对应到productpage service查看iptable转发流程

istio loki 是干嘛的_网络_24


最后被转发到10.244.1.176:9080

istio loki 是干嘛的_微服务_25


对应的pod为productpage-v1-6fc75ff57-bbxcq

值得注意的是enovy应该并没有通过iptables(kube-proxy)转发,而是使用out.productpage.default.svc.cluster.local cluster标记目的地址,直接进行转发。

istio loki 是干嘛的_运维_26


通过tcpdump抓包也可以证实这一点

内部SideCar 和 Route逻辑

查看pod中sidecar启动方式

$sudo kubectl get pod productpage-v1-6fc75ff57-bbxcq --output=yaml

istio loki 是干嘛的_istio loki 是干嘛的_27


istio loki 是干嘛的_运维_28

得知该pod中有两个continer:productpage 和 istio-proxy 其中 istio-proxy 作为 proxy 以sidecar的方式启动,自动代理所有网络流量。

进入该pod

$sudo kubectl exec productpage-v1-6fc75ff57-bbxcq -i -t -- /bin/bash

root@productpage-v1-6fc75ff57-bbxcq:/opt/microservices# iptables-save

istio loki 是干嘛的_网络_29


得知所有进出流量确被转发至continer:istio-proxy中的envoy进程中查看enovy转发规则

root@productpage-v1-6fc75ff57-bbxcq:/opt/microservices# curl http://127.0.0.1:15000/routes

istio loki 是干嘛的_网络_30


将对应的流量转发对应的至cluster中