XGBoost 项目中的自动化测试
本文档收集了使用 XGBoost 项目持续集成(CI)服务的技巧。
目录
测试技巧
使用 noLD
选项运行 R 测试
您可以使用带有编译标志 --disable-long-double
的自定义构建 R 来运行 R 测试。有关 noLD 的更多详细信息,请参阅此页面。这是将 XGBoost 保留在 CRAN(R 包索引)上的要求。与其他测试不同,此测试必须手动调用。只需在拉取请求中添加一个评审评论 /gha run r-nold-test
即可启动测试。(普通评论无效。必须是评审评论。)
修改 CI 容器
许多 CI 流水线使用 Docker 容器来确保在各种软件包下具有一致的测试环境。我们有一个单独的仓库,dmlc/xgboost-devops,用于托管构建和发布 CI 容器的逻辑。
要修改 CI 容器,请执行以下步骤
确定需要更新哪个容器。示例:
492475357299.dkr.ecr.us-west-2.amazonaws.com/xgb-ci.gpu:main
克隆 dmlc/xgboost-devops 并修改对应的 Dockerfile。示例:
containers/dockerfile/Dockerfile.gpu
。在本地构建容器,确保容器能够成功构建。有关此步骤,请参阅在本地重现 CI 测试环境。
向 dmlc/xgboost-devops 提交拉取请求,包含对 Dockerfile 的建议更改。记下拉取请求号。示例:
#204
克隆 dmlc/xgboost。找到文件
ops/pipeline/get-image-tag.sh
,该文件应该只有一行IMAGE_TAG=main
要使用新的容器,请将文件修改如下
IMAGE_TAG=PR-XX
其中
XX
是拉取请求号。现在向 dmlc/xgboost 提交拉取请求。CI 将使用新容器运行测试。验证所有测试是否通过。
合并
dmlc/xgboost-devops
中的拉取请求。等待 CI 在main
分支上完成。回到
dmlc/xgboost
的拉取请求,将ops/pipeline/get-image-tag.sh
改回IMAGE_TAG=main
。合并
dmlc/xgboost
中的拉取请求。
在本地重现 CI 测试环境
您可以通过在本地构建和运行 Docker 容器来重现与 CI 流水线相同的测试环境。
前置条件
安装 NVIDIA Docker 运行时:https://docs.nvda.net.cn/datacenter/cloud-native/container-toolkit/latest/install-guide.html。该运行时允许您在 Docker 容器内访问 NVIDIA GPU。
构建 Docker 容器
克隆仓库 dmlc/xgboost-devops 并按如下方式调用 containers/docker_build.sh
# The following env vars are only relevant for CI
# For local testing, set them to "main"
export GITHUB_SHA="main"
export BRANCH_NAME="main"
bash containers/docker_build.sh IMAGE_REPO
其中 IMAGE_REPO
是容器镜像的名称。包装脚本将查找 YAML 文件 containers/ci_container.yml
。例如,当 IMAGE_REPO
设置为 xgb-ci.gpu
时,脚本将使用 containers/ci_container.yml
中的相应条目
xgb-ci.gpu:
container_def: gpu
build_args:
CUDA_VERSION_ARG: "12.4.1"
NCCL_VERSION_ARG: "2.23.4-1"
RAPIDS_VERSION_ARG: "24.10"
container_def
条目指示 Dockerfile 的位置。容器定义将从 containers/dockerfile/Dockerfile.CONTAINER_DEF
获取,其中 CONTAINER_DEF
是 container_def
条目的值。在本例中,Dockerfile 是 containers/dockerfile/Dockerfile.gpu
。
build_args
条目列出了 Docker 构建的所有构建参数。在此示例中,构建参数为
--build-arg CUDA_VERSION_ARG=12.4.1 --build-arg NCCL_VERSION_ARG=2.23.4-1 \
--build-arg RAPIDS_VERSION_ARG=24.10
构建参数为 Dockerfile 中的 ARG
指令提供输入。
containers/docker_build.sh
完成后,您将可以通过(完全限定的)URI 492475357299.dkr.ecr.us-west-2.amazonaws.com/[image_repo]:main
访问容器。添加前缀 492475357299.dkr.ecr.us-west-2.amazonaws.com/
是为了将来可以将容器上传到 AWS Elastic Container Registry (ECR),一个私有的 Docker 注册表。
在 Docker 容器内运行命令
按如下方式从主 dmlc/xgboost
仓库调用 ops/docker_run.py
python3 ops/docker_run.py \
--image-uri 492475357299.dkr.ecr.us-west-2.amazonaws.com/[image_repo]:[image_tag] \
[--use-gpus] \
-- "command to run inside the container"
其中应指定 --use-gpus
将 NVIDIA GPU 暴露给 Docker 容器。
例如
# Run without GPU
python3 ops/docker_run.py \
--image-uri 492475357299.dkr.ecr.us-west-2.amazonaws.com/xgb-ci.cpu:main \
-- bash ops/pipeline/build-cpu-impl.sh cpu
# Run with NVIDIA GPU
python3 ops/docker_run.py \
--image-uri 492475357299.dkr.ecr.us-west-2.amazonaws.com/xgb-ci.gpu:main \
--use-gpus \
-- bash ops/pipeline/test-python-wheel-impl.sh gpu
(可选)您可以指定 --run-args
将额外参数传递给 docker run
# Allocate extra space in /dev/shm to enable NCCL
# Also run the container with elevated privileges
python3 ops/docker_run.py \
--image-uri 492475357299.dkr.ecr.us-west-2.amazonaws.com/xgb-ci.gpu:main \
--use-gpus \
--run-args='--shm-size=4g --privileged' \
-- bash ops/pipeline/test-python-wheel-impl.sh gpu
请参阅构建和发布 CI 容器和 VM 镜像的基础设施了解 CI 流水线中如何构建和管理容器。
示例:本地开发的有用任务
构建支持 GPU 的 XGBoost + 将其打包为 Python wheel
export DOCKER_REGISTRY=492475357299.dkr.ecr.us-west-2.amazonaws.com python3 ops/docker_run.py \ --image-uri ${DOCKER_REGISTRY}/xgb-ci.gpu_build_rockylinux8:main \ -- ops/pipeline/build-cuda-impl.sh
运行 Python 测试
export DOCKER_REGISTRY=492475357299.dkr.ecr.us-west-2.amazonaws.com python3 ops/docker_run.py \ --image-uri ${DOCKER_REGISTRY}/xgb-ci.cpu:main \ -- ops/pipeline/test-python-wheel-impl.sh cpu
使用 GPU 算法运行 Python 测试
export DOCKER_REGISTRY=492475357299.dkr.ecr.us-west-2.amazonaws.com python3 ops/docker_run.py \ --image-uri ${DOCKER_REGISTRY}/xgb-ci.gpu:main \ --use-gpus \ -- ops/pipeline/test-python-wheel-impl.sh gpu
使用 GPU 算法运行 Python 测试,并使用多个 GPU
export DOCKER_REGISTRY=492475357299.dkr.ecr.us-west-2.amazonaws.com python3 ops/docker_run.py \ --image-uri ${DOCKER_REGISTRY}/xgb-ci.gpu:main \ --use-gpus \ --run-args='--shm-size=4g' \ -- ops/pipeline/test-python-wheel-impl.sh mgpu # --shm-size=4g is needed for multi-GPU algorithms to function
构建和测试 JVM 包
export DOCKER_REGISTRY=492475357299.dkr.ecr.us-west-2.amazonaws.com export SCALA_VERSION=2.12 # Specify Scala version (2.12 or 2.13) python3 ops/docker_run.py \ --image-uri ${DOCKER_REGISTRY}/xgb-ci.jvm:main \ --run-args "-e SCALA_VERSION" \ -- ops/pipeline/build-test-jvm-packages-impl.sh
构建和测试支持 GPU 的 JVM 包
export DOCKER_REGISTRY=492475357299.dkr.ecr.us-west-2.amazonaws.com export SCALA_VERSION=2.12 # Specify Scala version (2.12 or 2.13) export USE_CUDA=1 python3 ops/docker_run.py \ --image-uri ${DOCKER_REGISTRY}/xgb-ci.jvm_gpu_build:main \ --use-gpus \ --run-args "-e SCALA_VERSION -e USE_CUDA --shm-size=4g" \ -- ops/pipeline/build-test-jvm-packages-impl.sh # --shm-size=4g is needed for multi-GPU algorithms to function
CI 基础设施概览
GitHub Actions
我们广泛使用 GitHub Actions 来托管我们的 CI 流水线。配置文件中列出的大多数测试都会在每个传入的拉取请求和每次对分支的更新时自动运行。
使用 RunsOn 的自托管运行器
RunsOn 是一个 SaaS(软件即服务)应用程序,使我们能够轻松创建自托管运行器以与 GitHub Actions 流水线一起使用。RunsOn 在底层使用 Amazon Web Services (AWS) 来提供可访问各种 CPU、内存和 NVIDIA GPU 资源的运行器。得益于此应用程序,我们能够在利用熟悉的 GitHub Actions 接口的同时测试 XGBoost 的 GPU 加速和分布式算法。
在 GitHub Actions 中,作业默认运行在 Microsoft 托管的运行器上。要选择自托管运行器(由 RunsOn 启用),我们使用以下特殊语法
runs-on:
- runs-on
- runner=runner-name
- run-id=${{ github.run_id }}
- tag=[unique tag that uniquely identifies the job in the GH Action workflow]
其中运行器在 .github/runs-on.yml
中定义。
全景:代码库中 CI 流水线的组织方式
XGBoost 项目将其 CI 流水线的配置作为代码库的一部分进行存储。因此,git 仓库不仅存储其源代码的更改历史,还存储 CI 流水线的更改历史。
CI 流水线组织在以下目录和文件中
.github/workflows/
:CI 流水线定义,使用 GitHub Actions 语法.github/runs-on.yml
:RunsOn 服务配置。指定自托管 CI 运行器的规格。ops/conda_env/
:Conda 环境定义ops/patch/
:补丁文件ops/pipeline/
:定义 CI/CD 流水线的 Shell 脚本。这些脚本大多数可以在本地运行(用于协助开发和调试);少数必须在 CI 中运行。ops/script/
:各种用于测试的实用脚本ops/docker_run.py
:在容器内运行命令的包装脚本
要检查给定的 CI 流水线,请按以下顺序检查文件
许多 CI 流水线使用 Docker 容器来确保在各种软件包下具有一致的测试环境。我们有一个单独的仓库,dmlc/xgboost-devops,用于托管构建 CI 容器的代码。该仓库的组织结构如下
actions/
:与 GitHub Actions 一起使用的自定义动作。有关更多详细信息,请参阅GitHub Actions 的自定义动作。containers/dockerfile/
:定义容器的 Dockerfilecontainers/ci_container.yml
:定义 Dockerfile 和容器之间的映射。还指定每个容器使用的构建参数。containers/docker_build.{py,sh}
:构建和测试 CI 容器的包装脚本。vm_images/
:定义用于构建 Amazon EC2 虚拟机 (VM) 镜像的引导脚本。请参阅VM 镜像说明了解 VM 镜像与容器镜像的关系。
请参阅在本地重现 CI 测试环境了解构建和使用容器的实用脚本。
通过 Amazon S3 在作业之间共享构件
我们通过将构件上传到 Amazon S3 来使一个工作流作业的构件对另一个作业可用。在 CI 中,我们利用脚本 ops/pipeline/manage-artifacts.py
来协调构件共享。
上传文件到 S3:在工作流 YAML 中,添加以下行
- name: Upload files to S3
run: |
REMOTE_PREFIX="remote directory to place the artifact(s)"
python3 ops/pipeline/manage-artifacts.py upload \
--s3-bucket ${{ env.RUNS_ON_S3_BUCKET_CACHE }} \
--prefix cache/${{ github.run_id }}/${REMOTE_PREFIX} \
path/to/file
--prefix
参数指定构件应放置的远程目录。构件将放置在 s3://{RUNS_ON_S3_BUCKET_CACHE}/cache/{GITHUB_RUN_ID}/{REMOTE_PREFIX}/
中,其中 RUNS_ON_S3_BUCKET_CACHE
和 GITHUB_RUN_ID
由 CI 设置。
您可以上传多个文件,可能使用通配符匹配
- name: Upload files to S3
run: |
python3 ops/pipeline/manage-artifacts.py upload \
--s3-bucket ${{ env.RUNS_ON_S3_BUCKET_CACHE }} \
--prefix cache/${{ github.run_id }}/build-cuda \
build/testxgboost python-package/dist/*.whl
从 S3 下载文件:在工作流 YAML 中,添加以下行
- name: Download files from S3
run: |
REMOTE_PREFIX="remote directory where the artifact(s) were placed"
python3 ops/pipeline/manage-artifacts.py download \
--s3-bucket ${{ env.RUNS_ON_S3_BUCKET_CACHE }} \
--prefix cache/${{ github.run_id }}/${REMOTE_PREFIX} \
--dest-dir path/to/destination_directory \
artifacts
您也可以使用通配符匹配。脚本将查找给定前缀下与通配符模式匹配的所有构件。
- name: Download files from S3
run: |
# Locate all artifacts with name *.whl under prefix
# cache/${GITHUB_RUN_ID}/${REMOTE_PREFIX} and
# download them to wheelhouse/.
python3 ops/pipeline/manage-artifacts.py download \
--s3-bucket ${{ env.RUNS_ON_S3_BUCKET_CACHE }} \
--prefix cache/${{ github.run_id }}/${REMOTE_PREFIX} \
--dest-dir wheelhouse/ \
*.whl
GitHub Actions 的自定义动作
XGBoost 实现了一些自定义的复合动作,以减少工作流 YAML 文件中的重复代码。自定义动作托管在一个单独的仓库中,dmlc/xgboost-devops,以便在拉取请求或分支中轻松测试对自定义动作的更改。
在工作流文件中,我们将引用 dmlc/xgboost-devops/actions/{custom-action}@main
。例如
- uses: dmlc/xgboost-devops/actions/miniforge-setup@main
with:
environment-name: cpp_test
environment-file: ops/conda_env/cpp_test.yml
每个自定义动作由两个组件组成
主脚本 (
dmlc/xgboost-devops/actions/{custom-action}/action.yml
):分派给特定版本的实现脚本(参见下一项)。主脚本从指定的分支克隆xgboost-devops
的特定引用,使我们能够轻松测试对自定义动作的更改。实现脚本 (
dmlc/xgboost-devops/actions/impls/{custom-action}/action.yml
):实现自定义脚本。
此设计灵感来自 Mike Sarahan 在 rapidsai/shared-actions 中的工作。
构建和发布 CI 容器和 VM 镜像的基础设施
Docker 容器说明
容器 CI 流水线
dmlc/xgboost-devops 仓库托管了一个 CI 流水线,按定期计划构建新的 Docker 容器。新容器在以下情况下构建
dmlc/xgboost-devops
的main
分支有新的提交。向
dmlc/xgboost-devops
提交了新的拉取请求。每周的设定日期和时间。
此设置确保 CI 容器保持最新。
包装脚本的工作原理
包装脚本 docker_build.sh
、docker_build.py
(在 dmlc/xgboost-devops
中)和 docker_run.py
(在 dmlc/xgboost
中)旨在透明地记录底层执行的命令。例如,当您运行 bash containers/docker_build.sh xgb-ci.gpu
时,日志将显示以下内容
# docker_build.sh calls docker_build.py...
python3 containers/docker_build.py --container-def gpu \
--image-uri 492475357299.dkr.ecr.us-west-2.amazonaws.com/xgb-ci.gpu:main \
--build-arg CUDA_VERSION_ARG=12.4.1 --build-arg NCCL_VERSION_ARG=2.23.4-1 \
--build-arg RAPIDS_VERSION_ARG=24.10
...
# .. and docker_build.py in turn calls "docker build"...
docker build --build-arg CUDA_VERSION_ARG=12.4.1 \
--build-arg NCCL_VERSION_ARG=2.23.4-1 \
--build-arg RAPIDS_VERSION_ARG=24.10 \
--load --progress=plain \
--ulimit nofile=1024000:1024000 \
-t 492475357299.dkr.ecr.us-west-2.amazonaws.com/xgb-ci.gpu:main \
-f containers/dockerfile/Dockerfile.gpu \
containers/
这些日志在调试容器构建时非常有用。
这是使用 docker_run.py
的示例
# Run without GPU
python3 ops/docker_run.py \
--image-uri 492475357299.dkr.ecr.us-west-2.amazonaws.com/xgb-ci.cpu:main \
-- bash ops/pipeline/build-cpu-impl.sh cpu
# Run with NVIDIA GPU
# Allocate extra space in /dev/shm to enable NCCL
# Also run the container with elevated privileges
python3 ops/docker_run.py \
--image-uri 492475357299.dkr.ecr.us-west-2.amazonaws.com/xgb-ci.gpu:main \
--use-gpus \
--run-args='--shm-size=4g --privileged' \
-- bash ops/pipeline/test-python-wheel-impl.sh gpu
它们被转换为以下 docker run
调用
docker run --rm --pid=host \
-w /workspace -v /path/to/xgboost:/workspace \
-e CI_BUILD_UID=<uid> -e CI_BUILD_USER=<user_name> \
-e CI_BUILD_GID=<gid> -e CI_BUILD_GROUP=<group_name> \
492475357299.dkr.ecr.us-west-2.amazonaws.com/xgb-ci.cpu:main \
bash ops/pipeline/build-cpu-impl.sh cpu
docker run --rm --pid=host --gpus all \
-w /workspace -v /path/to/xgboost:/workspace \
-e CI_BUILD_UID=<uid> -e CI_BUILD_USER=<user_name> \
-e CI_BUILD_GID=<gid> -e CI_BUILD_GROUP=<group_name> \
--shm-size=4g --privileged \
492475357299.dkr.ecr.us-west-2.amazonaws.com/xgb-ci.gpu:main \
bash ops/pipeline/test-python-wheel-impl.sh gpu
VM 镜像说明
在 dmlc/xgboost-devops 的 vm_images/
目录中,我们定义了 Packer 脚本来构建 Amazon EC2 上的虚拟机 (VM) 镜像。VM 镜像包含运行容器所需的最少驱动程序和系统软件集。
我们更新容器镜像的频率远高于 VM 镜像。构建一个新的容器镜像只需要 10 分钟,而构建一个新的 VM 镜像需要 1-2 小时。
为了实现快速的开发迭代周期,我们将大部分开发环境放在容器中,并保持 VM 镜像较小。测试所需的软件包应该烘焙到容器中,而不是 VM 镜像中。开发者可以修改容器并快速查看更改结果。
注意
Windows 平台的特别说明
在 Windows 上测试 XGBoost 时,我们不使用容器。所有软件都必须烘焙到 VM 镜像中。NVIDIA Container Toolkit 尚未原生支持 Windows,因此不使用容器。
dmlc/xgboost-devops 仓库托管了一个 CI 流水线,按定期计划(目前每月)构建新的 VM 镜像。