- 运维
- 配置管理
- 后端
【免费下载链接】salt
Software to automate the management and configuration of infrastructure and applications at scale.
rsync 是 Linux 生态中最常用的增量文件同步工具,而 Salt 将其封装为rsync执行模块,让你可以在任意受管 minion 上以统一的 Salt 调用方式完成文件镜像、增量备份与目录同步。本文基于 salt/modules/rsync.py 的完整实现,系统讲解rsync.rsync、rsync.version、rsync.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.files、salt.utils.path与salt.exceptions中的异常类型。
rsync.rsync:核心同步函数
rsync.rsync是模块的主体函数(实现于 salt/modules/rsync.py#L68-L211),负责把文件从src同步到dst。在 2016.3.0 版本中其返回值行为发生过一次重要变更:返回数据从原先cmd.run_all的字典,变为 rsync 命令的纯文本输出,便于直接在命令行和状态模块中拼接使用。
完整参数说明
| 参数 | 默认值 | 对应 rsync 选项 | 说明 |
|---|---|---|---|
src | 无 | 源路径 | 文件或目录的源位置;支持salt://协议(见下文) |
dst | 无 | 目标路径 | 文件或目录的目标位置 |
delete | False | --delete | 启用后删除目标目录中多余的文件,实现严格镜像 |
force | False | --force | 强制删除非空目录 |
update | False | --update | 跳过目标端存在且修改时间比源文件更新的文件 |
passwordfile | None | --password-file | 访问 rsync daemon 用的密码文件,文件内容仅含密码 |
exclude | None | --exclude | 排除匹配某 PATTERN 的文件;可传字符串或字符串列表 |
excludefrom | None | --exclude-from | 从指定文件读取排除模式 |
dryrun | False | --dry-run | 试运行,不实际修改任何文件 |
rsh | None | --rsh= | 指定远程 shell(如ssh),用于远程同步场景 |
additional_opts | None | 追加 | 额外的 rsync 选项,必须以列表形式传入 |
saltenv | "base" | — | 当src为salt://路径时,指定使用的 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")这里的语义是“显式参数优先,缺失时回退到配置”:
- 函数参数显式传入的非空值直接生效;
- 参数为空/未传时,通过
config.option("rsync.<param>")从 minion 配置或 pillar 中读取; - 由于 Salt 的配置层级是“CLI/opts 覆盖 pillar、pillar 覆盖默认配置”,因此
opts中的rsync.*键会覆盖 pillar 中的同名键; - 若最终
src或dst仍为空,直接抛出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_all抛OSError时,断言抛出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_version:cmd.run_stdout返回"A B C\n"时,断言version()返回"C",验证了“取首行第三个字段”的解析逻辑。
这些测试对理解模块行为、以及在 CI 中回归验证 rsync 相关改动都很有参考价值。
实践建议与注意事项
综合源码实现,使用本模块时有几点值得留意:
- 目标机必须预装 rsync:模块在
__virtual__阶段就探测二进制,因此部署前应确保 minion 已安装 rsync(如pkg.installed状态管理),否则模块整体不可加载。 delete=True是镜像语义:它会把目标目录中源端没有的文件一并删除,适合发布目录、备份镜像等场景,但误用会删数据,建议先用dryrun=True试跑确认。- 参数优先级:函数显式参数 > opts > pillar;在 pillar 中集中配置
rsync.*键可以大幅简化命令行,但要注意 opts 会覆盖 pillar。 salt://源有临时文件成本:目录源会被完整拉到 minion 临时目录再同步,大目录会占用临时磁盘空间;同步完成后由模块自动清理,无需手动干预。- 附加参数走
additional_opts列表:模块未内建的 rsync 高级选项(如--partial、--bwlimit、--compress-level)都应通过该参数以列表形式透传。 - 返回值是纯文本: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.
相关推荐
Salt 远程命令执行模块 cmdmod 完全指南:cmd.run、cmd.run_all 与脚本执行实战
Salt 远程命令执行模块 cmdmod 完全指南:cmd.run、cmd.run_all 与脚本执行实战 导读 :本文是 Salt 核心执行模块 cmdmod
运维配置管理后端Salt 执行模块 systemd_service 完全指南:基于 systemd 的服务管理实战
Salt 执行模块 systemd_service 完全指南:基于 systemd 的服务管理实战 本文是 Salt 官方参考文档 doc/ref/module
运维配置管理后端Salt aliases 执行模块实战指南:用 Salt 批量管理邮件别名文件
Salt aliases 执行模块实战指南:用 Salt 批量管理邮件别名文件 导读 本文基于 Salt 仓库中的 aliases 执行模块文档 https:/
运维配置管理后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考