[Docker] ubuntu18.04 | CUDA 10.0 | cudnn 7 | Python 3.6 이미지 빌드하기

johyonghoon·2023년 3월 30일
0

1. 기본적인 구축 아이디어

docker image를 빌드하기 위해서는 base image container를 구축하고 개발환경을 정상적으로 구성하는 것이 우선시되어야 한다. Dockerfile에 Image 구축을 위한 명령어들을 머릿 속으로 구성하여 build를 진행하면서 오류를 잡아나가는 것은 어찌보면 우거진 숲 속을 직선으로 뚫고 끝까지 도달하는 것과 같다. 누군가는 그게 위험하더라도 빠른 길이라고 이야기할 수 있겠지만, 조금 늦더라도 숲에 들어가기 전에 가능성 있는 길들을 열어두고 그 길을 한 번에 뚫고 지나갈 수 있게 계획하는 것이 안전하고 가장 빠른 길이라고 판단했다.

필자는 첫째로 RTX 2080 GPU가 구축되어 있는 Windows 환경에서 Docker Image 빌드를 시작했지만, 도커 컨테이너에서 GPU를 인식하지 못하고, 패키지가 정상적으로 작동하지 않는 문제에 부딪혔다. WSL을 설치해서 리눅스 환경에서 구축을 시도해보았지만, 그 마저도 정상적으로 이루어지지 않았다. 두번째 방법으로, Linux 호환이 되는 m1 Mac을 사용하여 빌드를 시도했지만, mac에서는 GPU가 없기도 하거니와 Unix OS라서 Linux와 완전히 일치하지 않는 문제가 발생하였다. 그래서 결국, RTX 3070ti GPU PC에 ubuntu20.04 OS를 설치하였고, 많은 삽질 끝에 Docker Image 빌드에 성공하였다.

깨끗한 Ubuntu20.04 환경에서 nvidia driver 설치, nvidia-docker 설치, CUDA Toolkit이 적용된 nvidia/cuda 도커 이미지를 가져오는 방법, CUDA 버전에 맞는 cuDNN를 설치하는 방법, 최종적으로 Docker Image를 빌드하고 Docker Hub에 올리는 과정을 다룰 것이다.


2. 구축 계획

  • ubuntu 18.04
  • CUDA 10.0
  • cuDNN 7.6.5
  • Python 3.6

같은 방법으로 아래 개발환경도 구축하였다

  • ubuntu 18.04
  • CUDA 11.1
  • cuDNN 8.1.1
  • Python 3.8

3. ubuntu Desktop 개발환경 구축하기

헷갈릴 수도 있는데, 도커 이미지 빌드를 위해 수행하는 과정이 아니라, 데스크탑에 OS를 설치하고 해주는 과정을 설명하는 것이다. 이미지 빌드를 보고자 한다면 바로 4번으로 넘어가도 된다.(하지만 이 과정도 어찌보면 중요할 지도...)

3.1. NVIDIA Driver 설치하기

ubuntu20.04 Desktop OS를 PC에 설치하였다. 추후 작성 예정
여기에서 우선적으로 nvidia driver가 설치되어야 GPU를 사용할 수 있다.

$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo ubuntu-drivers devices

⚠️ 3번째 코드를 통해 현재 환경에서 설치 가능한 nvidia driver 목록이 나오는데
recommand driver 이름 끝에 open이 붙어있다면 open이 붙어있지 않은 버전으로 설치
추정컨데, open이 붙은 드라이버를 설치하면 부팅할 때 검은 화면에 직면할 수 있으니 주의!

⚠️ 설치 후 재부팅했을 때 해상도가 달라지거나 아래 명령어가 작동하지 않으면 다시 시도

$ nvidia-smi
No devices were found

위 에러가 발생한다면 설치된 nvidia-drivers 버전 끝에 open으로 되어있는지 확인
❓ Software & updates >> Additional Drivers 에서 사용 중인 드라이버 확인 가능
만약 Open이 붙어있다면 Open이 붙어 있지 않은 버전으로 재설치

참고자료 : Nvidia-smi outputs “No devices were found”

=> 정상적으로 설치 시 nvidia-smi 명령으로 NVIDIA 드라이버와 GPU 정보를 알 수 있다.

3.2. 도커 설치하기

참고자료 : Docker Official Document

# apt 패키지 업데이트 및 설치
$ sudo apt-get update
$ sudo apt-get install -y ca-certificates curl gnupg

# 도커 공식 GPG 키 추가
$ sudo mkdir -m 0755 -p /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
 
# 레포지토리 셋업
$ echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 도커 설치하기
$ sudo apt-get update
$ sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 정상적으로 설치되었는지 확인하는 과정
$ sudo docker run hello-world

=> 도커가 정상적으로 작동하는 것을 볼 수 있다!

3.3. 도커 컨테이너 구축하기

도커 사용자 권한 부여하기

참고자료 : [Docker][해결방법] Got permission denied ...

# 사용자에게 관리자 권한을 주기 보다는 docker 권한을 주는 것이 낫다.
$ sudo usermod -a -G docker $USER
# 재부팅 필요
$ id

=> 사용자 권한에 도커가 들어온 것을 알 수 있다!

도커 이미지 pull / 컨테이너 생성하기

도커 이미지 출처 : DockerHub nvidia/cuda

# 도커 이미지 pull
# docker pull {사용자명}/{레파지토리}:{태그} 형태로 image를 가져올 수 있다.
$ docker pull johyonghoon/django_project:1.0

# 도커 컨테이너 생성하기
# docker run <OPTIONS> --name <컨테이너이름:사용자지정> <이미지이름:Tag>
$ docker run -it --name django johyonghoon/django_project:1.0
  • -i: 컨테이너와 상호작용을 위해 표준 입력(STDIN)을 열어놓는 옵션
  • -t: 컨테이너 내부에서 가상 터미널을 사용할 수 있도록 하는 옵션
  • -it : i/t 옵션을 함께 사용하면, 컨테이너에 직접 접속할 수 있는 인터랙티브한 셸을 실행
# 컨테이너 / 이미지 삭제하기

# 컨테이너 중단하기
$ docker stop {container ID or NAME}
# 컨테이너 삭제하기
$ docker rm {container ID or NAME}
# 실행 중인 컨테이너 강제 삭제하기
$ docker rm -f {container ID or NAME}

# 단일 이미지 삭제하기
$ docker image rm {image ID}
# 전체 이미지 삭제하기
$ docker rmi $(docker images -q) -f
# 전체 로그 삭제하기
$ docker system prune --volumes

nvidia docker 설치하기

GPG키 입력 및 stable 저장소 추가

$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
   && curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - \
   && curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list

$ sudo apt-get update
$ sudo apt-get install -y nvidia-docker
$ sudo systemctl restart docker  # 재시작

3.4. 도커 이미지 구축 :: ubuntu18.04 CUDA10.0 cuDNN7 python3.6

nvidia/cuda 이미지 가져오기

$ docker pull nvidia/cuda:10.0-cudnn7-devel-ubuntu18.04

(선택) 도커 이미지 레포지토리 및 태그 변경하기

이름이 긴 이미지를 사용하면 명령을 반복할 때 복잡하기 때문

$ docker tag nvidia/cuda:10.0-cudnn7-devel-ubuntu18.04 howsfit:cuda10.0
$ docker images

도커 컨테이너 실행하기

$ docker run -it --gpus all --name="humanparse" -p 8000:8000 howsfit:cuda10.0
# gpu 사용여부 확인하기
$ nvidia-smi
# CUDA 버전 확인하기
$ nvcc -V
# cudnn 버전 확인하기
$ cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2 (cuDNN 7.x 까지)
$ cat /usr/local/cuda/include/cudnn_version.h | grep CUDNN_MAJOR -A 2 (cuDNN 8.x 부터)

=> cuDNN 7.6.5가 설치된 이미지를 가져온건데 왜인지 없었다. 그래서 설치할 것임.

cuDNN 7버전 설치하기

cuDNN 설치 링크 : NVIDIA cuDNN Archive

=> cuDNN Library for Linux 를 선택하여 설치

설치 위치에서 터미널 실행(ctrl + alt + t) 후 압축 풀기

# 압축 해제
~/Downloads $ ls
cudnn-10.0-linux-x64-v7.6.5.32.tgz ...
~/Downloads $ tar -xzvf cudnn-10.0-linux-x64-v7.6.5.32.tgz

local의 cuDNN 파일들을 컨테이너 안으로 복사

~/Downloads $ cd cuda 
~/Downloads/cuda $ docker cp include/cudnn* humanparse:/usr/local/cuda-10.0/include
~/Downloads/cuda $ docker cp lib64/* humanparse:/usr/local/cuda-10.0/lib64
# sudo 패키지가 설치된 상태에서 실행 가능
~/Downloads/cuda $ sudo chmod a+r /usr/local/cuda-10.0/lib64/libcudnn*

⚠️ 3번째 코드를 할 때 2번째 코드와 다르지 않다고 생각했는데 안되서 폴더 자체를 옮긴 뒤
폴더 파일을 도커 컨테이너 내부에서 이동시키는 방법을 사용했다.

기본 패키지 설치

$ apt update
$ apt install -y sudo vim python3.6 python3-pip ubuntu-drivers-common curl

python / pip alias 적용하기

$ vim ~/.bashrc

$ source ~/.bashrc
# python / pip 버전 확인
$ python -V
$ pip -V


4. Dockerfile 작성하기

위 과정을 통해 어떤 명령어로 기본적인 개발환경을 구축했는지 알 수 있다. 개발환경을 구축한 도커 컨테이너로 바로 이미지를 빌드할 수도 있지만, 구축하는 과정에서 실수하거나, 반영했음에도 불구하고 기록하지 않은 것들까지 이미지에 반영될 수 있기 때문에 별도로 Dockerfile을 작성하는 방식으로 작업했다.

4.1. Dockerfile

FROM nvidia/cuda:10.0-cudnn7-devel-ubuntu18.04

RUN apt update && \
    apt install -y sudo wget vim python3.6 python3-pip curl && \
    echo 'alias python="python3.6"' >> ~/.bashrc && \
    echo 'alias pip="pip3"' >> ~/.bashrc && \
    apt clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Activate bashrc
RUN . ~/.bashrc

# Install cuDNN 7.6.5
COPY include/cudnn* /usr/local/cuda-10.0/include
COPY lib64/* /usr/local/cuda-10.0/lib64/
RUN chmod a+r /usr/local/cuda-10.0/lib64/libcudnn*

RUN . ~/.bashrc

4.2. tree

.
├── Dockerfile
├── include
│   └── cudnn.h
├── lib64
│   ├── libcudnn.so -> libcudnn.so.7
│   ├── libcudnn.so.7 -> libcudnn.so.7.6.5
│   ├── libcudnn.so.7.6.5
│   └── libcudnn_static.a
├── main.py
├── requirements.txt
└── test_main.http

여기에서 include 폴더와 lib64는 위 과정을 잘 읽어보았다면 알 수 있겠지만 cuDNN 7.6.5 버전 설치를 통해 가져온 것이다. 그 파일들을 cli 명령으로 설치하고 반영하는 것이 (시도해보았을 때) 불가능해서 로컬에서 도커 컨테이너로 복사하는 방식으로 반영해주었다.

참고로 main.py는 FastAPI 프로젝트 생성으로 만든 파일이다. FastAPI 기본 세팅 테스트용으로 활용하면 좋다.

4.3. Docker Image Build

터미널 도커 로그인

(base) hoon@hoon-Z690-GAMING-X-DDR4:/$ docker login -u johyonghoon
Password: 
WARNING! Your password will be stored unencrypted in /home/hoon/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

⚠️ base64 형태로 인코딩되어 있어서 디코딩하면 고스란히 비밀번호가 노출된다.
따라서, 개인 PC라면 괜찮지만 공용 네트워크 공간이라면 주의해야 한다.
링크에서 설명하는 방법으로 보안 조치를 할 수 있다고 하니 참고 바란다.

도커 빌드하기

# docker build {옵션} -t {user_name/image_name:tag} {Dockerfile_path}
$ docker build -t johyonghoon/howsfit:cuda10.0 .

마지막 . 은 현재 위치를 의미한다. 아무 의미없이 쓴 것이 아니라는 뜻이다.

도커허브에 도커 이미지 push하기

# docker push {user_name/repository:tag}
$ docker push johyonghoon/howsfit:cuda10.0

Success!

낯선 OS를 접하는 것부터 다른 환경의 컨테이너를 구축하는 것까지 많은 시간을 소요했지만 배운 게 많은 시간이라고 생각한다. 이로써, 파이썬의 가상환경으로 패키지를 관리하는 것과는 별개로, 아예 개발환경 자체를 컨트롤할 수 있는 능력을 갖추었다고 생각하니 스스로가 든든하다. 그리고, OS에 구애받지 않고 어떤 환경에서도 개발할 수 있다고 생각하니 마음이 편하다. 다음으로는 Docker Compose로 컨테이너를 동시에 띄우는 방법을 가져오도록 하겠다!

3개의 댓글

comment-user-thumbnail
2023년 8월 10일

nvidia/cuda10.0-cudnn7-devel-ubuntu18.04 를 pull 하면 not found 에러가 발생합니다.
docker hub 홈페이지 들어가보면 cuda10.0 관련한 이미지는 안보이는데 어디서 설치를 해야하는지 궁금합니다.

답글 달기
comment-user-thumbnail
2024년 3월 11일

지나가는 길에 하나 알려드리자면 도커에서 cudnn 버전 확인은
cat /usr/include/cudnn_version.h | grep CUDNN_MAJOR -A 2 이며
/usr/include 안에 있습니다.

1개의 답글