ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Salt master_tops 的 reclass 适配器:用外部数据源动态生成 Highstate Top 数据

Salt master_tops 的 reclass 适配器:用外部数据源动态生成 Highstate Top 数据 运维配置管理后端【免费下载链接】saltSoftware to automate the management and configuration of infrastructure and applications at scale.项目地址https://gitcode.com/gh_mirrors/sa/salt点击查看免费下载导读本文围绕 Salt 内置的master_tops插件reclass_adapter虚拟模块名reclass展开讲解如何将 reclass 外部配置数据库作为 Top 数据的唯一来源让 Salt master 在每次 highstate 运行时按 minion 动态计算应执行的 state 列表。读完本文你将掌握master_tops.reclass的完整配置方法、与ext_pillar.reclass复用同一份 reclass 配置的 YAML 锚点技巧、从源码目录运行 reclass 的方式以及该适配器从加载、参数解析到错误处理的源码级工作原理。一、背景master_tops 可插拔系统Salt 的 state 系统默认通过 Top 文件top.sls决定各 minion 需要执行的 state。当需要把 top 数据的决策权交给外部系统时可以使用master_tops机制。根据 master 配置文档master_tops默认值为{}它取代了早期的external_nodes选项形成一套可插拔的「外部 top 数据生成」体系master_tops: ext_nodes: Shell command which returns yamlmaster_tops下可以同时注册多个后端例如cobbler、varstack、saltclass以及本文的主角reclass。所有内置 tops 模块的清单见 doc/ref/tops/all/index.rst模块源码统一位于 salt/tops/ 目录cobbler.py、ext_nodes.py、mongo.py、reclass_adapter.py、saltclass.py、varstack_top.py。值得注意的两点从 3007.0 版本开始无 master 的 minionmasterless同样支持 master_tops 模块见 doc/topics/master_tops/index.rst。master_tops 返回的数据会与 top 文件中的匹配结果叠加而不是互相替代不过 2018.3.0 起可以通过 minion 端配置master_tops_first: True让 minion 把 master_tops 视为首选来源。二、reclass 适配器最小配置reclass 是一个外部配置管理数据库Salt 通过 salt/tops/reclass_adapter.py 这个适配器从中读取 top 数据。该模块的完整使用文档以 docstring 形式保存在源码顶部并由 doc/ref/tops/all/salt.tops.reclass_adapter.rst 通过automodule指令自动生成。启用方式很简单在 master 配置的master_tops列表中注册reclass插件并用几个选项告诉 reclass 在哪里、以何种格式寻找 inventorymaster_tops: reclass: storage_type: yaml_fs inventory_base_uri: /srv/saltstorage_type: yaml_fsreclass 从 YAML 文件系统读取 inventory。inventory_base_uri: /srv/saltinventory 的根路径。配置完成后reclass 会从/srv/salt/nodes与/srv/salt/classes两个子目录读取 inventorynodes目录存放每个节点的数据节点即 minionclasses目录存放可被节点引用/继承的类定义。这也意味着若要使用该插件需要按照 reclass 的目录约定在这些位置维护 inventory 文件。master_tops在 conf/master 示例配置中有注释说明默认保持注释状态master_tops: {}。三、与 ext_pillar 共享同一份 reclass 配置如果同时把 reclass 用作ext_pillar插件见 salt/pillar/reclass_adapter.py避免在两处重复填写相同参数的最优雅做法是利用 YAML 锚点reclass: reclass storage_type: yaml_fs inventory_base_uri: /srv/salt reclass_source_path: ~/code/reclass ext_pillar: - reclass: *reclass master_tops: reclass: *reclass这里需要特别留意ext_pillar与master_tops两处数据结构类型的差异ext_pillar是一个列表其中每一项是「单键哈希」- reclass: *reclassmaster_tops是一个映射reclass: *reclass。两者的取值都引用同一个锚点reclass因此修改一处即可同时作用于 pillar 与 top 两个数据通道保证数据源一致性。四、从源码运行 reclass不必安装文档明确给出了两种让 master 找到 reclass 库的方式通过PYTHONPATH环境变量将 reclass 源码目录加入 Python 搜索路径通过配置选项reclass_source_path指定源码路径例如上面的~/code/reclass。在源码层面salt/utils/reclass.py 的prepend_reclass_source_path()负责把配置中的路径规范化后插入sys.pathdef prepend_reclass_source_path(opts): source_path opts.get(reclass_source_path) if source_path: source_path os.path.abspath(os.path.expanduser(source_path)) sys.path.insert(0, source_path)注意它对路径做了expanduser展开~与abspath转为绝对路径处理因此写~/code/reclass是可行的。五、源码级原理剖析5.1 虚拟名与延迟加载由于模块文件名如果叫reclass.py会与第三方包reclass在 import 时互相遮蔽源码注释专门说明了这一点真正的插件文件命名为reclass_adapter.py但通过__virtualname__ reclass让加载器仍以reclass之名注册它salt/tops/reclass_adapter.py。__virtual__()的实现L67-L78体现了「先探测、再补救」的策略def __virtual__(retryFalse): try: import reclass return __virtualname__ except ImportError: if retry: return False opts __opts__.get(master_tops, {}).get(reclass, {}) prepend_reclass_source_path(opts) return __virtual__(retryTrue)若 reclass 已可导入直接返回虚拟名reclass插件被激活若首次导入失败则尝试从master_tops.reclass配置中取出reclass_source_path并把它插入搜索路径然后递归重试一次重试仍失败则返回False加载器会跳过该插件而不会让整个加载扫描崩溃。5.2 top() 的调用链每个 tops 模块都必须实现top(**kwargs)。reclass 适配器的top()L81-L143核心流程如下def top(**kwargs): from reclass.adapters.salt import top as reclass_top from reclass.errors import ReclassException reclass_opts __opts__[master_tops][reclass] filter_out_source_path_option(reclass_opts) set_inventory_base_uri_default(__opts__, kwargs) minion_id kwargs[opts][id] return reclass_top(minion_id, **reclass_opts)取配置从__opts__[master_tops][reclass]取出插件参数。源码注释提到Salt 的 tops 接口与 ext_pillar 存在不一致#5786因此需要通过解析配置来提取参数该适配器把这一内部细节隐藏了起来。过滤内部选项reclass_source_path只是 Salt 侧用于加载库的路径reclass 本身不关心调用filter_out_source_path_option()把它从传给 reclass 的参数中剔除salt/utils/reclass.py#L17-L20。默认 inventory 路径若用户未指定inventory_base_uriset_inventory_base_uri_default()会将其初始化为file_roots中base环境的第一个根目录salt/utils/reclass.py#L23-L29def set_inventory_base_uri_default(config, opts): if inventory_base_uri in opts: return base_roots config.get(file_roots, {}).get(base, []) if base_roots: opts[inventory_base_uri] base_roots[0]传入 minion idSalt 要求 top 数据按 minion 过滤适配器从kwargs[opts][id]中取出当前 minion 的 ID源码注释引用 issue #6930 说明这一取法。委托给 reclass最终调用reclass.adapters.salt提供的top(minion_id, **reclass_opts)。这里刻意不把__opts__、__salt__、__grains__传给 reclass保持 reclass 只依赖自己的配置、不猜测 Salt 内部结构。5.3 面向用户的错误处理top()把 reclass 抛出的底层异常统一翻译成可读的SaltInvocationErrorImportError且消息含reclass→master_tops.reclass: cannot find reclass module in sys.pathTypeErrorunexpected keyword argument→master_tops.reclass: unexpected option: arg直接指出是哪个配置项不合法KeyError含reclass→master_tops.reclass: no configuration found in master config提示缺少master_tops.reclass配置段reclass 自身的ReclassException→master_tops.reclass: 错误内容。六、master 端如何调用 tops 模块6.1 加载器按白名单加载master 通过 salt/loader/init.py 的tops()函数加载 tops 模块并以master_tops配置的键作为白名单def tops(opts, loaded_base_nameNone): if master_tops not in opts: return {} whitelist list(opts[master_tops].keys()) ret LazyLoader( _module_dirs(opts, tops, top), opts, tagtop, whitelistwhitelist, ... )这意味着只有出现在master_tops:映射中的模块才会被加载未配置的 tops 模块不会被实例化。6.2 请求处理与结果合并当 minion 向 master 请求 top 数据时masterapi的_master_tops()salt/daemons/masterapi.py#L592-L627会遍历所有已配置的 tops 函数for fun in self.tops: if fun not in self.opts.get(master_tops, {}): continue ret salt.utils.dictupdate.merge( ret, self.topsfun, merge_listsTrue )每个 tops 函数接收optsopts与grainsgrains关键字参数——这正是top(**kwargs)中kwargs[opts][id]的来源。单个 tops 函数失败不会中断整体流程而是记录错误日志后继续。master 端还通过asyncio的run_in_executor将_master_tops这类可能阻塞的同步调用移出事件循环salt/master.py#L2631-L2648避免阻塞 master worker。6.3 与 top 文件的叠加顺序在 minion 侧HighState会把 top 文件匹配结果与 master_tops 结果合并salt/state.py#L4286-L4295ext_matches self._master_tops() for saltenv in ext_matches: top_file_matches matches.get(saltenv, []) if self.opts.get(master_tops_first): first ext_matches[saltenv] second top_file_matches else: first top_file_matches second ext_matches[saltenv] matches[saltenv] first [x for x in second if x not in first]默认情况下 top 文件匹配结果排在前面master_tops 的结果附加其后当 minion 配置master_tops_first: True该选项自 2018.3.0 引入默认False见 minion 配置文档时顺序反转master_tops 成为优先来源实现「单一事实来源」的效果。七、验证与测试7.1 使用state.show_top验证配置完成后可用state.show_top实现见 salt/modules/state.py#L2464查看 minion 最终会拿到哪些 top 数据salt minion state.show_top返回结果中会出现由 reclass 生成的各 saltenv 及 state 列表与 top 文件数据合并展示。7.2 单元测试仓库在 tests/pytests/unit/pillar/test_reclass_adapter.py 中为 reclass 适配器提供了单元测试虽然测试文件位于 pillar 目录但其中部分断言同样约束 tops 适配器依赖的契约覆盖的关键行为包括__virtualname__必须等于文档约定的reclass以保证加载器能把reclass:条目绑定到该适配器当第三方reclass包不可导入且未配置源码路径时__virtual__()应返回False而非抛异常抛异常会导致加载器扫描崩溃ext_pillar必须委托给reclass.adapters.salt.ext_pillar并原样透传 minion id、已有 pillar 以及storage_type、inventory_base_uri等关键字选项。这些测试同时守护了适配器「clean failure」与「参数透传」两条契约避免 3008 分支清理社区扩展时再次出现回归。八、排查要点小结结合源码与文档配置master_tops.reclass时最常见的三类问题及其对应报错如下问题报错信息原因与解法reclass 库未安装master_tops.reclass: cannot find reclass module in ...安装 reclass或通过reclass_source_path/PYTHONPATH指定源码路径配置项写错master_tops.reclass: unexpected option: arg检查master_tops.reclass下的键名拼写缺少配置段master_tops.reclass: no configuration found in master config确认 master 配置中存在master_tops.reclass:段落此外若未显式设置inventory_base_uri适配器会自动使用file_roots.base的第一个路径作为默认值因此在大规模多环境部署中建议显式指定避免隐式依赖 base 环境的文件根路径。赞分享运维配置管理后端【免费下载链接】saltSoftware to automate the management and configuration of infrastructure and applications at scale.项目地址https://gitcode.com/gh_mirrors/sa/salt点击查看免费下载相关推荐Salt 外部 Pillar 实战用 PostgreSQL 查询动态生成 Pillar 数据salt.pillar.postgres 模块深度解析Salt 外部 Pillar 实战用 PostgreSQL 查询动态生成 Pillar 数据salt.pillar.postgres 模块深度解析 本文围运维配置管理后端如何使用Mobiledoc-Kit创建自定义卡片和原子组件完整指南如何使用Mobiledoc Kit创建自定义卡片和原子组件完整指南 Mobiledoc Kit是一个强大的框架无关的WYSIWYG编辑器构建工具包它通过自定前端Eidos数据导入外部数据源集成Eidos数据导入外部数据源集成 概述 在现代数据管理场景中数据导入Data Import是连接外部世界与个人知识库的关键桥梁。Eidos作为一个离线的上一篇F´ Data Products 子拓扑DataProducts Subtopology完全指南数据产品从分配、写入到优先级下链的复用构建块下一篇碧蓝航线自动化脚本AzurLaneAutoScript一键托管日常与大世界完整教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表