#ssh #run-command #command-runner #queue #node #host #yaml

应用 pegasus-ssh

多节点 SSH 命令执行器:Pegasus

12 个稳定版本

1.4.0 2024年5月19日
1.3.0 2024年5月16日
1.2.1 2023年11月29日
1.1.3 2023年9月4日
1.0.0 2022年7月8日

#308命令行工具

35 每月下载量

MIT 许可证

1MB
862

多节点 SSH 命令执行器:Pegasus

在一系列 SSH 节点上运行命令列表。带有一些可选的参数化。

演示

asciicast

特性

  • 无密码 SSH 是你所需要的全部。
  • 简单配置用于简单场景,灵活配置用于复杂场景。
  • 两种模式
    • 广播 模式在每个节点上运行每个命令。
    • 队列 模式在每个节点上运行每个命令一次。
  • 在 Pegasus 运行时修改基于文件的队列(queue.yaml)。
  • 参数化 主机和命令。

通过示例入门

要使用 Pegasus,

  1. 从 GitHub 发布版或使用 cargo install pegasus-ssh 安装 Pegasus。
  2. 为您的节点设置无密码 SSH。
  3. 填充 hosts.yamlqueue.yaml,然后运行 Pegasus。

Pegasus 将逐个从 queue.yaml 的顶部删除条目,并在开始执行时将其移动到 consumed.yaml

队列模式:完成一袋工作

使用两个节点运行四个 Python 命令。

# hosts.yaml
- node-1
- node-2
# queue.yaml
- . /opt/miniconda3/etc/profile.d/conda.sh; python train.py --bs 8
- . /opt/miniconda3/etc/profile.d/conda.sh; python train.py --bs 16
- . /opt/miniconda3/etc/profile.d/conda.sh; python train.py --bs 32
- . /opt/miniconda3/etc/profile.d/conda.sh; python train.py --bs 64
$ pegasus q  # stands for Queue

广播模式:地形化节点

为多个节点运行相同的命令。

# queue.yaml
- mkdir workspace
- cd workspace && git clone https://github.com/jaywonchung/dotfiles.git
- . workspace/dotfiles/install.sh
$ pegasus b  # stands for Broadcast

使用节点参数并行执行

将节点分割成并行运行命令的子节点。下面,保持 四个 SSH 连接,并 并行 运行 四个 命令。

# hosts.yaml
- hostname:
    - node-1
    - node-2
  container:
    - gpu0
    - gpu1

当参数化节点时,只需确保指定 hostname 键。

您可以在命令中使用这些参数。顺便说一句,模板引擎是 Handlebars。

# queue.yaml
- docker exec {{ container }} python train.py --bs 8
- docker exec {{ container }} python train.py --bs 16
- docker exec {{ container }} python train.py --bs 32
- docker exec {{ container }} python train.py --bs 64

四个子节点和四个作业。因此,所有作业将同时开始执行。

为简洁性参数化命令

如果你可以参数化节点,为什么不参数化命令呢?

# queue.yaml
- command:
    - docker exec {{ container }} python train.py --bs {{ bs }}
  bs: [8, 16, 32, 64]

这会产生与上面示例中完全相同的作业。当参数化命令时,只需确保指定 command 键。

测验

队列模式下将执行多少个命令?

# hosts.yaml
- hostname:
    - node-1
    - node-2
  laziness:
    - 1
- hostname:
    - node-3
  laziness:
    - 2
# queue.yaml
- echo hi from {{ hostname }}
- command:
    - for i in $(seq {{ low }} {{ high }}); do echo $i; sleep {{ laziness }}; done
    - echo bye from {{ hostname }}
  low:
    - 1
    - 2
  high:
    - 3
    - 4

请注意,尽管 echo bye from {{ hostname }} 并未真正使用 lowhigh 参数,但它仍然会运行 2 * 2 = 4 次。

答案是 1 + 2 * 2 * 2

锁模式:修改队列

queue.yaml 实际上是队列。

Pegasus 在有可用主机时将移除 queue.yaml 的第一条条目。如果你在 Pegasus 拖取之前删除条目,它们将不会执行。如果你向 queue.yaml 添加条目,它们将执行。

问题:我为什么需要这个功能?

想想当剩余命令的数量小于空闲节点数量时的情况。如果没有向 Pegasus 提交更多作业的方法,那些空闲节点将保持空闲,直到所有命令执行完毕,你再次启动一个全新的 Pegasus 实例。

通过提供在命令仍在运行时向队列添加的方法,用户可以实现更高的节点利用率。能够从队列中删除只是一个副产品;向队列添加是关键功能。

问题:但这在 queue.yaml 上是一个竞态条件。

锁模式将锁定 queue.yaml 并为你启动一个命令行编辑器。

$ pegasus l --editor nvim  # l stands for Lock

编辑器的优先级是 --editor > $EDITOR > vim。当你保存并退出时,队列锁将被释放,Pegasus 可以访问 queue.yaml

问题:如果我添加到 queue.yaml 之前 Pegasus 终止了怎么办?

启用守护进程模式,即使 queue.yaml 为空,Pegasus 也不会终止。它将等待你再次填充 queue.yaml 并执行它们。

$ pegasus q --daemon

使用 Handlebars 的高级模板化

Handlebars 是一个模板引擎,Pegasus 使用 Handlebars 将参数填充到 hostnamecommand 中。

看看这个例子,看看这有多有用

# queue.yaml
- command:
  - python main.py --model-path {{ model }} --output-path {{ replace model "/" "--" }}.json
- model:
  - facebook/opt-13b
  - facebook/opt-30b
  - facebook/opt-66b

上面的命令展开为

# queue.yaml
- python main.py --model-path facebook/opt-13b --output-path facebook--opt-13b.json
- python main.py --model-path facebook/opt-30b --output-path facebook--opt-30b.json
- python main.py --model-path facebook/opt-66b --output-path facebook--opt-66b.json

在这里,facebook/opt-13b 是一个模型块的名字(并且你需要包含 / 以便 Hugging Face 能够理解),但是如果你只是让脚本以 facebook/opt-13b.json 的格式输出结果,它将创建一个名为 facebook 的目录,并将 opt-13b.json 保存在其中。这不是一个好的做法。相反,我们只是使用了来自 handlebars_misc_helpers字符串转换 部分的 replace 助手,基本上执行了 model.replace("/", "--")

详细信息

Pegasus 使用 sh -c 来运行命令

你放入 queue.yaml 中的命令会被 sh -c 单独包装,并通过 SSH 执行。在你的主机上,sh 可能链接到 bashdash 或其他,并且某些语法可能允许或不允许(例如,双重括号)。

队列.yaml

这是队列文件。从 queue.yaml 中读取条目,逐个读取。此外,只有在有新主机可供执行新命令时,条目才会被读取。读取的条目会立即附加到 consumed.yaml 中,以“规范形式”呈现,其中每个条目都有一个 command 键。因此,你可以执行类似 tail -n 2 consumed.yaml > queue.yaml 的操作来重新执行之前的单行命令。

如前所述,当需要修改 queue.yaml 时,始终使用锁模式。

广播模式

在广播模式下,主机之间保持同步。也就是说,当所有主机完成执行上一个命令后,将从 queue.yaml 中获取下一个命令并在所有主机上执行。

考虑以下情况

              fast-host   slow-host
- command1     success     success
- command2     success      fail!
- command3     success
- command4     running

在这种情况下,我们希望为 command2 预先添加一个撤销命令(例如,rm -rf repo || true)并从那里重新开始,但是 fast-host 已经远远领先,这使得事情变得复杂。因此,特别是在使用 Pegasus 修改节点时,保持主机同步应该是有益的。

广播模式还有一个 -e--error-aborts 标志,当主机在命令中失败时,会自动中止 Pegasus。

取消和终止

很难找到一种通用的方法来取消通过 SSH 启动的命令(见 #11)。因此,Pegasus 目前的限制是,当事情顺利时,它工作得很好,但当事情不顺利时,取消和终止变得困难。你需要进入每个节点并手动终止命令。话虽如此,你仍然可以使用广播模式来自动化这个过程。

依赖项

~11–26MB
~348K SLoC