使用Docker搭建实验室GPU服务器

版权声明:原创 https://blog.csdn.net/hangvane123/article/details/88639279

引入

背景

实验室已有一台GPU服务器由学长管理,BOSS新购了一台服务器并希望能够像已有的那个服务器一样,让多人共同使用GPU资源而不相互干扰,同时系统资源分配比较灵活。经过一番考虑和踩坑,终于完美搭建GPU平台,记录下以供后人参考。

服务器配置

  1. CPU: I7-9700K
  2. 主板: 微星Z390 gaming pro carbon
  3. 内存: 海盗船16G DDR4 3200MHZ x2
  4. 固态: Intel 760P 1T NVME固态
  5. 机械: 希捷2T
  6. 显卡: 技嘉2080Ti WF3 x2

方案决策

  1. 宿主机选择Ubuntu16.04 LTS,引导方式为UEFI,1T固态挂载点为/,2T机械挂载点为/home
  2. 虚拟机容器选择Docker,为了支持在虚拟机中使用GPU资源,使用nvidia-docker插件。
  3. 容器使用bridge方式联网,通过端口映射开放部分端口,与宿主机映射部分共用文件夹以方便管理和文件传输。
  4. Docker日常管理使用Shipyard中文版这个Docker Web GUI。

为什么选择这个方案

  1. 考虑过LXD,其实第一台服务器就是学长使用LXD搭建的。但是经过一段时间的踩坑,发现LXD生态完全不能和Docker相比,很多问题只能Google甚至无从查找,存储等设置对于笔者这种不太懂Linux的同学也过于复杂(创建存储池?指定已存在的块设备还是新建?zfs loop设备无法自定义存储位置?如何从虚拟机打包镜像?),很多问题不知道该怎么解决也很难搜索到解答,和学长再次交流后决定弃LXD转Docker,同样是踩坑,起码Docker还可以baidu到解答。
  2. 经过调研,Docker只要使用nvidia-docker插件就能够使容器访问GPU,并支持GPU资源的灵活分配。
  3. 为了能够充分利用固态和机械,仿照之前那台服务器的做法将机械挂载到/home,之后将docker存储设置到/home里就可以充分利用机械盘。
  4. 为了以后能够更方便的管理服务器,调研了Weave Scope、Shipyard、DockerUI三款产品。由于Weave Scope并非专门的Docker Web UI,DockerUI没有登录体系,功能也少,另外我发现了Shipyard的中文版,脚本安装也非常简单,直接选择Shipyard。

安装步骤

概述

本文详细记述了从安装系统到最后实际运行的全过程,主要包括:

  1. 安装Ubuntu16.04 LTS
  2. 安装显卡驱动
  3. 安装nvidia-docker并配置
  4. 编辑Docker容器,生成模板镜像供以后使用

接下来要注意的问题

  1. 系统的EFI分区建议分配大于550M
  2. 安装完nvidia-docker前不要配置daemon.json文件,安装时会覆盖
  3. 设置apt源时一定要设置对应系统版本的源,否则不会立即报错但会出现各种问题

安装Ubuntu16.04 LTS

记得勾选安装第三方软件
在安装选项那里选择自定义安装,全部设置为主分区,硬盘具体分区为:

硬盘 大小 格式 挂载点
固态 550M EFI -
固态 4G SWAP -
固态 900G+ EXT4 /
机械 2T EXT4 /home

安装过程比较简单,但是估计是因为从MBR格式转为GPT格式的问题,安装过程一直失败,最后经过一次全默认安装后再自定义安装就可以了。

宿主机换apt源

接下来使用vi命令,直接在本机显示器上操作的可以将下面带vi的指令替换为gedit,在本机打开文本编辑器更好操作。

备份

sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak

删除旧源

sudo rm /etc/apt/sources.list

写入新源

sudo vi /etc/apt/sources.list

清华源的链接参考https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu/
粘贴进去保存退出(按 I 进入insert模式,粘贴后按 ESC 退出insert模式,再输入wq!回车)

更新

sudo apt-get update

禁止Linux内核更新

查看已安装的内核

sudo dpkg --get-selections | grep linux

禁止更新内核

sudo apt-mark hold linux-image-x.xx.x-xx-generic

以后如需要恢复更新

sudo apt-mark unhold linux-image-x.xx.x-xx-generic

安装显卡驱动

参考
去官网 http://www.nvidia.com/Download/index.aspx?lang=en-us 下载合适驱动.run文件,放到~或其他目录下

卸载原有驱动(可能由于2080Ti没有默认驱动,我执行后并没有卸载掉什么包)

sudo apt-get purge nvidia*

禁用nouveau

sudo vi /etc/modprobe.d/blacklist-nouveau.conf

内容写上

blacklist nouveau
options nouveau modeset=0

更新

sudo update-initramfs -u

结束X-window服务

sudo service lightdm stop

Ctrl + Alt + F4进入tty4控制台

安装驱动

cd ~
sudo sh NVIDIA*

这里开始会有个警告不用管他,安装完后会提示是否更新X-windows配置要选yes
重启X-window

sudo service lightdm start

如果没有图像,按 Ctrl + Alt + F7 返回图形界面

检查能否读取到显卡信息

nvidia-smi

如果安装不成功需要卸载重来,或重启后偶尔出现找不到显卡,运行 nvidia-smi 提示 NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver. 的情况下,需要重装驱动

sudo sh NVIDIA* --uninstall

安装Docker

参考

和官网教程一致,主要是改为了国内源改善安装速度

通过https,允许apt使用repository安装软件包

sudo apt-get install -y \
       apt-transport-https \
       ca-certificates \
       curl \
       software-properties-common 

添加Docker官方GPG key

curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | apt-key add -

验证key的指纹

apt-key fingerprint 0EBFCD88

添加稳定版repository

add-apt-repository \
   "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

更新apt包索引

apt-get update

安装最新版本的Docker CE

apt-get install -y docker-ce

验证docker CE正确安装(可选,之后更换存储位置后建议将默认位置/var/lib/docker删除)

docker run hello-world

之后不要急着配置Docker,因为安装nvidia-docker会覆盖daemon.json。

安装nvidia-docker2

参考

添加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

安装nvidia-docker2并重新载入daemon.json

sudo apt-get install -y nvidia-docker2

加入docker组

加入docker组,以允许非root用户免sudo执行docker命令

sudo gpasswd -a 用户名 docker

如果不重启并重连ssh客户端的话,需要手动重启服务并刷新docker组成员

sudo service docker restart
newgrp - docker

Docker换源,换存储路径,限制容器日志大小

备份daemon.json

sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.bak

修改daemon.json

sudo vi /etc/docker/daemon.json

安装完nvidia-docker后默认应该是这样的配置

{
    "runtimes": {
        "nvidia": {
            "path": "nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}

添加国内源,修改存储位置为/home/docker,限制日志大小后daemon.json为

{
    "runtimes": {
        "nvidia": {
            "path": "nvidia-container-runtime",
            "runtimeArgs": []
        }
    },
    "registry-mirrors":[
        "https://kfwkfulq.mirror.aliyuncs.com",
        "https://2lqq34jg.mirror.aliyuncs.com",
        "https://pee6w651.mirror.aliyuncs.com",
        "https://registry.docker-cn.com",
        "http://hub-mirror.c.163.com"
    ],
    "data-root": "/home/docker",
    "log-opts": { "max-size": "50m", "max-file": "1"}
}

保存退出,并在/home下建立docker文件夹

cd /home
mkdir docker

在重启前,建议一并更改下网络设置,否则使用bridge模式连接的Docker容器可能无法访问外网
不用dnsmasq

vi /etc/NetworkManager/NetworkManager.conf 

加#号注释掉dns=dnsmasq
如果不重启电脑的话需要重启服务以完成更改,但还是连着Docker一并重启电脑了吧

sudo restart network-manager
sudo restart docker

重启后使用docker info查看是否修改成功

...
Docker Root Dir: /home/docker
...
Registry Mirrors:
 https://kfwkfulq.mirror.aliyuncs.com/
 https://2lqq34jg.mirror.aliyuncs.com/
 https://pee6w651.mirror.aliyuncs.com/
 https://registry.docker-cn.com/
 http://hub-mirror.c.163.com/
 ...

可以删除之前的docker存储目录了。

安装Shipyard中文版

官网脚本链接已失效,下载github缓存的脚本文件

wget https://raw.githubusercontent.com/shipyard/shipyard-project.com/master/site/themes/shipyard/static/deploy

修改脚本

vi deploy

将deploy脚本中IMAGE=${IMAGE:-dockerclub/shipyard:latest}修改为 IMAGE=${IMAGE:-shipyard/shipyard:latest}即可切换为Docker Hub中的中文版镜像源
修改SHIPYARD_PORT=${PORT:-7777}中的端口号可以自定义Shipyard的Web访问端口
修改完成后保存执行

sudo bash deploy

有选择的话默认即可
安装完成后等待docker容器启动完毕即可通过localhost:port访问到Web控制台,默认用户名admin,密码shipyard

创建容器

由于运行容器需要nvidia-docker run而不是docker run才能正确初始化使用GPU的容器,接下来还是采用命令行执行。当容器使用nvidia-docker run正确启动后,就可再使用Docker或Shipyard进行管理了。

下载有CUDA和CUDNN环境的镜像

docker pull nvidia/cuda:10.1-cudnn7-runtime-ubuntu16.04

这里如果直接pull nvidia/cuda的话拉取的是latest版本的镜像,系统为ubuntu18.04,因此我们手动指定版本为CUDA:10.1,CUDNN:7,Ubuntu:16.04的镜像。更多版本的镜像可以通过 https://hub.docker.com/r/nvidia/cuda/tags 查看。下面是以Ubuntu16.04为实例进行配置。

创建容器

nvidia-docker run -dit -p2345:22 --name=cuda1 -h=LAB_VM nvidia/cuda:10.1-cudnn7-runtime-ubuntu16.04
参数 含义
-dit 容器保持后台运行
-p2345:22 容器22端口映射到宿主机2345端口
–name=cuda1 命名容器
-h=LAB_VM 命名机器

进入容器

docker exec -it cuda1 /bin/bash

查看GPU是否能正常访问

nvidia-smi

配置容器环境

容器内的Ubuntu是一个非常精简的系统,缺乏包括ping vim等一系列常用指令,需要按照以下方式配置

更新apt源

apt-get update

如果一直卡在connect这里进行不下去,说明容器内无法连接外网,此时需要配置

退出容器命令行

exit

设置Linux内核允许IP转发

sudo sysctl net.ipv4.conf.all.forwarding=1

将iptables FORWARD 的值更从DROP更改为ACCEPT:

sudo iptables -P FORWARD ACCEPT

资料显示这里的两个配置不会持久化,每次重启都需要重新配置,需要将它们添加到start-up stript,但是笔者实测不需要重新配置

之后重新进入容器控制台执行apt-get update,应该可以执行成功了,如果又卡在一半的地方是在更新NVIDIA的源,需要检查网络环境是否能够连通NVIDIA的网站

安装vi

apt-get install vim

更换apt源

cp /etc/apt/sources.list /etc/apt/sources.list.bak
rm /etc/apt/sources.list
vi /etc/apt/sources.list

粘贴入16.04的清华源内容https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu/
这里如果意外设置成18.04的源不会马上报错,但是接下来会出现各种问题,一定要看好

更新apt源

apt-get update

安装ssh

apt-get install openssh-server

允许root远程连接

vi /etc/ssh/sshd_config

PermitRootLogin prohibit-password
修改为PermitRootLogin yes

为root设置密码

passwd root

如果是宿主机Ubuntu16.04,安装ssh服务应该会自行启动并且已经加入自启项,但是在容器内安装则不会自动启动,而且一般设置自启项的方法对Docker容器不起作用,需要按照如下方法设置启动脚本

启动ssh服务(此时可以通过ssh://root:密码@宿主机IP:2345连接容器了)

service ssh start

创建启动脚本

cd /home
vi startup.sh

输入启动脚本内容

#!/bin/bash
service ssh start
/bin/bash

/bin/bash的作用是保持Docker容器的后台运行,使用-dit参数的时候会附加执行这个命令,但是当设置了启动脚本后就不会附加执行了,需要手动执行。

附加执行权限

chmod 777 shartup.sh

这样基本的环境就配置好了,接下来还可以再安装些常用的库如ping等等,如果需要安装Anaconda的话会提示缺少一个库,通过apt-get install bzip2即可安装。还可以修改下ubuntu的ssh连接欢迎文字,下一步就开始打包了。

打包为镜像

为了便于以后新建虚拟机,将容器作为镜像打包

nvidia-docker commit -m "一些说明" -a "作者" cuda1 cuda-base:1.0

其中cuda1为容器的名字,cuda-base:1.0为镜像的名字和tag。

使用Dockerfile构建镜像

上述方法如果出现了误操作则需要重新构建,为此我将相关指令编写为Dockerfile

FROM nvidia/cuda:10.1-cudnn7-runtime-ubuntu16.04
MAINTAINER author<[email protected]>
RUN echo "export LANG=C.UTF-8" >>/etc/profile \
&& cp /etc/apt/sources.list /etc/apt/sources.list.bak \
&& echo "deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial main restricted universe multiverse\n\
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-updates main restricted universe multiverse\n\
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-backports main restricted universe multiverse\n\
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-security main restricted universe multiverse" >/etc/apt/sources.list \
&& apt-get update \
&& apt-get install vim openssh-server iputils-ping wget curl -y \
&& sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config \
&& echo "root:rootpassword" | chpasswd \
&& echo "#!/bin/bash\n\
service ssh start\n\
/bin/bash" >/home/startup.sh \
&& chmod 777 /home/startup.sh \
&& echo "\
printf '\\\n'\n\
printf '\\\033[0;34m'\n\
printf ' 欢迎使用GPU服务器\\\n'\n\
printf '\\\033[0m'\n\
printf '\\\n'\n\
" >>/etc/update-motd.d/10-help-text

ENTRYPOINT /home/startup.sh

镜像打包指令,注意要在有 Dockerfile 文件的目录下执行

nvidia-docker image build -t cuda-base:1.0 .

新建容器

新建公共目录

cd /home
sudo mkdir docker-common-dir

新建两个容器

NV_GPU=0 nvidia-docker run -dit --restart=on-failure -v /home/docker-common-dir:/home/common-dir -p6001:22 --name=vm1 -h=LAB_VM cuda-base:1.0 /home/startup.sh
NV_GPU=1 nvidia-docker run -dit --restart=on-failure -v /home/docker-common-dir:/home/common-dir -p6002:22 --name=vm2 -h=LAB_VM cuda-base:1.0 /home/startup.sh

其中NV_GPU环境变量是为了控制容器可访问的GPU,如果不加这个参数则容器可以访问全部的GPU。--restart=on-failure是为了设置容器随Docker自动启动。将宿主机的/home/docker-common-dir映射到了容器的/home/common-dir目录,便于和容器的文件传输,容器的文件存储以及容器之间的文件传输。最后跟的是启动脚本,如果不输入则每次容器启动都不会自动启动ssh服务。

如果一切正常的话,现在已经可以通过ssh客户端连接两个容器了。

尾言

容器启动完成后,就可以使用Shipyard进行容器和镜像的可视化管理了,和不使用nvidia-docker一样。也可以给老师和同学们分配Shipyard的账户让他们自行管理,但是建立账户还是需要在宿主机的终端执行nvidia-docker run命令。
建议不要删除模板容器cuda1,以后更新cuda-base镜像还需要这个容器,如果再通过cuda-base镜像创建容器更新的话就会有一连串镜像依赖。

参考

猜你喜欢

转载自blog.csdn.net/hangvane123/article/details/88639279