Docker 入門筆記&使用心得 提到,我們實驗室是有自己的 server,但過去都沒有特別好好整合,常常讓實驗沒辦法好好進行,於是就想了一個方案,設法解決這些問題!

大學實驗室的問題

通常資工所,每個實驗室會用經費自己買 GPU 組 Server,因為用雲端運算平台跑實驗太貴了,自己組 Server 的好處就是「便宜」,但問題就是要有學生去管 Server。

對於團體

例如,我們實驗室有很多台主機,每個實驗室成員都可以用這些主機做實驗,除了協調大家 GPU 使用情形外,最大的問題就是「環境」會互相干擾。在早期,每個成員都會有 sudo 的權限,方便大家安裝一些軟體:

  • 在多人共用同一臺電腦時,CUDA 版本或 apt install 的東西,常常影響到其他人
  • 如果不給大家 root 權限,就必須每次麻煩管理員幫忙安裝或執行指令

對於個人

  • 在做研究時,有些論文會把原始碼釋出,如果要跑這些不同模型的程式碼,環境常常很不一樣
  • 如果一台電腦被佔用,就要搬動程式碼到另一臺機器

解決方案

使用 Docker!

  • 只需要安裝 nvidia-driver 和 Docker
  • 使用 Docker 只需要給每個使用者執行 docker 的權限(docker group),或甚至由管理員為每個使用者開 Container再 ssh 進那個 Container(國網中心的做法),讓他們在各自的 container 裡面,要做什麼就做什麼,就算是 sudo rm -rf / 也不會影響到別人
  • 使用 Docker 對效能沒有什麼損失,也可以用 GPU
  • 可以依據不同的實驗,快速建立環境
  • 可以隨時把環境搬到其他電腦

缺點:

  • 要會使用 docker 才能靈活運用,但可以透過管理員協助
  • Container 沒辦法 mount /, 意思就是即便在 container 裡像是 apt install 安裝的東西還是在 Container 關掉之後消失。
    • 解法1:在 build Dockerfile 的時候就把所有會用到的東西寫好,或是每次都要重新 build
    • 解法2:每次都要 docker commit 讓狀態保留

個人使用的經驗

在推廣給其他同學之前,我自己先使用了一段時間:

  • 基本上我自己先做了一個 Image,安裝了一些必須的東西(gittmuxhtop等等)
  • 在 HOST 我有一個資料夾 hank/docker-home 會 mount 到 Container 的家目錄 /root,所有檔案都在 /root 資料夾下面運作
  • 最大的問題是(但其實只有初期會發生),變動到根目錄的操作都會在 Container 關掉之後被還原。當我要動到根目錄(簡而言之就是要執行 sudo 指令的事),例如:apt install 的時候,就要考慮:
    • a. 寫在 Dockerfile
    • b. docker commit,要搬動的話就把 Image push 到 DockerHub 上。(我是比較常這麼做,也比較方便)

Docker 是被設計為把「環境建好」後,就能快速部屬到不同機器的工具。在 Container 中還要去安裝其他東西,這種把他當作 VM 的使用方法算是有點邪惡的作法,但除了在初期比較有變動,其實之後使用上都算很方便了。

教學

Installation

  1. Install Docker & Docker-compose
  2. Install nvidia toolkit

Base Image

Nvidia 官方的 nvidia/cuda Repo,只需要在 Host computer 安裝好 nvidia driver 就可以執行了。

Note: 必須選擇 devel 的版本

Dockerfile

Dockerfile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
FROM nvidia/cuda:10.2-devel-ubuntu18.04
WORKDIR /root

## set timezone
ENV TZ=Asia/Taipei
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt-get update
RUN apt-get install -y apt-utils


## install some useful tools
RUN apt-get install -y git vim wget curl zsh tmux zip htop



## install pyenv dependencies
RUN apt-get install -y --no-install-recommends make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev
RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash

COPY init.sh /src/init.sh

Build Dockerfile

建立名稱為 lab 的 Image。

1
docker build -t lab - < Dockerfile

就會得到一個包含以下的 Image 了。

  • 一些常用到的軟體
  • CUDA 版本為 10.2 的 Ubuntu 18.04

Initial script

在家目錄安裝

  • zsh
  • pyenv & python 3.8
  • jupyter
1
docker run -v $(pwd)/docker-home:/root lab sh /src/init.sh
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
## /src/init.sh

## install zsh
printf "Y\n" | sh -c "$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)"

## install pyenv
curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash

/root/.pyenv/bin/pyenv install 3.8.5
/root/.pyenv/bin/pyenv virtualenv 3.8.5 lab
/root/.pyenv/bin/pyenv global lab

echo 'export PATH="/root/.pyenv/bin:$PATH"\neval "$(pyenv init -)"\neval "$(pyenv virtualenv-init -)"\nexport PYENV_VIRTUALENV_DISABLE_PROMPT=1' >> .zshrc

## install jupyter
/root/.pyenv/versions/3.8.5/envs/lab/bin/python -m pip install --upgrade pip
/root/.pyenv/versions/3.8.5/envs/lab/bin/python -m pip install jupyterlab

mkdir -p /root/.jupyter
echo "c.ServerApp.ip = '0.0.0.0'\n\
c.ServerApp.open_browser = False\n\
c.ServerApp.notebook_dir = '/root'" > /root/.jupyter/jupyter_lab_config.py
1
docker run -it -v $(pwd)/docker-home:/root lab zsh

Docker-compose

.env file

  • DOCKER_USER: 使用者名稱(用來辨別)
  • JUPYTER_PORT: 的外部 port,Container 內部為 8888
  • DOCKER_HOME: 家目錄位置
1
2
3
DOCKER_USER=your_username
PORT=21348
DOCKER_HOME=./docker-home

docker-compose.yml 這時候要把 GPU 資源加上去。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
version: "3.4"
services:
  jupyter:
    hostname: "${DOCKER_USER}-docker"
    container_name: ${DOCKER_USER}_server
    image: test
    volumes:
      - ${DOCKER_HOME}:/root
    working_dir: /root
    ports:
      - ${PORT}:8888
    command: zsh -c '/root/.pyenv/shims/jupyter lab --allow-root'
    deploy:
      resources:
        reservations:
          devices:
            - capabilities: [gpu]
              driver: nvidia
1
docker-compose up

前往 <your_ip>:<JUPYTER_PORT>。第一次開啟 jupyter,輸入上圖反白部份的 Token 就可以設定密碼了。

背景執行:

1
docker-compose up -d

查看 Container

1
docker ps
1
2
CONTAINER ID   IMAGE    COMMAND                  CREATED        STATUS         PORTS                      NAMES
d4695e6ea584   test   "zsh -c '/root/.pyen…"   5 hours ago    Up 3 seconds   0.0.0.0:21348->8888/tcp        hank_server

連接到正在執行的 Container 的 shell

1
docker exec -it hank_server zsh

懶人包

Installation

  1. Install Docker & Docker-compose
  2. Install nvidia toolkit

Build Image

1
2
3
4
5
6
7
8
git clone https://github.com/kehanlu/server-docker

cd server-docker

docker build -t lab .
docker run -v $(pwd)/docker-home:/root lab sh /src/init.sh

# Done!

Run Container

修改 .env

1
2
3
DOCKER_USER=username
JUPYTER_PORT=8990
DOCKER_HOME=./docker-home

啟用 Jupyter

1
docker-compose up -d

在 Container 打開一個 shell

1
docker exec -it username_server zsh