蚊章汇 - 部署
type: channel
link: https://linuxcn.bwsl.wang/tag/%E9%83%A8%E7%BD%B2/
items: 解决 CI/CD 中的仓库阻抗失配
items: 一位老极客的眼中的开发和部署

解决 CI/CD 中的仓库阻抗失配
type: item
date: 2022-03-26T19:18:00+08:00
description: 对齐部署镜像和描述符是很困难的，但是某些策略可以使整个过程更高效。在软件架构中，当两个组件之间有某些概念性或技术上的差异时会出现 阻抗失配 impedance mismatch 。这个术语其实是从电子工程中借用的，表示电路中输入和输出的电子阻抗必须要匹配。在软件开发中，存储在镜像仓库中的镜像与存储在源码控制管理系统（LCTT 译注：SCM，Source Code Management）中它的 部署描述符 deployment descriptor 之间存在阻抗失配。你如何确定存储在 SCM 中的部署描述符表示的是正确的镜像？两个仓库追踪数据的方式并不一致，因此将一个镜像（在镜像仓库中独立存储的不可修改的二进制）和它的部署描述符（Git 中以文本文件形式存储的一系列修改记录）相匹配并不那么直观。注意：本文假定读者已经熟悉以下概念： 源码控制管理 Source Control Management （SCM）系统和分支Docker 或符合 OCI 标准的镜像和容器 容器编排系统 Container Orchestration Platforms （COP），如 Kubernetes 持续集成/持续交付 Continuous Integration/Continuous Delivery （CI/CD） 软件开发生命周期 Software development lifecycle （SDLC）环境阻抗失配：SCM 与镜像仓库为了更好地理解阻抗失配在什么场景下会成为问题，请考虑任意项目中的软件开发生命周期环境（SDLC），如开发、测试或发布环境。测试环境不会有阻抗失配。现在使用 CI/CD 的最佳实践中开发分支的最新提交都会对应开发环境中的最新部署。因此，一个典型的、成功的 CI/CD 开发流程如下：向 SCM 的开发分支提交新的修改新提交触发一次镜像构建新生成的镜像被推送到镜像仓库，标记为开发中镜像被部署到容器编排系统（COP）中的开发环境，该镜像的部署描述符也更新为从 SCM 拉取的最新描述符。换句话说，开发环境中最新的镜像永远与最新的部署描述符匹配。回滚到前一个构建的版本也不是问题，因为 SCM 也会跟着回滚。最终，随着开发流程继续推进，需要进行更多正式的测试，因此某个镜像 —— 镜像对应着 SCM 中的某次提交 —— 被推到测试环境。如果是一次成功的构建，那么不会有大问题，因为从开发环境推过来的镜像应该会与开发分支的最新提交相对应。开发环境的最新部署被允许入库，触发入库过程最新部署的镜像被标记为测试中镜像在测试环境中被拉取和部署，（该镜像）对应从 SCM 拉取的最新部署描述符到目前为止,一切都没有问题，对吗？如果出现下面的场景，会有什么问题？场景 A：镜像被推到下游环境，如 用户验收测试 user acceptance testing （UAT），或者是生产环境。场景 B：测试环境中发现了一个破坏性的 bug，镜像需要回滚到某个确定正常的版本。在任一场景中，开发过程并没有停止，即开发分支上游有了一次或多次新的提交，而这意味着最新的部署描述符已经发生了变化，最新的镜像与之前部署在测试环境中的镜像不一致。对部署描述符的修改可能会也可能不会对之前版本的镜像起作用，但是它们一定是不可信任的。如果它们有了变化，那么它们就一定与目前为止你测试过的想要部署的镜像的部署描述符不一致。问题的关键是：如果部署的镜像不是镜像库中的最新版本，你怎么确定与部署的镜像相对应的是 SCM 中的哪个部署描述符？ 一言以蔽之，无法确定。两个库直接有阻抗失配。如果要详细阐述下，那么是有方法可以解决的，但是你需要做很多工作，这部分内容就是文章接下来的主题了。请注意，下面的方案并不是解决问题的唯一办法，但是已经投入到生产环境并已经对很多项目起了作用，而且已经被构建并部署到生产环境中运行了超过一年。二进制与部署描述符源码通常被构建成一个 Docker 镜像或符合 OCI 标准的镜像，该镜像通常被部署到一个容器编排平台（COP）上，如 Kubernetes。部署到 COP 需要部署描述符来定义镜像被如何部署以及作为容器运行，如 Kubernetes 部署 或 CronJobs。这是因为在镜像和它的部署描述符之间有本质差异，在这里可以看到阻抗失配。在这次讨论中，我们认为镜像是存储在镜像仓库中不可修改的二进制。对源码的任何修改都不会修改镜像，而是用另一个新的镜像去替换它。相比之下，部署描述符是文本文件，因而可以被认为是源码且可修改。如果遵循最佳实践，那么部署描述符是被存储在 SCM，所有修改都会提交，而这很容易回溯。解决阻抗失配建议的解决方案的第一部分，就是提供一个能匹配镜像仓库中的镜像与对保存部署描述符的 SCM 做的代码提交的方法。最直接的解决方案是用源提交的哈希值标…

一位老极客的眼中的开发和部署
type: item
date: 2017-05-25T21:08:00+08:00
description: 多年前，我曾是一名 Smalltalk 程序员，这种经验让我以一种不同的视角来观察编程的世界，例如，需要花时间来适应源代码应该存储在文本文件中的这种做法。我们作为程序员通常会区分“开发”和“部署”，特别是我们在开发的地方所使用的工具不同于我们在之后部署软件时的地点和工具时。而在 Smalltalk 世界里，没有这样的区别。Smalltalk 构建于虚拟机包含了你的开发环境（IDE、调试器、文本编辑器、版本控制等）的思路之上，如果你需要修改任何一处代码，你得修改内存中运行副本。如果需要的话，你可以为运行中的机器做个快照；如果你想分发你的代码，你可以发送一个运行中的机器的镜像副本（包括 IDE、调试器、文本编辑器、版本控制等）给用户。这就是上世纪 90 年代软件开发的方式（对我们中的一些人来说）。如今，部署环境与开发环境有了很大的不同。起初，你不要期望那里（指部署环境）有任何开发工具。一旦部署，就没有版本控制、没有调试、没有开发环境。有的是记录和监视，这些在我们的开发环境中都没有，而有一个“构建管道”，它将我们的软件从开发形式转换为部署形式。作为一个例证，Docker 容器则试图重新找回上世纪 90 年代 Smalltalk 程序员部署体验的那种简单性，而避免同样的开发体验。我想如果 Smalltalk 世界是我唯一的编程方面的体验，让我无法区分开发和部署环境，我可能会偶尔回顾一下它。但是在我成为一名 Smalltalk 程序员之前，我还是一位 APL 程序员，这也是一个可修改的虚拟机镜像的世界，其中开发和部署是无法区分的。因此，我相信，在当前的时代，人们编辑单独的源代码文件，然后运行构建管道以创建在编辑代码时尚不存在的部署作品，然后将这些作品部署给用户。我们已经以某种方式将这种反模式的软件开发制度化，而不断发展的软件环境的需求正在迫使我们找回到上世纪 90 年代的更有效的技术方法。因此才会有 Docker 的成功，所以，我需要提出我的建议。我有两个建议：我们在运行时系统中实现（并使用）版本控制，以及，我们通过更改运行中的系统来开发软件，而不是用新的运行系统替换它们。这两个想法是相关的。为了安全地更改正在运行的系统，我们需要一些版本控制功能来支持“撤消”功能。也许公平地说，我只提出了一个建议。让我举例来说明。让我们开始假设一个静态网站。你要修改一些 HTML 文件。你应该如何工作？如果你像大多数开发者一样，你会有两个，也许三个网站 - 一个用于开发，一个用于 QA（或者预发布），一个用于生产。你将直接编辑开发实例中的文件。准备就绪后，你将把你的修改“部署”到预发布实例。在用户验收测试之后，你将再次部署，这次是生产环境。使用 Occam 的 Razor，让我们可以避免不必要地创建实例。我们需要多少台机器？我们可以使用一台电脑。我们需要多少台 web 服务器？我们可以使用具有多个虚拟主机的单台 web 服务器。如果不使用多个虚拟主机的话，我们可以只使用单个虚拟主机吗？那么我们就需要多个目录，并需要使用 URL 的顶级路径来区分不同的版本，而不是虚拟主机名。但是为什么我们需要多个目录？因为 web 服务器将从文件系统中提供静态文件。我们的问题是，目录有三个不同的版本，我们的解决方案是创建目录的三个不同的副本。这不是正是 Subversion 和 Git 这样的版本控制系统解决的问题吗？制作目录的多个副本以存储多个版本的策略回到了版本控制 CVS 之前的日子。为什么不使用比如说一个空的的 Git 仓库来存储文件呢？要这样做，web 服务器将需要能够从 git 仓库读取文件（参见 mod\_git）。这将是一个支持版本控制的运行时系统。使用这样的 web 服务器，使用的版本可以由 cookie 来标识。这样，任何人都可以推送到仓库，用户将继续看到他们发起会话时所分配的版本。版本控制系统有不可改变的提交； 一旦会话开始，开发人员可以在不影响正在运行的用户的情况下快速推送更改。开发人员可以重置其会话以跟踪他们的新提交，因此开发人员或测试人员就可能如普通用户一样查看在同台服务器上同一个 URL 上正在开发或正在测试的版本。作为偶然的副作用，A/B 测试仅仅是将不同的用户分配给不同的提交的情况。所有用于管理多个版本的 git 设施都可以在运行环境中发挥作用。当然，git reset 为我们提供了前面提到的“撤销”功能。为什么不是每个人都这样做？一种可能性是，诸如版本控制系统的工具没有被设计为在生产环境中使用。例如，给某人推送到测试分支而不是生产分支的许可是不可能的。对这个方案最常见的反对是，如果发现了一个漏洞，你会想要将某些提交标记为不可访问。这将是另一种更细粒度的权限的情况；开发人员将具有对所有提交的读取权限，但外部用户不会。我们可能需要对现有工…