移除 TripleO Heat 模板中的 merge.py

https://blueprints.launchpad.net/tripleo/+spec/tripleo-juno-remove-mergepy

merge.py 是我们 Heat 模板中长期积累技术债务的地方[0],我们的意图是在 Heat 满足我们的模板需求时逐步摆脱它。

它的主要功能包括将较小的模板片段组合成一个描述完整 TripleO 部署的模板,合并某些资源以减少重复,同时保持片段本身作为独立模板的功能,以及支持对 Heat 资源进行手动扩展。

本文档描述了为了迁移到不依赖 merge.py 的模板所需的更改。我们将尽可能使用 Heat 的原生功能,并记录其余部分,可能推动 Heat 模板格式的新增功能。

这很大程度上基于 2014 年 4 月在 openstack-dev 中的讨论[1]

问题描述

由于 merge.py 的文档记录不完整,我们的模板对于新手来说难以理解或修改(即使他们已经熟悉 Heat)。

它一直被认为是一种短期措施,而 Heat 现在可以提供我们在模板中所需的大部分功能。

提议的变更

我们将从对我们的模板和 merge.py 进行小的、保持正确性的更改开始,这些更改将使我们能够使用更多的 Heat 原生功能。如果由于某种原因我们无法进行更改,我们将向 Heat 提交错误报告,并与他们合作以解决该问题。

一旦我们达到需要对模板结构进行重大更改的阶段,我们将它们拆分为新文件,并在我们的 CI 中启用它们作为并行实现。

当我们确信新模板满足与原始模板相同的要求时,我们将弃用旧模板,弃用 merge.py,并将其切换为默认模板。

完成过渡所需的行动项目列表如下。

1. 移除自定义资源类型

TripleO Heat 模板和 merge.py 包含两个自定义类型,它们(在迁移到软件配置[8][9] 后)不再用于任何目的

  • OpenStack::ImageBuilder::Elements

  • OpenStack::Role

我们将从模板中删除它们,并在 merge 工具中弃用它们。

2. 移除组合白名单资源类型

如果我们在模板中拥有两个名称相同的 AWS::AutoScaling::LaunchConfiguration 资源,merge.py 将合并它们的 PropertiesMetadata。在软件配置更新后,我们的模板不再使用此功能。

3. 将 TripleO Heat 模板移植到 HOT

在移除了大部分非 Heat 语法后,将我们的 CFN/YAML 模板移植到纯 HOT 格式[2] 应该会很直接。

我们还需要更新 merge.py。我们应该能够同时支持旧格式和 HOT。

我们可以通过查找 HOT 语法中是必需的顶级部分 heat_template_version 来区分两者。

merge.py 的大部分更改将围绕拼写(Parameters -> parametersResources -> resources)以及不同的内置函数名称等(Fn::GetAtt -> get_attr)。

这项任务需要对我们所有的模板进行语法更改,不幸的是,这并不是不同的人可以逐步更新的事情。我们应该能够分别更新 undercloud 和 overcloud 部分,但我们不能只更新 overcloud 的一部分。此时,我们仍然使用 merge.py 将模板组合在一起,并且最终会得到一个同时具有 CFN 和 HOT 片段的模板。

4. 迁移到 Provider 资源

Heat 允许在部署堆栈时传入多个模板。这些模板可以映射到自定义资源类型。每个模板将代表一个角色(计算服务器、控制器、块存储等),其 parametersoutputs 将映射到自定义资源的 propertiesattributes

这些角色将从主模板(overcloud.yamlundercloud.yaml)引用,并最终封装在扩展资源(OS::Heat::ResourceGroup[5])或我们采用的任何扩展机制中。

注意

Provider 资源代表完全功能、独立的模板。任何 provider 资源模板都可以传递给 Heat 并转换为堆栈,或在更大的部署中作为自定义资源处理。

以下是 compute.yaml 的假设大纲

parameters:
  flavor:
    type: string
  image:
    type: string
  amqp_host:
    type: string
  nova_compute_driver:
    type: string

resources:
  compute_instance:
    type: OS::Nova::Server
    properties:
      flavor: {get_param: flavor}
      image: {get_param: image}

  compute_deployment:
    type: OS::Heat::StructuredDeployment
    properties:
      server: {ref: compute_instance}
      config: {ref: compute_config}
      input_values:
        amqp_host: {get_param: amqp_host}
        nova_compute_driver: {get_param: nova_compute_driver}

  compute_config:
    type: OS::Heat::StructuredConfig
      properties:
        group: os-apply-config
        config:
          amqp:
            host: {get_input: amqp_host}
          nova:
            compute_driver: {get_input: nova_compute_driver}
          ...

我们将对所有其他角色(controller.yamlblock-storage.yamlswift-storage.yaml 等)使用类似的结构。也就是说,每个角色将包含 OS::Nova::Server、相关的部署以及任何其他必需的资源(随机字符串生成器、安全组、端口、浮动 IP 等)。

我们可以使用 Heat 环境[4] 将角色映射到自定义类型。

role_map.yaml:

resource_registry:
  OS::TripleO::Compute: compute.yaml
  OS::TripleO::Controller: controller.yaml
  OS::TripleO::BlockStorage: block-storage.yaml
  OS::TripleO::SwiftStorage: swift-storage.yaml

最后,我们将有一个将所有内容组合在一起的主模板。

overcloud.yaml:

parameters:
  compute_flavor:
    type: string
  compute_image:
    type: string
  compute_amqp_host:
    type: string
  compute_driver:
    type: string
  ...

resources:
  compute0:
    # defined in controller.yaml, type mapping in role_map.yaml
    type: OS::TripleO::Compute
    parameters:
      flavor: {get_param: compute_flavor}
      image: {get_param: compute_image}
      amqp_host: {get_param: compute_amqp_host}
      nova_compute_driver: {get_param: compute_driver}

  controller0:
    # defined in controller.yaml, type mapping in role_map.yaml
    type: OS::TripleO::Controller
    parameters:
      flavor: {get_param: controller_flavor}
      image: {get_param: controller_image}
      ...

outputs:
  keystone_url:
    description: URL for the Overcloud Keystone service
    # `keystone_url` is an output defined in the `controller.yaml` template.
    # We're referencing it here to expose it to the Heat user.
    value: { get_attr: [controller_0, keystone_url] }

以及类似地,undercloud.yaml

注意

各个角色(compute.yamlcontroller.yaml)的结构方式是,它们可以作为独立堆栈启动(即,为了测试计算实例,可以键入 heat stack-create -f compute.yaml -P ...)。事实上,Heat 内部将 provider 资源视为嵌套堆栈。

5. 移除 merge.py 中的 FileInclude

FileInclude 的目标是使单个角色(借用 TripleO UI 中的术语)作为可以作为独立模板启动的模板可行。规范示例是 nova-compute-instance.yaml[3]

随着迁移到 provider 资源,FileInclude 变得不再必要。

6. 将模板迁移到 Heat 原生扩展

资源的扩展当前由 merge.py 处理。命令行参数 --scale 接受资源名称并根据需要复制它(这比这更复杂,但无关紧要)。

Heat 具有原生扩展资源 OS::Heat::ResourceGroup[5],它基本上执行相同的操作

scaled_compute:
  type: OS::Heat::ResourceGroup
  properties:
    count: 42
    resource_def:
      type: OS::TripleO::Compute
      parameters:
        flavor: baremetal
        image: compute-image-rhel7
        ...

这将创建 42 个计算主机实例。

7. 将 Merge::Map 替换为扩展组的内部属性

我们正在使用自定义的 Merge::Map 辅助函数来从扩展服务器中获取值

ResourceGroup 资源支持选择内部资源的属性,以及从所有资源获取相同的属性并将其作为列表返回。

获取控制器节点 IP 地址的示例

{get_attr: [controller_group, resource.0.networks, ctlplane, 0]}

(controller_group 是我们的控制器节点的 ResourceGroupctlplane 是我们的控制平面网络)

获取所有控制器节点名称的列表示例

{get_attr: [controller_group, name]}

Merge::Map 的更复杂用法涉及以某种方式格式化返回的数据,例如为 haproxy 构建 {ip: ..., name: ...} 字典,或生成 /etc/hosts 文件。

由于我们的 ResourceGroups 不会直接使用 Nova 服务器,而是使用 provider 资源和环境的自定义角色类型,我们可以将此数据格式化放入角色的 outputs 部分,然后使用上述相同的机制。

构建 haproxy 节点条目的示例

# overcloud.yaml:
resources:
  controller_group:
    type: OS::Heat::ResourceGroup
    properties:
      count: {get_param: controller_scale}
      resource_def:
        type: OS::TripleO::Controller
        properties:
          ...

  controllerConfig:
    type: OS::Heat::StructuredConfig
    properties:
      ...
      haproxy:
        nodes: {get_attr: [controller_group, haproxy_node_entry]}



# controller.yaml:
resources:
  ...
  controller:
    type: OS::Nova::Server
    properties:
      ...

outputs:
  haproxy_node_entry:
    description: A {ip: ..., name: ...} dictionary for configuring the
      haproxy node
    value:
      ip: {get_attr: [controller, networks, ctlplane, 0]}
      name: {get_attr: [controller, name]}

替代方案

这个提议非常具体且与 Heat 相关。另一种选择是无所作为,继续使用和发展 merge.py。这从来不是意图,并且核心团队的大多数成员不认为这是一个可行的长期选择。

安全影响

此提议不会以任何方式影响 TripleO 的整体功能。它只是更改了 TripleO Heat 模板的存储和编写方式。

如果真有,这将使我们转向更标准,从而更容易审计的模板。

其他最终用户影响

对于使用 vanilla TripleO 的用户来说,不应有任何影响。

更高级的用户可能希望自定义现有的 Heat 模板或编写自己的模板。当我们将依赖于标准 Heat 功能时,这将变得更容易。

性能影响

这将把一些模板组装负担从 merge.py 转移到 Heat。它可能最终也会在后台产生更多的资源和嵌套堆栈。

据我们所知,没有人以我们不可避免地要达到的规模测试过这些功能。

在着手可能影响这些功能(provider 配置和扩展)的更改之前,我们需要在 Tempest 中运行规模测试,以确保 Heat 可以应对负载。

这些测试可以模拟 large_ops 场景:一个 Heat 模板,它创建和销毁一个包含 50 个 Nova 服务器资源以及相关软件配置的堆栈。

我们应该有两个测试来评估更改前后的性能

  1. 一个带有 50 个相同服务器资源和软件配置/部署副本的单个 HOT 模板。

  2. 一个带有单个服务器及其软件配置/部署的模板,一个带有自定义类型映射的环境文件,以及一个将新类型包装在具有 50 个计数的 ResourceGroup 中的总体模板。

其他部署者影响

部署者可以继续使用 merge.py 和现有的 Heat 模板,就像以前一样——现有的脚本不应该中断。

使用新模板,Heat 将被直接调用,并且需要资源注册表(在 Heat 环境文件中)。这将意味着部署过程中的变化。

开发人员影响

这不应影响非 Heat 和非 TripleO OpenStack 开发人员。

TripleO 开发人员在编写和理解我们的 Heat 模板时可能会有一定的学习曲线。很可能,我们也会在用 Heat 功能替换 merge.py 时遇到错误或预料之外的并发症。

对 Heat 开发人员的影响将包括处理我们发现的错误和功能请求。希望这不会是一场雪崩。

实现

负责人

主要负责人

Tomas Sedovic <lp: tsedovic> <irc: shadower>

工作项

  1. 移除自定义资源类型

  2. 移除组合白名单资源类型

  3. 将 TripleO Heat 模板移植到 HOT

  4. 迁移到 Provider 资源

  5. 移除 merge.py 中的 FileInclude

  6. 将模板迁移到 Heat 原生扩展

  7. 替换 Merge::Map 为扩展组的内部属性

依赖项

  • Heat 的 Juno 版本

  • 能够在 Heat 中杀死特定节点(用于缩小规模或因为它们出现故障)- 相关的 Heat 蓝图:autoscaling-parameters

测试

所有这些更改都将对 tripleo-heat-templates 存储库进行,并且应该可以通过我们的 CI 进行测试,就像任何 t-h-t 更改一样。

此外,我们需要添加 Tempest 场景进行扩展,以确保 Heat 可以处理负载。

文档影响

我们需要更新 devtest部署 TripleO使用 TripleO 文档,并创建一个编写 TripleO 模板的指南。

参考资料