🐳

Docker + DeepSpeed + MultiGPU 사용 중 NCCL posix_fallocate failed: No space left on device 에러 대응하기

Tags
NLP
MLDL Framework
Published
Published May 20, 2021

어쩌다 이렇게...

학과 GPU서버 클러스터 액세스가 두 가지 방법으로 제공되는데,
  • Jupyter가 포함된 Docker container를 띄워주거나
  • root 액세스가 없는 계정으로 ssh 액세스를 하거나
이렇게 두 가지 방법을 쓸 수 있다.
보통의 경우 Root 권한을 사용가능한 Docker container 환경을 선호하는데, 해당 환경의 경우 이미 K8s등에서 띄워진 상태이기에 docker run ~~하는 config를 수정할 수가 없다.
한편 DeepSpeed(혹은 다른 Distributed trainer)에서 MultiGPU를 사용할 경우 GPU간 통신을 할 때 여러 방법을 사용하는데, 만약 GPU/Node간 InfiniBand같은 전용 통신시설이 설치되지 않았다면 PCI-E로 연결된 CPU, 즉 일반 RAM에 액세스를 해야한다.
하지만 Distributed trainer 사용시 보통 GPU 별로 process를 따로 띄워 사용하기 때문에, Gradient등을 GPU간 공유하기 위해서는 공유 메모리인 /dev/shm에 액세스를 해야 한다.
그런데, 이때 문제가 생긴다.
만약 Local(Host)에서 작업시 `/dev/shm`은 실제 램의 1x~2x 정도의 충분하고도 넘치는 양이 할당되기 때문에 이슈가 발생하지 않는다. (램이 8G라면 16G 정도가 기본값이다.)
하지만 Docker instance를 띄울 경우 기본값이 64MB로, 아주 작은 값이 할당된다.
NCCL의 경우 InfiniBand등 GPU간 전용 통신기기가 발견되지 않을 경우 Default Fallback으로 SHM을 사용하는데, 이로 인해 문제가 발생한다.
게다가.. 이건 deepspeed 혹은 pytorch단에서 에러가 발생하는 것이 아니라서, NCCL Debug를 켜두지 않으면 발견도 못하고 단순히 exit with code 1으로 알수 없게 프로그램이 종료되는 것으로밖엔 보이지 않는다.
따라서 해결책은 NCCL에서 SHM을 사용하지 않도록 만드는 것이다.
(그 외에... GPU P2P를 disable하거나 NCCL Buffer size를 조절하는 등 방법도 써봤지만 결국은 동일한 에러로 도달한다. 기본 Fallback이 SHM이기 때문.)
이렇게 SHM을 사용하지 않으면 GPU/worker간 통신이 모두 네트워크를 통하게 된다고 한다.
1Node일 경우 결국 로컬단에서 처리되기 때문에 속도 저하가 "그렇게" 크지는 않겠지만, MultiNode의 경우 네트워크 i/o 속도가 속도 저하의 가장 큰 원인이 될 수 있는 옵션일 것 같다.
(속도 저하가 얼마나 되는지는 큰 모델을 직접 Full로 학습해서 비교해보면 알 수 있을 듯 하다.)

Env & Command

실행 환경

  • Docker Container
  • /dev/shm 용량이 64MB(기본 세팅)으로 잡혀있음

Code Repo

Bash command

export NCCL_IB_DISABLE=1 export BS=8 export NCCL_DEBUG=INFO # Debug 필수 deepspeed run_mlm.py \ --seed 42 \ --model_type bert \ --tokenizer_name beomi/KcELECTRA-base \ --train_file ./sampled_20190101_20200611_v2.txt \ --num_train_epochs 2 \ --per_device_train_batch_size $BS \ --per_device_eval_batch_size $BS \ --do_train \ --output_dir ./test-bert-zero2-multigpu \ --fp16 \ --logging_first_step \ --max_seq_length 300 \ --deepspeed ./ds_zero2_1gpu.json \

Error Log

# [..생략..] deepspeed_init jupyter-beomi:9223:9776 [0] include/shm.h:28 NCCL WARN Call to posix_fallocate failed : No space left on device jupyter-beomi:9223:9776 [0] NCCL INFO include/shm.h:41 -> 2 jupyter-beomi:9223:9776 [0] include/shm.h:48 NCCL WARN Error while creating shared memory segment nccl-shm-recv-d1133d3d8fb2856f-0-1-0 (size 9637888) # [..생략..]
  • 위와 같이 Shared memory 부분에서 No space left on device, 즉 저장공간 부족이 나타난다.
  • OOM이 아닌 이유: /dev/shm 은 로컬 스토리지처럼 인식되지만 사실 위치는 램이기 때문에 나타나는 이슈
  • 위 로그는 NCCL Debug를 켜지 않으면 나타나지 않기 때문에 쉽게 발견하기 힘들다.

Solution

근본적 해결책

Docker container를 띄울 때 Shared Memory 부분을 늘려주거나, Host와 공유하도록 설정하자.
# Shared memory 늘리기 docker run --shm-size=256m --other-config....

docker config 수정이 불가능할 때

  • NCCL에서 SHM을 사용하지 않도록 세팅한다.
  • NCCL_SHM_DISABLE 을 활성화(값 1) 시켜주면 된다.
  • 단, 이때는 기기간 InfiniBand 등 기기간 연결이 활성화 되어있지 않다면, 네트워크를 통해 전달하기 때문에 속도 저하가 발생할 수 있다.
export BS=8 export NCCL_DEBUG=INFO export NCCL_SHM_DISABLE=1 # <- 이 부분을 추가하면 된다! deepspeed run_mlm.py \ --seed 42 \ --model_type bert \ --tokenizer_name beomi/KcELECTRA-base \ --train_file ./sampled_20190101_20200611_v2.txt \ --num_train_epochs 2 \ --per_device_train_batch_size $BS \ --per_device_eval_batch_size $BS \ --do_train \ --output_dir ./test-bert-zero2-multigpu \ --fp16 \ --logging_first_step \ --max_seq_length 300 \ --deepspeed ./ds_zero2_1gpu.json \

Reference