HPC

DeepOps

DevOps, MLOps와 함께 딥러닝을 중심으로 DeepOps라고 부르며, NVIDIA가 공개한 자사 DGX 서버에 Slurm clusters를 구성하는 툴의 이름이기도 하다. aws에 Ubuntu 환경(공식 Deep Learning AMI 18.04)에서도 설치를 지원한다. 그러나 ansible로 설치하는 스크립트의 특성상 많은 시행착오가 뒤따르며, 한 번에 제대로 설치되진 않는다. 처음에는 몇 대가 failed로 나왔다가 한 번 더 해보면 정상으로 진행되기도 한다.

  • slurm: 각 노드에 배치작업을 실행하는 스케줄러, 노드에는 작업을 관리하는 데몬이 떠 있다.
  • enroot: 도커 이미지를 sqsh(squashfs) 파일로 관리
  • pyxis: slurm의 컨테이너 플러그인으로, srun에서 sqsh 컨테이너 파일을 주고 받을 수 있다.

slurm

srun 실행 후 pyxis 플러그인으로 컨테이너 이미지(sqsh)를 얹어 실행해보면 각 노드에 네트워크로 전송하는 것을 확인할 수 있다. nload 에서 볼 수 있다. nvcr.io/nvidia/pytorch:22.03-py3는 10GB 이므로 전송에 aws g4dn에서 1m 40s 이상 소요된다. 디스크를 좀 더 좋게 설정해(AWS gp3 → io2) 40s까지 줄였다. 각 노드 로컬에서 직접 $ enroot create를 할 경우 30s까지 줄어든다.

$ srun -N2 --container-image=alpine grep PRETTY /etc/os-release # import 후 OS 출력
$ srun -N2 --container-image=./alpine.sqsh grep PRETTY /etc/os-release # sqsh 전달하여 OS 출력

NCCL Tests1를 위한 dockerfile은 다음과 같다.

FROM nvcr.io/nvidia/pytorch:22.03-py3

RUN apt-get update
RUN apt install net-tools

ENV NCCL_SOCKET_IFNAME=en
ENV NCCL_TESTS_COMMITISH=f773748b46

WORKDIR /opt/nccl_tests
RUN wget -q -O - https://github.com/NVIDIA/nccl-tests/archive/${NCCL_TESTS_COMMITISH}.tar.gz | \
        tar --strip-components=1 -xzf - \
        && CC=mpicc CXX=mpicxx make MPI=1
RUN cp -R /opt/nccl_tests/build/* /usr/local/bin/

DRAIN 노드 복구

노드가 drain 상태일때 다음과 같이 복구한다.

$ sudo scontrol update nodename=gpu[01-02] state=resume

# 상태 조회
$ sinfo -l
Mon May 23 11:44:05 2022
PARTITION AVAIL  TIMELIMIT   JOB_SIZE ROOT OVERSUBS     GROUPS  NODES       STATE NODELIST
batch*       up   infinite 1-infinite   no       NO        all      8        idle gpu[01-04,10-13]

$ sinfo -N -l
Mon May 23 11:43:57 2022
NODELIST   NODES PARTITION       STATE CPUS    S:C:T MEMORY TMP_DISK WEIGHT AVAIL_FE REASON
gpu01          1    batch*        idle 64     2:16:2 467190        0      1   (null) none
gpu02          1    batch*        idle 64     2:16:2 467190        0      1   (null) none
gpu03          1    batch*        idle 64     2:16:2 467190        0      1   (null) none
gpu04          1    batch*        idle 64     2:16:2 467190        0      1   (null) none
gpu10          1    batch*        idle 64     2:16:2 467190        0      1   (null) none
gpu11          1    batch*        idle 64     2:16:2 467190        0      1   (null) none
gpu12          1    batch*        idle 64     2:16:2 467190        0      1   (null) none
gpu13          1    batch*        idle 64     2:16:2 467190        0      1   (null) none

# Tasks 조회
$ watch -n 1 squeue -l

전체 노드 스펙은 /etc/slurm/slurm.conf에 있고, 각 노드는 /etc/nhc/nhc.conf에 정의되어 있다.

deepops에 slurm-validation.yml이 포함되어 있으므로 다음과 같이 nccl_tests를 수행할 수 있다.

$ ansible-playbook -l slurm-cluster playbooks/slurm-cluster/slurm-validation.yml \
-e '{srun_exports: "OMPI_MCA_btl_tcp_if_include=ens5,OMPI_MCA_pml=^ucx,NCCL_SOCKET_IFNAME=ens"}' \
-vvv

interactive 모드는 다음과 같이 접속한다. (salloc의 정확한 용도 파악 필요)

$ srun --gres=gpu:1 \
--export="OMPI_MCA_btl_tcp_if_include=ens5,OMPI_MCA_pml=^ucx,NCCL_SOCKET_IFNAME=ens" \
-N2 \
--container-image=./pytorch-slurm.sqsh \
--pty bash

계속 DRAIN 상태로 빠지는 경우

gpu03 노드에만 별도 패키지를 설치했더니 그 서버가 명령을 실행하면 자꾸만 drain 상태로 빠진다.

$ sudo tail -f /var/log/slurm/slurmctld.log
[2022-06-10T05:06:29.819] sched: _slurm_rpc_allocate_resources JobId=591 NodeList=gpu03 usec=470
[2022-06-10T05:06:29.909] error: Prolog launch failure, JobId=591

Prolog launch failure 에러가 발생하며, /etc/slurm/slurm.conf에서 Prolog=/etc/slurm/prolog.sh를 찾았다. 해당 스크립트는 /etc/slurm/prolog.d 디렉토리를 실행한다.

-rwxr-xr-x 1 slurm slurm  734 May 24 06:29 50-all-enroot-dirs*
-rwxr-xr-x 1 slurm slurm  169 May 24 06:29 50-exclusive-cpu*
-rwxr-xr-x 1 slurm slurm  156 May 24 06:29 50-exclusive-gpu*
-rwxr-xr-x 1 slurm slurm  196 May 24 06:29 50-exclusive-ssh*
-rwxr-xr-x 1 slurm slurm   93 Jun 10 05:11 95-all-rootless*

Slurm의 리소스 제약을 보조하는 스크립트와 함께 95-all-rootless는 singularity를 실행하는 명령인데, 원래 설치되어 있지 않기 때문에 실행되지 않아야 하나 얼마전 gpu03 노드만 별도로 singularity를 설치한 적이 있다. 이 노드에만 명령이 실행되면서 이후 명령에 오류가 발생. gpu03 노드도 실행되지 않도록 조치하여 복구.

운영 명령

Job 히스토리 보는 법

$ sacct

사용자 조회

$ sacctmgr list user

그룹(account) 조회

$ sacctmgr show account

특정 파티션 전체 노드 테스트

$ srun --partition=a-all --gres=gpu:8 --nodes=40 nvidia-smi -L

drained 이벤트 조회

$ sacctmgr list event

pyxis

이미지 배포에 오랜 시간이 걸리는데, 각 노드에 $ enroot list로 이미지가 존재하면 container-name 지정으로 호출할 수 있다. 이 경우 이미지 전송을 하지 않으므로 매우 빠르게 실행 가능하다.

$ srun --gres=gpu:1 -N2 -l --container-name=pytorch-slurm nvidia-smi

pyxis 플러그인은 pyxis_pytorch-slurm를 찾는다. 따라서 enroot로 미리 생성할때는 접두어 pyxis_를 붙여야 한다. 그러나 slurm을 실행할 때 pyxis가 각 노드의 enroot 이미지를 날려버리는 이슈가 있다.

enroot PyTorch hook

srun은 SLURM_으로 시작하는 다양한 환경변수를 셋팅해준다. 하지만 RANK, WORLD_SIZE는 torchrun이 셋팅해주는데, enroot의 hooks/extra에 50-slurm-pytorch라는 hook2이 있어서, 만약 PyTorch 이미지인 경우 PYTORCH_VERSION 환경변수가 있는지 확인하고, SLURM_*을 이용해 torchrun이 해주는 RANK, WORLD_SIZE, MASTER_ADDR등을 대신 설정해준다.

따라서 pyxis로 enroot 이미지를 srun 할 경우 따로 torchrun 할 필요 없으며, rank/size를 얻기 위한 MPI 통신도 필요없다. (애초에 PyTorch에는 MPI가 built-in 되어 있지 않다) enroot쪽에는 pytorch hook이 있다는 내용은 문서화 되어 있지 않으며, 이 내용은 PyTorch-Ignite 코드의 주석3에서 힌트를 얻어 enroot 코드를 뒤져서 찾아냈다.

또한 enroot의 hooks/extra는 default로 비활성화 되어 있는데 deepops를 이용해 클러스터 설치시, pyxis 설치할 때 이 extra hooks가 기본으로 실행될 수 있도록 링크를 걸어준다.4

실험 환경

  CPU 실행 GPU 실행 MPI NCCL
mpirun o o o o
srun o o x o
srun + pyxis o o o o
srun + pyxis(PyTorch)     o o

CPU

mpirun

$ mpirun --host gpu01,gpu02 cat /etc/os-release | grep PRETTY

srun

$ srun -N2 -l cat /etc/os-release | grep PRETTY

srun + pyxis

$ srun -N2 -l --container-image=./openmpi.sqsh cat /etc/os-release | grep PRETTY

GPU

mpirun

$ mpirun --host gpu01,gpu02 nvidia-smi

srun

$ srun --gres=gpu:1 -N2 -u -l nvidia-smi -L

srun + pyxis

$ srun --gres=gpu:1 -N2 -l --container-image=./nccl.sqsh nvidia-smi

MPI

mpirun
conda 환경에서는 다른 mpirun이 실행될 수 있으므로 유의

$ mpirun --host gpu01,gpu02 \
--mca btl_base_warn_component_unused 0 \
--mca btl_tcp_if_exclude docker0 \
./mpic

srun(x)
orte_ess_init failed가 발생한다. slurm은 slurmstepd가 실행하는데, 여기서는 MPI 통신을 위해 굳이 orted를 실행하려다 발생하는 오류로 보인다.

$ srun --gres=gpu:1 -N2 \
--export="OMPI_MCA_btl_tcp_if_include=ens5" \
./mpic

srun + pyxis

$ srun --gres=gpu:1 -N2 \
--export="OMPI_MCA_btl_tcp_if_include=ens5" \
--container-image=./openmpi.sqsh \
./mpic

srun + pyxis(PyTorch)
15GB, 40s 소요

$ srun --gres=gpu:1 -N2 \
--export="OMPI_MCA_btl_tcp_if_include=ens5,OMPI_MCA_pml=^ucx" \
--container-image=./pytorch-nccl-tests.sqsh \
./openmpi/mpic

NCCL

mpirun
conda 환경에서는 다른 mpirun이 실행될 수 있으므로 유의

$ mpirun -x NCCL_SOCKET_IFNAME=ens \
-x LD_LIBRARY_PATH=/usr/local/cuda/lib:$LD_LIBRARY_PATH \
--host gpu01,gpu02 \
--mca btl_base_warn_component_unused 0 \
--mca btl_tcp_if_include ens5 \
./all_reduce_perf -b 100M -e 110M -c 0 -n 1

srun

$ srun --gres=gpu:1 \
-u \
-l \
--nodelist=gpu01,gpu02 \
--export=ALL,"NCCL_SOCKET_IFNAME=ens" \
python nccl.py

srun + pyxis

$ srun --gres=gpu:1 -N2 \
--export=ALL,"OMPI_MCA_btl_tcp_if_include=ens5,NCCL_SOCKET_IFNAME=ens" \
--container-image=./nccl.sqsh \
all_reduce_perf -b 100M -e 110M -c 0 -n 1

srun + pyxis(PyTorch)
mpirun에 비해 1/2 속도밖에 나오지 않는다.

$ srun --gres=gpu:1 -N2 \
--export=ALL,"OMPI_MCA_btl_tcp_if_include=ens5,OMPI_MCA_pml=^ucx,NCCL_SOCKET_IFNAME=ens" \
--container-image=./pytorch-nccl-tests.sqsh \
all_reduce_perf -b 100M -e 110M -c 0 -n 1

Last Modified: 2022/08/02 02:18:01

is a collection of Papers I have written.
© 2000 - Sang-Kil Park Except where otherwise noted, content on this site is licensed under a CC BY 4.0.
This site design was brought from Distill.