2024-11-21 一站式 AI 平台生日大派对!2024-11-21 一站式 AI 平台生日大派对! 无问芯穹特别推出多项超值福利!立即参与
Skip to content

训练故障处理

本文介绍了智算云平台任务功能故障排查思路及解决方案。

获取任务状态通知

AIStudio 任务功能已支持配置飞书通知。为了及时获取任务异常、或运行失败等情况,建议您配置飞书机器人告警通知

利用容错定位问题

推荐开启任务容错功能。开启容错后,平台会在训练任务启动时执行启动检测(Bootcheck),训练失败时会执行异常定位(Troubleshoot),均会检查关键的软件和硬件环境(GPU/RDMA/IO/通信)。

在任务详情页的容错日志中,平台会记录当前任务的最大重启次数、已重启次数、因硬件问题导致的重调度次数。如果发现掉卡错误,会重新调度健康节点,并尝试重启训练。

alt text

如果启动检测和异常定位都通过,那么很可能是训练代码出现了问题。

如果持续出现硬件问题,请联系无问芯穹。

WARNING

  • 硬件错误(如掉卡)将阻断任务继续执行,平台将自动帮您进行重调度至健康节点,再重启训练。如集群中无冗余健康节点,容错功能将无法为您恢复训练。
  • 其他异常问题(如慢节点)不会阻断任务继续执行,平台将继续执行用户代码。

任务与 Worker 日志

如果训练代码存在问题,导致任务失败,您可以在任务详情中查看任务日志和 Worker 日志。

  1. 当发生错误时,可以从任务列表中点击日志,查看所有的错误日志记录,找到第一个异常日志记录及其对应的 Pod 信息。
  2. 根据第一条错误日志的记录信息,查找相应的代码位置,分析可能的原因,例如通信超时退出、计算错误退出、硬件错误退出、Pod 或进程失去连接退出等。

在查看任务日志时,可选择 Worker,并根据时间筛选日志(精确到秒)。

alt text

运行环境问题

系统级 CUDA/cuDNN

镜像中可能缺失系统级 CUDA 和 cuDNN 的问题,无法使用 nvcc。例如 DeepSpeed 明确依赖 nvcc。

Conda

在训练服务中使用镜像中的 Conda,可能会遇到 conda activate 命令报错,导致无法激活 Conda 虚拟环境。

修改镜像

如果您接受修改镜像,可以直接在镜像中心以预置镜像/自定义镜像的基础,执行以下命令,构建新版镜像。使用新版镜像运行训练任务即可。

shell
conda init --system

alt text

不修改镜像

如果您直接在任务的启动命令中执行 conda activate,解决方案有以下两种(请自行确认镜像中 conda 的实际安装路径):

  • 通过 conda hook 初始化,先确保 conda 命令可用,然后初始化 conda 的 shell 函数和环境变量

    shell
    # 请自行确认镜像中 conda 的实际安装路径
    export PATH="/usr/local/miniconda3/bin:$PATH"
    eval "$(conda shell.bash hook)"
    # 在 activate 前执行上述命令
    conda activate your-env
  • 更简洁的方式,直接使用 conda 预置的初始化脚本 conda.sh

    shell
    # 请自行确认镜像中 conda 的实际安装路径
    source /usr/local/miniconda3/etc/profile.d/conda.sh
    # 在 activate 前执行上述命令
    conda activate your-env

如果在任务启动命令中执行了外部 Shell 脚本,脚本内部执行 conda activate,解决方案有以下两种(请自行确认镜像中 conda 的安装路径):

  • 修改引用的外部 Shell 脚本,在脚本中先 source conda 预置的初始化脚本 conda.sh

    shell
    # 请自行确认镜像中 conda 的实际安装路径
    source /usr/local/miniconda3/etc/profile.d/conda.sh
    # 在 activate 前执行上述命令
    conda activate your-env
  • 如果镜像的 .bashrc 中已存在有效的 conda 配置,可以不修改脚本内容,仅修改脚本的执行方式为使用 interactive shell 执行(bash -i):

    shell
    # -i 方式会在子 Shell 中加载当前 Shell 的 .bashrc 
    bash -i myscript.sh

WARNING

在训练场景下,为了缩小镜像体积、降低复杂性,最佳实践是不要使用 Conda 虚拟环境,而是制作一个最小化专用镜像。

NOTE

  • 平台在执行任务启动命令时,会加上 --login 参数。Login shell 会依次读取 /etc/profile ~/.bash_profile, ~/.bash_login, ~/.profile,默认不读取 ~/.bashrc
  • 修改/制作自定义镜像时,可以考虑使用 conda init --system,将 conda 相关配置写入系统全局配置,如 /etc/profile.d/conda.shconda init 默认写入用户设置,如 ~/.bashrc)。

保留现场

任务失败后平台将清理 Pod,导致无法进入训练环境。为了保留现场,可通过重跑训练任务,在启动命令最后添加 sleep 10000,使任务失败后仍将 Pod 保留一段时间,方便排查。

推荐优化启动命令,在出现问题时自动执行 sleep 操作。参见下方优化训练任务的启动命令

登录任务的 worker

在任务详情页可查看任务 worker 的状态,可点击刷新获取当前最新状态。​必要时可以登录 worker 进行错误排查。

Worker 的登录入口在任务详情页 Worker 信息中。

alt text

您也可以自行运行基准测试以检查 Pod 和硬件是否存在问题。如果这些测试都通过,那么很可能是代码出现了问题。

  • 计算基准测试,如 gpu-burn
  • 通信基准测试,如 nccl-test
  • 训练基准测试等

NOTE

  • 仅在任务运行中时可登录 Worker。
  • AIStudio 在任务 Worker 的 Web Terminal 中内置了 atlctl 调试工具,可对所有 Worker 下发调试命令。

调试 NCCL

在调试和排障时,使用以下 NCCL 环境变量有助于排查问题。

设置 NCCL 在运行时打印丰富的日志信息,帮助调试和分析 NCCL 相关的性能问题和错误。

# 调试时打印日志使用​
export NCCL_DEBUG=INFO

NOTE

打印大量调试信息可能会影响 NCCL 的性能,因此建议只在调试需要时启用此设置。

暂时绕过自动选择接口的机制,指定 NCCL 通信使用的网络接口。

shell
# 设置socket建环使用eth0的通道
export NCCL_SOCKET_IFNAME=eth0

如果您的镜像是用的 NGC 镜像,或者加载了 NCCL 的 sharp 等插件的话,有可能会出错。可临时添加如下环境变量,帮助诊断问题或验证网络配置。

shell
# 指示 NCCL 不使用任何网络通信插件
export NCCL_NET_PLUGIN=none ​

优化训练任务的启动命令

在 AIStudio 中进行模型训练时,我们希望能够提供较好的排障体验。我们将诊断一个训练任务启动命令的问题,并演示如何改进。

原始命令

该训练任务的启动命令存在以下问题:

  • 缺乏日志记录,无法跟踪每个阶段的开始和结束时间以及执行状态。
  • 使用 tee 命令重定向输出,即使训练任务失败也无法识别错误。
  • 缺少错误处理机制,无法在训练任务失败时采取措施。
shell
wget https://s3.amazonaws.com/models.huggingface.co/bert/gpt2-vocab.json​
wget https://s3.amazonaws.com/models.huggingface.co/bert/gpt2-merges.txt​
mkdir -p checkpoints/gpt2_345m​
cd checkpoints/gpt2_345m​
wget --content-disposition https://api.ngc.nvidia.com/v2/models/nvidia/megatron_l​
m_345m/versions/v0.0/zip -O megatron_lm_345m_v0.0.zip​
unzip megatron_lm_345m_v0.0.zip​
rm megatron_lm_345m_v0.0.zip​
cd ../..​
git clone https://github.com/EastInsure/Megatron-DeepSpeed.git​
cd Megatron-DeepSpeed​
bash train/7B1_test.sh $MASTER_ADDR $MASTER_PORT $WORLD_SIZE $RANK | tee $(pwd)/M​egatron-DeepSpeed.log

改进方案

为了提高训练任务的易用性和鲁棒性,建议按照以下步骤进行改进:

  1. 添加日志记录:在每个关键操作前后添加日志记录,记录时间、Pod 名称和操作状态(例如是否已开始或结束)。这一改动将使我们能更好地跟踪脚本的执行进度。在容器内部获取 $HOSTNAME 环境变量可得到 Pod 名称。

    例如:

    shell
    echo "[$(date +"%Y-%m-%d %H:%M:%S")] \$HOSTNAME starts to download gpt2-vocab.json and gpt2-merges.txt"
    wget https://s3.amazonaws.com/models.huggingface.co/bert/gpt2-vocab.json
    wget https://s3.amazonaws.com/models.huggingface.co/bert/gpt2-merges.txt
    echo "[$(date +"%Y-%m-%d %H:%M:%S")] \$HOSTNAME has completed the vocab download and starts to download the checkpoints."
  2. 使用 tee 命令并启用 set -o pipefail。原始命令最后一个操作将输出结果通过管道形式重定向到了日志文件中,因为 tee 的命令总是会成功的,导致这种方式即使任务出错了也无法正确识别到。

    例如使用 set -o pipefail 确保管道中的任何错误都会导致脚本退出。

    shell
    set -o pipefail
    bash train/7B1_test.sh $MASTER_ADDR $MASTER_PORT $WORLD_SIZE $RANK | tee $(pwd)/Megatron-DeepSpeed.log

    同理,如果重定向操作在脚本内部完成,也可以这样改造,从而可以把出错信息完整的保留。

    shell
    bash train/7B1_test.sh $MASTER_ADDR $MASTER_PORT $WORLD_SIZE $RANK > >(tee -i $(pwd)/logs/$$HOSTNAME.log) 2>&1
  3. 添加错误处理机制,保留现场。在训练任务完成后,检查其返回值。如果返回值非零,则表示训练任务失败,需要采取措施,例如记录错误信息并保留容器。

    例如,在 bash train/7B1_test.sh 命令之后使用 ret=$? 捕获退出代码。如果退出代码非零,则意味着训练失败,打印错误消息并保留 10000 秒。使用 exit $?bash train/7B1_test.sh 执行结果状态返回给容器。

    shell
    ret=$?
    if [[ ${ret} -ne 0 ]]; then
        echo "[$(date +"%Y-%m-%d %H:%M:%S")] $HOSTNAME has crashed! The onsite reservation time is 10000s"
        sleep 10000
    fi
    exit $?

以下是改造后的完整脚本:

shell
set -o pipefail

# 下载 gpt2-vocab.json 和 gpt2-merges.txt
echo "[$(date +"%Y-%m-%d %H:%M:%S")] \$HOSTNAME 开始下载 gpt2-vocab.json 和 gpt2-merges.txt"
wget https://s3.amazonaws.com/models.huggingface.co/bert/gpt2-vocab.json
wget https://s3.amazonaws.com/models.huggingface.co/bert/gpt2-merges.txt
echo "[$(date +"%Y-%m-%d %H:%M:%S")] \$HOSTNAME 已完成 vocab 下载,开始下载检查点"

# 创建检查点目录并下载检查点
mkdir -p checkpoints/gpt2_345m
cd checkpoints/gpt2_345m
wget --content-disposition https://api.ngc.nvidia.com/v2/models/nvidia/megatron_lm_345m/versions/v0.0/zip -O megatron_lm_345m_v0.0.zip
unzip megatron_lm_345m_v0.0.zip
rm megatron_lm_345m_v0.0.zip
cd ../..

# 克隆训练代码
echo "[$(date +"%Y-%m-%d %H:%M:%S")] \$HOSTNAME 已完成检查点下载,开始拉取训练代码"
git clone https://github.com/EastInsure/Megatron-DeepSpeed.git
cd Megatron-DeepSpeed

# 运行训练脚本
echo "[$(date +"%Y-%m-%d %H:%M:%S")] \$HOSTNAME 已完成代码拉取,开始运行训练脚本"
bash train/7B1_test.sh $MASTER_ADDR $MASTER_PORT $WORLD_SIZE $RANK | tee $(pwd)/Megatron-DeepSpeed.log

# 检查训练结果
ret=$?
if [[ ${ret} -ne 0 ]]; then
  echo "[$(date +"%Y-%m-%d %H:%M:%S")] \$HOSTNAME 训练失败!现场保留时间为 10000s"
  sleep 10000
fi

exit $?