产品级微服务的八大原则
虽然微服务架构给开发者带来很大的自由,但是确保服务的可用性却要求对微服务进行很好的架构,运维以及组织标准。
O'Reilly这本免费的电子书 Microservices in Production介绍了微服务标准化的挑战,以可用性作为微服务标准化的目标,提出了八个标准化微服务的原则,包括在整个工程组织中实现production-readiness标准的策略。
这本书的作者是 Susan J. Fowler, Uber 的 SRE (site reliability engineer),她在Uber也主要做促使Uber项目中的各个微服务达到产品级的状态,所以这本小书也是她的工作思考之作。
微服务的挑战
一般在刚开始开发应用的时候会采用单体应用的架构(monolithic)。很多应用的架构都是在公司初创的时候设计的,所以那时候追求的是“糙快猛”。随着公司的发展,应用需要扩大规模,增加更多的功能,这时候开发人员会发现他们被应用最初的架构所制约,比如语言的选择、可用的库、开发工具、回归测试工具等。对应用的重构都不得不受限于初始架构的制约。显然,初始架构的选择决定了应用的未来。
微服务的架构使用给开发人员带来了缕缕清风,他们不再局限于过去的架构,他们可以他们所期望的服务:自由的语言、自由的数据库、自由的开发工具,他们是自己的王,自由的君主。经常听到的微服务设计的一个原则就是:一个微服务就干一件事情-只干一件事情-把一件事情干好,用你所想建你所要,确保它们工作即可。
是不是太理想化了?当你拥有成百上千的微服务甚至上万个微服务在运行的时候,它们之间必须无缝的交互,不应该有质量低下的服务影响整个微服务的整体,或者说影响产品的性能和功能。如果要保证产品的整体性能和功能工作的非常好,就需要有一定的标准,它的每一个部分也得遵循这样的标准。
为一个特定的微服务制定标准非常简单,但是要想为微服务制定一个通用的标准确实相当难的,这也是production-ready的由来。
Availability:标准的目标
衡量微服务是否成功的一个最通用的方法就是可用性(Availability)。如果一个服务有很高的可用性(宕机时间很少),那我们就相当信任这个服务。
而且计算可用性的指标也很简单: uptime (正常服务时间)、downtime(宕机时间)、以及总运行时间(uptime + downtime)。
尽管可用性指标很有用,但是它并不是衡量微服务的一个标准,而是这些标准要达到的目标。之所以不是一个标准法则,是因为它不能指导我们如何开发微服务。告诉一个开发人员提高他们的微服务的可用性,而不告诉他们如何去做就想白说一样。光说可用性是没有任何的可实践的步骤,而这本书后面的章节为我们提供了提高可用性的步骤。
计算可用性一般使用几个9来表示。
- 99%: 允许宕机的时间为 3.65天/年
- 99.9%: 允许宕机的时间为 8.76小时/年
- 99.99%: 允许宕机的时间为 52.56分钟/年
- 99.999%: 允许宕机的时间为 5.26分钟/年
Production-Readiness 标准
Production-Readiness背后隐含的含义是:产品或者服务值得信赖,它们可以满足产交互。
我习惯将 Production-Readiness
翻译成 产品级
, 它实际的意义代表可以应用在产品中,满足产品的需要。
有很多产品和库标榜自己是产品级的,但是我们需要准确的衡量。标准化如果没有可操作的原则也是没有意义的,作者提出了衡量服务是否是产品级的八个原则: stability
、 reliability
、 scalability
、 fault-tolerance
、 catastrophe-preparedness
、 performance
、
monitoring
、 documentation
,它们一起为微服务提供了高可用性,但是单一的原则并不能保证微服务的可用性。
Stability
微服务架构的采用为开发者提供了很大的自由度,他们可以每天增加和发布新特性,修改bug,用新技术代替旧技术,可以重写或者弃用过时的微服务。
虽然这些自由度允许我们快速修改bug,即时更改服务,但是我们必须小心这些操作避免影响微服务的可用性。
一个稳定的微服务应该是这样子的:它的开发、发布、新技术/新特性的增加、Bug的修改、服务的停止使用以及服务的弃用都不应该影响大的微服务生态系统的稳定性。
为了避免开发过程、发布过程、更改停止弃用时出现问题,我们需要在每个过程仔细考虑,执行到位,不会影响其它服务。
Reliability
稳定性并不是唯一影响微服务可用性的因素,微服务必须保障可靠性(Reliability)。一个可靠的微服务值得它的client所信任,以及它的依赖和整个生态圈。
稳定性和解决微服务的改变带来的副作用相关,而可靠性是和信任相关,这两个原则密不可分。每一个稳定性的需求都带来可靠性的需求。
通过cache可以提供可靠性的保证。通过defensive cache在服务出现问题时提供兜底。我记得淘宝前端的同学写过使用这个技术的文章: 淘宝首页兜底容灾方案
在路由routing和服务发现的处理中,为了保证可靠性,health check应该是准确的,request和response应该送达、错误处理应该仔细的被处理。
Scalability
微服务的业务处理很少是恒定的,成功的微服务总能平稳地处理业务量的增大。不能规模扩展的微服务在业务量增大的情况下会增加服务的延迟、低可用性,极限情况下还可能导致意外事故或者停机。
一个可扩展的微服务可以同时应付大量的任务或请求。为了确保微服务可扩展规模,我们需要知道它的定性的增长规模(它是扩展页面浏览还是客户订单),还需要知道它的定量的增长规模(每秒它能处理多少请求)。一旦我们知道了扩展规模,我们就可以为未来的容量进行规划,找出资源瓶颈和需求。
可扩展的微服务应该能应对突然爆发的请求,比如秒杀,防止服务整体垮掉。当然这说起来简单,做起来很难。想想每年的电商做活动的时候或者春节买火车票时候的系统就理解了。
我们还得从整个微服务系统考虑可扩展性,当服务业务量超过它的预期的时候应该报警。服务扩展的时候也会要求它的依赖能满足扩展的需求。
微服务的数据存储也必须满足可规模扩展。
Fault Tolerance and Catastrophe preparedness
即使最简单的微服务系统也是复杂的系统。复杂系统经常会出现失败,失败的场景会出现在微服务的整个生命周期的每个环节。微服务并不是完全隔离的,它们是整个微服务系统中的一环。每一个微服务都应该是容错的和提供灾备。
容错和灾备的微服务应该能够承受内部错误和外部错误。内部错误可能是微服务自己导致,例如内部代码的导致的未捕获的错误,外部错误可能是数据中心的停电、错误的配置等。
我们可以充分地(不一定完全)为这些错误和潜在灾难准备预案。识别错误和灾难场景是创建可容错和灾备的第一个需求,难点在于对这些失败和灾难的处理预案,它出现在整个微服务生态圈的每一个环节。
Performance
我们在谈论微服务的时候,scalability谈论的是服务能处理多少请求 (how many),而性能performance谈论的是微服务处理这些请求的性能 (how well)。
一个高性能的微服务处理处理请求很快,任务处理很高效,正确地使用资源。
处理一大堆网络调用的微服务不是有效的。同步处理任务性能也不是很高,异步(非阻塞)地处理任务能提供服务的性能和可用性。
占用大量资源比如CPU的微服务而不是使用它们,这不是有效的使用方法。使用硬件资源会影响服务底线,因为硬件资源不便宜。需要规划好容量。
Monitoring
另一个保障微服务可用性的原则就是正确的服务监控。好的监控要包括三大组件:
- 所有重要的正确的日志和相关信息
- 使用图形化的显示(dashboard)
- 关键指标的告警
作者指出,版本化的微服务是不被鼓励的,所以你的日志不会包含服务的版本信息,这要求你的日志要包含足够的信息。
所有关键的监控指标,比如硬件的使用率、数据库的连接、响应时间、API的状态等,应该图形化的显示,可以直观的观察到服务的状态。
告警信息应该传达给负责的工程师,为监控指标设置合适的阈值。
告警信息应该有用,并且可以处理,通常会有对应的处理文档。
Documentation
微服务的架构会带来技术债,减少它的办法就是文档。
即使是一群工程师面对面的在一个会议室内讨论一个微服务的细节,肯定也会有工程师理解的不全面,每个工程师在心里都有自己的理解。
最好的文档包含微服务的所有的基础知识:架构图、入手和开发手册、请求流的细节、API、告警的运维手册等。
后记
作者在每一个原则后面都列出了每个原则的需求,它们提供了检查微服务的可实践的方法和步骤。作者最后介绍了一下应用这个标准的实践过程。
不仅在Uber,在其它一些公司, 都存在着SRE (site reliability engineering)组织。比如今年Google的工程师推出了一本 《Site Reliability Engineering: How Google Runs Production Systems》,专门介绍SRE,已经出中文版了,名字叫《SRE:Google运维解密》,有兴趣的读者可以买来看看。