伴随着最近的“容器革命”,一种新概念变得流行起来:不可变基础设施。事实上,「不可变基础设施」这一概念不是刚刚冒出来的,它也不是必须需要容器技术。然而,通过容器,它变得更易于理解,更加实用,并引起了业内广泛注意。
什么是不可变基础设施?
我将其定义为在生产环境中仅通过替换组件而不是修改组件来更改基础设施。具体地说,这意味着一旦我们部署了一个组件,我们就不会再修改它了。这并不是说组件没有任何状态变化(毕竟丝毫不变也就意味着它不是一个非常实用的软件组件),而是说运维人员无须在程序的原始 API /设计之外引入任何改变。
这种情况并不罕见,举例来说,假设我们想要更改某一配置文件,而该配置文件又正在被某些应用程序使用,如果是动态基础设施,我们可能需要使用一些脚本或配置管理工具来进行这种更改。它会对有问题的服务器进行网络调用,然后执行一些代码来修改文件。它还可能了解并修改该文件的依赖关系(比如需要重启的程序)。随着时间的推移,这些关系可能会变得越来越复杂,这就是为什么许多 CM 工具都有一个资源依赖模型来帮助管理它们。
动态基础设施 VS.不可变基础设施
动态基础设施与不可变基础设施之间的权衡其实非常简单。使用网络和磁盘 IO 等资源,动态基础设施效率更高。在这种效率下,传统上它的速度要比不可变快,因为它不需要像许多版本的组件那样需要推送那么多的比特或存储。回到我们更改文件的例子。传统上,比起更换整个服务器,更换单个文件无疑更快。
另一方面,不可变的基础设施为结果提供了更强有力的保障。不可变的组件可以在部署之前预先构建,生成一次,然后重复使用,这与动态基础设施不同,后者的逻辑需要在每个实例中进行评估。这就可能造成意料之外的结果,因为您的某些环境可能处于您期望的不同状态,导致部署出现错误。您可能只是在配置管理代码中犯了某个错误,但您又无法在本地复制生产环境的状况,因此可能很难测试该结果并发现错误所在。毕竟,这些配置管理语言本身很复杂。
在“计算机协会”(ACM)杂志 ACM Queue 的一篇文章中,Google 的工程师清楚地阐述了这一挑战:
结果是,人们试图通过消除应用程序源代码中硬编码的参数来避免这种神秘的“配置”。它不会降低操作复杂性或使配置更容易调试或更改;它只是将计算从真正的编程语言转移到特定于领域的编程语言,而这个领域通常具有较弱的开发工具(例如调试器、单元测试框架等)。
容器时代的变局
效率的权衡一直是计算机工程的核心。然而,随着时间的推移,这些决策的经济学(包括技术和金融层面)都在改变。
在编程的早期,开发人员被教导使用简短的变量名,以牺牲可读性为代价来节省几字节的宝贵内存。为了解决早期硬盘驱动器的空间限制,开发了动态链接库,以便程序可以共享公共的 C 库,而不是各自需要自己的副本。
而在过去的十年里,由于计算机系统的强大功能的改变,这两种情况都发生了变化。现在,开发者的时间比我们通过缩短变量节省的成本要昂贵得多。像 Golang 和 Rust 这样的新语言甚至带来了静态编译的二进制文件,因为如果发生错误的 DLL,将无法处理平台兼容性问题。
基础设施管理正处于类似的十字路口。公有云和虚拟化不仅使得服务器(虚拟机)的速度快了几个数量级,而且像 Docker 这样的工具已经创建了易于使用的工具,来处理预先构建的服务器运行时,来通过层缓存和压缩来实现高效的资源使用。这些功能使得不可变的基础设施变得实用,因为它们是如此的轻量级和无摩擦。
Kubernetes 在不久之后也进入了这一领域,接过这一火炬并继续朝着目标,创建了一个“云原生”原语的 API,它假定并鼓励了一种不可改变的哲学。例如,ReplicaSet 假设在我们的应用程序生命周期的任何时候,我们都可以(并且可能需要)重新部署我们的应用程序。为了平衡这一点,Pod Disruption Budgets(Pod 应急预算)将指导 Kubernetes 应用程序如何重新部署。
以上种种进步的融合使我们进入了不可改变的基础设施时代。而且随着更多的公司参与进来,这个数字还会增加。今天的工具使我们比以往更容易接受这些模式。那么,你还在等什么?
评论