XGBoost 项目中的自动化测试
本文档收集了使用 XGBoost 项目持续集成 (CI) 服务的技巧。
目录
测试技巧
R 测试
使用 noLD
选项运行 R 测试
您可以使用带有编译标志 --disable-long-double
的自定义构建 R 来运行 R 测试。有关 noLD 的更多详细信息,请参阅此页面。这是将 XGBoost 保留在 CRAN(R 包索引)上的要求。与其他测试不同,此测试必须手动调用。只需在拉取请求中添加评论 /gha run r-nold-test
即可启动测试。(普通评论不起作用。它需要是评论评论。)
使用来自 r-hub 的容器镜像
修改 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
是拉取请求号。例如:PR-204
。现在向 dmlc/xgboost 提交拉取请求。CI 将使用新容器运行测试。验证所有测试都通过。
合并
dmlc/xgboost-devops
中的拉取请求。等待 CI 在main
分支上完成。回到
dmlc/xgboost
的拉取请求,将ops/pipeline/get-image-tag.sh
改回IMAGE_TAG=main
。合并
dmlc/xgboost
中的拉取请求。
在本地复现 CI 测试环境
您可以通过在本地构建和运行 Docker 容器来复现与 CI 管道相同的测试环境。
先决条件
安装 Docker:https://docs.dockerd.com.cn/engine/install/ubuntu/
安装 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
构建和测试 JVM 包,支持 GPU
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/
:使用 GitHub Actions 语法定义 CI 管道.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 镜像。