1.4 声明式与命令式结合:声明式脚本流水线

在1.3节中,我们讲解了常见的声明式系统及其优势:对使用者屏蔽细节,只关心最终状态,且声明式描述文件更加容易进行版本化管理。

对使用者屏蔽细节意味着在软件内部需要考虑并处理这些细节问题。细节越多,软件的开发和维护成本也就越高,因为声明式中的每个字段定义都包含了软件的抽象和实现。显然,声明式系统在带来便利的同时,也引入了新的问题——软件的复杂性和维护成本。

那么,是否有一种方法在拥有声明式系统优点的同时能避免其缺点呢?答案是声明式脚本。

Spinnaker结合声明式脚本的思想,使用它对持续部署流水线进行具体的行为描述,即“声明式脚本流水线”。

1.4.1 核心思想

声明式脚本的核心思想是同时结合声明式和命令式,脚本则可以被看作一种命令式的思想。声明式脚本提供了一种非完全声明式、部分脚本化的方法。

这种方法避免了软件实现完全声明式的高复杂度,又保留了命令式直观、简单和易用的特点。

而将声明式脚本应用在持续部署,则可以得到一种全新的思想方法——声明式脚本流水线。

借鉴声明式的思想——实现代码(描述文件)即基础设施(持续部署流水线),便于我们对流水线进行版本化管理。借鉴命令式的思想,则更加容易实现持续部署系统流水线逻辑和命令的执行步骤。

1.4.2 代码即流水线

与Terraform的“代码即基础设施”的理念相同,“代码即流水线”意味着使用代码来描述流水线每个阶段需要完成的任务。这种思想的好处是,使用代码描述能更好地进行被描述实体的版本化管理。此外,代码描述意味着标准化,即能够被复制、继承及复用。

Spinnaker描述流水线的方式是使用JSON格式的代码。代码描述了流水线的具体行为,并能够被复制和复用。以下为具体示例。

在以上示例中,每个字段的定义如下。

• application:流水线所属的应用程序的名称。

• pipelines:定义流水线的详细信息。

• pipelines[*].name:流水线的名称。

• pipelines[*].stages:组成流水线的一系列阶段。

这段典型的Spinnaker流水线代码描述了流水线只包含一个阶段(Stage),具体的动作是运行等待(wait)阶段10秒后,流水线结束。

1.4.3 步骤执行

声明式脚本能够实现串行或并行运行,且有依赖顺序执行的描述方式。在Spinnaker声明式脚本流水线中,步骤顺序和依赖关系是通过代码描述阶段的顺序及每个阶段的依赖定义来实现的。

上面的代码一共定义了4个阶段。

• 阶段one会首先运行,10秒后完成。one阶段完成后,two-a和two-b阶段将同时并行开始,因为它们都描述了依赖于first-stage的完成。

• 阶段two-a将在15秒后完成。

• 阶段two-b将在two-a阶段完成后15秒完成。

• 阶段last将在two-a阶段及two-b(通过refIds标识)阶段均完成后再开始运行。

stages集合保存的索引顺序即为阶段的执行顺序,此外,每个阶段使用requisiteStageRefIds来标记当前阶段需要依赖的阶段。这种步骤执行的思想来源于命令式脚本,例如Shell脚本用于描述从上到下有顺序的一组命令。