背景介绍
service mesh 和 istio概述
Service Mesh是专用的基础设施层,轻量级高性能网络代理。提供安全的、快速的、可靠地服务间通讯,与实际应用部署一起,但对应用透明。
为了帮助理解, 下图展示了服务网格的典型边车部署方式:
图中应用作为服务的发起方,只需要用最简单的方式将请求发送给本地的服务网格代理,然后网格代理会进行后续的操作,如服务发现,负载均衡,最后将请求转发给目标服务。当有大量服务相互调用时,它们之间的服务调用关系就会形成网格,如下图所示:
Istio——一个用来连接、管理和保护微服务的开放service mesh平台。Istio提供一种简单的方式来建立已部署服务网络,具备负载均衡、服务间认证、监控等功能,而不需要改动任何服务代码。想要为服务增加对Istio的支持,您只需要在环境中部署一个特殊的边车(sidecar),使用Istio控制面板功能配置和管理代理,拦截微服务之间的所有网络通信。
istio结构如下图所示:
kubernetes网络概述
pods间通信
为了解决docker容器间跨主机通信的问题,k8s引入了flannel等overlay网络通信机制。
flannel是CoreOS提供用于解决Docker集群跨主机通讯的覆盖网络工具。它的主要思路是:预先留出一个网段,每个主机使用其中一部分,然后每个容器被分配不同的ip;让所有的容器认为大家在同一个直连的网络,底层通过UDP/VxLAN等进行报文的封装和转发。
flannel底层采用了vxlan机制作为跨网段转发基础。
vxlan作用:
vxlan主要用于在不同网段上构建局域网,通过udp隧道机制,在三层网络上组建一个二层vlan。
vxlan报文结构:
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之间通信使用。
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的代理服务。废话不多说,其原理如下如图所示:
可见,这种mode最大的问题是,service的请求会先从用户空间进入内核iptables,然后再回到用户空间,由kube-proxy完成后端Endpoints的选择和代理工作,这样流量从用户空间进出内核带来性能损耗。
而ServiceMesh正式基于这种机制,并极大增强了kube-proxy的功能,比如流量限制,流量灰度,流量追踪。
另一种mode是iptables,它完全利用内核iptables来实现service的代理和LB。是v1.2及之后版本默认模式,其原理图如下所示:
如果集群中存在上万的Service/Endpoint,那么Node上的iptables rules将会非常庞大,会打折扣。
kube-dns
kube-dns可以解决Service的发现问题,k8s将Service的名称当做域名注册到kube-dns中,通过Service的名称就可以访问其提供的服务。
SkyDNS是用于服务发现的开源框架,构建于etcd之上。作用是为k8s集群中的Pod提供DNS查询接口。kube2sky是k8s实现的一个适配程序,它通过名为kubernetes的Service(通过kubectl get svc可以查看到该Service,由集群自动创建)调用k8s的list和watch API来监听k8s Service资源的变更,从而修改etcd中的SkyDNS记录。
通过示例程序,分析istio网络转发过程
示例程序部署环境
示例程序bookinfo结构:
在本示例中,我们将部署一个简单的应用程序,显示书籍的信息,类似于网上书店的书籍条目。在页面上有书籍的描述、详细信息(ISBN、页数等)和书评。
BookInfo 应用程序包括四个独立的微服务:
productpage:productpage(产品页面)微服务,调用 details 和 reviews 微服务来填充页面。
details:details 微服务包含书籍的详细信息。
reviews:reviews 微服务包含书籍的点评。它也调用 ratings 微服务。
ratings:ratings 微服务包含随书评一起出现的评分信息。
开发环境的k8s集群部署在两个Node上:
master node 和 other node
示例程序包括如下四个服务:
和如下一些POD:
PODS网段为10.244.0.0/16
Ingress流程(流量外部入口)
查看外部端口
$sudo kubectl get svc -n istio-system | grep ingress
通过此命令得知服务监听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.html
iptables详解: http://blog.csdn.net/reyleon/article/details/12976341
在任意node上查看30701端口转发规则
$sudo iptables-save | grep -i 30701
得知流量被转发到KUBE-SVC-JSIH6CCNAROIS6ON服务中
继续跟踪
最终流量被转发到10.244.1.150:80
查看该ip对应的pod
得知流量被转发到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
发现pod中运行了envoy转发服务
root@istio-ingress-84c7ddcb7f-kx7gn:/# netstat -anop | head
80端口也确实被enovy监听
查看envoy路由规则
/productpage路径下的流量被转发到out.productpage.default.svc.cluster.local cluster中对应的k8s service为productpage.default.svc.cluster.local
root@istio-ingress-84c7ddcb7f-kx7gn:/# ping productpage.default.svc.cluster.local
对应的ip为10.109.223.13
该ip对应到productpage service
查看iptable转发流程
最后被转发到10.244.1.176:9080
对应的pod为productpage-v1-6fc75ff57-bbxcq
值得注意的是enovy应该并没有通过iptables(kube-proxy)转发,而是使用out.productpage.default.svc.cluster.local cluster标记目的地址,直接进行转发。
通过tcpdump抓包也可以证实这一点
内部SideCar 和 Route逻辑
查看pod中sidecar启动方式
$sudo kubectl get pod productpage-v1-6fc75ff57-bbxcq --output=yaml
得知该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
得知所有进出流量确被转发至continer:istio-proxy中的envoy进程中
查看enovy转发规则
root@productpage-v1-6fc75ff57-bbxcq:/opt/microservices# curl http://127.0.0.1:15000/routes
将对应的流量转发对应的至cluster中
参考:
https://ieevee.com/tech/2017/01/20/k8s-service.html
http://blog.csdn.net/reyleon/article/details/12976341
https://yaoguais.github.io/article/istio/routing.html
https://www.hi-linux.com/posts/30481.html
https://zhuanlan.zhihu.com/p/29586032
http://developer.huawei.com/ict/cn/site-agile-network/article/site-doc-vxlan/
http://www.cnblogs.com/hbgzy/p/5279269.html
https://blog.csdn.net/liyingke112/article/details/76022267
https://www.cnblogs.com/ilinuxer/p/6188804.html
http://istio.doczh.cn/docs/guides/bookinfo.html