TL;DR:如果您在项目中使用了 GPU 代码,请立即设置一个 GitHub 托管的 GPU 运行器。这操作相当快速,并且可以免除您手动运行测试的麻烦。
为您的代码库编写自动化测试,当然也包括为其中更复杂的部分编写自动化测试,已经变得像每天早上刷牙一样平常。拥有一个系统,可以为每个 Pull Request 自动运行项目的测试,这完全是正常现象。然而,直到最近,建立一个能够在具有 GPU 的系统上运行测试的系统仍然非常复杂且昂贵。这意味着,在处理与 GPU 相关的代码时,我们又回到了黑暗时代,不得不依赖手动测试。
在这篇博文中,我将描述我们如何为 scikit-learn 项目设置基于 GitHub Actions 的 GPU 运行器,以及我们在过程中学到的东西。我们的目标是为您提供一些有关我们现在使用的设置的额外信息和细节。
配备 GPU 的更大运行器#
您 GitHub 项目的所有工作流都在运行器上执行。通常,所有工作流都在默认运行器上运行,但您也可以拥有其他运行器。如果您愿意,您可以在自己的基础设施上自行托管一个运行器。直到现在,这是获得配备 GPU 的运行器的唯一途径。但是,自行托管运行器非常复杂,并且在安全性方面存在陷阱。
从大约 2024 年 4 月开始,GitHub 已将配备 GPU 的更大运行器普遍可用。
要使用这些运行器,您必须为您的组织设置信用卡。配置支出限额,以免您最终收到巨额账单而感到惊讶。对于 scikit-learn,我们目前使用 50 美元的限额。
当添加新的 GitHub 托管运行器时,请确保在选择虚拟机的镜像时选择“合作伙伴”选项卡。您需要选择“用于 AI 和 HPC 的 NVIDIA GPU 优化镜像”,以便稍后能够选择 GPU 运行器。
可以将运行器分配到的组配置为仅允许特定存储库和工作流使用该运行器组。最好只为计划使用它的存储库启用运行器组。限制运行器将获取哪些工作流需要在工作流设置中进行额外的间接级别,因此我不会在本博文中介绍它。
将您的运行器组命名为 cuda-gpu-runner-group
以与以下示例中使用的名称匹配。
虚拟机镜像内容#
GPU 运行器使用 NVIDIA 提供的磁盘镜像。这意味着与默认运行器使用的镜像有一些差异。
默认情况下未安装 gh
命令行实用程序。如果您想执行某些操作,例如从 Pull Request 中删除标签或其他此类任务,请记住这一点。
与标准镜像的最大区别在于 GPU 镜像包含 conda 安装,但文件权限不允许工作流用户修改现有环境或创建新环境。因此,对于 scikit-learn,我们通过 miniforge 再次安装 conda。conda 环境是从锁定文件创建的,因此我们不需要运行依赖项解析器。
工作流配置#
GPU 运行器和默认运行器之间的主要区别在于,项目必须为 GPU 运行器的时间付费。这意味着您可能希望仅对某些 Pull Request 而不是所有 Pull Request 执行 GPU 工作流。
运行器中可用的 GPU 性能并不强劲,这意味着对于那些希望滥用免费 GPU 资源的人来说,它并不是一个很有吸引力的目标。尽管如此,偶尔还是有人会尝试。这也是不默认运行 GPU 工作流的另一个原因。
一种很好的方法是在某种形式的人工审查后才运行工作流,即使用标签。为了将 Pull Request (PR) 标记为在 GPU 运行器上执行,审阅者会应用特定标签。与使用特殊评论触发工作流不同,应用标签不会向所有 PR 参与者发送通知。在以下示例中,CUDA CI
标签用于标记要执行的 PR,并且 runs-on
指令用于选择 GPU 运行器。这是来自scikit-learn 存储库中使用的完整 GPU 工作流的代码片段。
name: CUDA GPU
on:
pull_request:
types:
- labeled
jobs:
tests:
if: contains(github.event.pull_request.labels.*.name, 'CUDA CI')
runs-on:
group: cuda-gpu-runner-group
steps:
- uses: actions/setup-python@v5
with:
python-version: '3.12.3'
- name: Checkout main repository
uses: actions/checkout@v4
...
为了再次删除标签,我们需要一个具有提升权限的工作流。它需要能够编辑 Pull Request。此权限不适用于从分支的 Pull Request 触发的工作流。相反,工作流必须在主存储库的上下文中运行,并且只应执行最少的工作量。
on:
# Using `pull_request_target` gives us the possibility to get a API token
# with write permissions
pull_request_target:
types:
- labeled
# In order to remove the "CUDA CI" label we need to have write permissions for PRs
permissions:
pull-requests: write
jobs:
label-remover:
if: contains(github.event.pull_request.labels.*.name, 'CUDA CI')
runs-on: ubuntu-20.04
steps:
- uses: actions-ecosystem/action-remove-labels@v1
with:
labels: CUDA CI
此代码片段来自我们在 scikit-learn 中使用的标签移除工作流。
额外内容#
对于 scikit-learn,我们已经使用 GPU 运行器大约六周了。到目前为止,我们一直保持在我们设置的每月 50 美元的支出限额以下。这包括开始时的一些用于调试设置的运行。
scikit-learn 的一位贡献者创建了一个人们可以使用 Colab 笔记本来设置和运行 scikit-learn 测试套件的 Colab 笔记本。这对于那些没有轻松访问 GPU 的贡献者很有用。他们可以测试他们的更改或调试错误,而无需等待维护者标记 Pull Request。我们计划添加一个工作流,在 PR 上添加有关如何使用此笔记本的信息,以提高其可发现性。
结论#
总的来说,设置 GPU 运行器并不太困难。在处理虚拟机镜像内容的差异以及如何设置工作流方面,我们需要进行一些调整,因为我们希望手动触发它们。
GPU 运行器一直可靠地工作并在请求时获取工作。它为我们(维护者)节省了很多时间,因为我们不必在本地检出 PR 并手动运行测试。
到目前为止的成本是可以承受的,并且值得花钱,因为它消除了审查工作流中重复且乏味的手动任务。但是,它确实需要有资金和信用卡。