OUTRUN允许您使用另一台Linux计算机的处理能力执行本地命令。
它必须安装在您自己的计算机以及您将用来运行命令的任何计算机上。在其他机器上,您必须确保全局安装它,以便可以使用ssh user@host outrun这样的命令启动它。
除了超越自身之外,您还必须安装FUSE 3.x库。大多数Linux发行版都将其包含在一个名为fuse3的包中。
您必须具有对另一台计算机的root访问权限,方法是能够以root身份直接通过SSH连接到该计算机,或者具有sudo访问权限。这是必要的,因为Otrunn使用chroot。
由于远程计算机将以您当前用户的身份完全访问您的本地文件系统,因此请确保仅使用您信任的计算机。
然后,您可以让outtrun使用user@host的处理能力执行相同的命令,如下所示:
FFMPEG不需要安装在另一台机器上,并且input.mp4/output.mp4将像您预期的那样从您的本地硬盘驱动器读取和写入到您的本地硬盘驱动器。
第一次运行新命令时,可能需要一些时间才能启动,因为必须先将其依赖项复制到另一台计算机。即使另一台计算机以前已经在其上运行过FFMPEG,也可能会发生这种情况,因为它的版本可能略有不同。此操作所需的时间在很大程度上取决于您的带宽和延迟。
有关自定义行为的可用选项的概述,请参见outrun--help的输出。可以通过创建~/.outrun/config文件来配置每台远程计算机上的永久缓存,如下所示:
让我们考虑示例中的ffmpeg命令,并评估它在另一台机器上运行所需的条件,就好像它是在本地运行一样。
首先,我们需要某种方式来在另一台机器上运行命令并观察它们的输出,因此一切都从正常的SSH会话开始。尝试运行ssh-tt user@host HTOP,您会发现使用SSH运行交互式远程程序非常容易,它提供的体验与本地运行的程序非常相似。
当然,我们对运行安装在另一台机器上的软件不感兴趣,而是对在我们自己的机器上运行ffmpeg程序感兴趣。开始的一种直接方式是将/usr/bin/ffmpeg可执行文件SCP到另一台计算机,并尝试在那里运行它。不幸的是,当您尝试执行它时,您可能会发现以下情况:
这是因为可执行文件是动态链接的,它会尝试从另一台计算机上不存在的文件系统加载其库依赖项。您可以使用LDD查看像这样的ELF可执行文件依赖于哪些共享库:
$LDD`其中ffmpeg`linux-vdso.so.1(0x00007fffb7796000)libavdevice.so.58=>;/usr/lib/libavdevice.so.58(0x00007f2407f2a000)libavfilter.so.7=>;/usr/lib/libavfilter.so.7(0x00007f2407be0000)。/usr/lib/libpostproc.so.55(0x00007f2406414000)libswresample.so.3=>;/usr/lib/libswresample.so.3(0x00007f24063f4000)...。
我们也可以煞费苦心地复制所有这些库,但这并不一定是它的结束。例如,像Blender这样的软件在开始运行后会额外加载许多其他依赖项,如Python文件。即使我们用所有这些依赖项来扰乱远程文件系统,我们仍然不能运行原始命令,因为input.mp4不存在于另一台机器上。
为了处理程序可能需要访问本地文件系统中的任何文件这一不可避免的现实,outrun只需通过网络挂载本地计算机的整个文件系统即可实现镜像。这是通过在远程计算机上挂载FUSE文件系统来实现的,该文件系统将其所有操作(如在目录中列出文件)转发到本地计算机上的RPC服务。此RPC服务仅公开readdir()和stat()等函数来与本地文件系统交互。
您可能会想,为什么我们不使用现有的网络文件系统解决方案,如NFS或SSHFS。这些方法的问题在于很难自动快速设置即席会话。NFS需要使用配置文件进行设置,并且SSHFS要求远程计算机能够通过SSH连接回本地计算机。相反,包含的RPC文件系统是一个轻量级TCP服务器,它具有每个会话的令牌,可以通过我们已经在使用的SSH会话安全地传输该令牌。拥有定制的解决方案也为大量优化提供了机会,我们稍后将会看到这一点。
假设我们将本地计算机的文件系统挂载到/tmp/local_fs。我们现在可以轻松地访问/tmp/local_fs/usr/bin/ffmpeg中的FFMPEG以及/tmp/local_fs/usr/lib中的所有库依赖项。不幸的是,我们似乎还没有取得太大进展:
问题是我们仍然在远程机器的/usr/lib中查找它的依赖项。我们可以通过使用$LD_LIBRARY_PATH这样的环境变量来解决这个问题,让FFMPEG在/tmp/local_fs/usr/lib中查找库,但是这样我们就可以再次一次解决一个小问题。如果我们可以假装/tmp/local_fs是根文件系统,以便/usr/lib自动重定向到/tmp/local_fs/usr/lib,我们就不必担心这个问题。我们使用chroot就可以做到这一点。
$chroot/tmp/local_fs/bin/bash#ffmpeg-i input.mp4-vcodec libx265-crf 28 output.mp4ffmpeg版本n4.2.3版权所有(C)2000-2020 FFmpeg开发者...input.mp4:没有这样的文件或目录。
它起作用了!。嗯,差不多了。我们仍然缺少一些要在其中执行原始命令的上下文。为了能够找到input.mp4并将output.mp4存储在正确的位置,我们还需要切换到相同的原始工作目录。
虽然FFMPEG已经按预期工作,但我们也应该引入正确的环境变量。例如,$HOME可能会影响配置文件的加载位置,而$lang可能会影响某些程序的显示语言。
如果您现在回到您自己的机器上并查看原始的/home/user/video,您将看到output.mp4就在那里,就好像我们没有在完全不同的机器上运行FFMPEG一样!
虽然此方法有效,但性能仍有许多不尽如人意之处。如果您要通过Internet连接到服务器,即使是位于您附近地区的服务器,您的延迟可能至少为20ms。这也是每个文件系统操作所需的时间,并且很快就会累积起来。例如,在我的机器上,FFMPEG有110个共享库需要加载,这意味着仅查找它们的属性就已经需要2.2秒了!如果我们必须像这样天真地在网络上执行每一项文件系统操作,那么“跑得更快”就不太可行了。这就是为什么Outtrun的网络文件系统带有两种类型的优化:缓存和预取。
Outtrun持久地缓存从系统目录(如/usr/bin和/lib)读取的所有文件,这些目录已知包含应用程序及其依赖项。假设这些目录在外跑会话期间保持不变。每个新会话将检查是否有任何缓存的文件已更改,并根据需要更新它们。这个简单的策略足以使同一个程序在第二次启动时速度更快,而且通常对其他程序也有帮助,因为许多依赖项(如glibc)是共享的。LRU缓存策略用于丢弃已有一段时间未使用的文件。
对于其他目录,outrun允许内核执行基本优化,比如读取大块文件,并将小写操作缓冲到一个大写操作中,这与NFS非常相似。
Outtrun的文件系统与通用网络文件系统的真正不同之处在于其积极的预取方法。例如,当读取/usr/bin/ffmpeg这样的可执行文件时,很可能会执行该文件,然后加载其共享库。因此,当/usr/bin/ffmpeg的open()调用传入时,它不仅会提前完整地传输该可执行文件,还会在单个操作中传输所有110个共享库和相关的文件系统元数据。如果这个假设是正确的(通常是正确的),我们已经将数百个stat()/readlink()/open()/read()/close()调用减少到单个RPC调用,一次性开销为20ms。
这种预取的另一个例子是假设如果访问一个.py文件,它很可能会被解释,并且Python很快就会查找它编译后的.pyc。因此,该文件会立即随其一起发送。如果__pycache__目录不存在,则我们只需预取因尝试访问它而导致的ENOENT错误。
由于编译后的文件往往可以很好地压缩,因此所有完整读取的文件内容也会使用LZ4压缩进行传输,以节省带宽。
如果您想阅读更多关于OUTRUN及其设计决策的详细信息,请查看源代码。每个模块(文件系统、RPC、操作编排)在其文档字符串中包含更具体的文档。
文件系统性能仍然是一个瓶颈,因此最合适的工作负载是计算受限的任务,如光线跟踪和视频编码。对类似git状态的内容使用outrun可以工作,但不推荐使用。
由于要执行的软件是从您自己的计算机复制到远程计算机的,因此它必须是二进制兼容的。例如,不可能建立从x86机器到ARM机器的会话。
该命令将使用远程计算机的网络和日期/时间。如果要访问本地端点,则必须使用SSH FLAGS参数显式转发它们以设置远程转发。
OUTRUN是否同时支持多个会话?是的,您可以在多台不同的机器上同时运行软件。每台远程机器还可以支持许多不同的机器同时使用非常不同的文件系统连接到它。
为什么用Python编写OUTUN?OUTRUN的原始原型是用C++编写的,但事实证明,Python及其标准库使编排流程和操作系统交互变得更加容易。就文件系统性能而言,这没有太大区别,因为到目前为止,网络延迟是最大的瓶颈。
编写outrun的目的是严重依赖类型提示进行文档编制,并允许使用mypy进行静态分析。除此之外,flke8用于加强代码样式,pylint用于捕获其他问题。
由于OUTRUN中的许多功能依赖于操作系统交互,因此它的测试套件还包括完全集成测试,用于模拟VM连接到另一个VM时的使用情况。这些是使用Vagant设置的,并且可以通过包含--VAMRANT标志来运行: