导读
2018 CNUTCon全球运维技术大会在上海举行,FreeWheel具有实战经验的一线技术专家受邀出席大会的“解决方案”专场。会上,专家就FreeWheel 微服务演化过程中因模块之间数据流不断调整而产生的挑战,以及为解决该问题进行的技术探索展开分享,围绕 Istio 的原理和技术实现,介绍了 Istio 1.0 在 FreeWheel 微服务化中的应用经验。
FreeWheel是一家数字视频解决方案公司,我们核心业务系统可以理解为一个Web ERP。最初核心业务系统完全基于Rails,是一个典型的三层架构,但这个大型单体应用在发展了近十年以后,维护成本越来越大,而且难以扩展。2016年FreeWheel开始迁移到微服务架构,所有的业务功能都用Golang来重写,同时引入了Kubernetes作为服务的部署平台。
但在迁移到微服务之后,FreeWheel面临很多通信方面的矛盾。一方面,平台层的运维开始和应用层的改动互相牵制。有时升级核心网络设备,整个系统都会被波及。另一方面,随着应用层越来越快的迭代,模块之间开始互相牵制,进而影响了客户,同时也降低了应用迭代的效率。归结起来是两方面的问题:一个是通信的控制,另外一个是通信的可见性,可以理解成监控。
最初我们尝试用一个中心化的服务网关Gateway来解决这种矛盾。但随着迁移微服务的模块越来越多,这种服务出现了两个矛盾:一个是服务和服务之间的流量互相影响,第二个是在微服务中服务网关的配置有很多个性化的地方,大锅饭带来了复杂的配置管理,渐渐难以为继。
在这样的背景下,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提供数据采集的能力,通过该服务将数据汇总。
如果从整个系统设计上看,也可以分成四个板块:
1. 反向代理。
2. 网络安全,目前主要兼容Spiffe标准实现。
3. 为C++实现的Proxy接入K8s的动态配置管理。
4. 对于Attribute的有限状态机模型,授权、容量、管理监控等所有的基础。
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分发合法的证书。
04
FreeWheel如何充分利用Istio?
FreeWheel已经有一套复杂的自定义认证、授权机制,为了充分利用Istio,我们通过扩展Istio来整合这些系统,涉及两方面:
扩展Sidecar:加入认证支持,提供了对业务系统的认证支持,将用户相关信息以header的形式传入Mesh,后续的授权、监控、限流都可以用Istio原生的机制来完成。
扩展Mixer:选择一部分流量来应用对应的授权逻辑。
FreeWheel自定义认证和授权模块的原理图
扩展Sidecar接入认证
FreeWheel原本就有一个简单的服务网关实现,将认证逻辑抽取到这个模块中,认证完成之后在反向代理中为下游的服务插入一些header,比如认证后得到的用户信息等。
Istio没有开发这种对请求完全定制的接口,所以要想修改请求和响应的内容,就必须要定义反向代理。我们没有替换Envoy,改为在它下面接一个反向代理来实现,这个反向代理同时还接入了配置管理服务,可以利用原生的k8s配置管理接口来实现动态配置管理。不过需要注意的是,Sidecar里面的容器的流量默认是不被Iptables劫持的,如果需要纳入流量劫持,需要显式指定Aannotation来包含端口或者CIDR。
接入Sidecar其实就是修改Istio-system/istio-sidecar-injector的ConfigMap,并加入自定义的反向代理。
这个反向代理很简单,写死了token和用户信息的映射关系,如果上游传入了token,就将用户信息塞到header中传给下游,然后流量被转发给应用服务,在反向代理和应用服务之间还有一个检查授权、容量的过程,这是通过定制Mixer来实现的。
通过在Sidecar中增加FreeWheel自定义认证支持,下游可以充分利用Istio提供的授权、限流、监控接口。不过在Sidecar Injector用了一段时间以后,发现里面有两个问题:
Sidecar没有K8s自动注入的secret,也无法通过容器内环境变量自动建立Master连接,需要管理额外的Kubernetes。
Sidecar内的服务流量默认是不被劫持的,如果需要劫持需要添加额外的Annotation。
扩展Mixer接入授权
我们为实现高度定制的请求和响应内容时,用Sidecar实现扩展,但服务在授权、自定义限流的时候,很多类似的功能并不需要修改请求响应,只是在请求到达下游服务之前算一下,结果拒绝,或者通过。这时候就可以通过Mixer来扩展。
Mixer是一个高度可定制的状态机模型,Envoy提供两个接口:Report 和Check。这两个接口会在连接和请求的不同生命后期被多次调用,我们经常用到的Quota,Metrics,Tracing 之类的功能都是通过它来实现的。
下图为Mixer的基本原理,Template是对Proxy上报的Attribute的特定处理机制的框架,支持四类:
Preprocess: 汇总流量相关元数据和环境(k8s)相关的元数据。
Report: 上报数据。
Check: 决策是否允许当前访问。
Quota: 决策容量是否足够。
Mixer的基本原理
FreeWheel是如何扩展Mixer的呢?
首先,增加了一个adapter来实现授权的模板。其实,FreeWheel已有一个默认的RBAC模板。但新增一个授权能力的时候,我们希望做到绝对可控。比如,通过一个动态配置,在只有某一部分user或访问某一类URL的时候打开这个授权,其他的时候则把它关掉。
Mixer提供了一种非常灵活的模型,让Handler可以在流量中动态地选择一部分来引入额外的机制(如权限控制、限流等),在应用运维中这是很重要的能力,只要是不修改请求、响应的功能都可以采用扩展Mixer来实现。
这里,mymock会完全拒绝所有被匹配到的流量:
mymock Handler的基本原理
在扩展Mixer的过程中有三个关键要素:
1. Handler的配置。这是一个默认行为,让checknothing匹配到user1的时候返回到400,然后全部拒绝。
2. 系统进程instance。这是每个模块固定的东西,定义了这种输入以后,里面可以有一个黑盒,是面向对象的概念。
3. RUL。当用户身份是user1的时候,instance会被handler来处理。如果要扩展,第一步需要定义handler的数据结构;第二步,实现time的接口,这里有两个接口;第三把定义的handler的文件里面相关go的接口通过代码生成。最后,注册到mixer里面,重新编译打包mixer以后就可以直接用了。
需要注意的是,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