news 2026/9/24 15:59:27

Salt rsync 执行模块实战指南:基于 salt.modules.rsync 的远程文件同步方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Salt rsync 执行模块实战指南:基于 salt.modules.rsync 的远程文件同步方案
  • 运维
  • 配置管理
  • 后端

【免费下载链接】salt

Software to automate the management and configuration of infrastructure and applications at scale.

项目地址:https://gitcode.com/gh_mirrors/sa/salt
点击查看免费下载

rsync 是 Linux 生态中最常用的增量文件同步工具,而 Salt 将其封装为rsync执行模块,让你可以在任意受管 minion 上以统一的 Salt 调用方式完成文件镜像、增量备份与目录同步。本文基于 salt/modules/rsync.py 的完整实现,系统讲解rsync.rsyncrsync.versionrsync.config三个函数的全部参数、配置与 pillar 回退机制、salt://文件服务器集成原理,并结合单元测试给出可复现的实战调用方式。

本文对应的 API 文档页为 doc/ref/modules/all/salt.modules.rsync.rst,该页面通过 Sphinx 的automodule指令自动渲染模块 docstring,因此真正的技术内容全部内嵌于模块源码的文档字符串与实现中;模块同时登记在 doc/ref/modules/all/index.rst 的模块索引内。

模块概述:何时可用、如何加载

rsync执行模块自2014.1.0版本加入 Salt,其定位是“rsync 的封装(Wrapper)”。模块本身并不重新实现同步算法,而是把 rsync 二进制的命令行参数组织、远程 shell 指定、排除规则、salt://文件拉取等繁琐细节封装成可直接调用的 Salt 函数。

从源码结构看,模块的加载由virtual() 控制:

__virtualname__ = "rsync" def __virtual__(): if salt.utils.path.which("rsync"): return __virtualname__ return ( False, "The rsync execution module cannot be loaded: " "the rsync binary is not in the path.", )

关键点:

  • 只有目标 minion 的PATH中存在rsync二进制(通过salt.utils.path.which探测)时,模块才会被 loader 加载;
  • 若未安装 rsync,__virtual__返回(False, 原因字符串),此时调用salt '*' rsync.rsync ...会得到“模块不可用”的明确错误,而不是含糊的命令失败;
  • 模块不依赖额外的 Python 第三方库,仅依赖salt.utils.filessalt.utils.pathsalt.exceptions中的异常类型。

rsync.rsync:核心同步函数

rsync.rsync是模块的主体函数(实现于 salt/modules/rsync.py#L68-L211),负责把文件从src同步到dst。在 2016.3.0 版本中其返回值行为发生过一次重要变更:返回数据从原先cmd.run_all的字典,变为 rsync 命令的纯文本输出,便于直接在命令行和状态模块中拼接使用。

完整参数说明

参数默认值对应 rsync 选项说明
src源路径文件或目录的源位置;支持salt://协议(见下文)
dst目标路径文件或目录的目标位置
deleteFalse--delete启用后删除目标目录中多余的文件,实现严格镜像
forceFalse--force强制删除非空目录
updateFalse--update跳过目标端存在且修改时间比源文件更新的文件
passwordfileNone--password-file访问 rsync daemon 用的密码文件,文件内容仅含密码
excludeNone--exclude排除匹配某 PATTERN 的文件;可传字符串或字符串列表
excludefromNone--exclude-from从指定文件读取排除模式
dryrunFalse--dry-run试运行,不实际修改任何文件
rshNone--rsh=指定远程 shell(如ssh),用于远程同步场景
additional_optsNone追加额外的 rsync 选项,必须以列表形式传入
saltenv"base"srcsalt://路径时,指定使用的 Salt fileserver 环境

配置与 pillar 回退机制

模块 docstring 开篇即声明:参数数据也可以经由 pillar 传入,且传入 opts 的选项会覆盖传入 pillar 的选项。这一机制体现在 rsync() 函数的前半段:

if not src: src = __salt__"config.option" if not dst: dst = __salt__"config.option" if not delete: delete = __salt__"config.option" # ... force / update / passwordfile / exclude / excludefrom / dryrun / rsh 依此类推 if not src or not dst: raise SaltInvocationError("src and dst cannot be empty")

这里的语义是“显式参数优先,缺失时回退到配置”:

  1. 函数参数显式传入的非空值直接生效;
  2. 参数为空/未传时,通过config.option("rsync.<param>")从 minion 配置或 pillar 中读取;
  3. 由于 Salt 的配置层级是“CLI/opts 覆盖 pillar、pillar 覆盖默认配置”,因此opts中的rsync.*键会覆盖 pillar 中的同名键;
  4. 若最终srcdst仍为空,直接抛出SaltInvocationError("src and dst cannot be empty"),避免生成非法 rsync 命令。

对应的 pillar 示例(可参考 Salt 官方的 Pillar Walkthrough 了解 pillar 的基本用法):

rsync: src: /data/www dst: /backup/www delete: true exclude: - cache - tmp

这样便可以在不修改执行命令的情况下,通过 pillar 集中管理各 minion 的同步策略。

salt:// 文件服务器集成

rsync.rsync的一大特色是src支持salt://协议,即直接从 Salt master 的文件服务器拉取文件再执行同步。对应实现 的逻辑如下:

tmp_src = None if src.startswith("salt://"): _src = src _path = re.sub("salt://", "", _src) src_is_dir = False if _path in __salt__"cp.list_master_dirs": src_is_dir = True if src_is_dir: tmp_src = tempfile.mkdtemp() dir_src = __salt__"cp.get_dir" if dir_src: src = tmp_src if not src.endswith("/"): src = f"{src}/" else: raise CommandExecutionError(f"{src} does not exist") else: tmp_src = salt.utils.files.mkstemp() file_src = __salt__"cp.get_file" if file_src: src = tmp_src else: raise CommandExecutionError(f"{src} does not exist")

可以提炼出以下机制要点:

  • 目录判定:通过cp.list_master_dirs判断salt://路径在指定saltenv(默认base)下是否为目录;
  • 目录处理:若是目录,用tempfile.mkdtemp()创建临时目录,cp.get_dir递归拉取,且强制在临时源路径后补/(注释明确说明这是为了让 rsync 同步的是目录内容而非临时目录本身);
  • 单文件处理:若是文件,用salt.utils.files.mkstemp()创建临时文件,cp.get_file拉取;
  • 失败即抛错:拉取失败统一抛出CommandExecutionError(f"{src} does not exist")
  • 自动清理:函数末尾的finally分支在同步结束后调用__salt__"file.remove"清理临时文件,保证 minion 上不残留垃圾数据。

底层命令组装:_check 函数

所有布尔开关与路径参数最终由私有函数 _check() 组装为 rsync 参数列表,默认选项固定为-avz(归档模式 + 详细输出 + 压缩传输):

def _check(delete, force, update, passwordfile, exclude, excludefrom, dryrun, rsh): options = ["-avz"] if delete: options.append("--delete") if force: options.append("--force") if update: options.append("--update") if rsh: options.append(f"--rsh={rsh}") if passwordfile: options.extend(["--password-file", passwordfile]) if excludefrom: options.extend(["--exclude-from", excludefrom]) if exclude: exclude = False if exclude: if isinstance(exclude, list): for ex_ in exclude: options.extend(["--exclude", ex_]) else: options.extend(["--exclude", exclude]) if dryrun: options.append("--dry-run") return options

值得注意的实现细节:

  • excludefrom优先于exclude:一旦指定了排除文件,传入的exclude会被强制置为False,避免两种排除机制同时生效造成歧义;
  • exclude双形态支持:既可传单个模式字符串,也可传模式列表,列表会被逐个展开为多个--exclude参数;
  • additional_opts追加:若用户传入列表形式的additional_opts,会被直接追加到选项末尾(见 L200-L201),这是把--partial--bwlimit等未内建参数透传给 rsync 的通用通道。

最终命令通过cmd.run_all(cmd, python_shell=False)执行(L203-L208),python_shell=False确保参数不被 shell 二次解释,避免路径含空格或特殊字符时出错;若底层抛OSError,则包装为CommandExecutionError抛出。

官方 CLI 示例(直接可用)

以下为模块 docstring 中提供的官方示例,可直接在 master 上执行:

# 基础同步,带删除、更新与密码文件 salt '*' rsync.rsync /path/to/src /path/to/dest delete=True update=True passwordfile=/etc/pass.crt exclude=exclude/dir # 使用排除文件(--exclude-from) salt '*' rsync.rsync /path/to/src delete=True excludefrom=/xx.ini # 列表形式的排除项 + 附加 rsync 选项 salt '*' rsync.rsync /path/to/src delete=True exclude='[exclude1/dir,exclude2/dir]' additional_opts='["--partial", "--bwlimit=5000"]'

第三条命令演示了组合用法:exclude传列表、additional_opts--partial(断点续传)与--bwlimit=5000(限速 5MB/s),适合大文件或弱网环境。

rsync.version:获取版本号

version() 用于查询 minion 上 rsync 的版本,调用形式为salt '*' rsync.version。其实现先通过cmd.run_stdout(["rsync", "--version"], python_shell=False)获取输出,再取首行第三个字段作为版本号:

out = __salt__"cmd.run_stdout" try: return out.split("\n")[0].split()[2] except IndexError: raise CommandExecutionError("Unable to determine rsync version")

注意其返回值在 2016.3.0 之后也简化为纯版本号字符串。若输出格式异常导致解析失败,会抛出CommandExecutionError,便于在状态判断中捕获。

rsync.config:读取 rsyncd.conf

config() 返回 minion 上 rsync daemon 配置文件的内容,默认路径为/etc/rsyncd.conf,可通过conf_path参数覆盖。调用示例:salt '*' rsync.config

实现上逐行读取文件并做 unicode 转换,同时对常见的OSError场景做了精细的差异化报错:

errno 场景抛出的错误
ENOENT(文件不存在)"{conf_path} does not exist"
EACCES(无权限)"Unable to read {conf_path}, access denied"
EISDIR(路径是目录)"Unable to read {conf_path}, path is a directory"
其他"Error {errno}: {strerror}"

这一函数适合在批量巡检时快速核对各 minion 的 rsyncd 配置是否一致。

单元测试验证:参数与行为的可复现证据

模块的单元测试位于 tests/pytests/unit/modules/test_rsync.py,从测试断言中可以反向印证上述实现细节:

  • test_rsync:当src/dst为空时,config.option返回False,断言抛出SaltInvocationError;当cmd.run_allOSError时,断言抛出CommandExecutionError;正常路径下断言返回cmd.run_all的结果"A"
  • test_rsync_excludes_list:传入exclude=["test/one", "test/two"]后,断言最终生成的命令为:
    ["rsync", "-avz", "--exclude", "test/one", "--exclude", "test/two", "src", "dst"]

    并以python_shell=False调用cmd.run_all——这精确验证了_check的列表展开逻辑与最终命令结构。

  • test_rsync_excludes_str:传入单个字符串exclude="test/one"时,生成["rsync", "-avz", "--exclude", "test/one", "src", "dst"]
  • test_versioncmd.run_stdout返回"A B C\n"时,断言version()返回"C",验证了“取首行第三个字段”的解析逻辑。

这些测试对理解模块行为、以及在 CI 中回归验证 rsync 相关改动都很有参考价值。

实践建议与注意事项

综合源码实现,使用本模块时有几点值得留意:

  1. 目标机必须预装 rsync:模块在__virtual__阶段就探测二进制,因此部署前应确保 minion 已安装 rsync(如pkg.installed状态管理),否则模块整体不可加载。
  2. delete=True是镜像语义:它会把目标目录中源端没有的文件一并删除,适合发布目录、备份镜像等场景,但误用会删数据,建议先用dryrun=True试跑确认。
  3. 参数优先级:函数显式参数 > opts > pillar;在 pillar 中集中配置rsync.*键可以大幅简化命令行,但要注意 opts 会覆盖 pillar。
  4. salt://源有临时文件成本:目录源会被完整拉到 minion 临时目录再同步,大目录会占用临时磁盘空间;同步完成后由模块自动清理,无需手动干预。
  5. 附加参数走additional_opts列表:模块未内建的 rsync 高级选项(如--partial--bwlimit--compress-level)都应通过该参数以列表形式透传。
  6. 返回值是纯文本:2016.3.0 起三个函数的返回值都已简化为纯字符串,编写引用该模块的状态或 runner 时不要再按字典取值。

总而言之,salt.modules.rsync用约 270 行代码把 rsync 的常用能力、配置回退与salt://文件服务器无缝整合进 Salt 执行体系,配合 pillar 驱动与cmd.run_all的安全调用,适合作为大规模基础设施中文件分发、目录镜像与增量备份的标准执行单元。

  • 运维
  • 配置管理
  • 后端

【免费下载链接】salt

Software to automate the management and configuration of infrastructure and applications at scale.

项目地址:https://gitcode.com/gh_mirrors/sa/salt
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 15:52:34

PRQL 的 from 数据源:指定关系、别名与特殊标识符的完整指南

PRQL 的 from 数据源&#xff1a;指定关系、别名与特殊标识符的完整指南 【免费下载链接】prql PRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement 项目地址: https://gitcode.com/gh_mirrors/pr/prql from 是 PRQL 管…

作者头像 李华
网站建设 2026/9/24 15:47:41

EMC测试必懂:PK、QP、AV三种检波方式原理与实战应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华