1. 创建overlay共享网络
可以看这位大佬的文章讲的很详细docker容器中deepspeed多机多卡集群分布式训练大模型众所周知,大模型的训练需要大量的显存资源,单卡很容易就爆 - 掘金 (juejin.cn)
后面和代码大多借鉴的这位大佬的文章,但大佬跳过了一些基础内容,我进行了一些补充
2. 运行容器搭建
1. 训练框架准备
同样是建立这种目录结构
├── workspace/
├── code/
├── docker-compose.yml
├── Dockerfile
使用的是llamafactory框架进行,你也可以尝试其他框架
# 进入工作目录
$ cd workspace
# 克隆项目到code文件夹
$ git clone https://github.com/hiyouga/LLaMA-Factory code
# 进入特定commit
$ git checkout 2caf91f824320b226daa4666eda2da7cb853db9c
如果没有指定特定的commit,后面的运行代码会没有train_bash.py替换成train即可
requiement.txt版本
torch==2.0.1
transformers==4.33.1
datasets==2.14.6
deepspeed==0.12.3
accelerate==0.24.1
peft==0.6.0
trl==0.7.2
gradio==3.38.0
scipy==1.11.3
sentencepiece==0.1.99
protobuf==4.25.0
tiktoken==0.5.1
jieba==0.42.1
rouge-chinese==1.0.3
nltk==3.8.1
uvicorn==0.24.0
pydantic==1.10.11
fastapi==0.95.1
sse-starlette==1.6.5
matplotlib==3.8.1
更改后运行这个代码可以一键安装适合版本的,如果那个软件包安装报错可以试试跳过他,在requirement中删除对他相应的版本约束
pip install -r requirements.txt
2. 启动容器
我用的基础镜像是nvidia/cuda:11.7.1-devel-ubuntu22.04
,需要安装一下python3.10,Dockerfile文件如下:
FROM nvidia/cuda:11.7.1-devel-ubuntu22.04
# 更新系统包
RUN apt-get update && apt-get install -y git build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libsqlite3-dev libreadline-dev libffi-dev liblzma-dev libbz2-dev curl wget net-tools iputils-ping pdsh
# 安装Python
WORKDIR /home/user
RUN wget https://www.python.org/ftp/python/3.10.6/Python-3.10.6.tgz && \
tar -zvxf Python-3.10.6.tgz && cd Python-3.10.6 && \
./configure --enable-optimizations && make -j 4 && make install
这里我们分别到 manager
, worker
节点使用 docker compose up -d --build
命令启动容器,相应的配置文件如下:
version: "3"
services:
llm:
build:
context: .
dockerfile: Dockerfile
container_name: llm
tty: true
restart: always
ulimits:
memlock: -1
stack: 67108864
shm_size: 40G
deploy:
resources:
reservations:
devices:
- capabilities: [gpu]
volumes:
- ./code:/home/user/code:cached
networks:
- test-net
networks:
test-net:
external: true
3. 验证网络
然后去 manager
节点执行 docker exec -it llm bash
命令进入容器, ping 一下 worker
节点
在这里我遇到了双方无法ping通,我的问题和解决方法可以供参考
需要检测Swarm 节点之间的网络连接畅通,特别是端口 7946
和 4789
(ufw)Ubuntu 和 Debian 系统的默认防火墙工具
sudo ufw status
至 动作 来自
- -- --
22 ALLOW IN Anywhere
22 (v6) ALLOW IN Anywhere (v6)
当时大概是显示了这些内容但是没有7946 和 4789
开放端口
sudo ufw allow 7946/tcp
sudo ufw allow 4789/udp
添加规则后,重新加载 ufw 配置:
sudo ufw reload
然后查看 ufw 状态以确认规则已经添加:
sudo ufw status verbose
应该会看到类似下面的输出,其中包括刚刚添加的端口规则:
至 动作 来自
- -- --
22 ALLOW IN Anywhere
22 (v6) ALLOW IN Anywhere (v6)
7946/tcp ALLOW IN Anywhere
4789/udp ALLOW IN Anywhere
这时候测试连接应该就可以了
3. 开启容器间免密访问
1. 安装openssh-server服务
首先分别去manager
,worker
节点的容器中安装openssh-server
服务并启动
# 安装ssh服务
apt-get install openssh-server -y
# 启动ssh服务
/etc/init.d/ssh start
2. 配置免密登录
注意:以下操作都是在
manager
,worker
节点容器内部
分别去manager
,worker
节点的容器中执行 ssh-keygen -t rsa
命令,一直回车即可
root@2ef29f638705:/home/user/code# ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:tUB6SjLnvqM7p2l+bmHUZGNqUyyOPmXGyiMp3tC9xNA root@2ef29f638705
The key's randomart image is:
+---[RSA 2048]----+
| .. |
| .oB |
| +++Oo.. |
| ..E@o.o . |
| .++Bo.S . |
|..o.*=o |
|..o..+o. |
| . .oo=. |
| o*Xo. |
+----[SHA256]-----+
后
- 将
manager
节点中的~/.ssh/id_rsa.pub
的内容复制写入到manager
节点和worker
节点中的~/.ssh/authorized_keys
文件中。 - 将
worker
节点中的~/.ssh/id_rsa.pub
的内容复制写入到manager
节点和worker
节点中的~/.ssh/authorized_keys
文件中。
可以使用vim命令复制,如果安装vim命令失败,可以使用cat命令
查看 worker 节点的公钥:
cat ~/.ssh/id_rsa.pub
将 worker 节点的公钥内容复制到 authorized_keys 文件中:
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
将公钥内容保存到临时文件中
在你要保存公钥的节点上,使用以下命令将复制的公钥内容保存到文件中:
cat <<EOF > worker_id_rsa.pub
(粘贴复制的公钥内容)
EOF
解释:
cat <<EOF > worker_id_rsa.pub 启动了一个多行输入模式,直到遇到 EOF。
(粘贴复制的公钥内容) 是你从上一步复制的公钥内容。
EOF 表示结束标记,> 符号表示将内容写入到 worker_id_rsa.pub 文件中。
直接重定向到文件
接着分别去manager
,worker
节点的/etc/hosts
文件中增加映射
10.0.3.2 6a732892
10.0.3.6 123232ds
解释123232ds是容器id
由于docker swarm网络会随机分配ip地址,每次服务器重启后,容器ip都会改变,所以都可需要修改映射,读者可以尝试如何固定容器静态IP
最后测试服务器容器之间是否可以免密登录
root@2ef29f638705:/home/user/code# ssh root@worker
Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 3.10.0-1160.102.1.el7.x86_64 x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
This system has been minimized by removing packages and content that are
not required on a system that users do not log into.
To restore this content, you can run the 'unminimize' command.
Last login: Tue Nov 21 12:01:18 2023 from 10.0.3.2
4. 分布式训练
1. 模型准备
先去下载chatglm2-6b
模型,然后放到容器的 /home/user/code
尽量是下载好本地模型将其上传到服务器,实验过程中发现拉取文件不完全,后面发生报错,花费纠错时间较长
2. 安装依赖
root@2ef29f638705:~# cd /home/user/code/
root@2ef29f638705:/home/user/code# pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
3. 准备配置文件
/home/user/code/目录下新建 hostfile
文件,写入:
6a732892 slots=1
123232ds slots=1
/home/user/code/目录下新建 ds_config.json
文件,写入:
{
"train_batch_size": "auto",
"train_micro_batch_size_per_gpu": "auto",
"gradient_accumulation_steps": "auto",
"gradient_clipping": "auto",
"zero_allow_untested_optimizer": true,
"fp16": {
"enabled": "auto",
"loss_scale": 0,
"initial_scale_power": 16,
"loss_scale_window": 1000,
"hysteresis": 2,
"min_loss_scale": 1
},
"zero_optimization": {
"stage": 2,
"allgather_partitions": true,
"allgather_bucket_size": 5e8,
"reduce_scatter": true,
"reduce_bucket_size": 5e8,
"overlap_comm": false,
"contiguous_gradients": true
}
}
manager
和worker
节点需要执行同样的操作,保证训练代码和训练运行的环境一致
4. 执行训练命令
进入manager
节点的 /home/user/code/
文件夹,执行
deepspeed --hostfile hostfile src/train_bash.py \
--deepspeed ds_config.json \
--stage sft \
--model_name_or_path /home/user/code/chatglm2-6b \
--do_train \
--dataset alpaca_gpt4_zh \
--template chatglm2 \
--finetuning_type lora \
--lora_target query_key_value \
--output_dir chatglm2_sft \
--overwrite_cache \
--overwrite_output_dir \
--per_device_train_batch_size 4 \
--gradient_accumulation_steps 4 \
--lr_scheduler_type cosine \
--logging_steps 10 \
--save_steps 10000 \
--learning_rate 5e-5 \
--num_train_epochs 0.25 \
--plot_loss \
--fp16
这里的train_bash.py如果没有用commit就换成train.py
可以看到,训练已经跑起来了,并且输出了两台机器的日志
分别去两台服务器中执行 nvidia-smi
,可以看到显卡均有被使用,至此,分布式训练完毕。