포스팅 제목은 Docker 에서 GPU 사용하기 입니다만, 사실은 포스팅을 하게 된 계기는 다음과 같습니다.

"Unity 의 3D 렌더링을 서버에서 하기"



Docker 에서 Hardware Accelerated OpenGL 사용하기



앞서 말씀드린데로, 제가 처한(?) 상황은 다음과 같습니다.

Unity 로 개발된 3D 프로젝트가 있습니다. (Standalone)

이 녀석을 서버로 만들 예정입니다.

쉽게 말해, 클라이언트의 요청에 따라 어떤 3D 렌더링 결과물을 뽑아내는 서버입니다.


이게 생각보다 쉽지가 않습니다. Unity 가 그것을 더 어렵게 만들기 때문입니다.





Unity 의 실행




유니티는 기본적으로 Display 가 있어야 렌더링이 됩니다.

이것 없이 (headless)로 작업하려고 하면 까다롭습니다. 어쩌면 불가능 할지도 모릅니다.

Unity  에서 지원해주는 headless 로 빌드를 하게되면, 정말로 rendering 관련된 작업을 아예 하지 않기 때문입니다.

그래서 headless 로 빌드를 하면 안됩니다.

단, batchmode 로 실행하는 것은 무관합니다.(어차피 화면의 표시 유무이므로 무의미 하긴 합니다)





가상의 Display 를 만들어 보자




어쨌든 Display 가 있어야 합니다.

이를 해주는 유틸이 있으니, 이름하야! 열파참! Xvfb 라는 놈입니다.

말 그대로 가상 Display 를 만들어 줍니다.

그러나~

이는 2D X Server 로서 Display 역할만 할뿐, 3D X Server 랑은 무관합니다.






3D X Server를 만들어 보자




3D X Server 유틸이 있었으니, 그것은 VirtualGL 입니다.

이놈과 앞서 말씀드린 Xvfb 를 이용하면 다음과 같은 구조로 3D 렌더링을 하드웨어 도움을 받아 가능하게 됩니다.


3D X Server -----(3D 를 2D로 렌더링)----> 2D X Server -----(2D 를 Display) ----> 이미지 결과물


아 물론 구조는 간단하죠.

그런데 처음 말씀드린데로, 목표는 서버화 입니다.

서버를 graphical 모드로 돌릴것이 아니라, multi-user 모드로 돌려야 합니다.


Ubuntu 의 X 서버(Descktop)환경이 아니라, 터미널로 서버를 대량 돌려야 한다는 이야깁니다.

결국 최종적으로는 Docker 를 이용해서 돌릴것이라는 말이죠.


이를 가지고 여러가지 실험을 해 보았습니다.



먼저 아래처럼 기본적인 Ubuntu 설치와 Xorg 그리고 Virtual GL, Xvfb 를 설치합니다.


# System Update

sudo apt-get update

sudo apt-get upgrade

sudo apt-get dist-upgrade



# Common Programs

sudo apt-get -y install vim net-tools wget xvfb openssh-server

sudo systemctl enable sshd

sudo systemctl start sshd



# Install Graphics Driver

## set to multi-user mode

sudo apt-get remove --purge nvidia-*

sudo add-apt-repository ppa:graphics-drivers/ppa

sudo apt-get update

sudo apt-get install nvidia-driver-410

## return to graphical mode

## sudo apt install cuda-10-0

sudo apt-get install nvidia-settings



# Install & Run Xorg

sudo apt-get install -y xserver-xorg-core libxv1 x11-xserver-utils

sudo /usr/lib/xorg/Xorg vt1 -noreset -keeptty -verbose 3 &



# Install virtual GL

wget -O virtualgl_2.6.1_amd64.deb https://downloads.sourceforge.net/project/virtualgl/2.6.1/virtualgl_2.6.1_amd64.deb?r=https%3A%2F%2Fsourceforge.net%2Fprojects%2Fvirtualgl%2Ffiles%2F2.6.1%2Fvirtualgl_2.6.1_amd64.deb%2Fdownload&ts=1548987112

sudo dpkg -i virtualgl_2.6.1_amd64.deb

sudo printf "1\nn\nn\nn\nx\n" | /opt/VirtualGL/bin/vglserver_config



Xvfb 에 대한 간단한 실행방법입니다.


Xvfb :99 -screen 0 1024x768x24 +extension GLX +render -noreset &

DISPLAY=:99.0


# or

Xvfb :1 -screen 0 1920x1080x24:32 &


# or

xvfb-run --auto-servernum --server-args='-screen 0 1920x1080x24:32' ./unity.x86_64 -batchmode



그리고 이건 VirtualGL 사용법입니다.


vglrun ./linux.x86_64


아래는 각종 실험에 대한 결과입니다.




Graphical Mode 에서 실험 (X server 실행 중)


[Desktop 환경에서]

1. Xvfb 만 가지고 실행할 경우, OpenGL core 가 없어서 unity 실행이 안됨

2. vglrun 과 Xvfb 를 가지고 실행할 경우, 하드웨어(nvidia) 가속을 받으며 실행 됨


[SSH 로 외부에서 접속]

1. Xvfg 만 가지고 실행할 경우, 디스플레이(Display)가 없어서 Unity 실행 안됨

2. vglrun 과 xvfb 를 가지고 실행할 경우, 디스플레이(Display)가 없어서 Unity 실행 안됨

3. 아래처럼 실행할 경우, 하드웨어(nvidia) 가속을 받으며 실행됨

xvfb-run --auto-servernum --server-args='-screen 0 1920x1080x24:32' vglrun ./linux.x86_64 -batchmode

: 사실 2번의 경우 DISPLAY 포트를 제대로 export 안해서 실행 안된걸로 예상됨(3번이 되면 2번도 되야 맞으니까)





Multi-User Mode 에서 실험 (X server 실행 안한 상태)


[SSH 로 외부에서 접속]

1. 아래처럼 실행할 경우, Display 가 없어서 Unity 실행 안됨

xvfb-run --auto-servernum --server-args='-screen 0 1920x1080x24:32' vglrun ./unity.x86_64 -batchmode


2. xvfb 를 먼저 실행하고, vglrun 을 이용해서 unity 실행 안됨.

3. xvfb 만 가지고 실행할 경우, 디스플레이(display) 없어서 실행 안됨

4. 아래처럼 X 를 설치하고, xvfb 와 vglrun 을 이용하면 하드웨어(nvidia) 가속을 받으며 실행 됨

sudo apt-get install xorg

# sudo /usr/lib/xorg/Xorg vt1 -displayfd 0 -auth /run/user/1000/gdm/Xauthority -background none -noreset -keeptty -verbose 3 &

sudo /usr/lib/xorg/Xorg vt1 -noreset -keeptty -verbose 3 &


xvfb-run --auto-servernum --server-args='-screen 0 1920x1080x24:32' vglrun ./linux.x86_64 -batchmode





Docker 환경에서의 실험 (Host 에 X Server 실행된 상태)


[Docker 의 컨테이너 진입 후]

docker run --runtime=nvidia -v /home/crystalcube/Downloads/ScreenShot:/home -it --privileged nvidia/cuda:9.0-base /bin/bash


1. 그냥 unity 실행하면 모니터 없어서 안됨

2. 아래처럼 실행하면 가상 Graphics Driver 로 실행됨(하드웨어 가속 안됨)

xvfb-run --auto-servernum --server-args='-screen 0 1920x1080x24:32' ./unity.x86_64 -batchmode

3. 아래처럼 실행하면, 디스플레이(Display) 없어서 실행 안됨

xvfb-run --auto-servernum --server-args='-screen 0 1920x1080x24:32' vglrun ./linux.x86_64 -batchmode





Docker 환경에서의 실험 (Host 에 X Server 실행된 상태)


[Docker 의 컨테이너 진입 후]

docker run --runtime=nvidia -v /home/crystalcube/Downloads/ScreenShot:/home -it --privileged nvidia/cuda:9.0-base /bin/bash


1. 아래처럼 실행하면, 가상 Graphics Driver 로 실행됨(하드웨어 가속 안됨)

xvfb-run --auto-servernum --server-args='-screen 0 1920x1080x24:32' ./linux.x86_64 -batchmode




번외


XSOCK=/tmp/.X11-unix

XAUTH=/tmp/.docker.xauth

xauth nlist :0 | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -

docker run --runtime=nvidia -v /home/crystalcube/Downloads/ScreenShot:/home -it --privileged -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH -e XAUTHORITY=$XAUTH nvidia/cuda:9.0-base /bin/bash






다 때려 치우자. 새로운 접근을 해보자.




일단 실험 결과를 봤을때, 정상적으로 nvidia driver 의 하드웨어 가속을 받으며, 원하는 결과를 얻어오는 경우는 세가지 입니다.

그 중 두 가지가 graphics 모드로 동작하고 있을 경우라서 패스.

남은 한 가지 방법을 이용해서 docker 에서 사용해 보려 시도함.

그러나~ 잘 안됨.


그래서 처음으로 돌아가서 아래 접근법을 시도.

요약하자면, 난 Docker 를 돌릴거고, 이 컨테이너가 3D 가속을 받는 방법을 찾아봄



0. multi-user 모드로 부팅

1. host 에 X 서버를 실행함

sudo /usr/lib/xorg/Xorg vt1 -noreset -keeptty -verbose 3 &


2. 아래 git 를 clone 함

https://github.com/didzis/nvidia-xorg-virtualgl-docker


3. git 설명대로 설치

4. 아래처럼 실행

./run.sh -v /home/crystalcube/Downloads/ScreenShot:/home -it --privileged nvidia-docker vglrun /home/unity.x86_64 -batchmode

# or

./run.sh -v /home/crystalcube/Downloads/ScreenShot:/home -v /tmp/ScreenShot:/tmp/ScreenShot -it --privileged nvidia-docker vglrun /home/unity.x86_64 -batchmode



결과는 잘 실행됨.

근데 문제는 효율적이지 않음


X 서버를 계속 띄워야 하는 구조라, 메모리 낭비도 심하고 구림





간단하게 가보자



이런저런 방법 연구 끝에 간단한 방법으로 해결했습니다.

이것이 최종 방법입니다.


구조는 Host 에 X server 를 하나 띄웁니다.

Docker 컨테이너에서는 이것을 사용합니다.


1. Host 에 X 서버를 실행합니다.

sudo apt-get install -y xserver-xorg-core libxv1 x11-xserver-utils

sudo /usr/lib/xorg/Xorg vt1 -noreset -keeptty -verbose 3 &

2. xhost +

3. Host 에 설치된 동일한 버전의 NVIDIA-DRIVER 를 준비합니다.

4. ubuntu:16.04 이미지를 기반으로 시작합니다.

5. Host 에 설치된 동일한 버전의 NVIDIA-DRIVER 를 설치합니다.

6. 다음과 같이 실행합니다.

docker run --runtime=nvidia --privileged -e "DISPLAY=unix:0.0" -it -v="/tmp/.X11-unix:/tmp/.X11-unix:rw" -v="/home/crystalcube/Downloads/ScreenShot:/tmp" ubuntu_with_nvidia_driver /tmp/unity.x86_64



Dockerfile

FROM ubuntu:16.04 
MAINTAINER sw0826.kim@snowcorp.com 

ENV NVIDIA_DRIVER=410.79

RUN apt-get update 
RUN apt-get install -yq --no-install-recommends mesa-utils
RUN wget -O NVIDIA-Linux-x86_64-${NVIDIA_DRIVER}.run --no-check-certificate http://us.download.nvidia.com/tesla/${NVIDIA_DRIVER}/NVIDIA-Linux-x86_64-${NVIDIA_DRIVER}.run
RUN chmod +x NVIDIA-Linux-x86_64-${NVIDIA_DRIVER}.run
RUN sh ./NVIDIA-Linux-x86_64-${NVIDIA_DRIVER}.run --no-questions --accept-license --no-precompiled-interface --ui=none -a -N --no-kernel-module
RUN rm NVIDIA-Linux-x86_64-${NVIDIA_DRIVER}.run





HOST 세팅용 스크립트



# apt Update

apt-get update

apt-get upgrade

apt-get dist-upgrade


# Install utils

apt-get -y install vim net-tools wget



# Install Nvidia-Driver

[Geforece Series]

apt-get remove --purge nvidia-*

# apt install software-properties-common

add-apt-repository --yes ppa:graphics-drivers/ppa

apt-get update

apt-get install nvidia-driver-410

apt-get install nvidia-settings



[Tesla Series]

wget -O nvidia-driver.deb http://us.download.nvidia.com/tesla/410.79/nvidia-diag-driver-local-repo-ubuntu1604-410.79_1.0-1_amd64.deb

dpkg -i nvidia-driver.deb 

apt-get update

apt-get -y install cuda-drivers


[Tesla Series #2]

apt-get remove --purge nvidia-*

apt-get install -y software-properties-common

add-apt-repository --yes ppa:graphics-drivers/ppa

apt-get update

apt-get install -y nvidia-410

apt-get install -y nvidia-settings



# Install Docker & Nvidia Docker

https://docs.docker.com/install/linux/docker-ce/ubuntu/

https://github.com/NVIDIA/nvidia-docker



# Install & Start X Server

apt-get install -y xserver-xorg-core libxv1 x11-xserver-utils

/usr/lib/xorg/Xorg vt1 -noreset -keeptty -verbose 3 &



# Run docker image

xhost +

docker run --runtime=nvidia --privileged -e "DISPLAY=unix:0.0" -it -v="/tmp/.X11-unix:/tmp/.X11-unix:rw" [dockerImage] /bin/bash







추가정보



Nvidia Tesla P100 시리즈는 OpenGL 을 정식적으로 지원하지 않음.

=> 이것 때문에 개삽질 했네...


OpenGL Driver 를 공식적으로 지원하는 GPU 목록

https://developer.nvidia.com/opengl-driver


그러나~ 방법은 있다고 하는데, NV Grid 5.1 를 포함한 드라이버가 필요하다고 함

그런데 이게 유료 드라이버.

그리고 그렇게 쑤셔 넣고 돌려봤자 M40 보다 훨씬 느리다고 함.

또한 제약도 많음


관련하여 참고할 자료

https://devblogs.nvidia.com/hpc-visualization-nvidia-tesla-gpus/


결론은 Tesla P100 은 포기하는게 빠르다.




이상입니다 :)