SPI 方法验证镜像¶
https://blueprints.launchpad.net/sahara/+spec/validate-image-spi
本规范详细描述了向 Sahara 插件 SPI 添加一种方法,以验证所选镜像是否符合插件要求的规范。虽然预计插件编写者将无法对镜像进行足够深入的测试,以确保给定的任意镜像能够在集群生成中成功运行并在所有上下文中发挥作用,但希望通过良好地实现此方法,插件作者可以提供一个定义明确、机器可操作的契约,该契约将与插件本身一起进行版本控制。
问题描述¶
目前,Sahara 的镜像生成和集群配置功能几乎完全解耦:sahara-image-elements 生成镜像,服务器获取该镜像并假定其有效。这引入了 sahara-image-elements 和 Sahara 之间版本不兼容的可能性,以及在任一方添加或修改功能时出现故障(完全或部分故障、立即或静默故障)。
更大的背景¶
这个问题只是一个更大问题的一部分,本规范不会完全解决该问题,但本规范是朝着解决方案迈出的增量步骤。
目前,镜像生成和使用涉及的过程是
镜像打包(集群生成前)
干净镜像配置(从仅操作系统基础镜像构建集群)
镜像验证(确保先前打包的镜像确实可以用于插件,至少对于我们可以轻松检查的规则)
第一种,镜像打包,目前只能通过命令行脚本实现。理想的用户体验应该允许通过 OpenStack 或命令行脚本或 Sahara API 方法生成镜像。目前,这是不可能的。
第二种,在我们的当前架构中,需要实质上重写插件代码中通过命令行过程生成镜像所需的逻辑,从而导致在允许从干净镜像配置集群的任何地方出现重复逻辑和多个维护点。但是,应该注意的是,在干净镜像生成的情况下,从封装的角度来看,这种逻辑是正确的(它与插件代码一起维护和版本控制,从而实现轻松分离,而不是维护在服务所有插件的单体交叉切片库中。)
第三种根本没有作为单独的步骤正式执行;它将由本规范描述的功能实现。
在更大的问题背景下,可以将此功能视为朝着统一的镜像验证解决方案、统一干净和打包镜像生成逻辑以及通过 API 促进镜像打包迈出的第一步。一旦此 SPI 方法稳定、可用并表达了所有维护插件的完整测试集,则可以将验证规范重用为镜像打包的一系列幂等状态描述,然后可以通过 API 暴露给任何支持它的插件。
提议的变更¶
SPI 方法契约¶
将在 Sahara 的插件 SPI 中添加一种新方法
validate_images(self, cluster, reconcile=True)
此方法将在集群配置之前(这将是机器访问所必需的)和集群配置之后调用。此方法将接收集群定义作为参数,以及一个布尔标志,该标志描述插件是否应该尝试修复发现的问题。
如果插件未实现此方法,配置将像往常一样进行;由于这纯粹是一种安全功能,因此与先前插件版本的完全向后兼容性是可以接受的。
此方法契约是,在被调用时,插件将采取任何它认为适合的步骤来验证使用的镜像是否适合其用途。预计运行的所有测试对于集群的成功都是必需的,但并非所有测试集都绝对足以使集群成功(因为这实际上是在反驳普遍否定,并且需要如此深入的测试以至于变得荒谬。)
如果 reconcile 标志设置为 False,则指示插件仅测试镜像,但不更改任何内容,并在测试失败时报告错误。如果 reconcile 为 True(这将是默认设置),则插件还将采取任何它准备采取的步骤,以使集群的实例与它的期望保持一致。插件不需要提供此功能,就像它们不需要实现 validate_image 一样;如果它们希望在镜像不完美的情况下立即失败,那是它们的选择。但是,如果插件不支持调解,并且 reconcile 设置为 True,则必须引发错误;同样,如果插件收到 reconcile=False 但无法避免调解(例如,如果它的实现使用 Puppet 并且会根据需要进行更改),则必须引发错误。
sahara.plugins.images¶
Sahara 基本服务将提供一组实用程序来帮助插件作者验证其镜像。这些实用程序将在 sahara.plugins.images 中找到。使用这些实用程序完全是可选的;插件作者可以使用他们认为合适的任何框架来实现验证。值得注意的是,该模块可以立即编写以允许在匹配镜像验证与服务方面进行大量深入的功能,从而允许为特定 nodegroups 和服务集使用自定义镜像。但是,由于当前没有插件正在实现这样的功能集,因此更基本的首次迭代是合理的,并且下面描述的方法将允许插件作者在需要时执行此类特定验证。
images 模块将提供几个公共成员:最值得注意的(如果不是全部)的定义如下
def validate_instance(instance, validators, reconcile=True, **kwargs):
"""Runs all validators against the specified instance."""
class ImageValidator(object):
"""Validates the image spawned to an instance via a set of rules."""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def validate(self, remote, reconcile=True, **kwargs):
pass
class SaharaImageValidatorBase(ImageValidator):
"""Still-abstract base class for Sahara's native image validation,
which provides instantiation of subclasses from a yaml file."""
@classmethod
def from_yaml(cls, yaml_path, validator_map, resource_roots):
"""Constructs and returns a validator from the provided yaml file.
:param yaml_path: The path to a yaml file.
:param validator_map: A map of validator name to class. Each class
is expected to descend from SaharaImageValidator. This method
will use the static map of validator name to class provided in
the sahara.plugins.images module, updated with this map, to
parse he appropriate classes to be used.
:param resource_root: The roots from which relative paths to
resources (scripts and such) will be referenced. Any resource
will be pulled from the first path in the list at which a file
exists."""
class SaharaImageValidator(SaharaImageValidatorBase):
"""The root of any tree of SaharaImageValidators."""
def validate(self, remote, reconcile=True, env_map=None, **kwargs):
"""Validates the image spawned to an instance."""
:param env_map: A map of environment variables to be passed to
scripts in this validation."""
此外,将向 sahara.plugins.exceptions 添加两类错误
ImageValidationError:指示镜像验证失败的异常。
ImageValidationSpecificationError:指示镜像验证规范(yaml)存在错误。
SaharaImageValidator¶
在此框架中,插件作者完全可以使用幂等状态强制执行工具集,例如 Ansible、Puppet、Chef 等来验证和调解镜像。但是,为了使 Sahara 不需要绝对依赖这些工具,我们将提供 SaharaImageValidator 类。
此验证器将提供一个类方法,允许它从 .yaml 文件构建其验证。此验证器的首次迭代将非常有限,因此将仅提供几个抽象验证类型。此 yaml 将使用任何可用的顺序进行解释;由于字典在 yaml 中是无序的,因此此方案大量使用单项字典列表。
以下是显示修订版一验证器集的示例 .yaml 文件。请注意,这些并非旨在成为真实的、Sahara 准备好的定义,仅仅是从我们的经验中获取的示例
validators:
- os_case:
- redhat:
- package: nfs-utils
- debian:
- package: nfs-common
- any:
- package: java-1.8.0-openjdk-devel
- package: java-1.7.0-openjdk-devel
- script: java/setup-java-home
- package:
- hadoop
- hadoop-libhdfs
- hadoop-native
- hadoop-pipes
- hadoop-sbin
- hadoop-lzo
- lzo
- lzo-devel
- hadoop-lzo-native
这些资源声明将用于实例化以下基本验证器类型
验证器类型¶
SaharaPackageValidator(键:package)¶
验证是否安装了软件包或软件包。在 reconcile=True 的情况下,确保在诉诸联网工具之前查询本地软件包管理器,如下所示
`dpkg -s $package || apt-get -y install $package` # debian
`rpm -q $package || yum install -y $package` # redhat
此验证器的输入可以是单个软件包定义或软件包定义列表。如果将软件包分组在一个列表中,则将同时尝试安装所有软件包。软件包定义可以是单个字符串或嵌套结构,可以支持版本属性,如下所示
- package: hadoop
- package:
- hadoop-libhdfs
- lzo:
version: xxx.xxx
由于可靠的版本比较通常需要引用 epoch,并且由于该工具必须在离线上下文中成功运行,因此初始的、Sahara 核心提供的软件包验证器仅允许精确的版本固定。由于此版本是 yaml 可编辑的,因此不足以满足我们的目的,并且可以在需要和适当的情况下由插件开发人员扩展。
SaharaScriptValidator(键:script)¶
从资源根目录的相对路径指定的源运行任意脚本。
此验证器的输入必须是单个脚本定义。脚本定义可以是单个字符串或嵌套结构,可以支持以下属性(示例仅用于解释结构)
- script: simple_script.sh
- script:
java/find_jre_home:
output: JRE_HOME # Places the stdout of this script into the env map
# for future scripts
- script:
java/setup_java_home:
env_vars: # Sets only the named env vars from the env map
- JDK_HOME
- JRE_HOME
脚本始终提供环境变量 $SIV_DISTRO,该变量指定我们当前的 SIE 发行版约定中的 Linux 发行版,以及环境变量 $SIV_RECONCILE,如果仅应进行验证,则将其设置为 0,如果应采取纠正措施,则将其设置为 1。
其他变量从最初传递给 SaharaImageValidator.from_yaml 的 env_map 参数引用(并假定从集群配置信息中解析)。脚本资源的输出属性可用于在飞行中修改此映射,将脚本的输出放入(单个)命名变量中。更复杂的交互需要扩展。
此验证器是轻量级的。这些镜像验证和操作不应该过于复杂;如果需要深度配置,则更自由的配置引擎应该执行这些步骤,或者插件作者应该使用功能更全面的状态强制执行引擎,并承担所有依赖关系(或编写自定义验证器)。
请注意,Sahara 团队审查的所有脚本必须编写为幂等的。如果它们要采取不可重现的操作,则必须测试该操作是否已经采取。这对于该功能在长期内的成功至关重要。
SaharaAnyValidator(键:any)¶
验证其包含的至少一个验证器是否成功。如果 reconcile 为 true,则在尝试强制执行任何验证器之前,以 reconcile=False 模式运行所有验证器。如果所有验证器在 reconcile=False 模式下都失败,则它将尝试依次强制执行每个验证器,直到一个成功为止。
如果在 reconcile=true 时使用任何验证器,则可以在失败的分支中对镜像造成重大损害。但是,防止此类故障会限制此验证器的使用。因此,将记录警告,但负责任的使用留给验证规范的作者。
SaharaAllValidator(键:all)¶
验证其包含的所有验证器是否都成功。此类将由上述 yaml 工厂方法实例化,并将包含所有子验证器。
SaharaOSCaseValidator(键:os_case)¶
根据正在验证的实例的发行版进行切换。识别 DIB 中的 OS 系列名称 redhat 和 debian,并运行与第一个匹配案例下的所有验证器。
关于验证器¶
插件作者可以通过扩展 SaharaImageValidator 类型、实现接口并将键和类传递给 SaharaImageValidator.from_yaml 的 validator_map 参数来编写自己的验证器类型。
应该注意的是,当前的“干净”镜像生成脚本应该作为实施任何给定插件的此方法的初始工作的一部分移动到此层,即使它们表示为单体脚本资源。否则,干净的镜像很可能会验证失败。
还请注意,上述列表肯定需要,但随着实施者的工作,创建其他验证器(文件、目录和用户浮出水面作为可能的候选者)可能会变得有用。因此,上述列表不一定是完整的;但是,我犹豫是否列出我可以设想的所有可能的验证器类型,因为我担心会推动过度工程规范,并且我相信在代码审查期间可以等待进一步的次要验证器类型的设计审查,只要这个总体结构是可以接受的。
替代方案¶
我们有很多替代方案。
首先,对于合并我们的验证、打包和干净镜像配置逻辑的问题,我们可以选择将我们当前的镜像生成代码与我们的服务层合并。但是,这在测试方面提出了真正的困难,因为我们的镜像生成层虽然功能齐全,但缺乏服务层的稳定性,并且按原样合并可能会减慢项目的前进速度,因为我们与 CI 作斗争。
假设我们不想将我们当前的镜像生成层合并,我们可以立即开始在服务端实现新的镜像生成层。但是,这种真正革命性的步骤经常导致冷漠、冲突或两者兼而有之。提供一个镜像验证层,并有可能发展成一个干净的镜像生成 API,然后是一个镜像打包 API,是一个增量步骤,可以在短期内提供真正的价值,并且无论如何都是必需的。
假设我们正在构建一个镜像验证 API,我们可以将其与任何镜像准备逻辑(包括干净镜像配置)完全分离。这里有一种纯粹主义的职责分离论点,但资源测试和强制执行通常是相同的步骤这一事实表明,为了提高效率,我们应该将两者合并。
假设我们允许使用验证层调解镜像,我们可以不构建我们自己的轻量级验证层,而是立即要求插件作者采用 Ansible、Puppet、Chef、Salt 等之一。但是,三个因素使我不愿采用这种选择。首先,这些工具的正常使用默认情况下需要网络访问;在我们的上下文中,我们不想在绝对必要时使用外部网络,因为我们的实例可能未启用网络连接。虽然可以离线使用它们,但这样做需要一些小心,这可能会让不熟悉所选工具的 Sahara 新手感到沮丧。其次,Sahara 不应该对工具链过于固执己见,无论是对我们的团队还是对我们的用户群。通过提供清晰、良好封装的 API 点来促进使用 DevOps 工具链是一个很好的目标,但 Sahara 的工作不是在市场上选择一个赢家。第三,这样的框架是 Sahara 核心的一个重要依赖项,并且总是应该对这种大规模的依赖项持怀疑态度。因此,提供一个非常轻量级的框架来进行验证是值得的,这样我们就不需要绝对依赖任何这样的框架,即使在插件从服务仓库中抽象出来之前也是如此。
假设我们不希望立即采用这种框架,我们可以选择立即构建一个功能齐全的、幂等的资源描述语言,并构建更多带有更多选项的验证器。虽然我可能遗漏了必要的、基本选项,并欢迎反馈,但我强烈建议我们从一个最小化的框架开始,并在此基础上构建,而不是一开始就试图构建一个完整的系统。在这个规范中,我侧重于可扩展性而非完整性(因此在要实现的验证器集合中留出了一些明确的调整空间)。
数据模型影响¶
无。
REST API 影响¶
无;此更改仅涉及 SPI。
其他最终用户影响¶
对于使用 SaharaImageValidators 的插件,最终用户可以修改 .yaml 文件以添加软件包或在镜像启动时运行验证或修改脚本。
部署者影响¶
无。
开发者影响¶
此 SPI 方法是可选的;如果插件今天感觉有点“自由奔放”,它们可以继续从任何提供的镜像启动,而无需进行测试。因此,此规范对开发者没有严格的要求。
Sahara-image-elements impact¶
无。如果采用此规范,Sahara-image-elements 可以继续执行其功能。未来的相关规范可能会驱动我们期望镜像打包方式的变化(希望通过 OpenStack API 实现),但这并非该规范的内容,可以完全独立地批准。
Sahara-dashboard / Horizon 影响¶
无。
实现¶
负责人¶
- 主要负责人
egafford
- 其他贡献者
ptoscano
工作项¶
添加 SPI 方法并在配置流程中调用;包装以确保如果不存在,则不会引发错误。
按照上述规范构建 sahara.plugins.images,以及所有列出的验证器。
使用此机制为 CDH 和 Ambari 插件编写 .yaml 文件(其他插件可以随着时间的推移采用,因为 SPI 方法是可选的)。
添加单元测试。
依赖项¶
没有新的依赖项(尽管这提供了一个扩展点,插件可以选择采用新的依赖项)。
测试¶
假设进行单元测试,如所有情况一样。镜像验证机制本身不需要进行大量的新的集成测试;现有测试将覆盖积极情况。幂等性测试需要对服务器进行白盒访问,并且在场景框架中不可行;如果该系统最终用于镜像生成,那么我们将拥有黑盒钩子来通过对预打包镜像重新运行来测试幂等性(这应该不会产生任何变化,并且镜像仍然有效)。
文档影响¶
我们需要记录 SPI 方法、SaharaImageValidator 类以及描述它们的 .yaml 结构。
参考资料¶
无。