导读  

2018 CNUTCon全球运维技术大会在上海举行,FreeWheel具有实战经验的一线技术专家受邀出席大会的“解决方案”专场。会上,专家就FreeWheel 微服务演化过程中因模块之间数据流不断调整而产生的挑战,以及为解决该问题进行的技术探索展开分享,围绕 Istio 的原理和技术实现,介绍了 Istio 1.0 在 FreeWheel 微服务化中的应用经验。


Istio在FreeWheel微服务中的实践_第1张图片
FreeWheel的微服务之痛


FreeWheel是一家数字视频解决方案公司,我们核心业务系统可以理解为一个Web ERP。最初核心业务系统完全基于Rails,是一个典型的三层架构,但这个大型单体应用在发展了近十年以后,维护成本越来越大,而且难以扩展。2016年FreeWheel开始迁移到微服务架构,所有的业务功能都用Golang来重写,同时引入了Kubernetes作为服务的部署平台。


但在迁移到微服务之后,FreeWheel面临很多通信方面的矛盾。一方面,平台层的运维开始和应用层的改动互相牵制。有时升级核心网络设备,整个系统都会被波及。另一方面,随着应用层越来越快的迭代,模块之间开始互相牵制,进而影响了客户,同时也降低了应用迭代的效率。归结起来是两方面的问题:一个是通信的控制,另外一个是通信的可见性,可以理解成监控。


最初我们尝试用一个中心化的服务网关Gateway来解决这种矛盾。但随着迁移微服务的模块越来越多,这种服务出现了两个矛盾:一个是服务和服务之间的流量互相影响,第二个是在微服务中服务网关的配置有很多个性化的地方,大锅饭带来了复杂的配置管理,渐渐难以为继。


Istio在FreeWheel微服务中的实践_第2张图片


在这样的背景下,FreeWheel切换到了Service Mesh解决方案,选择了Istio。


FreeWheel选择Istio解决方案的主要原因,是因为FreeWheel的技术栈是Golang和Kubernetes,Istio是目前最合适的选择。


02

Istio的架构和基本原理


首先来看一下Istio的架构和基本原理。Istio有四个核心模块:


1. 反向代理模块: Istio Proxy劫持Pod的所有流量,管理Service Mesh里面的通信。同时,管理Mesh内外边界交互的流量也是反向代理。

2. Pilot模块: 管理所有Service Mesh的动态配置。

3. Citadel模块: 主要是自动维护服务之间通信的证书。

4. Mixer模块: 在Kubernetes部署了两组服务:一组服务提供授权和容量检查的能力,另外一组Policy提供数据采集的能力,通过该服务将数据汇总。


Istio在FreeWheel微服务中的实践_第3张图片


如果从整个系统设计上看,也可以分成四个板块:


1. 反向代理。

2. 网络安全,目前主要兼容Spiffe标准实现。

3. 为C++实现的Proxy接入K8s的动态配置管理。

4. 对于Attribute的有限状态机模型,授权、容量、管理监控等所有的基础。


Istio在FreeWheel微服务中的实践_第4张图片


03

Istio管理下的微服务


Service Mesh部署pod之后,发生了什么?


1. 创建Pod之前,首先由Sidecar Injector来将自定义的initContainer, sidecar container、volume添加到Pod中,这里的volume是Istio自带的通信安全相关的,Istio为Pod维护了动态的认证密钥。

2. 接下来创建容器启动Pod,其中initContainer先为当前Pod生成流量劫持的iptables规则。

3. 然后启动Pod中的sidecar和实际应用容器。

4. 接下来sidecar和Pilot建立连接接受动态配置和更新;和Mixer(policy)建立连接来检查授权、容量等;和Mixer(telemetry)建立连接来上报流量相关的监控元数据。


Pod启动完成并且接入Service Mesh后,这里面还有两个组件:


  • Galley:实现对动态配置的校验。

  • Citadel:检查Pod中mount的secret的有效性,并自动为Pod分发合法的证书。


Istio在FreeWheel微服务中的实践_第5张图片


04

FreeWheel如何充分利用Istio?


FreeWheel已经有一套复杂的自定义认证、授权机制,为了充分利用Istio,我们通过扩展Istio来整合这些系统,涉及两方面:


  • 扩展Sidecar:加入认证支持,提供了对业务系统的认证支持,将用户相关信息以header的形式传入Mesh,后续的授权、监控、限流都可以用Istio原生的机制来完成。

  • 扩展Mixer:选择一部分流量来应用对应的授权逻辑。

Istio在FreeWheel微服务中的实践_第6张图片


FreeWheel自定义认证和授权模块的原理图


Istio在FreeWheel微服务中的实践_第7张图片

扩展Sidecar接入认证


FreeWheel原本就有一个简单的服务网关实现,将认证逻辑抽取到这个模块中,认证完成之后在反向代理中为下游的服务插入一些header,比如认证后得到的用户信息等。


Istio没有开发这种对请求完全定制的接口,所以要想修改请求和响应的内容,就必须要定义反向代理。我们没有替换Envoy,改为在它下面接一个反向代理来实现,这个反向代理同时还接入了配置管理服务,可以利用原生的k8s配置管理接口来实现动态配置管理。不过需要注意的是,Sidecar里面的容器的流量默认是不被Iptables劫持的,如果需要纳入流量劫持,需要显式指定Aannotation来包含端口或者CIDR。


接入Sidecar其实就是修改Istio-system/istio-sidecar-injector的ConfigMap,并加入自定义的反向代理。


Istio在FreeWheel微服务中的实践_第8张图片


这个反向代理很简单,写死了token和用户信息的映射关系,如果上游传入了token,就将用户信息塞到header中传给下游,然后流量被转发给应用服务,在反向代理和应用服务之间还有一个检查授权、容量的过程,这是通过定制Mixer来实现的。


通过在Sidecar中增加FreeWheel自定义认证支持,下游可以充分利用Istio提供的授权、限流、监控接口。不过在Sidecar Injector用了一段时间以后,发现里面有两个问题:


  • Sidecar没有K8s自动注入的secret,也无法通过容器内环境变量自动建立Master连接,需要管理额外的Kubernetes。

  • Sidecar内的服务流量默认是不被劫持的,如果需要劫持需要添加额外的Annotation。


Istio在FreeWheel微服务中的实践_第9张图片

扩展Mixer接入授权


我们为实现高度定制的请求和响应内容时,用Sidecar实现扩展,但服务在授权、自定义限流的时候,很多类似的功能并不需要修改请求响应,只是在请求到达下游服务之前算一下,结果拒绝,或者通过。这时候就可以通过Mixer来扩展。


Mixer是一个高度可定制的状态机模型,Envoy提供两个接口:Report 和Check。这两个接口会在连接和请求的不同生命后期被多次调用,我们经常用到的Quota,Metrics,Tracing 之类的功能都是通过它来实现的。


下图为Mixer的基本原理,Template是对Proxy上报的Attribute的特定处理机制的框架,支持四类:


  • Preprocess: 汇总流量相关元数据和环境(k8s)相关的元数据。

  • Report: 上报数据。

  • Check: 决策是否允许当前访问。

  • Quota: 决策容量是否足够。


Istio在FreeWheel微服务中的实践_第10张图片


Mixer的基本原理


FreeWheel是如何扩展Mixer的呢?


首先,增加了一个adapter来实现授权的模板。其实,FreeWheel已有一个默认的RBAC模板。但新增一个授权能力的时候,我们希望做到绝对可控。比如,通过一个动态配置,在只有某一部分user或访问某一类URL的时候打开这个授权,其他的时候则把它关掉。


Mixer提供了一种非常灵活的模型,让Handler可以在流量中动态地选择一部分来引入额外的机制(如权限控制、限流等),在应用运维中这是很重要的能力,只要是不修改请求、响应的功能都可以采用扩展Mixer来实现。


这里,mymock会完全拒绝所有被匹配到的流量:


Istio在FreeWheel微服务中的实践_第11张图片


mymock Handler的基本原理


在扩展Mixer的过程中有三个关键要素:


1. Handler的配置。这是一个默认行为,让checknothing匹配到user1的时候返回到400,然后全部拒绝。

2. 系统进程instance。这是每个模块固定的东西,定义了这种输入以后,里面可以有一个黑盒,是面向对象的概念。

3. RUL。当用户身份是user1的时候,instance会被handler来处理。如果要扩展,第一步需要定义handler的数据结构;第二步,实现time的接口,这里有两个接口;第三把定义的handler的文件里面相关go的接口通过代码生成。最后,注册到mixer里面,重新编译打包mixer以后就可以直接用了。


Istio在FreeWheel微服务中的实践_第12张图片 Istio在FreeWheel微服务中的实践_第13张图片 Istio在FreeWheel微服务中的实践_第14张图片 Istio在FreeWheel微服务中的实践_第15张图片


需要注意的是,mixer接入的是授权的policy模块,可能会影响服务网格(Service Mesh)不工作。因此,重新部署这些模块的时候,如果不能忍受短时间的宕机,则可能需要做一些灰度发布的机制。


对于Istio配置管理,我们也发现一些性能瓶颈和局限性。


  • Kubernetes和对etcd配置管理存在性能瓶颈。单个Resource控制在K级别,能够比较正常的工作,一旦到达万级别可能会出现不工作,比如好几万的单个Resource。

  • Istio配置本身的局限性。首先是防抖动处理,集群里面部署变化再快,都不会阻塞Istio。其次Istio其他的配置没有防抖动处理,一旦用程序插入的时候一定要做限流。最后Istio用了CRD无法做到的兼容性设计,所以升级CRD也没有办法做到平滑的升级。



FreeWheel未来将怎么做?


  • 首先是Service Contract,封装Istio以及平台层其他配置的复杂度,抽象出一个安全,高效的应用运维体系,为未来技术上的改进留出空间。

  • 然后是Chaos Testing,解决复杂的微服务系统的持续运营和风险控制问题。这个也是目前比较火的一个主题。



现场问答


1. 对于Service Mesh外部流量进入,在Kubernetes有Ingress,在微服务Service Mesh的应用里面也有一个Gateway的情况下,它里面是把Gateway转到Service Mesh,这三个怎么联动让外部的流量能够顺畅地进去?


:很简单。Service Mesh可以理解成是一个封闭的盒子,你所有的外部的Ingress以前该怎么运作还怎么运作,我们现在就讨论在Service Mesh里面是怎么运作的。如果把它看作一个黑盒,现在你有一个Ingress,可以把它转到Istio里面的Ingress Gateway,过去流量会直接转发到各个Pod,是直接打到Pod,由Pod上Sidecar,再转给应用。

 

2. 您刚才说的Sidecar里的应该是直接把Pod里面的流量屏蔽掉,只能通过Service Mesh里面的Gateway流量才能够进到Pod里面?


:不会。你说的这个流量转发其实并没有任何的限制,Service Mesh里面每一个反向代理都可以承担Ingress Gateway的功能,仅仅是路由配置的问题。如果把这个弄好了,在任何一个Service Mesh上都可以配置这个功能。这个反向代理是一模一样的。


3. 我用Istio的Gateway,Kubernetes里面的Ingress关掉,能够跟外部之间联系。


:这个才是Istio比较推荐的模式,你管理这个集群外部进来流量的时候直接走Ingress Gateway,可以做到很好的限制。


4. 你们在落地的过程中有没有遇到一些坑?


:有。我们一开始试图把业务的权限数据直接转成原生的数据结构,因为我们想用它来做权限控制。但是后来发现它掌控不了那么大的数据量。第二个坑是Istio对它的一些Resource是没有防抖动处理的,可能会导致这个Service Mesh行为很不稳定,在客户端一定要做限流。


另外从长远看,Istio的这些模块目前都还是单点,可能导致Service Mesh不可用。我们以前也有遇到过跑了三个月莫名其妙出问题了,维护这种监控系统要频繁地做演习。我们公司现在一个月做一次演习,会定时在一个主生产环境里把问题拿出来,然后检查各个环节有没有正常工作,这也是我们未来会发展的一个事情。


阅读更多文章请点击;

https://mp.weixin.qq.com/s/ES4IiI13_8FNU3DsR_qZUA