【问题标题】:Why is copying a directory with Ansible so slow?为什么用 Ansible 复制目录这么慢?
【发布时间】:2023-11-16 07:07:02
【问题描述】:

我正在使用 Ansible 将目录(900 个文件,136MBytes)从一台主机复制到另一台主机:

---
- name: copy a directory
  copy: src={{some_directory}} dest={{remote_directory}}

这个操作需要惊人的 17 分钟,而简单的scp -r <src> <dest> 只需 7 秒。

我已经尝试过加速模式,根据ansible docs,“在启用 ControlPersist 的情况下,它比 SSH 快 2-6 倍,比 paramiko 快 10 倍。”,但无济于事。

【问题讨论】:

  • 我知道它会执行 MD5 哈希并对其进行验证,但是您看到的时间会非常长。
  • @CatManDo 实际上运行 sha1,这不负责任(尽管这是我的第一个猜测)。

标签: scp ansible


【解决方案1】:

我发现的最佳解决方案是压缩文件夹并使用unarchive 模块。

450 MB 文件夹在 1 分钟内完成。


unarchive:
   src: /home/user/folder1.tar.gz
   dest: /opt

【讨论】:

  • ...和@void 的答案有什么不同?
【解决方案2】:

虽然在这种情况下 synchronizecopy 更可取,但它是由 rsync 烘焙的。这意味着 rsync(客户端-服务器架构)的缺点仍然存在:CPU 和磁盘边界、大文件的文件内增量计算速度慢等。听起来对你来说速度很关键,所以我建议你找一个基于对等架构的解决方案,可快速且轻松地扩展到多台机器。类似于基于 BitTorrent 的 Resilio Connect。

【讨论】:

    【解决方案3】:

    synchronize 配置可以在具有become_user 的环境中为difficult。对于一次性部署,您可以归档源目录并使用 unarchive 模块复制它:

    - name: copy a directory
      unarchive:
        src: some_directory.tar.gz
        dest: {{remote_directory}}
        creates: {{remote_directory}}/indicator_file
    

    【讨论】:

    • 以及如何归档本地目录? archive 似乎只支持远程文件夹。
    • 这个答案不适合维护远程目录与不断变化的本地目录同步。它假设本地版本是一种不可变的镜像,只需要部署一次。在这种情况下,可以使用tar -cvpzf 将其存档,然后将生成的存档放入剧本的files/ 子文件夹中,然后使用unarchive 模块进行更快的部署,比问题中的scp 更快。
    • 我知道,谢谢。同步和不可变覆盖是两件不同的事情,我碰巧需要后者。为了潜在读者的兴趣,我使用delegate_to 解决了archive 的问题。
    【解决方案4】:

    TLDR:使用synchronize 而不是copy

    这是我正在使用的copy 命令:

    - copy: src=testdata dest=/tmp/testdata/
    

    作为猜测,我认为同步操作很慢。 files module documentation 也暗示了这一点:

    “复制”模块递归复制工具不能扩展到大量(>数百)个文件。对于替代方案,请参阅同步模块,它是 rsync 的包装器。

    挖掘源代码显示each file is processed with SHA1。那是implemented using hashlib.sha1。本地测试意味着 900 个文件只需要 10 秒(恰好占用 400mb 空间)。

    所以,下一条途径。副本由module_utils/basic.py's atomic_move method 处理。我不确定加速模式是否有帮助(它是mostly-deprecated feature),但我尝试了pipelining,将其放入本地ansible.cfg

    [ssh_connection]
    pipelining=True
    

    似乎没有提供帮助;我的样本运行了 24 分钟。显然有一个循环检查文件,上传文件,修复权限,然后开始下一个文件。这是很多命令,即使 ssh 连接保持打开状态。在字里行间阅读它有点意义——我认为“文件传输”不能在流水线中完成。

    所以,按照提示使用synchronize 命令:

    - synchronize: src=testdata dest=/tmp/testdata/
    

    即使使用pipeline=False,也需要 18 秒。显然,synchronize 命令是这种情况下要走的路。

    请记住synchronize 使用 rsync,它默认为 mod-time 和文件大小。如果您想要或需要校验和,请将checksum=True 添加到命令中。即使启用了校验和,时间也没有真正改变——仍然是 15-18 秒。我通过运行ansible-playbook-vvvv 验证了校验和选项是否打开,可以在此处看到:

    ok: [testhost] => {"changed": false, "cmd": "rsync --delay-updates -FF --compress --checksum --archive --rsh 'ssh  -o StrictHostKeyChecking=no' --out-format='<<CHANGED>>%i %n%L' \"testdata\" \"user@testhost:/tmp/testdata/\"", "msg": "", "rc": 0, "stdout_lines": []}
    

    【讨论】:

    • 复制模块有没有办法更快?这似乎是副本中的一个错误,它是如此缓慢?
    • 一旦你从copy切换到synchronize,如果你使用不同的端口/用户/配置的rsync/ssh,你需要指定rsync_optshairycode.org/2016/02/22/…跨度>
    • 如果我想在本地复制一个目录,即使用copy 模块并设置remote_src: yes 怎么办?在这种情况下很可能无法使用synchronize
    • 你值得喝一杯,很好的回答
    • 这是要走的路!!将我发送 vim 点文件和配色方案的时间从 175 秒和 157 秒减少到 0.19 秒和 0.17 秒(使用 profile_tasks 回调测试)。我不敢相信在我们实施它之前,我已经花了多少 MINUTES 观看那件事。注意:在同步操作完成后指示“文件”任务设置用户和组权限可能会有所帮助(用户/组功能在同步模块中没有用)。