딥러닝 연구자를 위한 Docker를 사용하여 재구현 가능한 환경 만들기

Gyeongju, Korea

개요

딥러닝 연구가 활발해지면서 SOTA(State of the art)를 갱신하는 새로운 네트워크 구조 혹은 획기적인 아이디어(예를 든다면 GAN)를 제안하고 실험 결과를 비교하는 것으로 논문을 평가하였습니다. 하루가 멀다하고 서로 더 좋은 결과를 제안하는 논문이 쏟아지다보니 언제부터인가 논문에 GitHub 주소를 공유하여 코드도 같이 검증받게 되었습니다.

그런데 여기에서 약간 개인의 경험치에 따라 논문 내용을 읽고 직접 구현하는 분들이 있을 수도 있으며, 아니면 GitHub에 공유된 코드를 잘 사용할 수도 있고, 그게 아니라면 “이거 안 돌아가는데?” 머리에 물음표가 마구 뜨면서 고생하는 경우가 있죠. 주관적인 생각입니다만, 소위 핫한 기관 혹은 연구실에서는 재구현이 가능하도록 환경 설정 방법을 설명해놨고, 그대로 따라하면 큰 문제없이 동작합니다.

연구 결과물 공유 뿐만 아니라 같은 연구를 하는 사이에서도 연구 머신이 다를 경우 설치된 패키지 버전이 달라져 deprecated method를 사용할 수 없어 에러가 날 수 있습니다.

힘들게 연구해서 공개했더니 제대로 소화 못한다고 나무라는 것은 옳지 않다고 생각합니다. 어찌보면 현재처럼 경쟁적인 구도에서는 연구도 세일즈처럼 홍보가 필요하고, 연구를 읽고 사용할 사람들에게 편의성을 제공해준다면 많은 사람들이 더 널리 퍼뜨려주겠죠.

이 글에서는 Docker를 사용해 재구현이 가능한 환경을 만들 수 있는 정보를 제공합니다.

단순히 샘플을 원하시면 TensorFlow PyTorch의 Dockerfile을 참고하세요.

목차

나의 상황 체크하기

  • OS 확인
  • GPU Architecture 확인
  • 가능한 CUDA version 확인

NVIDIA 계열 GPU를 가장 보편적으로 사용합니다. 그렇다면, 리눅스 또는 윈도우의 운영체제여야 합니다. 왜냐하면 맥에서는 NVIDIA GPU를 장착한 제품이 나오지 않기 때문이고, 우회적인 방법을 쓰더라도 비공식 지원이기 때문에 비추천합니다. 가능하다면 리눅스 중에서도 Ubuntu 운영체제를 권장하고, 꼭 윈도우에서 사용해야되는 상황이라면 아래 설정 내용을 참고해주세요.

맥에서 방법이 없는 것은 아닌데 비용도 많이 들고, 무엇보다 정식지원도 아니라 불안정한데다가 GPU와 머신 사이의 정보 교환에서 병목 현상이 일어나 효율성이 떨어집니다.

다음은 사용중인 GPU의 모델을 정확하게 알아야 합니다. Ubuntu의 경우 nvidia-smi 명령어로 GPU 모델을 알아내실 수 있고, 만약 명령어가 동작하지 않는다면 CUDA tool이 설치되어 있지 않은 경우일 수도 있으니 정보 검색해서 설치를 진행해주세요. 아니면 GPU 모니터링 4가지 방법을 참고해주세요. 윈도우 10 이상이라면 아래처럼 시스템 > 정보 > 장치관리자 > 디스플레이 어댑터에서 확인하시면 됩니다.

후훗.. 3090

저는 NVIDIA GeForce RTX 3090 입니다. 그렇다면 이 GPU는 어떤 Architecture를 사용하고 어떤 CUDA Version을 사용해야 할까요? 아래 표를 보면 이해하는데 도움이 될 것입니다.

Architecture Kepler Maxwell Pascal Volta Turing Ampere Hopper
GPUs Tesla K40
K80
GTX 9xx
TitanX
Jetson Nano
Tesla P100
GTX 10xx
TitanXp
Tesla P4
Teslta V100
Titan V
Jetson Xavier
GTX 1660
RTX 20xx
Tesla T4
Quadro RTX
RTX 30xx
RTX Axxx
Not yet
CUDA 5 O
CUDA 6 O O
CUDA 8 O O O
CUDA 9 O O O O
CUDA 10 O O O O O
CUDA 11 O O O O O
CUDA 12
참고: Matching CUDA arch and CUDA gencode for various NVIDIA architectures

신제품 기준으로 Ampere Architecture만 판매하고 있으므로 앞으로의 CUDA 버전은 11 이상을 사용하게 될 것입니다. 그렇지만 이미 연구용으로 사용하고 있는 GPU의 경우 Pascal이나 Volta를 사용하는 경우가 있을 것입니다. Turing의 경우 크립토 크런시 채굴 이슈와 맞물려 연구용으로 많이 보급되지는 않았습니다. 표를 보면 제 경우 선택의 여지가 없이 CUDA 11 버전을 사용해야 합니다.

그렇다면 무조건 CUDA 11을 사용해도 될까요? 이후에 설명할 사용중인 패키지(TensorFlow, PyTorch) 버전에 따라 CUDA 버전을 선택하게 됩니다. 다시 말하면 GPU Architecture ⟷ CUDA 버전 ⟷ Package 버전을 상호 고려 해야합니다.

🔼 목차로 이동

기본 설정 준비하기

이 글은 2021년 말 기준으로, 리눅스 중에서도 Ubuntu, 그리고 Windows 10 또는 11 버전을 대상으로 설정을 준비합니다. 공통적으로 위에서 계속 언급한 GPU에 맞는 DriverDocker, NVIDIA Docker를 설치해야 합니다.

리눅스

그래픽카드 드라이버 설치

인터넷 브라우저가 가능한 상황이라면, Driver 링크를 클릭하면 검색창에서 아래와 같이 나오고 자신의 환경에 맞는 드라이버를 찾아 다운로드합니다.

NVIDIA Drivers
Download a Driver

터미널 환경이라면 Install Nvidia Driver via Command Line 내용을 참고하여 Driver를 설치해줍니다. 링크한 자료는 Ubuntu 20.04 버전이고 영어로 된 자료입니다. 혹시 다른 버전이나 한글자료가 필요하시면 위 내용과 크게 다르지 않는 선의 자료로 검색하여 실행하시면 됩니다. 설치할 Driver 버전은 위의 이미지와 같이 인터넷 브라우저에서 찾은 버전과 가까운 것을 선택하거나 UNIX Driver Archive에서 선택하여 다운로드 받으시면 됩니다. 아래는 터미널 기준으로 명령어만 따로 적어놓은 것입니다.

# Update
sudo apt update

# Search a driver
sudo apt search nvidia-driver

# Pick and install
sudo apt install [driver_name]

# Reboot
sudo reboot

Docker 설치

Install on Ubuntu 내용을 참고해서 설치해주면 됩니다. 아래는 터미널 기준으로 설치하는 명령어만 따로 적어놓은 것입니다.

# Uninstall old versions
sudo apt-get remove docker docker-engine docker.io containerd runc
# Set up the repository
sudo apt-get update
sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
# Register a user to docker group
sudo usermod -aG docker $USER

NVIDIA Docker 설치

NVIDIA Docker 공식 설치 내용중 명령어만 따로 적어놓겠습니다.

# NVIDIA Container Toolkit
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
# Install
sudo apt-get update
sudo apt-get install -y nvidia-docker2
# Restart a Docker
sudo systemctl restart docker

윈도우

Windows에서는 WSL을 이용하여 NVIDIA Docker를 사용하는 방법을 공유할까 합니다. 검색을 할 경우 공식적인 지원으로 CUDA on WSL 내용을 그대로 따라하면 됩니다.

그런데 만약 Windows에서도 GPU를 사용하는 작업이 필요한 경우라면 어떻게 될까요? 2021년 말 기준 제 경험상 Windows용 드라이버가 먼저 설치되지 않았다면 WSL용 드라이버로는 문제가 발생합니다. 그래서 Windows용 드라이버를 우선 설치하고 WSL용 드라이버를 나중에 설치하면 왜인지는 모르겠지만 Windows와 WSL 모두 드라이버를 인식하게 되더군요. 이유를 정확히 모르고 공식 문서에도 나와있지 않다면 불안정성이 존재하는 방법입니다. 따라서 서두에 언급한 것처럼 Ubuntu 환경에서 권장하고 Windows에서는 권장하지 않는 이유가 여기에 있습니다.

이 방법이 불안정하거니와 단점도 존재합니다. 그것은 Docker for Window를 사용할 수 없기 때문에 발생하는 문제점들이라고 볼 수 있습니다. 이 문제점들은 개인 상황마다 다르고 예측하기 어렵기 때문에 이 부분을 감수할 수 있다면 아래 방법을 그대로 따라해주시면 됩니다.

그래픽카드 드라이버 설치

위에 말씀드린대로 Windows용 드라이버를 우선 설치하고, WSL용 드라이버를 설치하면 됩니다.

Docker 설치

이 부분이 공식 문서와 다른 핵심입니다. 핵심부터 말씀드리면 일반적인 Docker for Windows를 사용하면 안됩니다. 현재의 Docker for Windows는 Hiper-V가 아닌 WSL 사용을 권장으로 하고 있습니다. 사용할 Ubuntu for WSL과 다른 가상 공간을 만들어서 Docker를 서비스하게 되죠. 이 점 때문에 WSL에서 Docker를 사용할 경우 속도가 느린 이유입니다. 또한, 제가 구축할 환경에서는 정상적으로 사용할 수가 없죠.

따라서 Docker 설치는 WSL내에서 리눅스 설치와 같은 절차로 해주시기 바랍니다.

# Uninstall old versions
sudo apt-get remove docker docker-engine docker.io containerd runc
# Set up the repository
sudo apt-get update
sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
# Register a user to docker group
sudo usermod -aG docker $USER

그리고 WSL이 시작되면 Docker 엔진이 항상 정지상태에 있으므로 아래의 명령어로 시작해주는 것을 잊지 마세요.

sudo service docker start

NVIDIA Docker 설치

엄밀히 말하면 WSL이라는 가상환경, Ubuntu라는 운영체제에 Docker를 설치했기 때문에 NVIDIA Docker 설치 절차를 그대로 따라하면 됩니다.

# NVIDIA Container Toolkit
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
# Install
sudo apt-get update
sudo apt-get install -y nvidia-docker2
# Restart a Docker
sudo systemctl restart docker

🔼 목차로 이동

내 환경의 각종 버전 확인하기

이제부터 환경을 만들기 위한 버전을 확인해볼 차례입니다. 일반적으로 Ubuntu 환경의 서버에서 연구활동을 하고 있었다는 것을 가정합니다. 따라서 운영체제 Ubuntu의 버전, 설치되어 있는 CUDA의 버전, 마지막으로 Python에서 사용중인 패키지들의 버전을 알아보겠습니다.

버전이란

TensorFlow 2.6.0 혹은 PyTorch 1.9.0 과 같이 패키지 이후에 숫자가 있는 것을 보셨을 겁니다. 아시다시피 이것들은 소프트웨어의 버전을 의미하며 나름의 규칙을 가지고 버전을 변경하고 있습니다.

참고: Software versioning by Wikipedia

위의 그림처럼 일반적으로 주(Major), 부(Minor), 수(patch)로 구분을 하는데요. 몇가지 알고 있으면 좋은 내용은 주버전이 0으로 시작할 경우 초기 개발을 위해 쓰이는 것으로 안정성을 크게 보장하지 않으므로 1.0.0 버전이 정식 배포를 시작한 첫번째 버전으로 생각하면 됩니다. 또한 주버전의 숫자가 더 올라갈 경우 2.x.x가 된다면, 이전의 1.x.x 버전과 호환을 보장하지 않고 기존에 사용하던 API가 동작하지 않는 경우가 많습니다. 그리고, 부버전의 숫자가 변경되는 것은 이전 버전과 호환이 되고, 새로운 기능을 추가하거나 변경 등이 생길 경우를 말합니다. 보다 자세한 내용을 알고 싶으신 분은 유의적 버전(한글)의 설명을 읽어보시길 바랍니다.

나의 운영체제 버전 알아보기

일반적으로 Ubuntu 환경에서 연구를 하고 있다는 것을 가정하였으니 아래 명령어로 운영체제의 버전을 알 수 있습니다.

lsb_release -a

# Distributor ID: Ubuntu
# Description:    Ubuntu 20.04.3 LTS
# Release:        20.04
# Codename:       focal

현재 저는 Ubuntu 20.04.3 버전을 사용하고 있으며 뒤에 명시된 LTS는 Long-Term Service를 의미하고 말 그대로 오랜시간 동안 해당 버전을 유지관리할 계획이라는 것을 의미합니다. 그리고, focal은 Ubuntu 20.04의 코드명으로 정식 명칭은 Focal Fossa이며 2025년 4월까지 유지관리할 계획입니다. 많이 사용중인 다른 LTS 버전 내용은 아래의 표를 참고해주세요. (참고로 16.04 LTS는 공식 업데이트가 작년부로 종료되었습니다.)

Version Code name Release End Support End Life
22.04 LTS Jammy Jellyfish 2022년 4월 21일 2027년 4월 2032년 4월
20.04 LTS Focal Fossa 2020년 4월 23일 2025년 4월 2030년 4월
18.04 LTS Bionic Beaver 2018년 4월 26일 2023년 4월 2028년 4월
16.04 LTS Xenial Xerus 2016년 4월 21일 2021년 4월 2026년 4월
참고: Ubuntu: List of releases

CUDA 버전 확인하기

마찬가지로 Ubuntu 환경에서 아래 명령어로 운영체제의 버전을 알 수 있습니다.

nvidia-smi

# +-----------------------------------------------------------------------------+
# | NVIDIA-SMI 495.53       Driver Version: 497.29       CUDA Version: 11.5     |
# |-------------------------------+----------------------+----------------------+
# | 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. |
# |===============================+======================+======================|
# |   0  NVIDIA GeForce ...  On   | 00000000:01:00.0  On |                  N/A |
# | 30%   34C    P8    22W / 350W |   1528MiB / 24576MiB |     N/A      Default |
# |                               |                      |                  N/A |
# +-------------------------------+----------------------+----------------------+
# 
# +-----------------------------------------------------------------------------+
# | Processes:                                                                  |
# |  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
# |        ID   ID                                                   Usage      |
# |=============================================================================|
# |  No running processes found                                                 |
# +-----------------------------------------------------------------------------+

여기서 저의 CUDA 버전이 11.5 라는 것을 알 수 있고, 이 버전을 기억했다가 도커 파일을 만들때 해당 CUDA 버전에 맞게 환경을 설정해주게 됩니다.

파이썬 패키지 버전 가져오기

내가 사용하는 Python 패키지의 버전 관리를 잘 하신 분이라면 매우 쉬운 과정입니다만 제 경험상 경력 초반에 사용한 환경을 버전관리 없이 지속적으로 사용하는게 대부분입니다. 왜냐하면, 연구에 집중하기 위해 내가 잘 사용하고 있고, 굳이 상위 버전을 쓸 이유가 없다면 안정적인 환경을 사용하기 때문이죠. 개인적으로 안정적인 환경에서 새로운 버전으로 업데이트하는 것은 연구 집중도를 떨어뜨린다고 생각합니다. 그렇지만 앞서 살펴본 바와 같이 GPU Architecture가 달라져 CUDA 버전을 올려야만 하고, 그로 인해 사용하는 딥러닝용 라이브러리가 일정 버전 이상을 사용하는 것이 강요된다면 기존에 사용하던 환경과 달라지므로 이 경우엔 나의 연구환경을 다시 구축해야겠죠.

이제부터 여기서 제시하는 것은 일반적인 내용일뿐 각자의 연구 내용에 맞게 추가적으로 Ubuntu package를 설치하거나 환경 설정 중 발생하는 에러, 충돌을 직접 해결해야합니다.

여기서는 Python에서 가상환경을 사용한다고 가정합니다. 이는 매우 당연한 것으로 같은 연구 주제로 개발하더라도 연구마다 독립적 환경으로 하는 것은 보편화되어있고 실제 사용하는 것도 어렵지 않다고 생각합니다. 가상환경 중에 연구용으로 대표적인 것은 Conda가 있고, 실제 서비스 배포까지 생각한다면 Python에서 기본 제공하는 Virtual Environment가 있습니다.

첫번째로 아래의 명령어로 Python 버전을 확인해주세요.

python -V # or python3 -V

# Python 3.8.12

만약 Python Virtual Environment를 사용한다면, 아래의 명령어로 설치되어 있는 패키지의 목록 및 버전을 텍스트 파일로 만들 수 있습니다.

python -m pip freeze > requirements.txt

Conda를 사용한다면 두가지 방법이 있습니다. 현재 가상환경 이름과 Python 버전, 패키지까지 모두 가져오는 말 그대로 환경을 그대로 파일로 만들거나, 설치되어 있는 패키지 정보만 파일로 만들 수 있습니다. 예전에는 환경 그대로 만드는 방법을 자주 사용했지만 최근 Conda 문서에는 그 방법에 대해 나와있지 않으며 패키지 정보만 파일로 만드는 방법만 안내되어 있습니다.

# 환경 전체를 파일로
conda env export > environment.yaml
# 설치된 패키지 정보만 파일로
conda list --explicit > package-list.txt

이렇게 가져온 패키지와 버전 정보를 다음 단계에서 도커 환경내에서 사용할 예정입니다.

🔼 목차로 이동

연구 가능한 환경 만들기

앞서 알아본 정보를 조합하여 자신의 연구 환경을 도커이미지로 만들 차례입니다. 일반적인 베이스 도커 이미지를 선택하여 쉽게 구성할 수도 있지만 연구실, 팀의 환경과 개인 선호도에 따라 몇가지 설정을 추가로 할 수 있습니다. 여기에서는 다양한 선택지를 블록처럼 몇가지로 나눠서 설명하려고 합니다.

여기에서는 도커 명령어에 대해 설명하지 않습니다. 다만 커스텀으로 도커 이미지를 만들경우 Dockerfile에서 명령어 순서에 따라 도커 이미지 레이어가 쌓이기 때문에 레이어의 순서를 잘 구성한다면 보다 효과적으로 사용할 수 있습니다.

베이스 도커 이미지 선택하기

FROM을 통해 베이스 도커 이미지를 가져다가 사용할 수 있고, 크게 두 가지의 선택지로 나눠집니다.

첫째, 미리 잘 구성된 도커 이미지를 가져다가 사용한다. 이 경우 TensorFlowPyTorch가 각각 운영하는 Docker Hub를 사용할 수도 있고, NVIDIA에서 운영하는 NGC(NVIDIA GPU Cloud)에서 운영하는 TensorFlowPyTorch 이미지를 사용할 수 있습니다. 각 라이브러리에서 운영하는 것은 버전업이 될때마다 업로드하고, NVIDIA에서 운영하는 것은 매월 정기적으로 업데이트를 진행하는데 이때 라이브러리의 버전업뿐만 아니라 별도의 최적화를 진행한 버전을 올리고 있습니다.

FROM nvcr.io/nvidia/tensorflow:22.01-tf2-py3
FROM nvcr.io/nvidia/pytorch:22.01-py3
FROM tensorflow/tensorflow:2.7.1-gpu
FROM pytorch/pytorch:1.10.0-cuda11.3-cudnn8-devel

장점은 CUDA와 라이브러리버전을 잘 맞춰놓아 오류없이 잘 동작한다는 점입니다. 단점은 Root 권한으로 되어 있어 사용하는 환경에 따라 불편할 수도 있습니다. 왜냐하면 관습적으로 도커를 실행할 때 코드가 있는 디렉토리와 볼륨을 연결하여 사용하곤 하는데 이때 도커 내부에서 생성한 파일의 경우 소유자가 Root가 되어 불편함을 초래하거나 원할 때 수정하기 어렵죠.

둘째, CUDA 및 cuDNN이 설치된 이미지를 사용한다. 위와 같이 Root 권한의 사용이 곤란하거나 환경에 따라 추가 구성을 해야하는 상황이 발생할 수도 있는데 이때 CUDA 베이스 이미지 위에 필요한 설정을 추가하면 됩니다. 어디서 어떻게 시작해야될지 모를 경우 TensorFlowPyTorch의 Dockerfile 구성을 보면서 자신에게 맞는 것을 차용하는 것을 추천합니다.

FROM nvidia/cuda:11.3.1-cudnn8-devel-ubuntu20.04

장점은 경우에 따라 TensorFlow, PyTorch가 모두 설치된 것을 사용할 수 있고, 필요없는 기능을 제외하여 슬림하게 구성할 수 있습니다. 그렇지만 구성하는데 시간이 걸릴 수 있으며, 잘 구성된 예시를 찾기도 힘듭니다.

제 경우 후자에 속하며, 처음에는 기호에 맞는 도커 이미지가 없어 만들었던 JupyterLab이라는 것을 시작으로 연구실에서 Dockerfile을 더 고민해본 후 이 글을 작성하는 것으로 지금 예시로 새로운 도커 이미지 예시를 작성하였습니다.

팁을 드리자면, DockerHub에서 Tag 항목을 보면 자신의 환경에 맞는 것을 찾는 것이 중요합니다. CUDA 이미지의 경우 CUDA 버전 cuda:11.3.1, cuDNN 버전 cudnn8, Flavor devel, OS 버전 ubuntu20.04의 정보를 얻을 수 있고, PyTorch도 정보를 얻을 수 있습니다. 그에 반면 TensorFlow는 라이브러리 버전 정도만 알 수 있고, NGC는 그마저도 알 수 없어서 내부 문서를 읽어봐야 합니다.

Flavor는 base, runtime, devel이 있으며 순서대로 base가 가장 기본적인 이미지고 base를 바탕으로 runtime을 만들며, runtime을 바탕으로 devel을 구성합니다. 자세한 내용은 여기를 읽어보세요.

우분투 패키지 설치하기

베이스 이미지를 FROM으로 받아온 후 대체로 RUN apt ... 명령어를 사용하여 Ubuntu의 패키지를 설치합니다. 크게 자신의 연구 분야가 Computer Vision, NLP, Audio에 따라 추가로 설치하는 것이 필요하니 꼭 확인해보시기 바랍니다. (참고로 Ubuntu에 설치된 패키지 목록을 확인하려면 apt list --installed 명령어로 확인해보시면 됩니다.)

그리고 중요한 것이 관습적으로 Ubuntu에서 tzdata 설정이 필요해 에러가 발생하거나 이미지 빌드 중 멈추게 되는데 이를 방지하기 위해 환경 변수를 추가합니다. 전체적인 Ubuntu 패키지 설정 단계는 아래를 참고해주세요.

ENV DEBIAN_FRONTEND=noninteractive

SHELL ["/bin/bash", "-c"]
RUN apt-get update && apt-get install -y --no-install-recommends \
    ca-certificates \
    ccache \
    cmake \
    curl \
    git \
    libfreetype6-dev \
    libhdf5-serial-dev \
    libzmq3-dev \
    libjpeg-dev \
    libpng-dev \
    libsm6 \
    libxext6 \
    libxrender-dev \
    pkg-config \
    software-properties-common \
    ssh \
    sudo \
    unzip \
    wget
RUN rm -rf /var/lib/apt/lists/*

특히 커스텀으로 구성할 경우 TensorFlow로 profiling을 할 때 아래의 내용을 꼭 추가해주시길 바랍니다.

ENV LD_LIBRARY_PATH /usr/local/cuda-${CUDA}/targets/x86_64-linux/lib:/usr/local/cuda/extras/CUPTI/lib64:/usr/local/cuda/lib64:$LD_LIBRARY_PATH
RUN ln -s /usr/local/cuda/lib64/stubs/libcuda.so /usr/local/cuda/lib64/stubs/libcuda.so.1 && \
    echo "/usr/local/cuda/lib64/stubs" > /etc/ld.so.conf.d/z-cuda-stubs.conf && \
    ldconfig

가상환경 Conda 설치하기

NGC, TensorFlow의 경우 가상환경이 아닌 일반 Python을 설치하여 사용하고, PyTorch에서는 Conda를 설치하고 있습니다. Conda가 Python 패키지를 가상환경으로 잘 구분해주기도 하지만 편리하게 설치가능하게 패키지가 잘 구성되어 있을 뿐만 아니라 특정 Python 버전을 설치하려면 Conda만큼 쉬운 방법도 없습니다.

Conda는 Miniconda를 설치할 것이며 반드시 Root 권한으로 설치해줍니다.

ENV LANG C.UTF-8
RUN curl -o /tmp/miniconda.sh -sSL http://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh && \
    chmod +x /tmp/miniconda.sh && \
    bash /tmp/miniconda.sh -bfp /usr/local && \
    rm /tmp/miniconda.sh
RUN conda update -y conda

가상 환경을 설치하였다면 원하는 가상환경을 만들어줘야 합니다. 여기서 주의하실 점은 Root로 사용하실 경우 아래의 명령어를 바로 붙여서 해주시면 되지만 별도의 일반 User를 사용한다면 User를 생성하고 User로 변경한 후 아래의 명령어를 실행해주세요.

ARG PYTHON_VERSION=
ARG CONDA_ENV_NAME=

RUN conda create -n $CONDA_ENV_NAME python=$PYTHON_VERSION
ENV PATH /usr/local/envs/$CONDA_ENV_NAME/bin:$PATH
RUN echo "source activate ${CONDA_ENV_NAME}" >> ~/.bashrc

SSH 접속을 하려면

Docker 이미지를 만들고, 실행할 때 간혹 ssh 접속이 필요한 경우가 있습니다. 주로 같은 서버내에서는 docker exec로 접속을 해서 터미널처럼 사용할 수 있지만 vscode를 사용하고, 서버에 있는 docker에 접속하기 위해서는 내 로컬 머신에서는 바로 접속하는 방법이 없습니다. 그래서 Docker 내에 sshd를 실행시키기 위한 준비를 해줘야 하므로 아래처럼 해주시고, Docker를 실행시키면 별도로 sshd를 실행시켜줘야 합니다.

RUN echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config && \
    echo "PermitEmptyPasswords yes" >> /etc/ssh/sshd_config && \
    echo "UsePAM no" >> /etc/ssh/sshd_config

관리자 권한이 아닌 일반 유저 만들기

일반적으로 Root 권한으로 실행하고 볼륨이 연결되어 있는 경우 불편한 점이 한두가지가 아닌데 예를들면 notebook 파일을 실행하고 저장할 때 문제가 발생하고, artifacts를 읽거나 수정할 때 권한 문제 때문에 sudo 로 실행해야만 하죠. 그래서 저는 서버의 제 User 이름과 User ID 번호를 그대로 도커에 넣어 같은 User를 만들어주고, 해당 유저에 sudo 권한도 부여해줍니다. 따라서 Ubuntu 패키지에서 sudo를 설치해주는 것이 필수이고, Dockerfile의 ARG를 이용하여 외부에서 값을 입력할 수 있게 해줍니다.

ARG UID=
ARG USER_NAME=

RUN adduser $USER_NAME -u $UID --quiet --gecos "" --disabled-password && \
    echo "$USER_NAME ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/$USER_NAME && \
    chmod 0440 /etc/sudoers.d/$USER_NAME

아래의 명령어로 Docker 이미지를 빌드할 때 Dockerfile 내의 ARG에 로컬의 UID와 User 이름을 전달해줍니다.

docker build -t custom/mydocker:latest \
    --build-arg UID=$UID \
    --buidd-arg USER_NAME=$USER \
    -f Dockerfile \
    .

그리고 Docker 빌드 중 User를 변경하고 싶다면 아래의 명령어처럼 해주시길 바랍니다.

USER $USER_NAME
SHELL ["/bin/bash", "-c"]

난 Jupyter Notebook이 꼭 필요해

Vim과 tmux로 코딩하는 간지 넘치는 분들도 있지만 저는 vscode 또는 Jupyter notebook을 주로 사용합니다. 특별히 Jupyter Lab은 디렉토리 경로 내의 파일도 볼 수 있고, 내부 terminal도 실행시킬 수 있으므로 매우 추천합니다. 물론 이 패키지 설치는 conda가 설치되어 있어야 하고, User를 생성하였다면 User를 변경한 후 실행해야 합니다.

RUN source activate ${CONDA_ENV_NAME} && \
    conda install -c conda-forge jupyterlab && \
    jupyter serverextension enable --py jupyterlab --sys-prefix

파이썬 패키지 설치하기

이제 Python 패키지를 설치하는 일만 남았네요. 위에서 Python 패키지 버전을 파일로 저장했던 것을 이제 사용할 차례입니다. pip를 사용하여 설치한다면 아래처럼 해주세요.

RUN source activate ${CONDA_ENV_NAME} && \
    python -m pip install --no-cache-dir -r requirements.txt

Conda를 사용해 package-list.txt 혹은 environment.yaml을 사용한다면 어떻게 해야 할까요? 헷갈릴 수 있지만 위에서 설명한 내용 중 conda 환경을 생성하는 명령어가 있습니다. Root 권한일 때와 일반 User일때 실행 순서가 다르다고 말씀드렸고, 해당 명령어 부분을 아래처럼 변경해주시면 됩니다.

ARG CONDA_ENV_NAME=

# txt 파일 사용
RUN conda create -n $CONDA_ENV_NAME --file package-list.txt
# yaml 파일 사용, CONDA_ENV_NAME과 일치하는지 확인 필수
RUN conda env create -f environment.yaml

ENV PATH /usr/local/envs/$CONDA_ENV_NAME/bin:$PATH
RUN echo "source activate ${CONDA_ENV_NAME}" >> ~/.bashrc

🔼 목차로 이동

재구현 환경 공유하기

내 연구 환경을 만들었으니 이제 실행해보고 누구나 사용해서 재구현이 가능하도록 공유하는 일만 남았습니다. 공유하는 방법은 크게 두 가지로 나눌 수 있는데 첫번째는 GitHub에 공유하는 것에 방법에 따라 Dockerfile만 올려서 사용자가 직접 Docker 이미지를 만드는 방법과 두번째로 Docker 이미지를 Docker Hub에 올려 공유하는 방법이 있습니다.

.dockerignore 파일 작성하기

Docker에서도 git처럼 .dockerignore라는 파일을 작성하여 사용합니다. 일반적으로 .gitignore 내용을 그대로 복사한 후에 체크포인트 등과 같은 부수적인 artifacts 들을 넣어주어 이미지를 빌드할 때 불필요한 내용을 포함하여 빌드하지 않도록 해줍니다. 이는 빌드 속도를 높여주기 위한 방법일 뿐 필수 사항은 아닙니다.

도커 이미지 빌드

Docker 이미지 빌드는 위에서 언급한 바처럼 기본적인 명령어를 넣어주시면 되는데 코드 구성 구조에 따라 Docker 라는 디렉토리를 따로 만들어 Dockerfile을 별도로 관리한다면 상위 단계에서 실행해주는 것을 잊지 말아야 합니다.

docker build -t custom/mydocker:latest \
    --build-arg UID=$UID \
    --buidd-arg USER_NAME=$USER \
    --buidd-arg USER_NAME=$USER \
    --buidd-arg UBUNTU_VERSION=20.04 \
    --buidd-arg CUDA_VERSION=11.3.1 \
    --buidd-arg CUDA=11.3 \  # 중요: CUDA_VERSION 의 주, 부버전만 사용
    --buidd-arg CUDNN=$USER \
    --buidd-arg PYTHON_VERSION=3.8 \
    --buidd-arg CONDA_ENV_NAME=research \
    -f Dockerfile \
    .

도커 컨테이너 실행

일반적으로 Docker 컨테이너 실행을 할 때 연구 코드 디렉토리 상위에 있다고 가정합니다. 왜냐하면 Volume으로 연결하여 코드 및 체크포인트를 Docker 내부에서 사용할 수 있도록 설정할 수 있기 때문입니다. 아래의 명령어에서 가장 중요한 것은 --gpus 옵션이라고 할 수 있습니다. 글자에서 느껴지듯이 NVIDIA Docker를 설치하고 runtime으로 연결해주는 것인데요. 예전에는 nvidia-docker로 실행하거나 --runtime=nvidia와 같은 것을 사용했지만 최근에는 문서에 나온 것처럼 --gpus 옵션을 사용하는 것을 권장합니다. 옵션을 all로 설정할 경우 서버의 모든 GPU를 연결할 수 있고, GPU 번호를 입력하면 지정한 GPU만 사용할 수 있기도 합니다. (예: --gpus 0,1)

docker run -it --name $SOMETHING \
    --gpus all \
    -p 8888:8888 \  # 일반적인 jupyter notebook port 번호
    -v $PWD:/workspace \  # container 내부 경로 마음껏 설정
    -w /workspace \
    custom/mydocker:latest \
    /bin/bash  # jupyter lab 바로 실행 가능

Dockerfile 공유하기

앞서 말한 것처럼 GitHub에 Dockerfile을 함께 올려서 사용자가 직접 Docker 이미지를 생성하여 사용하는 방법이 있습니다. 이 경우 이미지 빌드, 컨테이터 실행 방법을 설명하고 예시 명령어를 같이 올려주는 것이 좋고, 이왕이면 적어도 Docker를 사용해본 경험이 있는 사용자가 쉽게 따라할 수 있도록 설명해주면 좋습니다.

도커 이미지 업로드하기

Docker 이미지를 Docker Hub에 직접 업로드해서 사용자가 docker pull로 직접 다운받아 사용하는 방법도 있습니다. 동일한 환경을 직접 다운로드 받기 때문에 사용자가 직접 Docker 이미지를 만드는 것과 다르게 완전 동일한 환경을 사용할 수 있지만 User를 사용하고 있다면 불편한 상황이 발생할 수 있습니다. Volume을 연결해서 사용할 때 Docker내의 사용자 UID가 서버의 UID가 겹칠 경우 엉뚱한 User 이름으로 파일이 작성되는 등 충돌 문제가 있습니다. 또한, 깨끗하게 이미지를 빌드한 경우라면 모르겠지만 컨테이너 자체를 스냅샷 찍는 docker commit을 사용한 이미지를 업로드할 경우 의도치않게 민감한 정보를 history에 남길 수도 있으며, 중요한 secret key를 남길 수도 있기 때문에 추천하지 않습니다.

🔼 목차로 이동

마무리

이런것까지 신경쓰고 해야하나 싶지만 개인이 아닌 팀단위 또는 조직단위에서 서버로 연구, 개발할 때 가상환경 또는 인프라 구성을 해서 사용하는게 일반적입니다. 일반적으로 Docker를 사용하는 경우도 많지만 Kubernetes와 같은 오케스트레이션 환경에서 연구를 하는 경우도 많으니까요. 적어도 내 연구 환경을 구성해놓는다면, 내 연구를 공유할 때 플러스가 되는 것뿐만 아니라 다른 물리적 환경의 변화, 예를들면 서버를 이동하거나 공동 연구하는 팀원에게 공유 또는 다른 곳으로의 이동, 에도 내 연구를 이어서 할 수 있다는 장점이 있습니다.

이 글에서는 Ubuntu를 기준으로만 설명하고자 하였으나 Windows 환경에서도 실행하고자 하는 니즈를 보게 되어 제가 직접 해본 후 공유드립니다 (물론, 시간이 지나면 Windows 내용은 변화가 있을 수도 있습니다).

마지막으로 적어도 제 글에서 아이디어를 얻어 본인만의 연구 환경을 만드는데 도움이 되길 바라며, 이번 글을 적으면서 작성한 TensorFlow PyTorch의 Dockerfile도 공유합니다. Gist로 공개한 것으로 별다른 라이선스는 없으며 공유 및 수정을 자유롭게 하셔도 됩니다. 또는 테스트로 DockerHub에도 올려놨으니 사용하셔도 무방합니다.

🔼 목차로 이동

참고