模块、单体和微服务_第1页
模块、单体和微服务_第2页
模块、单体和微服务_第3页
模块、单体和微服务_第4页
模块、单体和微服务_第5页
已阅读5页,还剩5页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

模块、单体和微服务最近,有人问我在什么情况下使用微服务是个好主意。在“系统设计诠释世界”一文中,我谈到了像第二系统效应、创新者困境等宏观问题。系统设计能回答微服务的问题吗?https://apenwarr.ca/log/20201227是的,但你可能不喜欢这个答案。首先,我们需要了解一些历史资料。1什么是微服务?你可以在网上找到各种各样的定义。我的观点是:微服务是对单体的最极端的抵制。当你将整个应用程序所需要的全部东西链接成一个很大的程序,并将其作为一个大型二进制包部署时,就会发生这样的情况。单体有很长的历史,可以追溯到像CGI、Django、Rails和PHP这样的框架。现在,我们要放弃这样的假设:单体和微服务群是仅有的两个选项。在“一个无所不能的巨型服务”和“无数个几乎什么都不做的微型服务”之间,存在着一个广泛而微妙的连续体。如果你赶时髦,你至少有那么一次已经构建了一个单体(不管是有意的,还是因为传统框架鼓励你这么做),后来发现了单体存在的一些问题,然后,你听说微服务就是自己需要的答案,于是开始把一切都重新设计成微服务。但是,不要赶时髦。在这两个极端之间,还有许多点。其中一个可能很适合你。更好的方法是从你想要将接口放在哪里开始。2方框和箭头接口是模块之间的连接。模块是相关代码的集合。在系统设计中,我们讨论“方框和箭头”设计:模块是方框,接口是箭头。更深层次的问题是:每个方框要多大?里面放多少东西?我们如何决定什么时候把一个大方框分成两个小方框?连接这些方框最好的方法是什么?有很多方法可以做到这一点。没人知道什么是最好的。这是软件架构中最困难的问题之一。在过去几十年里,我们经历了许多种“方框”。Goto语句被“认为是有害的”,主要是因为它们完全忽视了任何层次结构。然后我们添加了函数或过程;这些都是非常简单的方框,它们之间有接口(参数和返回码)。https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf根据你选择的编程路线,你会了解到递归函数、选择符(combinator)、静态函数原型、库(静态链接或运行时链接)、对象(OOP)、协程、受保护的虚拟内存、进程、线程、JIT、命名空间、沙盒、chroot、Jails、容器、虚拟机、supervisor、hypervisor、微内核和unikernel。/wiki/Unikernel这还只是方框!一旦有了彼此隔离的方框,就需要用箭头连接它们。为此,我们有ABI、API、系统调用、套接字、RPC、文件系统、数据库、消息传递系统和“虚拟化硬件”。如果你想为现代Unix系统画一个完整的方框和箭头图(我不会这样做),那太疯狂了:函数在线程内,线程在进程内,进程在容器内,容器在用户空间内,下层是内核,内核在VM里,运行在云提供商数据中心里机架上的一台硬件上,而这些硬件是通过一个编排系统连接在一起的,等等。每个抽象层上的每个方框都以某种方式隔离开来,然后连接到同层或其他层上的其他方框。有些方框在其他方框内部。如果你想在二维空间中画出这幅画的真实版本,那么其中的线条肯定纵横交错。这一切都是经过几十年的演变发展而来的。时髦的人称之为“路径依赖”。我称之为一个烂摊子。我们要清楚:其中的大部分已经没有多少价值。与其把注意力集中在那些丑陋的进化结果上,我们不妨来谈谈,人们在发明这些东西的时候想要达成什么目标。3对模块化的探索模块系统的首要目标是下面这几个:将一部分代码与其他部分隔离仅在明确指定的位置(通过定义良好的接口)重新连接这些部分保证你修改的部分仍然与其他部分兼容升级、降级以及扩展某些部分,其他部分都不需要同步升级计算机行业花费了大量时间,试图找出所有这些模块化问题的完美平衡,同时保证开发过程尽可能的轻松愉快。一句话,我们没有成功。到目前为止,我们做得最糟糕的一个方面是隔离。如果我们能真正有效地将一部分代码同其他部分隔离开来,那么其他的目标也就基本可以实现了。但我们根本不知道如何做到这一点。隔离是一个超级困难的问题。天晓得,人们已经尽力了。但浏览器沙箱逃逸仍然经常发生,未被发现的特权升级攻击在每一个操作系统上都存在,iOS越狱仍然会周期性地发生,DRM从来就无效(无论好坏),虚拟机和容器会经常被发现有漏洞,而[在像k8这样的系统中,其容器的配置默认就是不安全的。人们甚至已经知道,通过在因特网上适时地向远程服务器发送数据包,就可以找到加密密钥。与此同时,近年来,最惊人的隔离事故是Meltdown和Spectre攻击,它允许计算机上的任何程序,甚至是Web浏览器中的JavaScript应用程序,读取同一台计算机上其他程序的内存,甚至是跨沙盒或虚拟机。每一种新的隔离技术都会经历一个从乐观到绝望的周期:新想法:这次我们终于做对了,一劳永逸地!最初的试验似乎有效。(用户抱怨它比我们上次尝试的方法更慢、更费事。)早期的致命缺陷被发现并修复。广泛部署。越来越微妙的缺陷被陆续发现并修复。最终,我们会发现我们根本不知道如何修补的缺陷。不再对这种方法能实现有效的隔离抱有希望。但我们永远不能退役这种隔离方法,因为现在有太多的人依赖它。重复上述过程。举例来说,在这一点上,安全人员根本不相信以下任何一项(每一项都是当时最好的技术)是绝对安全的:Unix系统上的进程隔离和内存保护;当允许远程执行代码(即安全人员所说的RCE)时,OS进程之间的权限隔离;通过过滤系统调用来隔离进程;互不信任的进程共享一个CPU超线程;位于一个CPU内核上的虚拟机之间的内存隔离。据我所知,目前最先进的隔离技术,是类似于Chrome沙盒或gVisor这样的东西。大型浏览器供应商和云计算提供商都使用这样的工具。这些工具也不完美,但是供应商确实在尽可能快地追踪每一个新出现的漏洞,而且新漏洞出现的速度相当缓慢。隔离比以往任何时候都要好……如果你把所有的隔离都放在虚拟机(VM)级别,那么云提供商就可以帮你完成,因为其他人不知道怎么做,或者无法足够频繁地更新。如果你信任云提供商的VM隔离,那么就可以希望所有已知的问题都得到了缓解;但我们有充分的理由认为,更多的问题将被发现。从各方面考虑,这其实很不错。至少我们还有有效的东西。4很好!都用虚拟机!等一等。为每个小模块创建一个孤立的VM是一件很痛苦的事情。一个模块该多大?很久以前,当Java第一次出现时,人们的梦想是每个对象中的每个函数的每一行都有严格的权限限制,甚至是在相同应用程序二进制文件中的对象之间,这样就不需要CPU强制施加的内存保护。没人再相信他们能在这方面取得成功了。除了类似“云函数”这样的市场宣传,没有人真的认为你应该尝试一下。目前,已知的隔离方法中没有一种是完美的,但每一种都能达到某种近似的效果。越来越有经验的攻击者,或者越来越有价值的目标,需要更好同时也更恼人的隔离。现在,我们所知道的最好的隔离是由一级云提供商提供的inter-VM沙箱。在最坏的情况下,它的隔离效果会降到零。假如验证被跳过,由于大多数系统的耦合是如此之紧密,一个相当有经验的攻击者可以在模块之间横向突破。因此,举例来说,如果有人可以将恶意库链接到你的Go或C++程序中,那么他们可能会控制整个程序。类似地,如果程序具有数据库的写入权限,那么攻击者可能会让它写入数据库中的任何地方。如果它可以连接到网络,那么他们就可能连接到网络中的任何地方。如果它可以执行任意的Unix命令或系统调用,那么他们就可能获得Unix根访问权限。如果它在一个容器里,那么他们可能会从容器中挣脱出来,进入其他容器。如果恶意数据可以使png解码器崩溃,那么他们就可能让它做解码器程序可以做的任何事情,等等。/一种特别强大的攻击形式是获得提交代码的能力,因为这些代码最终将在开发人员的机器上运行,而某些开发人员或某处的生产机器可能有权执行你想要执行的操作。以上说法可能有点过于悲观,但是做出这些假设,可以帮助我们避免在不提高实际安全性的情况下使系统变得过于复杂。在”qmail1.0十年之际关于安全的一些思考“一文中,DanielJ.Bernstein指出,在qmail中添加的许多防御措施,特别是使用chroot和不同的Unixuid隔离各个不同的组件,并不值得,而且从未得到回报。无论如何,我们可以理所当然地认为,具有代码执行能力的攻击者“通常”可以在耦合模块之间横向跳转,这几乎适用于任何一种模块隔离技术。这意味着只有两种模块边界:可信的:两个模块彼此信任对方不存在恶意,并因此可以使用弱隔离边界;不可信的:模块彼此之间相互不信任,因此必须使用强隔离边界。我在这里并没有说什么非常深刻的东西。现代流行的平台已经是围绕这一区别构建出来的。例如,Chrome在强隔离的沙箱虚拟机中运行任意的WebJavaScript,因为网页是不可信的。大多数操作系统仅仅以进程(没有沙箱)的形式运行原生应用,共享文件系统、网络名称空间等,因为我们曾经认为,它们是相对可信的。(病毒就是这样产生的。)专家们不再信任多用户Unix系统,因为结果证明进程隔离很脆弱。云虚拟机默认可以无密码sudo,因为root与非root隔离都被证明很脆弱,所以为什么还要麻烦呢。(我们仍然要求用户在删除所有文件或其他东西时输入sudo,以减少人为错误的影响。)来自多个供应商的共享库和DLL被链接到来自其他供应商的应用程序中,因为所有代码都被假定是可信的。(这为通过开源库供应商进行供应链攻击打开了方便之门。我仍然感到惊讶的是,这类攻击并不经常发生。我有时怀疑,也许它们确实存在,只是很少被发现而已。)手机操作系统之所以会被破解,是因为应用商店的限制应该会让应用沙盒足够可信,但这种隔离总是被证明太脆弱。Kubernetes和Docker在一台机器或VM中运行多个不完全隔离的容器,因为这些容器都被默认为可信的。强烈建议你不要尝试运行“多租户”Kubernetes集群(不可信的应用程序代表独立的、相互不信任的用户),因为事实证明,容器的隔离效果很弱。还有,即使你对每个服务使用像gVisor'dVM那样的强隔离,如果代码本身不是使用强隔离的工具链构建的,也不会有什么帮助。如果一组人可以更新一个库,然后链接到一组应用程序,那么这些应用程序并不算是真正的相互隔离,无论它们以什么方式运行。5模块边界vs服务边界如果这么多隔离层都很脆弱,那么我们为什么还要费心使用它们呢?主要是历史沿革;如果我们抛弃这些层中的大部分,安全性不会受到太大影响,而简洁性将得到提升。我预计,随着时间的推移,这会发生。我们已经看到了这种趋势。多用户Unix系统已几乎绝迹;“无服务器”服务器放弃了除最强隔离类型之外的所有隔离类型,并试图将你锁定在你所在的云提供商那里。但是,让我们把历史放在一边。我必须介绍所有这些隔离概念,这样我才能说得简单点:你几乎从不出于安全原因定义模块边界。相反,模块边界通常遵循康威定律。人们分解模块的根据是他们希望如何细分团队中的开发工作,而模块之间的通信则取决于团队和团队成员之间的沟通方式。(康威定律很吸引人,也很真实,但你可以在很多其他地方读到它。)/wiki/Conway%27s_law模块边界并不能定义部署单元的大小。以操作系统为例:ChromeOS有成千上万的开发人员,但用户收到的一次更新中包含一个经过全面测试的组合,其中有Linux内核、设备驱动程序、窗口管理器、Web浏览器等。这些模块之间的接口可以在任何版本中更改,因为它们不需要向后兼容(当然,硬件和Web除外)。macOS、iOS和Android采用了类似的模式。DebianLinux有数千名开发人员,但用户下载和安装的是单个的软件包。你可以将来自古老的Debian稳定版本的程序包,和来自如今的Debian不稳定版本的程序包,一起运行,而且很可能行得通。可能没有人测试过你这样的特定组合,但它可能可以工作,因为程序包之间有定义得非常好的接口。(人们在开“桌面Linux”不可靠的玩笑时,谈论的总是第二种小众而难以测试的类型,而不是第一种主流的、容易测试的类型。我不认为人们感知到的质量差异实际上是由企业资金与开源差异所导致的。不同之处在于部署模型。)搜索公众号后端架构师后台回复“架构整洁”,获取一份惊喜礼包。两个系统都包含许多程序包(模块),这些包是由不同团队的许多开发人员开发的。它们的模块之间都有接口。如果你为每个系统画一个方框和箭头图,可能看起来会非常类似:内核、驱动程序、窗口系统、沙箱、Web浏览器,等等。然而,如果是后端云服务而不是操作系统,我们将分别称这两个模型为单体和微服务,因为它们的部署模型。一个只有一个要部署的“服务”,而另一个有许多,每个都要单独部署。相同的模块架构!这是怎么回事呢?模块边界和服务边界是两个不同的东西。6服务的边界应该在哪里?让我们回顾一下最初的模块化目标:隔离:如果出于安全考虑,确实需要强隔离,如果你需要单独的服务,那么唯一的方法就是分别提供虚拟机。(不过请注意:这更多的是隔离系统的限制,而非架构目标。“基础设施即代码”和蓝/绿部署会设法让这些服务再次同步,所以你可以有一个单体式的部署模型。)连接:遵循康威定律。模块边界倾向于遵循团队的个性化沟通模式。但与直觉相反,康威定律并不需要定义服务边界。兼容性保证:迫使你转向单体。如果你的单体是用一种类型安全的语言编写的,比如Go、TypeScript、Rust,甚至是C++,则尤其如此。(例如,Chrome就是一个巨大的二进制文件。)升级、降级和可扩展性:这些是决定服务边界的主要因素。关于这一点,让我们再进一步探讨下。以下是选择服务边界时需要考虑的一些事项:你的单体需要很长时间才能启动吗?这让升级变得很痛苦,所以你可能想把慢的部分分离出来,让其他东西可以更快的升级。你需要正确的数据存储模式版本吗?有时,这需要对后端所有实例进行同步升级/降级,以便它们处于相同的模式版本上。同步升级是有风险的,而且往往会妨碍回滚;有时,你希望使依赖于模式的部分尽可能小。持续集成测试经常失败吗?如果是这样,那真是个坏消息。那些失败的测试说明代码有问题。这是一个功能!拆分服务并单独推出可能会欺骗测试,使其通过,但在生产环境中,你将会遇到兼容性和版本不对称问题。那没

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论