[email protected]
2024.10.30
集群使用 PBS 调度系统管理 job,job 在容器中运行,容器与镜像关联,而集群中运行 job 必须使用自己制作的镜像。
如果按照使用手册中的镜像制作方法自己制作,会有许多问题,比如编译麻烦、有数量限制、提交后无法修改等。如果有安装其它包、包版本升级或从源码编译安装等需求,就需要重新制作镜像,而使用外挂环境可以避免这些问题。
简单来说,基础镜像就是一个简陋的只有开机必须文件的系统,按照使用手册制作的镜像就是在系统里安装运行代码所需的软件环境,挂载用户目录 ghome、gdata 等则相当于把外置硬盘接入系统,而外挂环境则相当于把运行代码所需的软件环境安装在外置硬盘上,这样就可以在不改变镜像的情况下修改软件环境。
- 申请集群账号
- 登录集群,推荐 VS code、Xshell、WinSCP、iTerm2、FileZilla 等工具
登录后进入 gwork 节点,ghome/username 目录:
[liuzp@gwork ~]$
- 安装并配置 Miniconda(⬅️ wget 速度慢的话可以点这里手动下载)
下面是 Miniconda 官网给出的安装命令:
# installing
mkdir -p ~/miniconda3
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda3/miniconda.sh
bash ~/miniconda3/miniconda.sh -b -u -p ~/miniconda3
rm ~/miniconda3/miniconda.sh
# After installing, close and reopen your terminal application or refresh it by running the following command:
source ~/miniconda3/bin/activate
# To initialize conda on all available shells, run the following command:
conda init --all
完成后,可以使用conda
命令,例如:
(base)[liuzp@gwork ~]$ conda env list
# conda environments:
#
base * /ghome/liuzp/miniconda3
上面的安装命令会把 Miniconda3 安装到 ghome 目录里,但是 ghome 目录限额 50G,所以建议把 Miniconda3 直接安装到 gdata 目录里,注意在后面 Dockerfile 中需要使用 Miniconda 的实际安装目录,当然也可以通过软连接在 ghome 生成一个 gdata/username/miniconda3 的快捷方式,这样就可以在 Dockerfile 中直接使用 /ghome/username/miniconda3 了。
如果 Miniconda3 已经安装在了 ghome 目录里,可以参考这里,但要注意解压缩会很慢。
这里建议首先考虑 cuda 版本,因为集群不同节点支持的 cuda 版本不同.
使用 ssh 登录集群,在 gwork 节点运行 chk_gpu 可以看到集群所有 job 和节点信息,[x] 表示卡被占用:
(base)[liuzp@gwork ~]$ chk_gpu
Jobid User JobName Req_parm Queue_time S Run_time Alloc_GPUS
108305 tangcb ..former_oldlong30 1:gpus=8:f 20241004 02:04:08 R 323:40:19 G145-gpu/7/6/5/4/3/2/1/0
...
...
...
113054 wangzx rmv 1:gpus=8:E 20241030 12:01:09 R 01:37:13 G114-gpu/7/6/5/4/3/2/1/0
GPU used detail:
--1080Ti-- Valid type: 1:S 2:D 4:Q 8:E ----
0 1 2 3 4 5 6 7
101(T): [ ][ ][ ]
102(E): [ ][ ][ ][ ][ ][ ][ ][ ]
...
--2080Ti-- Valid type: 1:s 2:d 4:q 8:e ----
0 1 2 3 4 5 6 7
171(e): [x][x][x][x][x][x][x][x]
...
...
...
Total 125 jobs.
使用 chk_gpuused <节点名> 可以查看节点具体卡的使用情况,例如:
(base)[liuzp@gwork ~]$ chk_gpuused G102
====Node G102====
Wed Oct 30 13:30:52 2024
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.76 Driver Version: 550.76 CUDA Version: 12.4 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
...
...
...
+-----------------------------------------+------------------------+----------------------+
+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
| No running processes found |
+-----------------------------------------------------------------------------------------+
其中 CUDA Version: 12.4 表示该节点支持的最高 cuda 版本,不同节点支持的最高 cuda 版本统计如下(2024年10月统计):
1080Ti
全为 12.42080Ti
全为 12.43080Ti
全为 12.43090
118(C): 11.4
121(C): 11.2
135(C): 11.4
136(C): 11.4
137(C): 11.4
138(F): 11.4
其他全为 12.4A40
全为 12.4
以使用 3090 为例,3090 节点中 cuda 最低为 11.2,且对应节点标识为 C,表示该节点最高支持 4 卡任务(标识符规则见PBS文件编写部分),在提交 4 卡 3090 任务时如果被分配到这个节点,我们使用的镜像中 cuda 版本就不能高于 11.2,否则会报错。
以使用 4 卡 3090 为例,在确定了 cuda 版本为最高 11.2 之后,再确定 torch 版本,到 pytorch 官方仓库寻找,访问以下链接:
https://download.pytorch.org/whl/cu112/torch
点击发现无法访问,那就降cuda版本到11.1,再次访问:
https://download.pytorch.org/whl/cu111/torch
按 Ctrl+F 搜索,输入 linux,找到想要的版本,例如:
torch-1.10.2+cu111-cp39-cp39-linux_x86_64.whl
也就是说确定 torch 版本为 1.10.2,一般这时 python 版本也确定了,即 cp39 代表 python3.9
接下来就可以制作镜像和conda环境了
先上Docker Hub,filter tags 处输入 11.1,选择需要的镜像,例如:
11.1.1-cudnn8-devel-ubuntu20.04
最好选带 devel 版本的,关于 devel 版本和 runtime 版本的区别可以自行百度
在 ghome/username 目录下创建一个文件夹,名为 dockertmp, 在该文件夹下创建文件,名为 Dockerfile,内容如下:
FROM nvidia/cuda:11.1.1-cudnn8-devel-ubuntu20.04
WORKDIR /
ENV TZ=Asia/Shanghai
RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && \
echo $TZ >/etc/timezone && \
apt update -y && apt upgrade -y && DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends \
gcc \
git \
git-lfs \
wget \
vim \
libopenmpi-dev \
openmpi-bin && \
apt autoremove -y && apt autoclean -y \
&& rm -rf /var/lib/apt/lists/* && rm -rf /tmp/* && rm -rf ~/.cache
ENV PATH="/ghome/liuzp/miniconda3/bin:${PATH}" \
PYTHONUNBUFFERED=1 \
GRADIO_SERVER_PORT=31893 \
API_PORT=8000 \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8
# ENV GRADIO_SERVER_PORT 31893
EXPOSE 31893
# ENV API_PORT 8000
EXPOSE 8000
需要修改的部分:
- FROM nvidia/cuda:11.1.1-cudnn8-devel-ubuntu20.04:关键步骤,选择的基础镜像
- ENV PATH="/ghome/liuzp/miniconda3/bin:${PATH}":关键步骤,将 miniconda 的 bin 目录加入 PATH,前面提到过镜像相当于一个系统,这一步相当于告诉系统 conda 在哪里
- 其他步骤不用改,具体含义可以问 ChatGPT
准备就绪后,切换到 g101 节点,进入 dockertmp 目录,运行以下命令查看管理员是否已将所需基础镜像拉过来:
(base) [liuzp@gwork ~]$ ssh g101
Last login: Thu Oct 24 14:12:38 2024 from 192.168.9.99
(base) [liuzp@G101 ~]$ cd dockertmp/
(base) [liuzp@G101 dockertmp]$ docker images -a
REPOSITORY TAG IMAGE ID CREATED SIZE
bit:5000/liulei2_thop latest 97673c096fea 22 hours ago 9.06GB
...
...
如果有,运行以下命令编译镜像:
(base) [liuzp@G101 dockertmp]$ docker build -t bit:5000/<image_name>:<image_tag> .
<image_name>:<image_tag>请自行定义
编译完成后联系管理员将镜像推送到集群私有仓库,然后就可以在计算节点使用了,例如:
在 gwork 或 g101 节点上,创建 conda 环境,例如:
(base) [liuzp@gwork ~]$ conda create -n <env_name> python=3.9 -y
激活环境:
(base) [liuzp@gwork ~]$ conda activate <env_name>
安装torch等软件包:
(<env_name>) [liuzp@gwork ~]$ pip install --no-cache-dir torch==1.10.2+cu111 -i https://download.pytorch.org/whl/cu111
(<env_name>) [liuzp@gwork ~]$ pip install --no-cache-dir transformers -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
如果需要依赖 cuda 等进行编译安装,可以在 g101 以交互式方式进入容器,然后在容器内激活环境进行编译安装,最后退出容器即可。集群不保存用户对镜像本身的修改,但是这样安装的软件包会保存在 ghome/username/miniconda3/envs, 具体操作过程类似于交互式调试代码
使用 WinSCP、FileZilla 等工具将代码、数据等 copy 到 ghome、gdata 等目录,然后找个文件夹编写 PBS 文件,命名为 xxxx.pbs,内容如下:
#PBS -N jobname
#PBS -o /ghome/liuzp/mycode/run.out
#PBS -e /ghome/liuzp/mycode/run.err
#PBS -l nodes=1:gpus=4:C
#PBS -r y
#PBS -m abef
SCRIPT_FILE_PATH=/ghome/liuzp/mycode/run.sh
IMAGE_NAME=bit:5000/liuzp_cu111:202410
cd $PBS_O_WORKDIR
echo Time is `date`
echo Directory is $PWD
echo This job runs on following nodes:
echo -n "Node:"
cat $PBS_NODEFILE
echo -n "Gpus:"
cat $PBS_GPUFILE
echo "CUDA_VISIBLE_DEVICES:"$CUDA_VISIBLE_DEVICES
chmod +x $SCRIPT_FILE_PATH
startdocker -P /ghome/liuzp -D /gdata/liuzp \
-u "--ipc=host --shm-size 192G -e HOME=/ghome/liuzp" \
-s $SCRIPT_FILE_PATH \
$IMAGE_NAME
需要修改的部分:
- #PBS -N jobname:jobname 为 job 名
- #PBS -o /ghome/liuzp/mycode/run.out:指定输出文件路径
- #PBS -e /ghome/liuzp/mycode/run.err:指定错误文件路径
- 4:C:申请的卡数和卡类型,根据 chk_gpu 查看,以 3090 为例,chk_gpu 显示Valid type: 1:A 2:B 4:C 8:F,表示如果想用一张 3090,就是 1:A,如果想用 4 张 3090,就是 4:C,以此类推
- SCRIPT_FILE_PATH=/ghome/liuzp/mycode/run.sh:指定运行脚本路径
- IMAGE_NAME=bit:5000/liuzp_cu111:202410:指定使用的镜像
- startdocker:指定挂载目录,-P 为挂载用户目录,-D 为挂载数据目录,192G 表示申请内存大小,-e HOME=/ghome/liuzp 表示将用户目录设为容器中的 HOME 目录,把 username 都换成自己的用户名即可
- 其他不用动,在 pbs 文件、要运行的脚本文件、python 代码中,要进行文件读写或运行文件时最好都用绝对路径
- ❗️❗️❗️重要:vim中编辑 pbs 文件末尾要有一个空行,如果在 VS code 中编辑,要有两个空行
SCRIPT_FILE_PATH=/ghome/liuzp/mycode/run.sh 编写如下:
#!/bin/bash
# 指定工作目录
DIR_PATH=/ghome/liuzp/mycode/
cd $DIR_PATH
# 初始化conda
. /ghome/liuzp/.bashrc
# 激活conda环境
conda activate <env_name>
# 运行代码
python run.py
# 这里就跟平时在本地终端运行代码一样了,也可以用 torchrun、accelerate launch 等启动方式
编写完后,提交 job:
(base) [liuzp@gwork pbs]$ qsub xxxx.pbs
大功告成!
(base) [liuzp@gwork ~]$ ssh g101
Last login: Wed Oct 30 14:36:42 2024 from 192.168.9.99
(base) [liuzp@G101 ~]$
(base) [liuzp@G101 ~]$ echo $CUDA_VISIBLE_DEVICES
(base) [liuzp@G101 ~]$ export CUDA_VISIBLE_DEVICES="0,1"
(base) [liuzp@G101 ~]$ startdocker -P /ghome/liuzp -D /gdata/liuzp -u "--ipc=host --shm-size 64G -e HOME=/ghome/liuzp" -s /ghome/liuzp/mycode/run.sh bit:5000/liuzp_cu111:202410
(base) [liuzp@G101 ~]$ docker ps -a
(base) [liuzp@G101 ~]$ docker stop <container_id>
# 登录到 g101 节点
(base) [liuzp@gwork ~]$ ssh g101
Last login: Wed Oct 30 22:34:07 2024 from 192.168.9.99
# 查看 gpu 使用情况
(base) [liuzp@G101 ~]$ nvidia-smi
...
# 指定要用的卡号,不指定的话会随机分一张卡
(base) [liuzp@G101 ~]$ export CUDA_VISIBLE_DEVICES="0,1"
# 以交互式方式进入容器,在 g101 使用 startdocker 命令默认挂载了用户目录,可按需手动挂载数据目录,
# -it 表示交互式启动,-e HOME=/ghome/liuzp 表示将用户目录设为容器中的 HOME 目录,
# -c /bin/bash 表示启动容器后打开 bash 命令行, bit:5000/liuzp_cu111:202410 为使用的镜像名
(base) [liuzp@G101 ~]$ startdocker -D /gdata/liuzp -u "-it -e HOME=/ghome/liuzp" -c /bin/bash bit:5000/liuzp_cu111:202410
# 进入容器后,可以看到命令行变为:
I have no name!@a915c03bade3:/$
# 查看容器中都有什么
I have no name!@a915c03bade3:/$ ls -a
. bin gdata lib32 mnt run tmp
.. boot ghome lib64 opt sbin usr
.dockerenv dev home libx32 proc srv var
NGC-DL-CONTAINER-LICENSE etc lib media root sys
# 可以看到常规 Linux 系统该有的目录这里都有,而且还有挂载的用户目录 ghome 和数据目录 gdata
# 此时可以执行一些命令检查镜像中的环境,例如:
I have no name!@a915c03bade3:/$ nvcc -V
I have no name!@a915c03bade3:/$ gcc -v
I have no name!@a915c03bade3:/$ nvidia-smi
# 要使用 conda,还需要进行初始化,运行:
I have no name!@a915c03bade3:/$ . /ghome/liuzp/.bashrc
# 然后就可以查看 conda 环境,运行:
I have no name!@a915c03bade3:/$ conda env list
# 激活 conda 环境,运行:
I have no name!@a915c03bade3:/$ conda activate <env_name>
# 检查 torch 版本,运行:
(<env_name>) I have no name!@a915c03bade3:/$ python -c "import torch; print(torch.__version__)"
# 调试代码
(<env_name>) I have no name!@a915c03bade3:/$ python xxxx/xxx/run.py
(base) [liuzp@gwork ~]$ chk_gpu
(base) [liuzp@gwork ~]$ qstat -u $USER
(base) [liuzp@gwork ~]$ qdel <job_id>
(base) [liuzp@gwork ~]$ sudo chk_res <结点名>
(base) [liuzp@gwork ~]$ dockerlog <jobid>
(base) [liuzp@gwork ~]$ sudo get_report
(base) [liuzp@gwork ~]$ getallreposimages
(base) [liuzp@gwork ~]$ du -sh .[!.]* * | sort -hr
注册Sever酱,获取 SendKEY,然后就可以给微信发送通知,及时获取 job 状态,有些节点会发送失败,请及时关注 out 文件
使用示例:
def send(title: str, desp: str=''):
try:
import requests
requests.post(url='https://sctapi.ftqq.com/<your_send_key>.send', json={'title': title, 'desp': desp, **{}}, headers={'Content-Type': 'application/json;charset=utf-8'}, timeout=20)
except Exception as e:
print("********************** send error **********************")
print(e)
print("********************************************************")
def main():
pass
if __name__ == '__main__':
send(title="Start train job", desp='<job name>')
try:
main()
send(title="Successfully train job", desp='<job name>')
except Exception as e:
print("********************* train error **********************")
print(e)
print("********************************************************")
send(title=f"Failed train job", desp='<job name>')
exit()
如果 miniconda3 虚拟环境过多导致ghome容量不够,可以用软连接把虚拟环境放到 gdata 目录下,参考教程
(base) [liuzp@gwork ~]$ conda clean -a -y
(base) [liuzp@gwork ~]$ conda clean -p -y
(base) [liuzp@gwork ~]$ pip cache purge
# 使用 pip 时直接加上 --no-cache-dir 参数,例如:
(<env_name>) [liuzp@gwork ~]$ pip install --no-cache-dir xxx
# 临时换源
pip install torch==1.10.2+cu111 -i https://download.pytorch.org/whl/cu111
pip install transformers -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
# 永久换源
pip install -i https://mirrors.ustc.edu.cn/pypi/simple pip -U
pip config set global.index-url https://mirrors.ustc.edu.cn/pypi/simple
# 其他源如阿里、豆瓣等可自行搜索