Monzo如何搞定1,600个微服务?使用Go语言、干净代码和一支强大的团队 - Tim Anderson


Monzo是一家伦敦数字银行,它们是如何在1600种微服务上运行其银行系统?

Monzo对可扩展,可扩展,有弹性和安全的技术平台的要求。最初的想法是从一些基本的银行服务开始,然后在时间和资源允许的情况下添加更多服务。
Monzo在早期就确信分布式系统的价值。银行不希望有一个具有复杂故障转移功能的单点的、大型弹性系统,这是维护人员DevOps永远不希望的。
但是,如果不使用那些故障转移模式,怎么确保它们可靠地工作?Monzo开始从Mesos实现集群管理,但到2016年,他们转而使用Kubernetes(K8s)作为“新兴市场领导者”。
目标之一是抽象化基础架构的复杂性。
基础架构扩展的所有复杂性,确保提供服务和数据库可用的问题,应该由特定的团队处理,以便工程师可以专注于产品。
K8s并非一帆风顺。在2017年,Monzo由于K8s的问题以及它与etcd和linkerd的交互作用而导致的故障很大,这是由于各种错误的组合,这些bug很难测试。
Monzo之所以选择Cassandra作为数据库是因为它可以水平扩展(这意味着您只需添加更多硬件即可扩展,而不必迁移到更大的系统)。
在编码方面:将Go用作主要的编程语言,它是静态类型的,很容易就可以招募人员。 Go具有向后兼容性保证,这意味着当出现一个新版本时,您可以简单地重新编译现有代码,从而获得更新诸如垃圾收集器等功能的好处。Go也非常适合有关错误处理的严格策略。
银行系统非常适合模块化方法。需要链接到许多不同的系统,例如BACS,CHAPS,Visa,万事达卡,Apple Pay和Google Pay。将这些东西设计为单独的微服务系统,能够使它们更简单。Monzo尽可能在内部构建集成,而不是使用第三方实现,以更好地控制弹性和性能(并且从长远来看还可以节省金钱)。他们甚至建立了自己的聊天系统,供内部使用和获得支持。
Monzo还构建了自己的工具来与AWS和K8进行交互,例如一个名为Shipper的工具,可以部署或回滚单个服务。托运人可以直接从拉取请求进行部署,这是对Git存储库中维护的代码的更新。
每个Monzo微服务都在Docker容器中运行。但是有一个共享的核心库,每个服务都可用。尽管构建过程会删除未使用的代码,但实际上将其复制到每个容器中。这意味着工程师不会重写诸如数据编组之类的核心抽象。它还为每项服务启用指标,以便在部署后立即将其显示在仪表板中,并分析CPU使用率,网络调用等。自动警报将识别降级的服务。
每个服务公开的接口或API都有很多想法。团队倾向于编写许多小型服务,每个小型服务都专用于一个目的,而不是减少复杂的服务。这是希望将变更的风险降到最低。例如,如果想改变非接触式支付的工作方式,就不会影响芯片和PIN系统。
鉴于无法在笔记本电脑上运行1,600个微服务,开发人员如何处理其代码?只要正在运行一个子集。有一个RPC过滤器,可以检测到您正在尝试向当前未运行的下游发送请求,它可以编译,启动它,然后将请求发送给它。
为什么微服务在Monzo能正常工作,而在某些情况下却增加了复杂性却没有带来太多收益?开发人员在团队(以及与管理层)之间进行交互的方式比他们所支持的任何开发方法都更为重要。另一个是渐进式方法可以胜过偶尔的大更改。迭代过程通常是在Monzo所关心的,无论是从基础架构角度还是从产品角度来看。通过经常进行小的更改,可以确保朝着正确的方向发展。
简单性胜于复杂性。总体而言,Monzo的系统工作非常复杂,但是它的系统设计方式是将这种复杂性划分为更小,更简单的部分,并将其抽象给从事代码工作的开发人员。开发人员不需要知道1,600种服务的工作方式。管理K8的艰巨任务委托给专家。
Monzo还标准化了“一小部分技术选择”,以便“作为一个整体,我们可以共同改进那些工具”。这对于具有不同技术偏好的开发人员可能会令人沮丧,但是由于每个人都学习相同的工具集,因此必须在协作方面提供实质性帮助。优化代码以提高可读性。Monzo工程原理之一是不优化[性能],除非它成为瓶颈。
如果您拥有包含许多组件的复杂系统,并且需要能够在需求变化或功能添加时迅速做出响应,那么Monzo展示的内容似乎是软件开发和部署的强大模板。
但是请注意,Monzo使用了许多不容易复制的自定义内部工具和库。此外,他们提出的许多原理(例如编写清晰,可读和规范的代码,专注于精心选择的一些技术并采用渐进方法)在任何软件体系结构中都是赢家。