导读:对于如何治理错综复杂的组件关系网,经过几十年的积累,开发者们已经提炼出了一套通用的解决方案:依赖管理(Dependency Management)。
前言
分布式云应用(也叫微服务)在大行其道的同时,也给应用本身的设计和运维带来巨大的复杂性。以前单体服务时代,尚可将这种复杂性隐藏于自身内部,而到了微服务时代,这种复杂性几乎扩散到了成百上千个“松耦合”的服务上面。尽管每个微服务都可以使用不同的语言构建,也可以快速地横向扩展,但他们作为一个整体,相比于单体服务,分布式的特性常常会带来规划、部署和安全上的难题。
这些问题也给云原生应用的管理和发展带来挑战。我们不禁要思考,怎样才能确保云原生应用的持续健康,如何才能取其精华,去其糟粕,既保持面向服务设计的优势,同时不增加开发维护等成本?
还好在微服务之前就遇到过类似的问题,对于如何治理错综复杂的组件关系网,经过几十年的积累,开发者们已经提炼出了一套通用的解决方案:依赖管理(Dependency Management)。
其实我们每天都在使用依赖管理器提供的公共或者私有的软件包,在这些包的基础之上增加自己的功能,最后再精心地打包为标准格式以供后续使用。笔者认为依赖管理正是释放分布式应用强大能力的关键,现在是时候引入依赖管理机制来推动云原生技术的发展了,具体来说,有以下5点原因。
1.开发者之间的相互协作
NPM, Pip, Maven等依赖管理器最重要的作用之一,就是促进了开发者之间的相互协作。通过提供一套统一的打包管理机制,依赖管理器可以让你的代码被其他团队使用或者增强。此外,不仅在企业内部团队之间,我们也看到了依赖管理大范围地用于促进开源社区上的协作。工具的一致性和使用的广泛性也使我们能够创建强大易得的软件库,从而供所有人使用并在其上进行构建。
这种级别的协作已经在各个语言社区中实现,比如JavaScript的NPM,Python的Pip等,但在云原生社区里还没有成型的落地方案。虽然有Docker定义了云服务打包的规范,但各种容器方案还缺乏足够的信息去解析和拓展服务间的依赖关系。如果我们希望微服务也能实现这种协作效果,就像各个语言对软件库做的那样,就非常有必要增加合适的依赖管理机制,使其能检索并处理各个终端服务之间的关系。
2.环境的自服务
依赖管理器的协作效果并不能凭空产生,而是得益于统一的依赖解析。统一的依赖解析之所以强大,主要是因为世界各地的开发者都能够使用相同的命令和程序来重现他们的效果。可重复性(Reproducibility)是依赖管理器的一个关键要素,缺少了这个要素,如果没有定制复杂的启动逻辑,开发者们就不能方便地下载和操作其他人创建的库和包,而这些将增加开发成本,成为大规模采用和分发的巨大障碍。
在这方面,微服务与各种语言库之间没有太大的差别。我们扩展他人工作的成果,取决于我们可以运行或调用期望的服务和应用程序的能力。目前,我们已经可以通过沙盒环境,集中QA的形式来完成任务,但还无法真正做到环境的完全重现,从而会产生一系列的问题。由于依赖他人的服务无法轻松交付,开发者并没有完全操控自己的开发环境的能力,需要被迫编写自己的脚本来本地或者远程地运行他人的应用程序,同时每个团队都需要开发生产级别的工具,自行保证网络问题和安全问题。
如果拥有统一的依赖关系管理解决方案,每个团队只需要声明其服务的网络依赖关系,即可为组织中的每个人提供相同的方式来运行其技术栈中的服务,并同时准备好服务的依赖,可以使每个开发者真正地拥有操控环境的能力。
3.自动化
自助服务优势不仅仅意味着开发者可以操控自己的环境,也意味着开发环境可以通过程序自动化来提供并且细化。只需使用一个命令或程序,就能实现依赖关系的解析、网络的丰富、安全性的自动保证,这是能否集成到CI/CD管道的关键。
如果每个服务都可以使用统一的机制运行(例如,使用容器平台)并且知道自己的依赖关系,那么每次Git合并请求就能提供一套新的环境,并且无缝地发布到预生产和生产环境。这意味着每个团队都可以为每个成员和每个添加到应用里的新服务提供非常灵活的基于Git的自动运维部署,即GitOps。
4.安全性
微服务架构其中的一个安全风险,是每个服务都需要暴露API接口以提供功能的调用。由于这些服务作为单独的进程存在,因此通过网络进行通信几乎是服务间相互连接、接收处理请求的唯一方式。这意味着每个新服务都会公开一些其他人可以访问的接口,如果开发者不小心,可能会错误地暴露接口给到本不允许访问的服务。
防止网络接口的意外暴露是依赖管理可以大展身手的另一个领域。通过带有网络依赖的结构化索引,我们不仅可以自动解析服务间的依赖,还可以丰富环境配置,生成对应的网络策略来保障接口的合法访问,也就是说只有相互依赖的服务才能相互访问。这种结构化的方式将极大减少开发者了解网络安全工具的需求,并让他们可以更自由地创建新服务。
5.灵活性
微服务或者说分布式应用的依赖管理的另一个好处是灵活性。一旦开发者确定好依赖项并关联到自己的服务上,解析器自己就可以在每个部署的环境中唯一地构建好关系网络。想尝试不同的API网关(API Gateway)或服务网格(Service Mesh)?想通过每个服务的入口和出口流量来进行链接追踪?通过依赖解析器的自动化分析,开发者可以自由地试验新工具和配置,而无需分散精力对现有的任何代码进行更改。
那为什么分布式应用的依赖管理还没出现?
依赖解析将是一个非常强大的解决方案,使得开发者能够相互协作并对云原生应用做出贡献,那么我们能使用现有的包管理器来帮助实现吗?尽管使用现有的工具也许可行,但解决网络应用的依赖关系和解决二进制库文件之间还是有着明显的差异。
依赖的二进制库文件是在主库文件编译构建时才下载的,但是微服务不会捆绑到单个二进制文件中,它们需要作为独立服务运行,然后通过网络连接交互形成一个完整的应用。这意味着解析有着额外的步骤,并且发生在与二进制库完全不同的生命周期阶段。事实证明,可以正确解析分布式应用依赖关系的最早时间是在部署阶段。正是在部署时,我们既知道技术栈中所有服务间的关系,也了解正确配置和连接服务所需的工具与目标环境细节。
结语
总而言之,目前还很难创建一个大规模的解析器用于解决网络依赖,但这样做会给工程团队和整个云社区带来巨大的好处。如果我们要正确引导云原生工具的发展方向,我们就需要从过去的依赖管理实践中总结学习。
原文标题:Why Distributed Apps Need Dependency Management,作者:David Thor