第08课:用

第08课:用 Docker 建立一个公用 GPU 服务器

首先声明一下,Docker 本来被设计用来部署应用(一次配置,到处部署),但是在这篇文章里面,我们是把 Docker 当做一个虚拟机来用的,虽然这稍微有悖于 Docker 的使用哲学,但是,作为一个入门教程的结课项目,我们通过这个例子复习之前学到的 Docker 指令还是很好的。

本文我们主要使用容器搭建一个可以供小型团队(10人以下)使用的 GPU 服务器,用来进行 Deep Learning 的开发和学习。如果读者不是深度学习研究方向的也不要担心,本文的内容依旧是讲解 Docker 的使用,不过提供了一个应用场景。另外,本文会涉及到一些之前没有提到过的 Linux 指令,为了方便 Linux 初学者,会提供详细解释或者参考资料。

本文参考了以下资料的解决思路,将 LXC 容器替换成 Docker 容器,并针对实际情况作了改动:

https://abcdabcd987.com/setup-shared-gpu-server-for-labs/

为什么要使用 Docker 来建立服务器?

深度学习目前火出天际(2017年),我所在的实验室也有相关研究。但是,深度学习模型的训练需要强悍的显卡,由于目前显卡的价格还是比较高的,所以不可能给每个同学都配备几块显卡。因此,公用服务器就成了唯一的选择。但是,公用服务器有一个问题:如果大家都直接操作宿主主机,直接在宿主主机上配置自己的开发环境的话肯定会发生冲突。

实验室一开始就是直接在宿主机配置账号,也允许每个人配置自己需要的开发环境,结果就是慢慢地大家的环境开始发生各种冲突,导致谁都没有办法安安静静地做研究。于是,我决定使用 Docker 把服务器容器化,每个人都直接登录自己的容器,所有开发都在自己的容器内完成,这样就避免了冲突。并且,Docker 容器的额外开销小得可以忽略不计,所以也不会影响服务器性能。

服务器配置思路

服务器的配置需要满足一些条件:

  • 用户可以方便地登录
  • 用户可以自由安装软件
  • 普通用户无法操作宿主主机
  • 用户可以使用 GPU 资源
  • 用户之间互不干扰

我的解决思路是,在服务器安装显卡驱动后,使用 nvidia-docker 镜像运行容器。

为什么使用 nvidia-docker 呢?因为 Docker 是平台无关的(也就是说,无论镜像的内容是什么,只要主机安装了 Docker,就可以从镜像运行容器),这带来的问题就是——当需要使用一些专用硬件的时候就会无法运行。

因此,Docker 本身是不支持容器内访问 NVIDIA GPU 资源的。早期解决这个问题的办法是在容器内安装 NVIDIA 显卡驱动,然后映射与 NVIDIA 显卡相关的设备到容器(Linux 哲学:硬件即文件,所以很容易映射)。这种解决办法很脆弱,因为这样做之后就要求容器内的显卡驱动与主机显卡硬件型号完全吻合,否则即使把显卡资源映射到容器也无法使用!所以,使用这种方法,容器显然无法再做到平台无关了。

为了解决这些问题,nvidia-docker 应运而生。nvidia-docker 是专门为需要访问显卡资源的容器量身定制的,它对原始的 Docker 命令作了封装,只要使用 nvidia-docker run 命令运行容器,容器就可以访问主机显卡设备(只要主机安装了显卡驱动)。nvidia-docker 的使用规则和 Docker 是一致的,只需要把命令里的“docker”替换为“nvidia-docker”就可以了。

然后,为了方便大家使用,为每个容器做一些合适的端口映射,为了方便开发,我还配置了图形界面显示功能!

最后,为了实时监控服务器的资源使用情况,使用 WeaveScope 平台监控容器运行状况(当然,这部分内容和 Docker 入门使用关系不大,大家随意看一下就好了)。

如果你没有 GPU 服务器,并且自己的电脑显卡也比较差,你可以不用 nvidia-docker,仅仅使用普通的 Docker 就好了。当然,你可能需要恩距自己的实际情况对后文提供的 Dockerfile 进行修改。

宿主主机配置

首先,服务器主机需要安装显卡驱动,你可以使用 NVIDIA 官网提供的 “.run” 文件安装,也可以图方便使用 apt 安装:

sudo apt install nvidia-387 nvidia-387-dev 

接下来,我们安装 nvidia-docker

# Add the package repositories
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | \
  sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/ubuntu16.04/amd64/nvidia-docker.list | \
  sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update

# Install nvidia-docker2 and reload the Docker daemon configuration
sudo apt-get install -y nvidia-docker2

我们以“tensorflow/tensorflow:latest-gpu”为基础镜像定制自己的镜像,所以先 pull 这个镜像:

sudo docker pull tensorflow/tensorflow:latest-gpu

使用 Dockerfile 定制镜像

这部分内容参考了这个项目。配置可以在浏览器显示的远程桌面:

FROM tensorflow/tensorflow:latest-gpu
MAINTAINER Shichao ZHANG <***@gmail>

ENV DEBIAN_FRONTEND noninteractive

RUN sed -i 's#http://archive.ubuntu.com/#http://tw.archive.ubuntu.com/#' /etc/apt/sources.list

# built-in packages
RUN apt-get update \
    && apt-get install -y --no-install-recommends software-properties-common curl \
    && sh -c "echo 'deb http://download.opensuse.org/repositories/home:/Horst3180/xUbuntu_16.04/ /' >> /etc/apt/sources.list.d/arc-theme.list" \
    && curl -SL http://download.opensuse.org/repositories/home:Horst3180/xUbuntu_16.04/Release.key | apt-key add - \
    && add-apt-repository ppa:fcwu-tw/ppa \
    && apt-get update \
    && apt-get install -y --no-install-recommends --allow-unauthenticated \
        supervisor \
        openssh-server openssh-client pwgen sudo vim-tiny \
        net-tools \
        lxde x11vnc xvfb \
        gtk2-engines-murrine ttf-ubuntu-font-family \
        libreoffice firefox \
        fonts-wqy-microhei \
        language-pack-zh-hant language-pack-gnome-zh-hant firefox-locale-zh-hant libreoffice-l10n-zh-tw \
        nginx \
        python-pip python-dev build-essential \
        mesa-utils libgl1-mesa-dri \
        gnome-themes-standard gtk2-engines-pixbuf gtk2-engines-murrine pinta arc-theme \
        dbus-x11 x11-utils \
    && rm -rf /var/lib/apt/lists/*

RUN echo 'root:root' |chpasswd
# tini for subreap                                   
ENV TINI_VERSION v0.9.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /bin/tini
RUN chmod +x /bin/tini

ADD image /
RUN pip install setuptools wheel && pip install -r /usr/lib/web/requirements.txt

EXPOSE 80
WORKDIR /root
ENV HOME=/home/ubuntu \
    SHELL=/bin/bash
ENTRYPOINT ["/startup.sh"]  

然后,由此 Dockerfile 构建镜像:

sudo docker build -t gpu:v0.1 .

等待镜像构建完成。

现在,从这个镜像运行一个容器:

sudo nvidia-docker run -d -ti --rm --name gputest -p 9999:80 -e VNC_PASSWORD=1234 gpu:v0.1

说明:

-e VNC_PASSWORD 设置登录密码。

我的服务器网址是“223.3.43.127”,端口是我们指定的9999,会要求我们输入密码:

enter image description here

输入你设置的密码,即可进入桌面环境:

enter image description here

好了,这样的话团队成员就可以方便地使用 GPU 服务器了!

简易服务器监控网站

我们使用一个开源项目来监控容器的运行——Weave Scope

首先,在宿主主机执行以下命令来安装和启动 Weave Scope:

sudo curl -L git.io/scope -o /usr/local/bin/scope
sudo chmod a+x /usr/local/bin/scope
scope launch

然后浏览器打开服务器 IP 地址,端口号4040,就可以实时监控了:

enter image description here

点击对应的容器即可查看容器信息,包括 CPU 占用率,内存占用,端口映射表,开机时间,IP 地址,进程列表,环境变量等等。并且,通过这个监控网站,可以对容器做一些简单操作:停止,重启,attach,exec 等。

这是一个很简单的 Docker 容器监控方案,使用者可以通过这个监控网站直接操作容器,所以无需登录宿主主机来进行相关操作,完美实现资源隔离

但是,这个方案也有一些缺点,比如每个用户都可以看到其它用户的密码,甚至可以直接进入其他用户的容器!不过,由于我们的使用背景是“实验室或者小团队的私密使用”,作为关系紧密的内部小团体,建立在大家相互信任的基础上,相信这也不是什么大问题。

服务器管理方案小结

现在总结一下我们的 GPU 服务器容器化的全部工作:

  • 宿主主机配置 Docker 和 nvidia-docker,安装显卡驱动;
  • 使用 Dockerfile 定制镜像;
  • 为每个用户运行一个容器,注意需要挂载需要的数据卷;
sudo nvidia-docker run -ti -d --name ZhangShichao -v /home/berry/dockerhub/zsc/:/root/zsc -v /media/zhangzhe/data1:/root/sharedData -p 6012:22 -p 6018:80 -p 6010:6000 -p 6011:6001 -p 6019:8888 -e VNC_PASSWORD=ZhangShichao     gpu:v0.1
  • 使用 WeaveScope 监控容器的运行情况;

在此之后,如果团队成员需要启动新的容器,管理员可以通过宿主主机为用户运行需要的容器。普通用户无法操作宿主主机,完美实现隔离!

接下来做什么?

本文主要是一个实战例子,相信通过这个例子复习 Docker 的使用,读者一定对 Docker 操作更加熟悉。下一篇文章,也是最后一篇文章,我们将简单介绍 Docker 生态以及 Docker 在实际中的大型应用。

上一篇
下一篇
目录