资讯动态

Ubuntu 16.04多机多卡训练环境搭建:NCCL+OpenMPI+Horovod实战指南

发布时间:2026/10/16 15:24:20 来源:尧图企业网站定制
1. 为什么这套组合在当年是刚需如果你在2019年前后接触过多机多卡训练大概率绕不开一个经典组合Ubuntu 16.04 NCCL OpenMPI Horovod。这套东西放到今天看似乎有点过时但在当时它几乎是中小团队把单机训练扩展到多机的唯一务实选择。TensorFlow的分布式原生方案配置繁琐PyTorch的DistributedDataParallel还没现在这么成熟而Horovod用几十行代码就能把单机脚本改成多机并行这个诱惑太大了。问题的核心在于Horovod本身只是一个胶水层。它自己不负责GPU之间的通信也不负责进程之间的启动和协调。GPU卡之间的数据交换靠的是NCCL跨节点的进程管理和通信靠的是OpenMPI。三者缺一不可而且版本之间的兼容性极其敏感。Ubuntu 16.04这个系统本身又比较老默认的GCC版本、内核版本、驱动生态都和后来的20.04有不小差异所以这套安装过程踩坑的概率相当高。我写这篇东西的目的很直接把当年在Ubuntu 16.04上从零搭起这套环境的完整路径梳理清楚包括每一步为什么这么做、哪些地方容易出错、出错之后怎么排查。适合的读者是那些手里有老机器、老系统又需要跑多机多卡训练任务的人。即使你现在用的是更新的系统里面关于版本匹配、编译参数、通信调试的思路依然有参考价值。需要提前说明的是这套方案对硬件和系统环境有明确要求。你需要至少两块NVIDIA GPU系统是干净的Ubuntu 16.04并且已经装好了匹配的显卡驱动。如果你用的是CPU或者AMD显卡这套方案不适用因为NCCL只支持NVIDIA GPU。另外多机场景下需要保证节点之间网络互通最好有InfiniBand或者至少千兆以上的以太网。2. 动手之前必须锁死的版本矩阵很多人装这套东西失败根本原因不是操作步骤错了而是一开始就没把版本关系理清楚。NCCL、OpenMPI、Horovod、CUDA、显卡驱动、GCC这六个东西之间存在一张隐形的兼容性网任何一个版本选错后面就是无尽的编译错误和运行时崩溃。2.1 CUDA与显卡驱动的对应关系Ubuntu 16.04上CUDA的版本选择直接决定了你能用哪个版本的NCCL。当年最稳的组合是CUDA 9.0或CUDA 10.0。CUDA 9.0对应驱动版本需要384以上CUDA 10.0对应驱动版本需要410以上。我建议用CUDA 10.0因为它对NCCL 2.x的支持更好而且Horovod对CUDA 10的适配也更完善。查看当前驱动版本的方法很简单nvidia-smi输出顶部会显示Driver Version。如果低于410先去NVIDIA官网下载对应驱动升级。注意Ubuntu 16.04的默认内核是4.4安装新驱动时可能需要先升级内核到4.15以上否则驱动编译会失败。升级内核的命令sudo apt-get install linux-generic-hwe-16.04装完重启用uname -r确认内核版本。2.2 NCCL版本的选择逻辑NCCL的版本和CUDA版本是强绑定的。NCCL 2.4.x支持CUDA 9.0和10.0NCCL 2.5.x之后对CUDA 10.1有了更好支持。在Ubuntu 16.04 CUDA 10.0的环境下我推荐用NCCL 2.4.8。这个版本稳定而且和Horovod 0.16.x到0.18.x都能配合。NCCL有两种安装方式用deb包或者用tar包手动编译。deb包安装简单但会往系统里塞一堆依赖tar包更干净适合需要精确控制安装路径的场景。我倾向于用tar包因为后面编译Horovod时需要指定NCCL的路径tar包的方式更透明。2.3 OpenMPI的版本陷阱OpenMPI的版本选择有个大坑Ubuntu 16.04的apt源里默认是OpenMPI 1.10.x这个版本太老Horovod编译时会报错。你需要手动编译OpenMPI 3.1.x或4.0.x。但注意OpenMPI 4.0.x在Ubuntu 16.04上编译时可能遇到UCX相关的问题所以3.1.3是更稳妥的选择。还有一个关键点OpenMPI编译时必须带上--with-cuda参数否则Horovod运行时无法正确调用GPU。这个参数需要指定CUDA的安装路径。2.4 Horovod与TensorFlow/PyTorch的版本匹配Horovod的版本要和深度学习框架匹配。如果你用TensorFlow 1.12到1.14Horovod 0.16.x是合适的如果用PyTorch 1.0到1.2Horovod 0.17.x更稳。我下面以TensorFlow 1.13 Horovod 0.16.4为例因为这个组合在当年被验证过无数次。把这张版本对照表记在心里后面每一步都围绕它展开组件推荐版本备注系统Ubuntu 16.04内核建议4.15显卡驱动410.xx以上对应CUDA 10.0CUDA10.0安装时不要装自带驱动NCCL2.4.8tar包方式OpenMPI3.1.3编译时带--with-cudaHorovod0.16.4源码编译安装TensorFlow1.13.1GPU版本3. NCCL的编译安装与路径规划NCCL的安装看起来简单但路径规划没做好后面Horovod编译时会找不到库。我习惯把NCCL装到/usr/local/nccl下面这样和系统自带的库分开升级和卸载都方便。3.1 下载与解压从NVIDIA官方渠道获取NCCL 2.4.8的tar包文件名类似nccl_2.4.8-1cuda10.0_x86_64.txz。下载后执行mkdir -p /usr/local/nccl tar -xvf nccl_2.4.8-1cuda10.0_x86_64.txz -C /usr/local/nccl --strip-components1解压后/usr/local/nccl下面应该有include和lib两个目录。用ls确认一下ls /usr/local/nccl/include ls /usr/local/nccl/libinclude里应该有nccl.hlib里应该有libnccl.so和libnccl_static.a。3.2 环境变量配置为了让编译器和运行时都能找到NCCL需要把路径加到环境变量里。编辑~/.bashrc追加export NCCL_ROOT/usr/local/nccl export LD_LIBRARY_PATH$NCCL_ROOT/lib:$LD_LIBRARY_PATH export CPATH$NCCL_ROOT/include:$CPATH export LIBRARY_PATH$NCCL_ROOT/lib:$LIBRARY_PATH然后source ~/.bashrc生效。这里有个细节CPATH和LIBRARY_PATH是给编译期用的LD_LIBRARY_PATH是给运行期用的。三个都要设少一个都可能在某个环节出问题。3.3 验证NCCL是否可用写一个最小的测试程序来验证NCCL能否正常编译和链接#include stdio.h #include nccl.h int main() { ncclUniqueId id; ncclResult_t res ncclGetUniqueId(id); if (res ncclSuccess) { printf(NCCL init OK\n); } else { printf(NCCL init failed\n); } return 0; }编译命令nvcc -o test_nccl test_nccl.cu -lnccl -I/usr/local/nccl/include -L/usr/local/nccl/lib如果编译通过并且运行输出NCCL init OK说明NCCL安装没问题。如果报找不到libnccl.so检查LD_LIBRARY_PATH是否包含/usr/local/nccl/lib。注意NCCL的测试程序必须在有NVIDIA GPU的机器上运行纯CPU环境会直接报错。另外如果机器上有多个GPUNCCL会自动选择不需要额外指定。4. OpenMPI编译时的关键参数与踩坑记录OpenMPI是这套组合里最容易出问题的环节。Ubuntu 16.04自带的OpenMPI版本太老必须手动编译。而手动编译时参数配置不对后面Horovod要么编译不过要么运行时报通信错误。4.1 编译前的依赖准备先装好编译OpenMPI需要的依赖sudo apt-get update sudo apt-get install -y build-essential gfortran libnuma-dev libhwloc-devlibnuma-dev和libhwloc-dev是OpenMPI做进程绑核和拓扑识别用的不装的话编译出来的OpenMPI功能不完整。4.2 configure参数详解下载OpenMPI 3.1.3源码解压后进入目录执行configure./configure --prefix/usr/local/openmpi-3.1.3 \ --with-cuda/usr/local/cuda-10.0 \ --with-ucxno \ --enable-mpi-thread-multiple \ --disable-getpwuid逐个解释这些参数--prefix指定安装路径我习惯装到/usr/local/openmpi-3.1.3方便多版本共存。--with-cuda让OpenMPI支持CUDA感知的通信Horovod依赖这个。--with-ucxnoUCX在Ubuntu 16.04上容易出兼容问题直接禁用用传统的TCP和共享内存通信。--enable-mpi-thread-multiple开启多线程MPI支持Horovod的多线程模式需要这个。--disable-getpwuid避免在某些容器环境下获取用户信息失败导致的问题。configure跑完后注意看输出末尾的汇总信息确认CUDA支持是yes。如果是no说明--with-cuda的路径不对检查CUDA安装路径。4.3 编译与安装make -j$(nproc) sudo make install编译过程大概需要5到10分钟取决于机器性能。安装完成后把OpenMPI的bin和lib加到环境变量export MPI_ROOT/usr/local/openmpi-3.1.3 export PATH$MPI_ROOT/bin:$PATH export LD_LIBRARY_PATH$MPI_ROOT/lib:$LD_LIBRARY_PATH用mpirun --version确认安装成功。如果输出版本号是3.1.3说明没问题。4.4 验证CUDA感知是否生效编译一个简单的MPI程序检查是否能调用CUDAmpicc -o test_mpi test_mpi.c然后在程序里调用MPI_Init和cudaGetDeviceCount如果能在MPI进程里正确获取GPU数量说明CUDA感知生效了。这一步很关键因为Horovod在初始化时会依赖这个能力来分配GPU。踩坑提醒如果configure时提示找不到cuda_runtime.h检查--with-cuda的路径是否指向CUDA的根目录而不是include子目录。另外如果编译过程中报undefined reference to cuXXX说明链接时没带上CUDA库需要在LDFLAGS里加-L/usr/local/cuda-10.0/lib64 -lcudart。5. Horovod源码编译的完整流程Horovod的安装方式有两种pip直接装和源码编译。pip装简单但默认不带NCCL和OpenMPI支持多机场景下性能会打折扣。源码编译虽然麻烦但能确保所有通信后端都正确链接。5.1 安装Python依赖先确保Python环境干净建议用virtualenv或者conda创建一个独立环境python -m venv horovod-env source horovod-env/bin/activate pip install --upgrade pip pip install tensorflow-gpu1.13.1 pip install numpy wheelTensorFlow的版本要和Horovod匹配1.13.1对应Horovod 0.16.4。5.2 设置编译环境变量在编译Horovod之前确保以下环境变量已经设置export HOROVOD_NCCL_HOME/usr/local/nccl export HOROVOD_NCCL_INCLUDE/usr/local/nccl/include export HOROVOD_NCCL_LIB/usr/local/nccl/lib export HOROVOD_GPU_ALLREDUCENCCL export HOROVOD_GPU_BROADCASTNCCL export HOROVOD_WITH_TENSORFLOW1 export HOROVOD_WITH_MPI1这些变量的含义HOROVOD_NCCL_HOME告诉Horovod去哪里找NCCL。HOROVOD_GPU_ALLREDUCENCCL指定GPU间的allreduce操作用NCCL实现这是性能关键。HOROVOD_GPU_BROADCASTNCCL广播操作也用NCCL。HOROVOD_WITH_TENSORFLOW1启用TensorFlow支持。HOROVOD_WITH_MPI1启用MPI支持多机通信靠它。5.3 源码编译与安装下载Horovod 0.16.4源码进入目录后执行python setup.py build_ext --inplace pip install -e .build_ext --inplace会先编译C扩展如果这一步报错说明前面的NCCL或OpenMPI配置有问题。常见的错误和解决方法错误信息原因解决方法nccl.h not foundNCCL路径没设对检查HOROVOD_NCCL_INCLUDEmpi.h not foundOpenMPI路径没设对检查PATH和CPATHundefined reference to ncclAllReduce链接时没找到NCCL库检查HOROVOD_NCCL_LIB和LD_LIBRARY_PATHCUDA version mismatchCUDA版本和编译时不一致确认nvcc版本和TensorFlow编译时用的CUDA一致编译成功后用pip show horovod确认版本是0.16.4。5.4 单机验证Horovod是否可用写一个最小的Horovod测试脚本import tensorflow as tf import horovod.tensorflow as hvd hvd.init() config tf.ConfigProto() config.gpu_options.visible_device_list str(hvd.local_rank()) config.gpu_options.allow_growth True with tf.Session(configconfig) as sess: x tf.constant(1.0) y tf.constant(2.0) z x y print(Rank:, hvd.rank(), Result:, sess.run(z))用horovodrun启动horovodrun -np 2 -H localhost:2 python test_horovod.py如果两个进程都输出正确结果说明单机多卡环境没问题。注意-np 2表示启动两个进程-H localhost:2表示在本机启动两个slot。实操心得第一次跑的时候如果报Blas GEMM launch failed大概率是GPU内存不够。在config里加上allow_growthTrue或者把batch size调小。另外如果报NCCL error检查LD_LIBRARY_PATH里NCCL的路径是否在CUDA路径之前顺序不对会导致加载错误的库版本。6. 多机环境下的网络配置与启动方式单机跑通只是第一步多机才是Horovod的真正价值所在。但多机环境的坑比单机多得多网络配置、SSH互信、MPI参数每一项都可能让你卡半天。6.1 SSH免密登录配置Horovod通过SSH来启动远程进程所以节点之间必须配置免密登录。假设有两台机器node1和node2在node1上执行ssh-keygen -t rsa ssh-copy-id usernode2然后在node2上反向操作一遍确保双向都能免密登录。测试方法ssh node2 hostname如果直接输出node2的主机名说明配置成功。6.2 MPI参数调优多机启动时OpenMPI需要知道用哪个网络接口通信。如果机器有多张网卡不指定的话可能走错网卡导致通信失败。在~/.bashrc里加上export OMPI_MCA_btl_tcp_if_includeeth0 export OMPI_MCA_oob_tcp_if_includeeth0把eth0换成实际的网卡名。用ifconfig或ip addr查看网卡名称。另外如果机器有InfiniBand网卡可以启用IB通信export OMPI_MCA_btl_openib_allow_ib1IB的带宽比以太网高一个数量级多机训练时性能提升明显。6.3 horovodrun的多机启动假设node1有两个GPUnode2有两个GPU启动命令horovodrun -np 4 -H node1:2,node2:2 python train.py-np 4表示总共4个进程-H node1:2,node2:2表示node1起2个node2起2个。Horovod会自动通过SSH在node2上启动进程并建立NCCL通信。如果启动时报Permission denied检查SSH免密是否配好。如果报Connection refused检查防火墙是否挡住了MPI的端口。Ubuntu 16.04默认的ufw可能开着临时关闭sudo ufw disable6.4 多机通信的验证方法跑一个简单的allreduce测试确认多机通信正常import horovod.tensorflow as hvd import tensorflow as tf hvd.init() with tf.Session() as sess: rank hvd.rank() tensor tf.constant([float(rank)]) summed hvd.allreduce(tensor, averageFalse) print(Rank:, rank, Sum:, sess.run(summed))如果4个进程都输出Sum: 6.00123说明allreduce正常工作。如果某个进程卡住或者输出不对检查网络配置和NCCL版本。踩坑记录有一次在两台机器上跑一台是CUDA 10.0另一台是CUDA 9.0结果NCCL初始化直接失败。NCCL要求所有节点的版本完全一致包括CUDA、驱动、NCCL本身。多机环境一定要保证软件栈统一。7. 常见报错与排查思路这套环境搭下来不遇到几个报错几乎不可能。我把当年踩过的坑整理成一张排查表遇到问题可以按图索骥。7.1 编译期报错编译期的问题主要集中在头文件和库找不到。核心排查思路是确认环境变量是否生效。用echo $CPATH和echo $LIBRARY_PATH检查路径是否包含NCCL和OpenMPI的目录。如果路径对了还报错用find / -name nccl.h 2/dev/null确认文件确实存在。另一个常见问题是GCC版本。Ubuntu 16.04默认GCC 5.4编译CUDA 10.0相关代码时可能报unsupported GNU version。解决方法是安装GCC 6并切换sudo apt-get install gcc-6 g-6 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 60 sudo update-alternatives --install /usr/bin/g g /usr/bin/g-6 607.2 运行期报错运行期最常见的报错是NCCL相关的报错信息可能原因排查方向NCCL error: unhandled system error驱动版本不匹配检查nvidia-smi和CUDA版本NCCL error: invalid argumentGPU数量或rank配置错误检查-np和-H参数NCCL error: unhandled cuda errorGPU被占用或显存不足用nvidia-smi查看GPU状态MPI_Init failedOpenMPI环境变量未生效检查PATH和LD_LIBRARY_PATH如果报错信息不明确可以开启NCCL的调试日志export NCCL_DEBUGINFO export NCCL_DEBUG_SUBSYSALL重新运行程序会输出详细的通信过程帮助定位问题。7.3 性能不达预期有时候环境搭好了程序也能跑但速度比单机还慢。这通常是通信瓶颈导致的。排查步骤用nvidia-smi确认所有GPU都在工作没有进程掉队。用iftop或nload查看网络流量确认走的是高速网卡。检查是否误用了TCP而不是IB。在日志里搜索NET/IB或NET/Socket如果是Socket说明走的是TCP。调整batch size太小的batch会导致通信占比过高。如果确认走的是TCP且没有IB网卡可以尝试启用NCCL的P2P和SHMexport NCCL_P2P_DISABLE0 export NCCL_SHM_DISABLE0这两个默认是开启的但某些环境下可能被禁用。8. 一些个人体会和后续建议这套环境我前后搭过不下十次每次都有新坑。最大的感受是版本管理比技术本身更重要。NCCL、OpenMPI、Horovod这三个东西的版本关系像一张蜘蛛网牵一发而动全身。我的建议是一旦搭好一套能跑的环境立刻用pip freeze和dpkg -l把版本信息导出保存后面换机器直接照抄不要试图优化版本。另一个体会是Ubuntu 16.04确实老了。如果条件允许尽量升级到18.04或20.04新系统对CUDA和NCCL的支持好很多GCC版本也更新能省掉不少编译期的麻烦。但如果机器不能重装那这套方案依然可用只是每一步都要更小心。最后分享一个小技巧在编译Horovod之前先用nccl-tests跑一遍NCCL的带宽测试确认NCCL本身没问题。nccl-tests是NVIDIA官方提供的测试工具能测出allreduce的实际带宽。如果带宽正常说明NCCL和驱动都没问题后面Horovod出问题就只需要排查OpenMPI和Python层。这个分步验证的思路比一股脑装完再调试要高效得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑