近些年来,以服务网格为代表的云原生技术成为技术开发人员的热门话题。从某种意义来说,云原生运动改变了微服务体系架构中应用程序设计与开发的方方面面。服务网格作为云原生中最典型的技术代表,它解决了以前微服务体系中搭建技术门槛高、代码侵入性强等许多棘手问题。如今,应用以 Istio 为代表的服务网格框架可让开发人员轻松打造一个微服务监控和管理系统。但是由于服务网格还面临着许多待解决的技术问题,将服务从原来的微服务体系迁移到服务网格中作为生产环境需要付出巨大的成本,且存在风险。基于以上情况,爱奇艺号技术团队在服务网格方面不断探索,目前有部分业务运行在服务网格中,本文主要分享爱奇艺在探索中的实践经验。
背景
应用在很久以前都是单体结构的,但随业务发展越来越多,会导致工程出现臃肿复杂的问题,进而促使了微服务设计思想的推出。微服务设计思想的核心是将应用进行拆分,按模块划分成一个个微服务,每一个微服务单独开发和部署。当应用功能简单时,划分的服务较少,此举确实提高了开发速率和维护效率。但当功能越做越多,服务划分也随之越来越多时,对大批微服务进行管理成为令开发人员头疼的问题。
当链路追踪、熔断、监控、服务发现等问题都需要解决时,服务网格出现了,如果你是第一次接触服务网格,难免会疑惑服务和网格两个词汇是如何关联的,那服务网格的含义到底是什么呢?这套设计能带给开发人员什么样的惊喜?下文将带大家寻找答案。
"A service mesh is adedicated infrastructure layer for handling service-to-service communication.It's responsible for the reliable delivery of requests through the complextopology of services that comprise a modern, cloud native application."
这是服务网格的提出者 William Morgan 给出的定义——服务网格是管理和监控各个微服务之间流量的'基础设施层',用浅显的方式来说,以前的微服务体系一般是将各个服务单独部署在自己的虚拟机内,向服务发现中心上报自己的位置和获取其他服务的位置,再通过 HTTP 或者 RPC 的方式调用其他服务。这一方式使得业务代码插入些无关业务的代码逻辑,引入各种混乱的 jar 包。而服务网格推翻了这些,当应用部署在'基础设施层'时,它对应用做了一个对其透明的轻量级网络代理,即服务发出的请求和接收的请求都被代理拦截,并且服务对这个代理没有感知。
所有服务间的通信都必须通过其代理完成,这样相当于在'基础设施层'中加入了代理层,服务代理组成一个大网,它们将服务编织在一起,就像一个个网格一样。这样做有什么好处呢?即代理层代替服务进行网络通信,那么服务发现,负载均衡、路由等微服务需要关注的点就可以全部迁移到'基础设施层'来做文章了,服务本身只关注它的业务代码就可以了。这样的设计模式是不是十分巧妙?
应用实践
基于对服务网格的兴趣,我们考虑到爱奇艺号有不少业务场景如果部署在服务网格中,其服务解决方案会十分的简单,于是我们搭建了测试和线上两套服务网格集群,环境之间互相隔离;并且搭建了相应的监控体系及 Dashboard 部署系统;目前爱奇艺号部分后台服务已经部署在集群中进行小规模运行。
在实践过程中,我们对业内关注度比较高的服务网格方案进行了调研,最终我们选择在 Kubernetes 集群上搭建服务网格 Istio。Kubernetes 是近几年十分火热的容器编排应用,它的底层设计与服务网格设计思想匹配度高,而 Istio 是Google、IBM 和 Lyft 三家公司协同开发的一款完全遵从服务网格设计思想的框架。作为明星公司的开源框架,Istio 推出后迭代十分之快,如今已逐渐趋于成熟。在推出的两年多里,Istio 吸引了大量的开发者,关于它的技术资料也较为丰富。基于以上因素考虑,我们搭建了这样的一套测试+线上集群的私有云环境,并将部分业务服务迁移到上面运行。下面主要介绍我们在实践中用到的服务网格特性和经验,主要包括服务的流量控制,自研的 Dashboard 和网格的监控等。
流量管理
上面的图展示了网格内流量管理中几个关键概念的关系。
Gateway:在网格边缘接受外部访问,将外部流量导入网格内部。
VirtualService:Istio 流量治理的核心,大部分流量控制规则由其配置,如重试、金丝雀,路由等。
DestinationRule:定义了服务的版本子集,Istio 支持服务多个版本同时运行,VirtualService 通过路由匹配将流量转发给相应的服务版本
Service:Kubernetes 提供的资源组件,最终由它将请求均衡转发到应用的 Pod 中。
Pod:Kubernetes 中能够创建和部署的最小单元,应用的容器运行在其里面。
在以前的微服务体系中,一般网关使用 Nginx 等负载均衡技术进行路由管理,将流量转发到各个服务中,如果需要做一些其他的流量控制功能,如重试、灰度等,单靠 Nginx 是无法实现的,这时候一般会由自己的中间团队在此基础上进行扩展,来实现这一部分功能。但对于一些小公司而言,这些工作并没有很多人力来做,使用 Istio 只需通过简单的配置上面几个组件,就可以轻轻松松实现这些功能,这对小规模公司来说是十分友好的。
目前爱奇艺某些服务的流量控制是基于Mashape/Kong 开发的 API 网关,并且已经在 API 网关做了安全、鉴权、限流、路由策略等一系列流量控制功能。在本次实践中,为了保留现有 API 网关的鉴权、限流等功能,我们通过 Consul Agent 方式,将服务网格入口机注册到 API 网关,同时通过服务网格的流量管理特性,增加故障注入、重试、熔断、多版本灰度等功能。
那服务网格是如何实现流量管理的呢?用一个词概括,就是"代理"。
服务网格的核心是代理,Kubernetes 在将应用的容器运行在 Pod 中时,Istio 会以 sidecar 的方式在其 Pod 注入一个伴生代理服务——Envoy,在Kubernetes 中,由于运行在同一个 Pod 内的容器共享网络命名空间及 IP 地址,所以 Envoy 可以全权代理应用服务对外的网络通信,并且使业务服务对此毫无感知。Envoy 作为一款高性能的网络代理,已经在生产环境中进行了长期验证,Istio 的流量管理功能默认是基于 Envoy 实现的。
在上面的几个组件中,VirtualServce 是整个流量控制链路的核心,每个 Pod 的 Envoy 会监听并拉取 VirtualService 的流控配置,并在代理流量的时候根据配置进行转发。在实践中,我们控制网格内的流量,也主要通过配置 VirtualService 的方式来实现。下面列举一些流量控制功能。
流量路由,流量路由是网格提供的最基本的转发功能。爱奇艺号内部业务微服务均绑定到同一域名的 Gateway,并在 VirtualService 中配置路径及服务的对应关系,并根据配置进行转发,服务网格会将对应的请求转发到相应的服务中。
服务多版本,服务上线时,考虑到风险问题,直接将老版本直接升级是非常危险的。所以一般做法是新老版本同时在线,新版本切入一部分到老版本中,待确定无误后再逐步扩大新版本流量,最终线上全部更新为最新版本。一般服务发布系统提供的灰度功能相对较单一,其仅支持灰度一个版本,且灰度版本的流量权重是根据容器比例来决定的,流量权重粒度不够细腻。相反,服务网格提供的灰度发布功能十分强大,它不仅支持灰度一个版本,而且可以同时让一个服务的多个版本同时运行,并配置不同的流量权重,这在灰度上线/金丝雀上线/蓝绿部署/AB测试等业务场景中十分有用。
流量镜像,流量镜像这个功能用的比较少,但是在某些特殊的场景下十分有用。之前为某个业务服务新增一个鉴权接口时,由于 QPS(QueriesPer Second)相对较高,为了减轻基础服务调用的压力,我们给接口加了分布式缓存。在鉴权接口上线之前,我们需要对缓存进行预热。在服务网格中,我们只需部署一个服务的临时版本,之后临时版本根据线上的流量不断更新用户权限信息到缓存中,使缓存预先预热,完成后再进行上线。除了缓存预热外,流量镜像还可以帮助我们进行下列操作:通过预生产环境测试来观察新系统对生产环境流量的处理能力;复制请求日志以进行安全分析;复制请求用于数据科学研究。
其他,除了以上列举的特性外,服务网格 Istio 还支持重试、故障注入、服务熔断、HTTP重定向、HTTP 重写等多种流量管理功能,且只需简单的几行 yaml 配置即可,由于我们用到的比较少,这里不一一介绍了,感兴趣的同学可以去 Istio 查询相关资料。
Dashboard
在上一节可以看到,在 Kubernetes 中应用都是以 yaml 文件进行描述和部署,Kubernetes 通过解析 yaml 文件的应用信息来分配集群资源运行应用容器。对不熟悉 Kubernetes 的开发人员来说,这种部署方式不太友好。所以,Kubernetes 提供了开源的 Dashboard,它以可视化页面的方式来部署应用。而当我们在 Kubernetes 基础上加了一层服务网格时,Kubernetes 的 Dashboard 就完全不够用了。为了对网格应用更加灵活精细的控制,我们结合爱奇艺内部资源开发了一套团队内部使用的定制版 Istio Dashboard,方便团队内部不熟悉的开发者将服务部署在服务网格中。
App 级操作,我们提出了 App 这一个概念,App 对应一个应用,其中包括一套 Deployment、Service、VirtualService、DestinitionRule、Gateway,在 Dashboard 中的所有操作都是针对 App 来执行的,用户无需关心 Gateway、VirtualService 等这些底层资源的概念。
一键部署,一个接口便可将应用部署在集群中,并且通过域名直接访问。
功能齐全,灰度、重试、流量配置等功能通过简单的操作便可实现。
为了实现 App 这一个概念,将一个应用的涉及的资源类型进行了封装,并定义了规范来保证各个应用间资源的隔离。
一个团队在 Istio 中拥有自己的命名空间,在同一个命名空间部署的应用采用同一个 ingressgateway 网关。
每一个应用拥有自己独自的 VirtualService ,一个空间内的 VirtualService 共用一个网关,各个应用在自己的 VirtualService 做流量控制。
每一个应用拥有自己独自的 DestinatioRule,但是可以拥有多个版本的 Deployment(Deployment 在Kubernetes 中类似于App 这个概念,是应用整个资源操作的封装体,但不包括服务网格的资源类型),一个应用的多个Deployment 通过标签的方式进行关联。比如查这个应用的所有版本,就是根据Deployment 的标签来聚合查询的。
在Dashboard 部署应用时,后端自动创建其所有的资源类型,并根据App 名字关联在一起。当删除时,会自动删掉其关联的所有资源。
我们的 Dashboard 后端接口基于开源的 Istio client进行开发,将各种资源的操作通过 HTTP/RPC 方式推送给 Kubernetes 集群,从而无需再编写 yaml 文件部署应用。
监控
除了让服务在网格内运行,对其进行监控也是必不可少的。我们来看看在服务网格中它的监控和平常微服务体系监控有什么不同。
前面提到 Envoy 代理了应用的网络,这时候脑洞大开的 Istio 开发者在此基础上设计了一个 Metric 数据收集和策略控制组件 Mixer。那它有什么用处呢?Envoy 代替应用发出请求时,先访问 Mixer 来获取目标应用的网络情况,然后再与目标应用的 Envoy 通信,通信完之后再将请求相关信息上报给 Mixer。这个 Mixer 就可以收集到网格内所有应用通信的情况。Mixer 收集到各种指标数据有什么用呢?聪明的 Istio 提供了可扩展的 Adpater,将这些指标数据适配给 APM 后端。网格使用方完全可以结合自己的业务场景来自定义自己的 Adpater,并在自己的 APM 后端对这些 Metric 做一些自己的事情,如实现特定业务场景的报警和容灾策略。
Prometheus 作为业内开源的十分流行的监控应用,在 Istio 的安装包默认包含了 Prometheus 相关 Adapter 和对应的 APM 后端,只要启用 Istio,网格内运行的服务相关数据就可以被 Prometheus 收集到了,真正做到了开箱即用。
仅仅监控服务间的通信远远是不够的,集群的各种资源数据指标也要采集进行监控。我们在网格内部部署了各种第三方 Exporter ,如 node_exporter,收集集群的 CPU、内存等 Metric 数据。kube-state-metrics,收集 Pod,Container,Deployment 等资源对象的状态指标。这些第三方 Exporter 将收集到的 Metric 数据暴露给 Prometheus 进行读取存储。
Prometheus 采集和存储各种需要监控的指标后,我们就可以在 Grafana 进行可视化查询和设置报警。当指标超过阈值时,其再将报警信息以 Webhook 方式推送给公司内部的统一报警平台,报警平台通过邮箱、热聊(公司内部聊天工具)、短信等方式将报警信息推送给订阅者。
这是集群在 Grafana 监控的 CPU、内存等指标。
另外,为了监控服务的运行错误,如报空错误等,我们结合公司的 Venus 日志采集系统,将服务中的日志采集到其中,此举一方面通过 Venus 将日志保存在 ElasticSearch 中来提供日志查询,另一方面我们单独开发了一套'关键节点报警服务'。该服务基于 Flink 进行开发, 服务持续消费 Venus 中的日志信息,并解析出日志中的错误信息来对订阅者进行报警。运行在网格中的服务一旦在日志中打印了报错信息,订阅者在 5 分钟内就可收到报警信息,并可以通过 Rover/Kibana 进行查看修复等操作。
其他特性
Istio 的三大特性之一——安全,它可以通过插拔的方式将安全证书配置在 Envoy 中,来加强服务间通信的安全性。
其次,Istio 的可扩展性强,它的 Mixer 组件所收集的几十上百个 Metric 数据可以通过可扩展的 Adapter 的方式提供给自己的 APM 后端使用,其中存在较大的实践空间。
另外,'混沌工程'在服务网格上也可以方便的实现,这只需要修改相应的 VirtualService 和 DestinationRule 配置,就可以模拟调用延迟及调用失败等情况。
最后,得益于 Kubernetes 的 CRD 扩展机制,我们可以很简单的实现各种扩展功能,例如:安全数据、聚合网关等功能。
总结与规划
服务网格是未来微服务的发展趋势,但是该领域现在仍面临着很多挑战,比如将服务运行在服务网格中的时候,难以界定开发人员和运维人员的界限;由于服务网格的高度中心化管理,可观察性、流量控制和安全的三大特性尽管体现了对开发者友好的特征,却也给运维人员带来了很大的挑战。服务网格技术目标远大,旨在解决复杂的微服务架构导致的维护问题,而这样的高目标也对开发人员有着更高的技术要求。尽管我们面对很多挑战,但通过探索,我们真切感受到服务网格的功能强大和架构设计的巧妙之处,它解决了我们之前面对的流量控制弱、监控和业务代码耦合等问题,这也是我们坚持继续探索服务网格的原因。
从最近各大公司的服务网格的产品动态可知,服务网格上云+托管成为一大趋势,开发者使用托管版本的 Kubernetes 和 Service Mesh,将底层系统的复杂度交给云厂商,这样客户只需要在云的基础上便可享受 Service Mesh 技术带来的使用便利和强大功能。面对如此的行业发展形势,我们相信将来会有越来越多的开发者将自己服务部署在服务网格上运行。
目前,爱奇艺号在服务网格的初步探索已经完成,实现了服务在网格内的正常部署和常规操作,并搭建了监控和报警附属设施来保证服务的正常运行。后续我们将在此基础上进行深入探索,在可靠性、可控性、可用性方面进一步优化,如跨集群路由,制定业务场景网络策略,扩展 Dashboard 功能等。
文章来源:爱奇艺技术产品团队,点击查看原文。