部署步骤框架¶
https://storyboard.openstack.org/#!/story/1753128
Ironic 社区希望支持可定制和可扩展的部署步骤,这将提供准备裸机节点(服务器)的能力,使其更好地满足使用这些节点的用户的需求。
为了支持这一点,我们建议将 Ironic 中现有的部署代码重构为一个部署步骤框架,类似于清理步骤框架。
问题描述¶
目前,Ironic 提供了一种在节点可用之前对其进行准备的方式(参见 状态图)。这是通过 清理 完成的。但是,在不知道节点用户需求的情况下,有时执行这些准备工作是不可能的、低效的或无效的。此外,可能有一些操作只有在知道用户需求后才能完成。
例如,在 清理 期间,可以配置节点以进行 RAID。但是,这可能不是节点用户想要的 RAID 配置。由于用户的需求只有在部署时才知道,因此在部署期间允许自定义 RAID 配置的机制是首选的。
诸如自定义 RAID 配置、BIOS 配置和自定义内核启动参数之类的功能,是可以通过在 Ironic 中部署时定义部署步骤来受益的一些用例。
通过部署步骤提供对此的支持是有意义的。这在概念上类似于 Ironic 已经支持的清理步骤。
提议的变更¶
本提案是提供基于用户需求执行不同部署操作的支持的第一步。(使用 traits 在部署时重新配置节点 的 RFE 是依赖于这项工作的示例。)
拟议的更改是实现一个部署步骤(或 deploy steps)框架,该框架与现有的自动化和手动 清理 框架非常相似。(这在 OpenStack Dublin PTG 上讨论并原则上达成一致。)
此更改是 Ironic 内部的。用户将无法比今天影响部署过程更多。
从概念上讲,清理步骤模型是一个简单的想法,操作员熟悉它。 具有类似的部署步骤可以提供一致性,并且由于操作员熟悉清理步骤,因此更容易采用。 它也很强大,因为最终(或者一年或两年后),一个步骤可以是清理步骤、部署步骤或两者兼而有之。
这包括重构代码以供清理和部署步骤使用。
现有的部署过程将实现为包含一个(或多个)部署步骤的列表。
什么是部署步骤?¶
与清理步骤类似,作为部署步骤的函数将用 @deploy_step 装饰,定义在 ironic/drivers/base.py 中,如下所示
def deploy_step(priority, argsinfo=None):
"""Decorator for deployment steps.
:param priority: an integer priority; used for determining the order in
which the step is run in the deployment process. (See below,
"When are deploy steps executed" for more details.)
:param argsinfo: a dictionary of keyword arguments where key is the name of
the argument and value is a dictionary as follows:
‘description’: <description>. Required. This should include
possible values.
‘required’: Boolean. Optional; default is False. True if this
argument is required.
另一种选择是使用一个装饰器来指定一个函数是清理步骤和/或部署步骤,例如:
@step(clean_priority=0, deploy_priority=0, argsinfo=None)
但是,清理步骤可以中止,而部署步骤则不能(尚未,参见下文),并且尚不清楚是否可能会为部署步骤装饰器添加其他参数。 因此,为部署步骤使用单独的装饰器似乎更安全、更简单。(将一个装饰器用于两种类型的步骤留作未来的练习。)
虽然 Ironic 允许中止清理,但 Ironic 不允许中止部署(尽管有一个 RFE 支持在 deploy_wait 中中止)。 因此,这不在本规范的范围内。
部署步骤可以由任何接口实现,而不仅仅是 DeployInterface。
何时执行部署步骤?¶
每个部署步骤都有一个优先级;一个非负整数。 在第一阶段,优先级将被硬编码。 将无法关闭或更改这些优先级。
步骤的执行顺序是从最高优先级到最低优先级。 优先级为零 (0) 的步骤将不会执行。 一个步骤必须完成,才能启动下一个步骤。
备选方案¶
可能还有其他方法来为每个用户/实例提供可定制的部署步骤支持,但似乎没有理由采用与清理步骤不同的设计。
我们可以选择不提供基于每个用户/实例的自定义部署步骤支持。 在这种情况下,一些当前解决此问题的解决方法包括:
预先配置节点组(使用清理步骤)以用于每种所需的配置组合。 这可能会导致奇怪的容量规划问题。
在每个节点部署后执行所需的配置步骤。 由于这些配置步骤是在部署后执行的,因此大多数步骤都需要重新启动节点,需要编排来正确执行这些重新启动,并且这会导致生产环境中不可接受的性能问题。 这种方法不适用于启动盘 RAID 等预部署步骤。
用户可以为每个用例创建自己的镜像。 但是,限制在于镜像的数量可以呈指数级增长,并且无法将特定类型的硬件与特定镜像匹配。
使用可定制的 DeployInterface,例如 ansible 部署接口(尽管 ansible 部署接口不建议用于生产环境)。 这可能无法实现对硬件或设置的相同级别的访问,从而产生相同的影响。
数据模型影响¶
与清理步骤类似,Node 对象将使用以下内容更新:
一个新的
deploy_step字段:这是当前正在执行的部署步骤,或者如果没有执行任何步骤,则为 None。 这需要更新数据库。driver_internal_info['deploy_steps']:要执行的部署步骤列表。driver_internal_info['deploy_step_index']:部署步骤列表中的索引(或如果没有执行任何步骤,则为 None);这对应于 node.deploy_step。
状态机影响¶
不会添加任何新的状态或转换。
节点的状态将在 states.DEPLOYING (deploying) 和 states.DEPLOYWAIT (wait call-back) 之间交替,用于每个异步部署步骤。
REST API 影响¶
不会有任何新的 API 方法。
GET /v1/nodes/*¶
返回节点信息的 GET /v1/nodes/* 请求将被修改,以同时返回节点的 deploy_step 字段以及节点 driver_internal_info 字段中的部署相关信息。
与 clean_step 字段类似,deploy_step 字段将是当前正在执行的部署步骤,或者如果没有正在进行的部署(或尚未开始),则为 None。
如果部署失败,deploy_step 字段将显示导致部署失败的步骤。
此更改需要一个新的 API 版本。 对于尚未使用部署步骤部署的节点,deploy_step 字段将为 None,并且 driver_internal_info 字段中将不会有任何部署相关条目。
对于旧的 API 版本,将不会提供此 deploy_step 字段,尽管 driver_internal_info 字段中的任何部署相关条目都将显示出来。
客户端 (CLI) 影响¶
唯一的更改(在指定新的 API 版本时)是,Node 的响应将包括新的 deploy_step 字段,并且在部署期间,节点 driver_internal_info 字段中的新的部署步骤相关条目。
“ironic” CLI¶
即使已弃用,响应也将包含上述更改。
“openstack baremetal” CLI¶
响应将包含上述更改。
RPC API 影响¶
无。
驱动程序 API 影响¶
与清理类似,这些方法将添加到 drivers.base.BaseInterface 类中
def get_deploy_steps(self, task):
"""Get a list of deploy steps this interface can perform on a node.
:param task: a TaskManager object, useful for interfaces overriding this method
:returns: a list of deploy step dictionaries
"""
def execute_deploy_step(self, task, step):
"""Execute the deploy step on task.node.
:param task: a TaskManager object
:param step: The dictionary representing the step to execute
:raises DeployStepFailed: if the step fails
:returns: None if this method has completed synchronously, or
states.DEPLOYWAIT if the step will continue to execute
asynchronously.
"""
实际的部署步骤将在编码阶段确定;我们将从一个大的部署步骤开始(以获得框架),然后将该步骤分解为更多步骤——由现有代码和约束(例如,对树外驱动程序的支持、发布 N 中的部署步骤在发布 N+1 中拆分为多个步骤时的向后兼容性)决定。
(一旦确定,本规范将使用实际的部署步骤进行更新。)
树外接口¶
虽然 conductor 仍然支持旧方式的部署(没有部署步骤),但这种支持将被弃用,并根据 标准弃用策略 移除。(如果供应商强烈要求,可以延长弃用期;我们很灵活。)
对于没有部署步骤的树外接口,conductor 将发出(记录)弃用警告,该树外接口应更新为使用部署步骤,并且所有使用旧方式部署的节点都需要完成部署,然后才能升级到不再支持旧方式的发布版本。
Nova 驱动程序影响¶
无
Ramdisk 影响¶
不应影响 ramdisk(IPA)。
将来,当我们允许配置和指定每个节点上的部署步骤时,我们可能会提供从 ramdisk 收集部署步骤的支持,但这不在本阶段的范围内。
安全影响¶
无
其他最终用户影响¶
无。
可扩展性影响¶
无。
性能影响¶
无。
其他部署者影响¶
无。
开发人员影响¶
DeployInterfaces(以及参与部署过程的任何其他接口)需要编写时考虑到部署步骤。
实现¶
负责人¶
- 主要负责人
rloo (Ruby Loo)
工作项¶
- Ironic
将部署步骤的支持添加到基本驱动程序
将现有代码重构为一个或多个部署步骤
更新 conductor 以获取部署步骤并执行它们
python-ironicclient:添加对 node.deploy_step 的支持
依赖项¶
无。
测试¶
所有新代码和更改行为的单元测试
CI 作业已经测试了部署过程;这些更改后它们应该继续工作
升级和向后兼容性¶
旧接口将与新的 BaseInterface 类一起工作,因为代码将在接口不支持
get_deploy_steps()时干净地回退。 将记录弃用警告,我们将根据 OpenStack 的弃用和移除策略移除对旧方式的支持。同样,具有
get_deploy_steps()的接口实现可以在旧版本的 Ironic 中工作。在冷升级中
如果 agent 心跳并且 driver_internal_info['deploy_steps'] 为空,则以旧方式进行。
如果部署由使用部署步骤(新代码)的 conductor 启动,这意味着所有 conductor 都使用新代码,因此部署可以在任何支持该节点的 conductor 上继续。
在滚动升级中
如果 agent 心跳并且 driver_internal_info['deploy_steps'] 为空,则以旧方式进行(类似于冷升级)
如果新的 conductor 被固定到旧版本(通过
pin_release_version配置选项),则不会使用部署步骤机制。 如果部署由使用部署步骤(新代码)的 conductor 启动,这意味着它未被固定,并且所有 conductor 都使用新代码,因此部署可以在任何支持该节点的 conductor 上继续。
文档影响¶
api-ref: https://developer.openstack.org/api-ref/baremetal/ 将更新为包含新的 node.deploy_step 字段