卡瓦尔康蒂:我在这里谈论的是如何利用软件架构。首先,我将在这里定义杠杆的含义。这是谷歌的定义。杠杆率是相对于你所做投资的深度,你可以获得的价值量。我们希望获得比您所做的投资更高的价值。在软件环境中,是您所做的决定、所做的选择,或者您所获得的与您所能创造的价值量相关的技术债务。我想看一看我们在Nubank的整个发展过程中所做的一些架构决策的例子,这些决策的目的是在当时获得尽可能高的杠杆率。你可能在你的公司中处于类似的位置,或者在未来的公司中处于你将做出这些决定的阶段。你可以以我们为例,或者至少有一种心态。
背景
我是卢卡斯·卡瓦尔康蒂。自2013年底以来,我一直是Nubank的首席软件工程师。七年多一点了。我住在巴西圣保罗。
在一个复杂的领域中迅速成长
Nubank是拉丁美洲领先的金融科技公司,是世界上最大的数字银行。在《时代》杂志上,我们获得了全球100家最具影响力的公司之一。我们也登上了《时代》杂志的特写。这是一个巨大的成就,来自一家拥有七年多一点历史的公司。这是我们的增长曲线的一个例子。以下是此图中绘制的实际客户数。我们现在有3500万客户。我们每天处理数十亿条卡夫卡消息和HTTP请求,在一个拥有数百个微服务并由数百名工程师签名的系统中。这是一个相当大的规模,并不总是这么大。
概述
我将介绍公司的一些阶段。第一个是启动时间,我们重视上市时间和反馈。进入成长期,我们将重点转向恢复力和适应性。接下来是整合时间,最重要的方面是可靠性和可观察性。当我们重视灵活性和可扩展性时,最后是扩展时间。这些是在这些阶段对我们很重要的价值观。
启动时间(2013-2015)
创业时间是一个神奇的时刻,任何事情都有可能发生,包括失败和没有公司。在我们的案例中,这发生在2013年末到2015年初。我们经历了不可思议的变化,那是一个神奇的时刻,当你有一个绿地项目,你可以选择任何你喜欢的技术。你必须有充分的理由这样做。在圣保罗一个友好社区的一间小办公室里,实际上是一所小房子。当我们推出第一款产品时,这是一款无需付费的数字信用卡,并提供了当时闻所未闻的实时体验。至少在巴西,我们是第一个这样做的。太多的未知数,我们不知道公司将走向何方,也不知道它是否会成功。有限的资源,只有十几个人在管理整个公司,我们需要让这一切顺利进行。在我们的案例中,我们有一个许可期限。如果我们在2014年5月之前没有运营,我们将不得不申请一个许可证,该许可证将需要两年的时间才能授予,这对公司来说基本上是死亡。
创业时间是一个神奇的时刻,任何事情都有可能发生,包括失败和没有公司。在我们的案例中,这发生在2013年末到2015年初。我们经历了不可思议的变化,那是一个神奇的时刻,当你有一个绿地项目,你可以选择任何你喜欢的技术。你必须有充分的理由这样做。在圣保罗一个友好社区的一间小办公室里,实际上是一所小房子。当我们推出第一款产品时,这是一款无需付费的数字信用卡,并提供了当时闻所未闻的实时体验。至少在巴西,我们是第一个这样做的。太多的未知数,我们不知道公司将走向何方,也不知道它是否会成功。有限的资源,只有十几个人在管理整个公司,我们需要让这一切顺利进行。在我们的案例中,我们有一个许可期限。如果我们在2014年5月之前没有运营,我们将不得不申请一个许可证,该许可证将需要两年的时间才能授予,这对公司来说基本上是死亡。
技术选择
我们需要做的第一个杠杆是技术选择。这里的价值是上市时间。我们需要尽快发射。这里的杠杆类型是最大化不需要做的工作量。不要创造一件你在那个阶段需要做的更复杂的事情。我们选择了Datomic作为一个数据库,一个非常小的数据库,它是一个不可变的事实分类账。你可以免费获得审计服务。每次更新都会保留历史记录,因此不会丢失以前的值。您可以在任何时间点查询数据库,因此这对以后的审核和调试都非常有用。我们选择了Clojure,这是一种在JVM上运行的函数式编程语言。我们可以利用整个Java生态系统。所有用Java编写的东西,我们都可以在Clojure中使用。默认情况下,我们获得了不变性。几乎每一个语言的决定都让我们变得简单。“简单变得容易,”是里奇·希基的一句好话。这是真的,我们在生产中使用它。我们有接近财务的函数式编程,这也是我们选择Clojure的原因。在函数式编程语言中映射财务逻辑更容易。
我们选择使用六边形架构,这样我们就可以有一种有组织的方式来查看代码。我们选择Kafka作为消息传递技术,这在当时非常流行,它有一个带有TTL的消息持久性日志。它不是永远的,但在一段时间内,您可以检查和查看生成的所有消息。我们可以重置偏移量,因此您可以在必要时重新处理旧消息。一开始我们不得不做几次。默认情况下,我们也会得到分区。当时,卡夫卡的推广也稍微容易一些。当时我们欠下的债务是我们选择了一些非常利基的技术,有些是未整合的。它们尚未建立。很难找到对这些语言有一定经验的人。在公司成立之初,我们基本上不需要为了教人们这一点而要求这样做。
Vendors
下一个杠杆是供应商。当你考虑上市时间时,好几次,购买而不是建造是最好的选择。第一个是使用云。在公司的这个阶段,您不想管理自己的机器。我们从一开始就将AWS与CloudFormation一起用于部署自动化。我们使用DynamoDB作为Datomic的后端数据库,这也很容易操作。我们选择购买现成的信用卡解决方案。我们并没有开始构建整个信用卡系统,而是开始使用一家已经处理信用卡交易的公司。我们可以利用,而不是建设,我们只是与该公司集成,我们可以更快地创建第一个产品。这里的债务是,现在,通过使用这些供应商,我们受到其增长、规模和应对我们问题的能力的限制,而这并不总是理想的。
实践
关于启动时间的最后一个问题是练习。这一次的价值是获得快速和早期的反馈。为了得到这一点,我们需要在顶部建立一个良好的基础,这样我们就可以在时间上更快地建立在上面。这里的事情是建筑基础需要时间,当你想启动的时候,你不可能总是在启动的时候使用。幸运的是,我们能做到。我们有一些时间。我们也利用这个机会构建了一个良好的CI/CD环境,因此持续部署在当时对我们来说非常重要。我们建立了一些连续部署的实践。我们有一个非常基本的容错能力,但仍然存在。我们从一开始就拥有不变的基础设施。每次部署时,我们都会在EC2上创建一个新实例并销毁旧实例,这样您就不会有处理基础架构更改的复杂性。我们从一开始就选择使用微服务,因为我们知道金融领域非常复杂。在这个例子中,将这种复杂性包含在小块中,一个较小的服务,在当时对我们来说非常重要。我们已经开始了。
关于启动时间的最后一个问题是练习。这一次的价值是获得快速和早期的反馈。为了得到这一点,我们需要在顶部建立一个良好的基础,这样我们就可以在时间上更快地建立在上面。这里的事情是建筑基础需要时间,当你想启动的时候,你不可能总是在启动的时候使用。幸运的是,我们能做到。我们有一些时间。我们也利用这个机会构建了一个良好的CI/CD环境,因此持续部署在当时对我们来说非常重要。我们建立了一些连续部署的实践。我们有一个非常基本的容错能力,但仍然存在。我们从一开始就拥有不变的基础设施。每次部署时,我们都会在EC2上创建一个新实例并销毁旧实例,这样您就不会有处理基础架构更改的复杂性。我们从一开始就选择使用微服务,因为我们知道金融领域非常复杂。在这个例子中,将这种复杂性包含在小块中,一个较小的服务,在当时对我们来说非常重要。我们已经开始了。
成长时间(2015-2016)
再往前走一点,如果我们幸运并且成功的话,公司将进入一个增长阶段,在我们的案例中,这是在2015年和2016年之间,当时我们经历了比预期更快的增长。我们曾期望在5年内获得100万客户,而我们在大约18个月内实现了这一目标。我们需要对此作出回应。起初,办公室规模不大,我们不得不搬到一个更大的地方。供应商没有进行扩展。信用卡处理器没有扩展,所以我们需要保持系统工作,即使供应商没有扩展。这项技术,我们在一开始做出的决定,也开始不具有规模。我们开始看到第一个瓶颈,在这种超增长场景中很难修复。
实践
关于成长时间的第一个杠杆是实践。有了可伸缩性或容错的价值,我们可以而且应该尽可能避免优化,或者至少延迟优化。因为优化代码比常规代码复杂得多。在一个复杂的领域,这可能会很快偏离轨道。为此,我们使用了基础设施分片,而不是只分片数据库或基础设施的一部分。我们有好几份Nubank系统的副本。每个碎片都是整个基础设施的副本,这些基础设施是可伸缩性单元。我们可以对在该副本上运行的客户数量进行限制,并在到达新客户集时转到下一个副本,并随着基础的增长不断创建副本。如果碎片足够小,就不必优化代码,或者可以尽可能地延迟代码。为此,我们必须改进CI/CD。我们需要频繁的自动部署。
我们从一开始就开始进行端到端测试,但测试规模开始缩小,因此测试开始需要一个多小时的时间。我们必须用消费者驱动的契约测试来代替它们,这将在保证少一点的情况下运行得更快,但最好保持频繁部署,而不是等待太多时间来部署。我们开始迁移到Docker,而不是使用EC2。这里的投资是与sharding一起运行的项目长达一年多,这在当时是公司的一个非常大的项目。我们必须设计新的工具来适应这种情况。这里的债务是,该项目的时间比预期的要长,客户群的增长比预期的要快。我们最终得到了比其他碎片更大的第一个碎片。在很长一段时间里,这个碎片是一个特殊的碎片,基本上是系统中任何性能问题的金丝雀,这是第一个碎片。此外,无论有多少客户,每个碎片都有一个最低成本。我们开始花很多钱来运行碎片的每个副本。
In-Housing(内部IT)
下一个操纵杆在内部IT。特别是,因为我们的供应商没有扩展,所以我们开始在业务最重要的方面掌握自己的命运。我们开始在内部处理信用卡,将信用卡的一个又一个功能带到内部,这样我们就可以控制自己的规模。客户支持也是如此。取悦客户是我们在公司的最大优势,因此我们还将客户支持工具和客户支持人员带到了公司内部。我们还必须为此进行设计。这里最大的投资是,实现信用卡的这些功能花费了18个多月。每一个小功能都是我们必须进行的迁移。这是一项巨大的投资,回报巨大。供应商不会扩展到3500万客户,我们可以。这里的债务是,由于带来了内部的几个功能,我们花了很长时间没有进行任何重大的产品更改。那有点糟糕。
巩固时期(2017-2018)
如果我们足够幸运,我们将进入下一阶段,也就是整合时间。2017年到2018年间,当我们在公司进入巡航模式时,我们可以扩展,但不是以稳定的方式,切分在扩展方面帮助很大。我们达到了这样的规模:每一个影响0.1%客户的小角落案例都发生在数千名客户身上。我们必须有一种比我们预期的更稳定的产品或系统。办公室也没有规模,所以我们需要搬到一个能容纳1000人的更大的办公室,靠近圣保罗最著名的街道之一的Avenuda Paulista。此时,我们推出了第二个产品,即支票账户。在这个阶段,我们已经生成了大量的数据,所以我们开始分析这些数据。这对我们来说也是非常重要的一点。
技术
技术方面的第一个优势是,我们的目标是可扩展性和适应性。这里的杠杆就是这样的规模,我们需要能够更轻松地进行基础设施更改,因此我们迁移到Kubernetes,这在当时也很繁荣。它附带了一个由多种基础设施工具组成的生态系统。随着我们获得的服务数量的增加,它比AWS CloudFormation扩展得更好。我们还开始建立更好的监控工具,以便使用Prometheus plus Grafana收集实时指标。这些指标也被其他工具(如Opsgenie、Slack或CI/CD)用于canary部署。这对我们扩大规模非常重要。这里的投资是另一个长达一年的项目,我们必须建立Kubernetes,并将碎片逐个迁移到Kubernetes,而该系统已经为数百万客户运行。他们是相当复杂的行动,我们能够完成。这里的债务是,虽然我们没有完全迁移,但我们开始在创建资源或资源数量方面达到AWS的限制,并在项目完成之前在重复的基础设施上花费大量资金。这是一件大事。
内部工具
我们还必须在用于恢复力和可观察性的内部工具上投入大量资金。我们需要让工程师们更容易操作这个系统,特别是在有这么多服务和人员的情况下。我们创建了一个名为NuCLI的命令行存储库,其中包含最常见的操作,如重新启动服务,或使用我们的凭据向服务发送HTTP请求,这些操作只需一个命令即可运行。此外,还有一个用于声明性infra的工具。一个存储库,当你可以描述你可以从你的服务中获得的资源时,它被工具自动应用。这里的投资是我们需要一个专门的团队来策划、维护和确保所有这些变化都得到应用。
数据
在这一点上,当我们进行整合时,数据变得非常重要。我们拥有的数据量无法通过常规工具处理。你几乎需要数据来做你公司的每一项决策,因此我们使用Scala plus Spark来处理所有数据,方法是从所有服务的数据库中跨所有碎片提取数据。我们有一个ETL过程,它是一个数据集定义的存储库,公司中几乎每个人或公司中几乎每个地方都参与其中,它将所有内容输出到数据仓库,以便每个人以后都可以访问。它与一些人工智能工具集成,我们可以用来支持我们的机器学习模型。我们也可以将其用作一致性工具。有了这么多的数据,并使用分布式体系结构,失败的分布式事务变得非常重要。我们还使用ETL检查一致性,同时检查系统。同样,创建初始ETL版本并开始对其进行迭代是另一个大项目。我们也有一个专门的团队,以确保这一切顺利进行。
扩张器(2019- )
最后,我们到了扩展时间,我们现在的位置。从2019年到现在,我们开始为每个人提供产品。这就是为什么你会在曲线上看到这个拐点。例如,我们不再对要求信用卡的客户说不,我们开始为每个人提供产品。我们开始在许多国家、许多办事处以及我们正在制造的许多其他产品推出。我们开始收购公司,因此这些公司之间的接口也变得很重要。
技术平台
这里关于扩展时间的第一个杠杆是可扩展性和生产力,我在这里称之为水平平台。基本上有一个专门的技术团队,为其他团队构建抽象的工具。例如,移动和网络,我们在颤振、设计系统和组件库方面有一个团队建设工具,这样普通工程师、非专家工程师仍然可以发展和使用该系统。对于基础设施也是一样,我们创建抽象工具,让每个工程师都能做到,而不是让每个人都知道如何操作Kubernetes。我们还建立了一个实验平台,因为在这种规模下,我们希望进行实验,我们希望改进您的产品。拥有一个平台,允许您在监控KPI和与测试相关的任何内容时轻松完成这项工作,这是非常关键的。我们现在需要专门的团队来创建、维护和操作这些平台,以确保每一位工程师都能在我们使用的任何技术上发挥生产力。
业务平台
最后,这里是面向领域专家团队的业务平台,用于构建供所有其他产品团队使用的抽象API。这里是我们可以进行创新的要点,我们可以构建的每一个可能的平台产品都有无限的可能性。it的例子是银行即服务,因此创建平台来运行银行的基本业务。例如,信贷平台。你不必弄清楚如何发放贷款,或者如何报告贷款,如何核算贷款,我们有一个平台,每个产品都可以发放贷款。一旦完成,产品就可以做它想做的任何事情。开放银行业务中的资产和支付平台也是如此。这些是我们可以用来建造银行的积木。此外,在信用卡方面,我们必须在其他国家推出信用卡,因此我们必须使系统更加通用。为了做到这一点,我们将系统划分为与信用卡最相关的部分。例如,在这种情况下,我们每个月都在处理信用额度或结账单,或者以不同的方式处理信用卡交易。如果客户未向我们全额支付账单,也会产生债务并重新协商客户债务。另外,当你得到太多的产品时,拥有一个灵活的采购流程也是非常重要的,所以我们使用它。这里的投资是,对于领域平台,我们需要对领域有非常深入的了解才能做到这一点。我们经过了长时间的讨论来设计平台端系统的正确突破点,因为在这里创建错误的抽象也会导致失败。您必须确保您正在创建抽象的广度,因为重建抽象的成本非常高。
简要回顾
在创业的时候,我们试图尽可能多地推迟编写代码,同时为成长奠定基础。当我们实现增长时,我们开始在内部提供我们需要的所有核心功能,并开始进行切分,以便能够更快地扩展。整合的时间是使我们的基础架构成熟并创建一个数据环境,这样每个人都可以使用数据来促进公司的发展。最后,扩展时间是关于构建横向和业务平台,因此可能性将成倍增加,每个人的生产力将成倍增加。
问答
波切利:你是从使用微服务开始的。通常,当人们选择微服务架构时,更多的是康威定律的正当性,当你很小的时候,你没有,但是你切换到那个,你考虑去做一个整体吗?这方面的思考过程如何?
卡瓦尔康蒂:我认为主要原因是领域的复杂性。我们知道金融领域非常复杂,因此当时我们对该系统的关注已经非常不同,比如处理信用卡交易、处理客户数据和处理收购流程。我们一开始只有很少的服务。我想大概有四五个,但已经进入了服务领域,因为我们知道,如果我们要成功并扩大规模,我们最终将不得不采用这种架构。
波切利:这是一个挑战,因为分布式系统比单个整体要复杂得多。
这也涉及到一些关于切分的问题。开始缩放时,您提到了切分。你在某种程度上平衡了切分和创建切分的挑战。你必须在这方面处理异常值吗?在这个切分中需要更多的资源?
卡瓦尔康蒂:我认为我们确实有一些人比其他人运行10倍或100倍的事务。它最终只会影响到特定的客户,比如他们的账单可能不会及时打开,因为那里有太多的交易。它不会影响其他操作,因为我们的大多数操作已经是批处理的。这对我们没有多大影响。我们在第一个碎片上确实遇到了这个问题,因为我们花了比预期更长的时间来构建碎片基础设施。第一个碎片与年龄太大的客户相处得太久。客户在Nubank的时间最长,客户数量最多。这给我们带来了一些挑战。有时仍然如此,但现在所有碎片的大小都差不多了。我们正在更好地管理它们。
波切利:那么,连接到切丁。迁移到库伯内特斯的情况如何?关于库伯内特家族的一些问题。你是否采用了像EKS之类的Kubernetes服务,或者你去了Kubernetes?从您的基础架构的这种转变是如何进行的?你已经提到它是不可变的。
卡瓦尔康蒂:我认为这里要考虑的主要问题是,这是七、八年前的事了。Kubernetes刚刚推出,2014年公开发布。我们创立公司时没有这些工具。我们成立公司时甚至没有Docker,所以我们不得不迁移到Docker,然后迁移到Kubernetes。然后最终迁移到EKS,因为EKS在当时也不存在。我想,它是从今年开始在圣保罗地区出现的。我想如果我们今天开始创业,我们可能会在亚马逊上使用EKS,就这样。我们当时没有那些工具。
波切利:我在这里经常看到的另一个问题是Clojure的使用,以及您提到的堆栈。我想说,这并不常见。你利用了生态系统。您还提到了JVM。你能帮我们理解一下这种二分法吗?你开始很容易,但你选择了很少有人会选择的东西。
卡瓦尔康蒂:对我们来说,Clojure最重要的一点是,语言从一开始就将你推向简单,从某种意义上说,你必须学习的语言数量是非常少的。你可以在Clojure学习几周来提高工作效率。我们所有的服务看起来都差不多。一旦你摆脱了那些括号,你就可以从大括号切换到括号,然后就可以了。其他一切都比较简单。我们没有大多数语言必须学习语法和语言结构的认知负荷。Clojure是Lisp的括号和符号。您不必学习很多语言特性,就可以像我们一样使用它或复制粘贴其他代码。
波切利:现在关于更多业务方面的一个问题是,您是如何将成本因素纳入架构决策的?当你从事金融业务时,你会遵守很多规定。您如何处理这一区别、基础设施、业务代码以及法规的所有挑战?
卡瓦尔康蒂:我们确实有特定的团队,比如当我们达到一定规模时,有一个专注于特定方面的团队是值得的。我们有一个团队只负责管理运营风险,或者一个团队只负责为公司的受监管部分提供平台。发放贷款在市场上是非常受监管的,至少在巴西是这样,所以我们围绕它创建了一个平台。每次我们需要贷款时,平台都会处理,我们有许多团队可以轻松发放贷款。这就是我们处理这件事的主要方式,就是有一个专门的团队,知道很多关于规章制度的知识,知道如何评估风险。然后,来自可能受it影响的其他团队的定期评估。
波切利:你提到过几次,有专门的团队和专家。Nubank的团队规模是什么样的?
卡瓦尔康蒂:通常,团队不仅仅是工程师。我们有BAs团队、产品经理团队、业务产品经理团队,有时还有数据科学家团队。对于工程部分,我们通常有一名技术经理,每个团队有两到六名工程师。它在上下文上有很大的不同,但每个团队的规模都差不多。
波切利:两个比萨饼规则。
现在让我们切换到数据。您提到了ETL,您使用什么工具进行ETL?
卡瓦尔康蒂:我们使用Spark构建所有基础设施来转换数据。我们必须在内部构建很多工具来提取数据组数据,并以ETL可以使用的方式进行转换。我不是数据方面的专家,所以我对细节不太了解。我知道我们使用Mesos集群来运行将运行ETL过程的集群。我们使用一些BI工具,比如Databricks,比如Looker。我认为整个生态系统都在使用几种工具。我们有自己的数据集定义存储库,由整个公司提供。它是Scala和Spark,以及我们创建的一些抽象概念,因此人们可以更容易地使用它。
波切利:你从卡夫卡开始,今天,很多事情都是关于流的,但是你选择了批处理ETL路线。有没有理由不全神贯注于流媒体?那时候不是很流行,你需要调整吗?
卡瓦尔康蒂:主要原因是,当我们需要ETL时,卡夫卡流在当时并不稳定或不被释放。当时我们已经从数十家微服务公司获得了Datomic的数据。我们很难在当时选择的架构上迁移到卡夫卡流模式。如果我们从今天开始,我们将进入流媒体。我们确实有一些更适合流的用例。例如,我们在实时收集指标时使用它。我们确实使用卡夫卡流。对于常规数据库部分,我们没有。