gitbook/持续交付36讲/docs/13535.md
2022-09-03 22:05:03 +08:00

11 KiB
Raw Permalink Blame History

20 | Immutable任何变更都需要发布

在专栏的第13讲[《容器技术真的是环境管理的救星吗?》](https://time.geekbang.org/column/article/12404)中我们初步结识了不可变基础设施Immutable Infrastructure这里我们再一起回顾一下

在这种模式中,任何基础设施的实例(包括服务器、容器等各种软硬件)一旦创建之后便成为一种只读状态,不可对其进行任何更改。如果需要修改或升级某些实例,唯一的方式就是创建一批新的实例来替换它。

这种思想与不可变对象的概念完全相同。

为什么我会说,不可变基础设施的思想对持续交付的影响非常深远呢?因为不可变的思想正是解决了持续交付一直没有解决的一个难题,即环境、顺序、配置这些基础设施在测试环节和生产环节的不一致性所带来的问题。

那么,今天我就来详细分析一下不可变基础设施的由来、影响,以及如何实现的相关内容。

从持续交付中来

如果你是一个程序员其实很容易理解不可变基础设施的概念以及其实现的方式。因为它就和Java中的不可变类完全相同类实例一旦创建就无法变更而可以变更的是指向实例的引用。

其实早在2011年出版的《持续交付发布可靠软件的系统方法》一书中就曾提到“蓝绿发布”的概念你需要更新一组实例但并不是直接在原有实例上进行变更而是重新启动一批对等的实例在新实例上更新然后再用新实例替换老实例。此时老实例仍旧存在以便回滚。

其实,这完全就是对不可变类的物理实现,也就是一个典型的不可变模型。

这里,我抛出了“不可变模型”的概念,那么我们再垂直一些,再来看看由不可变模型转化到不可变基础设施,又会有哪些具体的要求。

综合起来一句话,就是:

对任何的包、配置文件、软件应用和数据都不做CRUD创建、替换、更新、删除操作。

也就是说,对于已经存在的基础设施,不再在其上创造任何新的事物。根据不可变模型,推导得出取而代之的方法则是:

  1. 构建一个新的基础设施;

  2. 测试新的基础设施是否符合需求;

  3. 将引用指向这个新的基础设施;

  4. 保留原有基础设施以备回滚。

虽然不可变模型的设想很好,但其中也会有一些特殊情况存在。比如,涉及数据的部分,特别是数据库,你不可能每次都重建一个数据库实例来达到“不可变”的目的。为什么呢?其根本原因是,数据库是有状态的。所以,从这里可以清楚地看到,不可变Immutable的前提是无状态。

不可变基础设施的神话

说到为什么会需要“不可变基础设施”这种方法论,还是挺有意思的。

首先是一个假设:如何保证两个实例的行为完全一致?最有效的方式是,在两个实例上以同样的顺序执行同样的变更。

然后在2002年Steve Traugott 写了一篇名为《为什么顺序很重要》Why Order Matters的论文,虽然论文本身涉及了很多数学推理,有些难懂,但它却很好地解释了为什么大规模基础设施是不可变的,并证明了不可变的价值所在。

第一,一致是最终的目标

在论文中,作者讨论了发散、收敛和一致三种模型。

  1. 发散, 是我们通常会碰到的基础设施的管理模型。在这个模型中,基础设施随着我们的想法而变化,也就是我们想更新什么就更新什么,最终就会形成一种发散的形态。

《Why Order Matters》论文中的发散模型

  1. 收敛, 是Puppet和Chef遵循的设计原则。随着时间推移目标和实际需求汇聚达到一致。通过这个模型我们有了可扩展的基础设施的基础和实现。

《Why Order Matters》论文中的收敛模型

  1. 一致, 指的是整个基础设施始终把每一天当成是与第一天相同的模型。根据我们之前的假设,达到这一目的的关键点就在于,有序地正确执行从真正的第一天开始的所有变更。

《Why Order Matters》论文中的一致模型

那么你就会有疑问,为什么会有一致模型?通过发散和收敛这两个模型,没办法解决实例完全一致的问题吗?答案是,确实不行。

即使我们知道发散是一种不良状态,我们可以通过定期的收敛,将基础设施不断地趋向所期望的目标。但其中也会碰到很多问题。

  1. 顺序问题:你只有完全保证顺序的正确性,结果才会正确。但是,怎么保证顺序呢?特别是执行结果与你的预期不一致时,比如发生错误时,就会多出一些其他的处理步骤,直接影响原有的既定顺序。这也是为什么顺序那么重要的原因。

  2. 频率问题:假设你可以通过一些方法保证顺序,在面对大型基础设施时,应该如何制定收敛频率呢?最简单的回答,自然是越频繁越好。
    那么你就会陷入巨大的陷阱中,你会发现完全无法支撑并发的收敛工作。而且收敛工作与设施的规模成正比,直接否定了系统的可扩展性。

  3. 蝴蝶效应:你始终无法确定一个绝对的基准点,认为是系统的初始或者当前应该有的状态。因为你始终还在收敛中,只是无限趋近。因此任何小偏差,都会引起将来重大的、不可预知的问题。这就是蝴蝶效应。

但是,容器却通过分层镜像与镜像发布技术,解决了上面的顺序问题、频率问题和蝴蝶效应。所以说,容器是一个惊人的发明,它使得每一次变更都成为了一次发布,而每一次发布都成为了系统的重新构建, 从而使得“一致”模型的目标能够达成。

第二Immutable的衍生

当然除了容器之外Immutable 理念还有许多不同的衍生。比如黄金映像、VDI虚拟桌面、Phoenix Server和基础设施即代码。

  • 黄金映像,指的是将绝大部分不变的基础设施(包括操作系统、大多数软件、基本配置等),包含在映像内,只留很少一部分变更通过脚本执行解决;
  • VDI虚拟桌面指的是操作系统运行在后端的服务器上用户只使用属于他自己的虚拟桌面无法改变后端的系统内容
  • Phoenix Server指的是完全被破坏的服务器能够从灰烬中自动进行恢复
  • 基础设施即代码,指的是把基础设施的构建以代码的方式组织起来,从而通过运行代码可以完全构建出你想要的全部基础设施。

这些衍生技术都遵循Immutable的理念曾在不同的场景下比如快速灾备、快速恢复系统、增强系统健壮性等方面发挥了巨大的作用从而收益。

但是,随着技术的发展,这些衍生技术不再能够适应我们对速度和扩展性的要求,加之容器技术的蓬勃发展,使得这一系列的难题都可以通过容器技术解决,因此我们已经完全可以把注意力放到容器上了。

回到持续交付中去

虽然我们一起从持续交付中走了出来,了解了不可变基础设施的种种,但最终我们还是要回持续交付中去,去解决构建持续交付平台的问题。那么,接下来我们就一起看看面对容器时代的新形式,持续交付要做哪些变化吧。

“不可变”模型的好处,已经显而易见。而对于容器时代的持续交付,也显然已经从原来单纯交付可运行软件的范畴,扩展为连带基础环境一起交付了,所以我们需要为此做好准备。

上文中,我已经总结了一句话,每一次变更都是一次发布,而每一次发布都是系统重新构建,更形象点说,每一次发布都是一个独立镜像的启动。所有持续交付的变化也都可以表现为这样一句话,那具体怎么理解呢。

首先任何的变更包括代码的、配置的、环境的甚至是CPU、内存、磁盘的大小变化都需要制作成独立版本的镜像。

其次,变更的镜像可以提前制作,但必须通过发布才能生效。 这有2个好处

  1. 重新生成新的实例进行生效,完全遵循不可变模型的做法;

  2. 发布内容既包含代码也包含基础设施更有利于DevOps的实施。

再次,一组运行中的同一个镜像的实例,因为“不可变”的原因,其表现和实质都是完全一样的,所以不再需要关心顺序的问题。因为任何一个都等价,所以也就没有发布或替换的先后问题了。

最后,根据“一致”模型的要求,我们需要记录系统从第一天发展到今天的所有有序变更。 对Docker而言不仅要能向上追溯层层Base镜像的情况更建议将系统和软件的配置以Dockerfile的方式进行处理以明确整个过程的顺序。

这些理念,不仅传统的持续交付中没有涉及,甚至有些还与我们日常的理解和习惯有所不同。比如,你通常认为一个集群中的不同服务器的配置是可以不一样的,但在“不可变”模型中,它是不被允许的。

当然,我在之前的《容器技术真的是环境管理的救星吗?》一文中也提到过Immutable对持续交付的环境管理来说确实有点违反人性。所以容器对持续交付的影响可以说是利弊都有吧。

也因此,持续交付中遇到“不可变”,更应该去理解它的概念和用意,合理发挥其优势。

总结

首先,我分享了“不可变”模型的概念,以及它的由来,介绍了三个非常有价值的模型:发散模型、收敛模型和一致模型。

其次,我解释了为什么“不可变”如此重要的原因,也就是重复发散到收敛过程无法解决的三个问题:顺序问题、频率问题和蝴蝶效应。

最后,我针对“不可变”及容器,提出了持续交付面对的新问题,即:每一次变更都是一次发布,每一次发布都是一个独立的镜像的启动。

思考题

你所在的公司有没有什么地方可以体现出“不可变”思想?如果没有,是什么原因呢?

感谢你的收听,欢迎你给我留言。