惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Docker
P
Palo Alto Networks Blog
S
Secure Thoughts
V2EX - 技术
V2EX - 技术
Vercel News
Vercel News
人人都是产品经理
人人都是产品经理
S
SegmentFault 最新的问题
F
Full Disclosure
WordPress大学
WordPress大学
Martin Fowler
Martin Fowler
B
Blog RSS Feed
I
InfoQ
博客园 - 叶小钗
Google Online Security Blog
Google Online Security Blog
L
LangChain Blog
M
MIT News - Artificial intelligence
T
Troy Hunt's Blog
L
LINUX DO - 最新话题
Webroot Blog
Webroot Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
小众软件
小众软件
Microsoft Azure Blog
Microsoft Azure Blog
SecWiki News
SecWiki News
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Proofpoint News Feed
Schneier on Security
Schneier on Security
罗磊的独立博客
T
The Exploit Database - CXSecurity.com
T
The Blog of Author Tim Ferriss
Hacker News: Ask HN
Hacker News: Ask HN
Hacker News - Newest:
Hacker News - Newest: "LLM"
P
Privacy & Cybersecurity Law Blog
Forbes - Security
Forbes - Security
博客园 - 三生石上(FineUI控件)
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
W
WeLiveSecurity
Security Archives - TechRepublic
Security Archives - TechRepublic
L
LINUX DO - 热门话题
Cyberwarzone
Cyberwarzone
Scott Helme
Scott Helme
T
Tor Project blog
Help Net Security
Help Net Security
I
Intezer
T
Tenable Blog
博客园 - 司徒正美
AI
AI
博客园 - Franky
云风的 BLOG
云风的 BLOG
H
Heimdal Security Blog

陈少文的网站

巨变与机遇的未来十年 Kubernetes 平台管理软件压力测试方案 使用镜像部署 Hexo 静态页面 终于等到你 - GitHub 镜像仓库服务(ghcr.io) 一起来学 Go --(6)Interface 一起来学 Go --(5)Goroutine 和 Channel 什么是函数式编程 如何在 Kubernetes 集群集成 Kata 柯里化与偏函数 使用 PyGithub 自动创建 Label 软件产品是团队能力的输出 Helm 2 、Helm 3 比较 IoT 变现 Kubernetes 中的 DNS 服务 国内的 Helm 镜像源 Harbor 使用自签证书支持 Https 访问 DevOps 工具链之 Prow 如何使用 kfctl 安装 Kubeflow VS Code 无法下载 Go 插件的工具包 工程师更应具有服务精神 你不知道的 Docker 使用技巧 使用 Docker 运行 Tensorflow 论中国 什么是左移 如何清空 Git 仓库全部历史记录 一禅小和尚 有风吹过厨房 时间的玫瑰 如何在 CentOS 安装 GPU 驱动 开发 Tips(19) 使用 Velero 备份 Kubernetes 集群 Kubernetes Cheat Sheet 开发 Tips(18) 如何构建一个 Java 工程 开发 Tips(17) KubeSpray 安装 Kubernetes 报错 ip in ansible_all_ipv4_addresses 基于 Kubernetes 和 Jenkins 搭建自动化测试系统 在 Kubernetes 上动态创建 Jenkins Slave 使用 Jenkins 进行服务拨测 开发 Tips(16) Kubernetes 签发 Ingress 证书及日常故障运维 Kubernetes 中 Deployment 的基本操作 Kubernetes 中的证书 如何使用 KubeBuilder 开发一个 Operator Kubernetes 1.6.0 安装问题汇总 镜像管理工具 -- Harbor 开发 Tips(15) Docker 如何拉取镜像 开发 Tips(14) 使用 Helm 安装 harbor 开发 Tips(13) 使用 S2I 构建云原生应用 在 Kubernetes 中使用 emptyDir、hostPath、localVolume 开发 Tips(12) 开发 Tips(11) 代码质量分析工具 SonarQube 使用 Kubeadm 安装 Kubernetes 集群 一起来学 Go --(4)常用函数 Kubernetes 中的 Ceph Kubernetes 之 Volumes Kubernetes 之 Labels、Selectors 开发 Tips(10) 开源正在重构商业模式 Kubernetes 之网络 Kubernetes 之 API 使用 Helm 和 Operator 快速部署 Prometheus Kubernetes 复杂有状态应用管理框架 -- Operator Kubernetes 的包管理器 -- Helm 一起来学 Go --(3)Go Modules 如何一步一步地优化博客方案 kubectl 实用指南 Kubernetes 中的基本概念 搭建远程 Kubernetes 开发环境 大公司和小公司的 ToB 思路 开发 Tips(9) Go 入门指南 一起来学 Go --(2)数据与逻辑结构 如何预防 Web 富文本中的 XSS 攻击 django-xss-cleaner 云工作时代 一起来学 Go --(1)背景与特点 SaaS 开发团队的不同阶段 你不知道的 Git 使用技巧 输出既服务 微服务设计 继续奔跑 开发 Tips(8) 从账户安全到二次验证 Django 性能之数据库查询优化 Django 性能之分库分表 敏捷开发之研发流程 打造一致性的团队 开发 Tips(7) Pytest 进阶学习之 Mock PaaS 部署之 buildpack Go 开发配置 领域输出才是 PaaS 的核心竞争力 Pytest 入门学习 开发 Tips(6) 如何使用 Jenkins、Docker、GitLab 搭建 Django 自动化部署流程
使用 DLRover 托管作业进行弹性、容错训练
微信公众号 · 2024-08-17 · via 陈少文的网站

1. 分布式训练面临的问题

  • 预估训练资源困难,无法自动化

需要多少算力、需要多少时间、需要多少带宽、需要多少 CPU、需要多少内存,如果没有足够的积累,很难估算准确。导致的结果就是,超额申请、超额分配,造成极大的资源浪费。

需要去沉淀和提供解决方案。

  • 故障率高,排查困难,缺少高效的工具

算法同学不太了解 Kubernetes 基础设施,运维同学不太了解训练过程,想要当好 AI Infra Engineer 不是件容易的事。

训练场景下的故障率非常高,如何快速、准确地定位并解决故障,是我们需要思考的问题。

  • 单个节点发生故障,全部节点停止训练,需要人值守才能及时拉起作业继续训练

训练作业类似 Kubernetes 中的 StatefulSet。分布式训练中,由于数据、模型、流水线、张量等并行技术的存在,每个节点在训练过程中并不是完全对等的。

从发生故障到恢复训练,有很多技术点可以落地到工具、产品上。

同时,如果是非工作时间段发生故障,我们无法及时响应,模型迭代延期、AI 加速卡闲置都是巨大损失。能不能自动发现故障、自动恢复训练,也是值得研究的问题。

2. 什么是 DLRover

DLRover 就试图解决上诉问题的一个方案。下面是 https://github.com/intelligent-machine-learning/dlrover 项目的架构图。

管理平面的组件:

  • Brain Service,负责资源弹性优化。基于实时采集的训练速度和各个节点负载来自动优化作业的资源配置,还有一个事件采集服务 k8smonitor
  • Elastic Controller,是 Kubernetes 对象 ElasticJob、ScalePlan 的控制器

CRD 对象:

  • ElasticJob 用来描述弹性训练作业
  • ScalePlan 用于在 Brain、DLRover Master、Elastic Controller 之间传递信息,以做出优化调整。一般不需要主动创建,DLRover 会自动管理

训练作业相关的组件:

  • DLRover Job Master,负责弹性调度、容错自愈。每个训练作业拥有一个 master 节点, master 节点负责训练速度采集、节点负载收集、训练样本管理和弹性调度。
  • Elastic Agent 不用单独部署,是需要使用 dlrover-run 命令托管训练作业,负责与训练框架协调来支持训练的容错和弹性。每个节点上都有一个 Elastic Agent, Agent 从 master 上获取作业当前运行的节点信息,通知训练框架更新分布式训练状态。 Agent 还负责从 master 获取训练样本信息来供训练框架迭代模型,从而使训练样本分片支持 worker 的弹性。

下面这张图是其容错的架构设计:

DLRover Job Master 负责探测、处理故障,dlrover-run 作为 Agent 上报状态信息。

3. dlrover-run 命令及参数

  • 安装 dlrover 包
1
pip install dlrover[torch] -U -i https://mirrors.aliyun.com/pypi/simple
  • dlrover-run 只是对 dlrover.trainer.torch.main 的封装
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
cat /usr/local/bin/dlrover-run

#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import re
import sys
from dlrover.trainer.torch.main import main
if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(main())
  • 查看参数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
dlrover-run  --help
用法: dlrover-run [-h] [--nnodes NNODES] [--nproc-per-node NPROC_PER_NODE] [--rdzv-backend RDZV_BACKEND] [--rdzv-endpoint RDZV_ENDPOINT]
                   [--rdzv-id RDZV_ID] [--rdzv-conf RDZV_CONF] [--standalone] [--max-restarts MAX_RESTARTS]
                   [--monitor-interval MONITOR_INTERVAL] [--start-method {spawn,fork,forkserver}] [--role ROLE] [-m] [--no-python]
                   [--run-path] [--log-dir LOG_DIR] [-r REDIRECTS] [-t TEE] [--node-rank NODE_RANK] [--master-addr MASTER_ADDR]
                   [--master-port MASTER_PORT] [--local-addr LOCAL_ADDR] [--network-check] [--node-unit NODE_UNIT] [--auto_config]
                   [--auto_tunning] [--exclude-straggler] [--save_at_breakpoint] [--accelerator {nvidia.com/gpu,ascend-npu}]
                   training_script ...

Torch Distributed Elastic Training Launcher

位置参数:
  training_script       启动并行执行的训练程序/脚本的完整路径,后跟训练脚本的所有参数。
  training_script_args  训练脚本的参数

可选参数:
  -h,--help            显示帮助信息并退出
  --nnodes NNODES       节点数量或节点范围,格式为 <minimum_nodes>:<maximum_nodes>。
  --nproc-per-node NPROC_PER_NODE,--nproc_per_node NPROC_PER_NODE
                        每个节点的工作进程数量;支持的值:[auto,cpu,gpu,int]  --rdzv-backend RDZV_BACKEND,--rdzv_backend RDZV_BACKEND
                        Rendezvous 后端。
  --rdzv-endpoint RDZV_ENDPOINT,--rdzv_endpoint RDZV_ENDPOINT
                        Rendezvous 后端的端点;通常格式为 <host>:<port>。
  --rdzv-id RDZV_ID,--rdzv_id RDZV_ID
                        用户定义的组 ID。
  --rdzv-conf RDZV_CONF,--rdzv_conf RDZV_CONF
                        附加的 rendezvous 配置(<key1>=<value1>,<key2>=<value2>,...)。
  --standalone          启动一个本地独立的 rendezvous 后端,使用 C10d TCP 存储在端口 29400 上。用于启动单节点、多工作进程作业时非常有用。如果指定,则 --rdzv-backend、--rdzv-endpoint、--rdzv-id 会被自动分配;任何显式设置的值将被忽略。
  --max-restarts MAX_RESTARTS,--max_restarts MAX_RESTARTS
                        最大的工作进程组重启次数,超出此次数将失败。
  --monitor-interval MONITOR_INTERVAL,--monitor_interval MONITOR_INTERVAL
                        监控工作进程状态的时间间隔,单位为秒。
  --start-method {spawn,fork,forkserver},--start_method {spawn,fork,forkserver}
                        创建工作进程时使用的多进程启动方法。
  --role ROLE           工作进程的用户定义角色。
  -m,--module          将每个进程更改为将启动脚本解释为 Python 模块,行为与 'python -m' 相同。
  --no-python,--no_python
                        跳过在训练脚本前添加 'python' - 直接执行脚本。如果脚本不是 Python 脚本时非常有用。
  --run-path,--run_path
                        使用 runpy.run_path 在相同解释器中运行训练脚本。脚本必须提供为绝对路径(例如 /abs/path/script.py)。优先于 --no-python。
  --log-dir LOG_DIR,--log_dir LOG_DIR
                        用于日志文件的基础目录(例如 /var/log/torch/elastic)。相同的目录会被多个运行重用(会创建一个以 rdzv_id 为前缀的唯一作业级子目录)。
  -r REDIRECTS,--redirects REDIRECTS
                        将标准流重定向到日志目录中的日志文件(例如 [-r 3] 重定向所有工作进程的 stdout+stderr,[ -r 0:1,1:2] 重定向本地 rank 0 的 stdout 和本地 rank 1 的 stderr)。
  -t TEE,--tee TEE     将标准流分流到日志文件和控制台(参见 --redirects 格式)。
  --node-rank NODE_RANK,--node_rank NODE_RANK
                        多节点分布式训练中节点的排名。
  --master-addr MASTER_ADDR,--master_addr MASTER_ADDR
                        主节点(rank 0)的地址,仅用于静态 rendezvous。它应该是 rank 0 的 IP 地址或主机名。对于单节点多进程训练,--master-addr 可以简单地是 127.0.0.1;IPv6 应该是 `[0:0:0:0:0:0:0:1]` 的模式。
  --master-port MASTER_PORT,--master_port MASTER_PORT
                        主节点(rank 0)上用于分布式训练期间通信的端口。仅用于静态 rendezvous。
  --local-addr LOCAL_ADDR,--local_addr LOCAL_ADDR
                        本地节点的地址。如果指定,将使用给定的地址进行连接。否则,将查找本地节点地址。否则,默认为本地计算机的 FQDN。
  --network-check,--network_check
                        是否在启动训练过程前检查网络。
  --node_unit NODE_UNIT,--node-unit NODE_UNIT
                        要调度的节点数量单位。调度的节点数量应为 node_unit 的倍数。
  --auto_config,--auto-config
                        是否自动配置 nnodes 和 nproc_per_nodes。
  --auto_tunning,--auto-tunning
                        是否自动调优并行配置。
  --exclude-straggler,--exclude_straggler
                        布尔值,如果节点是滞后节点且参数为 True,则该节点将退出。该参数仅在 network-check 为 True 时有效。
  --save_at_breakpoint,--save-at-breakpoint
                        布尔值。如果为 True,主进程中的代理将在训练过程失败时将检查点保存到存储中。
  --accelerator {nvidia.com/gpu,ascend-npu}
                        机器的加速器芯片类型。

4. 使用 DLRover 快速托管训练

  • 启动测试容器
1
nerdctl -n k8s.io run --rm -it --gpus all  registry.cn-beijing.aliyuncs.com/intell-ai/dlrover:pytorch-example bash
  • 直接运行 Python 脚本
1
2
3
4
5
6
7
8
export RANK=0
export LOCAL_RANK=0
export WORLD_SIZE=1
export MASTER_ADDR="localhost"
export MASTER_PORT=1234
python3 examples/pytorch/mnist/cnn_train.py --num_epochs 1 \
                                            --training_data /data/mnist_png/training/ \
                                            --validation_data /data/mnist_png/testing/

我发现 --no-cuda 参数无效,只能通过控制 GPU 挂载来指定仅使用 CPU 训练。

  • 使用 DLRover 托管训练

不用设置环境变量直接运行即可,DLRover 帮我们省去了设置环境变量的步骤。

1
2
3
4
5
dlrover-run --network-check --nnodes=1 \
                  --nproc_per_node=1 --max_restarts=3  \
                  examples/pytorch/mnist/cnn_train.py --num_epochs 1\
                  --training_data /data/mnist_png/training/ \
                  --validation_data /data/mnist_png/testing/

由于使用的是单个容器进行训练,需要将 --nnodes 参数设置为 1,否则 DLRover 会一直等待新节点加入,max_restarts 设置为 3 允许失败之后重试 3 次。

5. 集群安装 DLRover 相关组件

这里安装的是 DLRover 最新的 Release 版本 v0.3.7 ,是今年 5.13 发布的。

  • 下载安装包
1
2
3
wget https://github.com/intelligent-machine-learning/dlrover/archive/refs/tags/v0.3.7.tar.gz
tar xvf v0.3.7.tar.gz
cd dlrover-0.3.7
  • 安装 ElasticJob Controller Manager
1
kubectl -n dlrover apply -k dlrover/go/operator/config/manifests/bases

需要注意的是默认的权限设置在 dlrover 空间下,如果想在其他空间使用,则需要创建与之对应的 default-role.yaml。

  • 查看工作负载
1
2
3
4
kubectl -n dlrover get pod

NAME                                          READY   STATUS    RESTARTS   AGE
dlrover-controller-manager-6d676545d7-nrc4g   2/2     Running   0          73s
  • 查看 CRD
1
2
3
4
kubectl get crd |grep elastic

elasticjobs.elastic.iml.github.io                     2024-08-15T07:27:29Z
scaleplans.elastic.iml.github.io                      2024-08-15T07:27:29Z

如果仅使用训练 worker 自动恢复的功能,那么不用安装 Brain 等组件。因为 Brain 依赖 MySQL,仅为测试,下面部署 MySQL 的方式并不可靠。

  • 安装 DLRover Brain
1
kubectl -n dlrover apply -f dlrover/go/brain/manifests/k8s
  • 创建数据库表

查看 MySQL Pod 名

1
2
3
kubectl -n dlrover get pod  |grep mysql

mysql-85dd8c7fdb-kdxtz                                  1/1     Running            0              7m41s

执行命令,创建数据库以及初始化表

1
2
3
kubectl exec -it mysql-85dd8c7fdb-kdxtz --namespace dlrover -- bash
cd dlrover
mysql -uroot -proot < dlrover-tables.sql
  • 重启 Brain 相关组件
1
kubectl -n dlrover rollout restart deployment dlrover-brain dlrover-kube-monitor
  • 查看工作负载
1
2
3
4
5
6
7
kubectl -n dlrover get pod

NAME                                                      READY   STATUS    RESTARTS   AGE
dlrover-brain-689c4b77d4-bqcz9                            1/1     Running   0          3m55s
dlrover-controller-manager-6b6f9dcd88-2h2fs               2/2     Running   0          6m19s
dlrover-kube-monitor-69ff5f99f4-gwqws                     1/1     Running   0          3m55s
mysql-799f94cbfd-864th                                    1/1     Running   0          4m17s

6. ElasticJob 创建训练作业

6.1 ElasticJob 对象定义

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
type ElasticJobSpec struct {
	// DistributionStrategy 指定作业的分发策略。
	// 目前,策略支持 parameter-server 和 ring-allreduce。
	DistributionStrategy string `json:"distributionStrategy,omitempty"`

	// ResourceLimits 指定作业的最大资源。例如,
	// {"cpu": "100", "memory": "10240Mi"} 表示最大 CPU 核数为 100,所有 Pod 的最大内存为 10Gi。
	ResourceLimits map[string]string `json:"resourceLimits,omitempty"`

	// OptimizeMode 指定优化作业资源的模式。
	// 目前支持 "manual"(手动)、"single-job"(单作业)、"cluster"(集群)。
	OptimizeMode string `json:"optimizeMode,omitempty"`

	// BrainService 指定 Brain 的地址,以优化作业资源。Brain 可以单独部署,配置在 ElasticJobSpec 中。
	// 仅在 optimizeMode 为 cluster 时使用。
	BrainService string `json:"brainService,omitempty"`

	// EnableElasticScheduling 启动 Pod 的弹性调度。
	EnableElasticScheduling bool `json:"enableElasticScheduling,omitempty"`

	// EnableDynamicSharding 启动数据集的动态分片。
	EnableDynamicSharding bool `json:"enableDynamicSharding,omitempty"`

	// 一个从 ReplicaType(类型)到 ReplicaSpec(值)的映射。指定训练集群的配置。
	// 例如,
	//   {
	//     "PS": ReplicaSpec,
	//     "Worker": ReplicaSpec,
	//   }
	ReplicaSpecs map[commonv1.ReplicaType]*ReplicaSpec `json:"replicaSpecs"`

	// Envs 指定作业 Pod 的环境变量。
	Envs map[string]*corev1.EnvVar `json:"envs,omitempty"`
}

其中的 ReplicaSpec 定义如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
type ReplicaSpec struct {
	commonv1.ReplicaSpec `json:",inline"`

	// RestartCount 是重新启动失败副本的次数。
	RestartCount int `json:"restartCount,omitempty"`

	// AutoScale 是一个标志,用于自动调整副本数量和每个副本的资源。
	AutoScale bool `json:"autoScale,omitempty"`

	// RestartTimeout 是等待挂起副本的时间。
	RestartTimeout int `json:"restartTimeout,omitempty"`

	// Priority 支持 high/low/0.5。0.5 表示一半的工作节点具有高优先级,
	// 另一半工作节点具有低优先级。默认值为 low。
	Priority string `json:"priority,omitempty"`
}

下面对其中的一些关键字段进行说明。

6.2 DistributionStrategy 策略

有两种 DistributionStrategy 策略:

  • ParameterServerStrategy

适用于使用 TensorFlow 的 parameter_server 作业,https://www.tensorflow.org/tutorials/distribute/parameter_server_training 。

  • AllreduceStrategy

适用于 Horovod 的 ring-allreduce 和 PyTorch 的 DistributedDataParallel 作业。

从这里可以看出,如果训练使用的是 PyTorch 就选 AllreduceStrategy,使用的是 TensorFlow 就选 ParameterServerStrategy。

PyTorch DistributedDataParallel 的 Allreduce 策略下,预训练过程中会保持 Global Batch Size 固定,也就不需要增删节点,不涉及弹性训练,主要使用的是 Job Master 的容错自愈能力。

TensorFlow parameter_server 的 ParameterServerStrategy 策略下,节点数可以调整,依赖 Brain 服务进行弹性训练。

6.3 optimizeMode 模式

有三种 optimizeMode 模式:

  • manual

调试模式,修改正在运行的作业时,作业不会重启,用来探索更佳的作业参数配置。

  • single-job

测试、快速验证的场景,不依赖额外的组件。使用 master 的内存存储历史的统计数据,如果主节点发生故障,历史统计数据会丢失。

  • cluster

正式环境的训练,Bran 服务会将作业的历史统计数据持久化到数据库中,进行集群级别的优化。即使主节点发生故障时,DLRover 可以重启主节点继续训练。

上面这段描述来自 DLRover 的设计文档。在测试时,发现 single-job\cluster 模式在 AllreduceStrategy 下没有明显区别,cluster 模式在 Brain 不能用时也能正常运行。同时,AllreduceStrategy 下的 Job Master 发生故障时,整个作业就失败了。

6.4 训练作业测试

  • 创建 ElasticJob 对象
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
kubectl apply -f - <<EOF
apiVersion: elastic.iml.github.io/v1alpha1
kind: ElasticJob
metadata:
  name: torch-mnist-single-job-testing-1
  namespace: dlrover
  labels:
    some-label: some-value
spec:
  distributionStrategy: AllreduceStrategy
  optimizeMode: cluster
  replicaSpecs:
    worker:
      replicas: 5
      template:
        spec:
          restartPolicy: Always
          containers:
            - name: main
              image: registry.cn-beijing.aliyuncs.com/intell-ai/dlrover:pytorch-example
              imagePullPolicy: Always
              command:
                - /bin/bash
                - -c
                - "dlrover-run --network-check --nnodes=1:10 \
                  --nproc_per_node=1 --max_restarts=10  \
                  examples/pytorch/mnist/cnn_train.py --num_epochs 500 \
                  --training_data /data/mnist_png/training/ \
                  --validation_data /data/mnist_png/testing/"
              resources:
                limits:
                  cpu: 2
                  memory: 3Gi
                  tencent.com/vcuda-core: 100
                requests:
                  cpu: 2
                  memory: 3Gi
                  tencent.com/vcuda-core: 100
EOF

一旦 ElasticJob 创建完成,DLRover 会马上开始创建 master 和 worker Pod,并不会判断是否有足够的资源,没有 gang-scheduler 的支持。根据社区沟通,DLRover 会结合 Volcano 的 gang-scheduler 支持此特性。

  • 查看作业状态
1
2
3
4
5
6
7
8
9
kubectl -n dlrover get pod

NAME                                                         READY   STATUS    RESTARTS      AGE
elasticjob-torch-mnist-single-job-testing-1-dlrover-master   1/1     Running   0             10s
torch-mnist-single-job-testing-1-edljob-worker-0             1/1     Running   0             5s
torch-mnist-single-job-testing-1-edljob-worker-1             1/1     Running   0             5s
torch-mnist-single-job-testing-1-edljob-worker-2             1/1     Running   0             5s
torch-mnist-single-job-testing-1-edljob-worker-3             1/1     Running   0             5s
torch-mnist-single-job-testing-1-edljob-worker-4             1/1     Running   0             5s
  • 主动删掉一个 worker
1
kubectl -n dlrover delete pod torch-mnist-single-job-testing-1-edljob-worker-3
  • 查看作业状态
1
2
3
4
5
6
7
8
9
kubectl -n dlrover get pod

NAME                                                         READY   STATUS    RESTARTS      AGE
elasticjob-torch-mnist-single-job-testing-1-dlrover-master   1/1     Running   0             6m18s
torch-mnist-single-job-testing-1-edljob-worker-0             1/1     Running   0             6m13s
torch-mnist-single-job-testing-1-edljob-worker-1             1/1     Running   0             6m13s
torch-mnist-single-job-testing-1-edljob-worker-2             1/1     Running   0             6m13s
torch-mnist-single-job-testing-1-edljob-worker-4             1/1     Running   0             6m12s
torch-mnist-single-job-testing-1-edljob-worker-5             1/1     Running   0             3m12s

DLRover 会自动拉起一个新的 worker 作业,继续训练任务,这就是其故障自动恢复的能力。当然,如果是其他故障,比如掉卡、程序异常退出、IO 异常等故障,DLRover 也可以自动处理,不需要人工干预。

  • 清理
1
kubectl -n dlrover delete elasticjob torch-mnist-single-job-testing-1
1
kubectl -n dlrover delete scaleplans.elastic.iml.github.io torch-mnist-single-job-testing-1

6.5 弹性作业原理

Allreduce 现在只支持容错,不支持弹性扩容。我们的目标场景是 PyTorch 的分布式预训练大模型,对 Tensorflow 相关的弹性没有强烈需求。这里仅仅只是简单了解下其弹性原理。

DLRover Brain 会根据当前训练任务的监控,利用算法计算出资源优化需要调整的数据。训练作业的 Job Master 拿到新的资源优化结果后,会生成一个 ScalePlan CRD,通知 ElasticJob Controller 更改训练作业的节点规模。

我测试过社区提供的 deepctr-auto-scale 示例,chief 占用了大量内存资源,并没有观察到 worker 副本数的增加。

1
deepctr-auto-scale-edljob-chief-0              1947m        185022Mi
1
2
3
4
5
6
7
8
deepctr-auto-scale-edljob-chief-0              1/1     Running   0               4h42m
deepctr-auto-scale-edljob-evaluator-0          1/1     Running   0               4h42m
deepctr-auto-scale-edljob-ps-0                 1/1     Running   0               4h42m
dlrover-brain-78b6484859-p67s5                 1/1     Running   0               16h
dlrover-controller-manager-688c767cb7-8bzgl    2/2     Running   1 (7h43m ago)   15h
dlrover-kube-monitor-88548c89f-kjjjw           1/1     Running   1 (16h ago)     16h
elasticjob-deepctr-auto-scale-dlrover-master   1/1     Running   0               4h42m
mysql-574fb78ddb-8l79r                         1/1     Running   0               16h

同时,这个作业依赖于 /nas 中的数据,文档中也没有给出配置说明,因此没有测试太多。

7. 一些问题

  • master 挂了之后,训练会停止
1
kubectl -n dlrover delete pod elasticjob-torch-mnist-single-job-testing-1-dlrover-master

之后

1
dlrover-controller-manager-6b6f9dcd88-2h2fs     1/2     CrashLoopBackOff   4 (35s ago)   58m

dlrover-controller-manager CrashLoopBackOff 的概率还挺高。等待一会儿,作业也会全部失败。

1
2
3
4
5
torch-mnist-single-job-testing-1-edljob-worker-0   0/1     Error     0             112s
torch-mnist-single-job-testing-1-edljob-worker-1   0/1     Error     0             112s
torch-mnist-single-job-testing-1-edljob-worker-2   0/1     Error     0             112s
torch-mnist-single-job-testing-1-edljob-worker-3   0/1     Error     0             112s
torch-mnist-single-job-testing-1-edljob-worker-4   0/1     Error     0             112s
  • ElasticJob 对象缺失非关键字段时,Worker 无法创建

不设置 spec.template.spec.restartPolicy 字段时,仅创建了 Job Master,而不会创建 Worker。在 ElasticJob Manager 和 Job Master Pod 的日志中看不到异常报错。

不设置作业 ElasticJob 的 labels 字段,也是这种情况。

  • 版本控制做得不好,可能触发很多潜在问题

在 Release 的版本中,镜像 Tag 还是 test、master,拉取策略是 Always。文档中的一些测试 Case 也没有太多版本控制,很容易触发兼容性问题。

还有就是 DLRover master 的 Dockerfile 中使用的 0.3.6 ,而 pytorch-example 中使用的是 0.3.4。

  • 项目完成度可能不是很高

我看到 EnableElasticScheduling 字段很快就联想到了 autoScale 字段,但是发现只有这个字段的定义,代码仓库中没有相关的实现。

可能是开源的同学支持内部压力大,没太多时间打磨社区版这些细节,有些包名还没来得从 EasyDL 改为 DLRover。

8. 总结

本篇是测试 DLRover 托管训练作用的一些记录,主要内容如下:

  • 在 Kubernetes 上进行训练,有其特定场景的问题需要解决,分布式训练任务形态类似 StatefulSet,节点(Pod) 之间不是完全对等
  • DLRover 能解决分布式训练时的一些问题,包括资源的配置,弹性训练,故障的检测、定位、恢复。
  • 在主机和 Kubernetes 基础设施上分别对 DLRover 进行了尝试
  • 目前 DLRover 项目还有些不完善的地方,但也给了我们共同参与的机会,希望项目能够走得远一点