由于深度学习的火热以及深度学习对 GPU 的强烈需求,实验室购置了一台性能强悍的 GPU 服务器,供大家一起使用。然而如果所有人都对这台服务器拥有控制权是十分危险的,例如误删他人文件,弄乱他人环境等。最直观的方法就是为每一个同学开启一个虚拟机,但是硬件虚拟化造成大量的资源浪费,同时GPU并不支持常规的虚拟化。
本文采用在宿主机上创建多个 LXD 容器的软件虚拟化方法,使得资源能够更好的利用。
基于上述背景提出以下需求:
- 不同用户之间不能相互影响且可以同时使用;
- 用户可以使用 ssh 方便地访问自己的“机器”;
- 用户拥有所有权限;
- 用户不被允许直接操作宿主机;
- 用户可以使用宿主机的所有资源,包括 CPU、GPU、内存、硬盘等。
本人在 Ubuntu 18.04 的宿主机上使用 LXD 容器中的 Ubuntu 18.04 系统完成了上述所有需求。
安装 lxd、zfs 及 bridge-utils
sudo snap install lxd |
我们需要安装LXD实现虚拟容器,ZFS作为LXD的存储管理工具,bridge-utils用于搭建网桥。由于apt安装的LXD不是最新版本,这里使用snap安装工具安装LXD。
LXD 初始化
sudo lxd init |
在初始化过程中,不要创建新的网桥,ZFS设置大小要尽量大,其他设置默认即可。详情如下:
Would you like to use LXD clustering? (yes/no) [default=no]: |
新建容器
如果网速允许可以尝试:
sudo lxc launch ubuntu:xenial yourContainerName |
如果网速不行可以添加清华大学的镜像:
sudo lxc remote add tuna-images https://mirrors.tuna.tsinghua.edu.cn/lxc-images/ --protocol=simplestreams –public |
使用如下命令查看清华镜像的所有系统:
sudo lxc image list tuna-images: |
选择容器系统并记录第二列的 FINGERPRINT,如 ubuntu/18.04 的FINGERPRINT为 0023c4e9dc6e
然后使用如下命令创建新容器:
sudo lxc launch tuna-images:0023c4e9dc6e yourContainerName |
配置网络环境
在实验室的其他电脑访问服务器中的 LXD 容器有两种方法。
- 给每个用户分配一个端口,利用 iptables 把这个端口转发到对应 LXD 容器的 22 端口;
- 使用桥接模式,每个容器的 IP 与实验室的 IP 域相同,物理外部、局域网内部的机器可以直接 ssh 容器的 IP,不需要端口号就可以登陆。
很明显,第 2 种方法比第 1 种方法好,不仅登录方便,而且每个容器的所有端口都是开放的,可以拿其他端口做一些事情,如搭建网站、开启服务等。
第 2 种方法的详细步骤参见本人博客 LXD 使用 Netplan 实现在同一网络访问
修改容器的用户名密码
由于容器默认的用户名为 ubuntu
,我们需要在安装其他程序之前更改用户名即密码,以避免路径冲突。
修改root密码:
passwd root |
修改用户ubuntu密码:
passwd ubuntu |
修改用户名:
usermod -l <newname> -d /home/<newname> -m <oldname> |
容器中安装 ssh 服务
sudo apt-get install openssh-server |
至此,已经可以使用实验室的任意电脑通过 ssh 直接访问容器而不需要操作宿主机。
安装显卡驱动
在宿主机中首先需要安装显卡驱动,使用如下命令安装推荐的显卡驱动:
sudo ubuntu-drivers autoinstall |
但是这个方法安装的驱动往往不是最新的,可以去 NVIDIA 官网 下载最新驱动并安装。具体方法参考 Ubuntu 16.04安装NVIDIA驱动 和 超详细! Ubuntu 18.04 安装 NVIDIA 显卡驱动
使用 nvidia-smi 查看驱动版本,并在官网下载对应的驱动,以便于在容器中安装和宿主机相同版本的NVIDIA驱动。
为容器添加所有GPU:
sudo lxc config device add <container> gpu gpu |
添加指定GPU:
sudo lxc config device add <container> gpu0 gpu id=0 |
在容器中安装显卡驱动:
sudo sh ./NVIDIA-Linux-x86_64-xxx.xx.run --no-kernel-module |
因为在容器中显卡驱动不需要安装内核文件,所以后面加上 --no-kernel-module
。
在容器中安装好显卡驱动重启后输入 nvidia-smi
检查驱动是否安装成功
宿主机安装 lxdui 管理容器
因为 LXD 相当于 LXC 增加了 RESTful API,所以可以通过 WEB 界面管理容器。lxdui 为不错的管理界面。具体安装方法详见 lxdui 的 github
目前 lxdui 版本为 2.1.2,我使用有一些 bug,例如 snapshot、clone 等操作只能在容器表格中的 Actions 进行操作,进入容器后顶部的 snapshot、clone 等按钮是无效的。所以 lxdui 主要还是方便查看和管理,具体的操作还是用命令吧。
容器快照管理
以上所有对容器的操作基本完成,现在需要建立快照,以备将来需要恢复快照,或者可以从快照新建一个容器,这样可以避免上面的重复劳动。
创建快照:
sudo lxc snapshot <container> <snapshot name> |
恢复快照:
sudo lxc restore <container> <snapshot name> |
从快照新建一个容器 (新旧容器 MAC 地址不同):
sudo lxc copy <source container>/<snapshot name> <destination container> |
这也是比较好的创建容器的方法。如果直接 clone 容器的话,MAC 地址等关键信息也会同样被复制。
容器和宿主机间复制文件:
在宿主机输入以下命令
sudo lxc file push <source path> <container>/<path> # 表示从容器中复制文件到宿主机 |
复制文件夹需要在最后加 -r
共享文件夹:
创建共享文件夹:
sudo lxc config set <container> security.privileged true |
其中 path
为容器路径,source
为宿主机路径。device-name
随意取名字即可。
移除共享文件夹:
sudo lxc config device remove <container> <device-name> |
CUDA 与 cuDNN
CUDA 与 cuDNN 的安装建议使用 Anaconda 安装,因为使用 Anaconda 安装 TensorFlow 或 PyTorch 都会自带 CUDA 与 cuDNN,并且据说有一定的优化。具体命令为:
conda create -n tf_gpu1.9 tensorflow-gpu=1.9 |
只要 CUDA 版本与 NVIDIA 驱动版本相对应即可,可以在 Release Notes :: CUDA Toolkit Documentation 查找。
安装远程桌面 (2019.08.11 更新)
参考文档:How to Connect to a Ubuntu 18.04 Server via Remote Desktop Connection using xRDP
1. 安装 xRDP
xRDP 是一款非常不错的远程桌面软件,且全平台支持。安装 xRDP 最好在干净的系统上安装。
- Windows 可以直接使用微软的远程桌面连接。
- Mac 可以使用
Microsoft Remote Desktop
mac 版,App Store 搜不到,这里放上 下载链接。 - Linux 可使用
FreeRDP
、Rdesktop
等开源软件。
sudo apt-get update |
2. 安装桌面环境
Ubuntu 有很多桌面环境,例如 XFCE, Lubuntu, Xubuntu 和 MATE 等。这里使用 XFCE 为例:
sudo apt-get install xfce4 |
现在就可以使用远程桌面软件,通过 IP 即可访问。
在 LXD / LXC 中使用 Docker 容器 (2019.09.01 更新)
参考文档:docker run hello-world still fails, permission denied - stackoverflow
在 LXD 环境中依然可以使用 Docker,但是需要更改一些配置,否则在 pull 镜像时会有 permission denied
错误,如下:
OCI runtime create failed: container_linux.go:345: starting container process caused "process_linux.go:430: container init caused \"rootfs_linux.go:58: mounting \\\"proc\\\" to rootfs \\\"/var/lib/docker/vfs/dir/c5cedb213621362913c6d950eec507ba91e04f2a933cd6d309f1c74a92c346ec\\\" at \\\"/proc\\\" caused \\\"permission denied\\\"\"": unknown |
我们只需要在宿主机对容器进行以下配置即可:
sudo lxc config set <container> security.nesting true |
RuntimeError: cuda runtime error (30) (2019.10.10 更新)
重启宿主机后,再使用容器里的环境时会找不到 CUDA,报下面的错误:
RuntimeError: cuda runtime error (30) : unknown error at /tmp/pip-req-build-58y_cjjl/aten/src/THC/THCGeneral.cpp:50 |
或者 torch.cuda.is_available()
是 false
网上其他人报这个错误可能是因为 CUDA 没有安装,但我使用的是 Anaconda 自动安装的 CUDA 和 cuDNN,容器和宿主机都可以运行 nvidia-smi
命令查看显卡驱动版本,并且在重启前是没有错误的,为什么重启后就报错了呢?为此我甚至重装系统,但这个问题依然存在。。。
目前临时的解决办法是:
重启宿主机后,需要使用宿主机的 Python 环境运行一次使用 CUDA 的程序;
例如,如果是pytorch环境,可以运行下面的代码:
python -c 'import torch; print(torch.cuda.is_available())'
重启所有容器。
这样容器里的 CUDA 就可以找到了。这可能是 LXC 的配置问题,如果有人遇到相同问题有更好的解决方案希望可以告知,万分感谢~
宿主机重启后找不到显卡驱动(2020.6.2 更新)
重启宿主机后,显卡驱动掉了,所有用到显卡的容器都无法启动,输入nvidia-smi
报错:
NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver. Make sure that the latest NVIDIA driver is installed and running |
原因可能是宿主机运行了sudo apt upgrade
命令并更新了系统内核,导致安装显卡驱动时的内核与现有内核版本不一致,解决办法参考此解决办法
sudo apt-get install dkms # DKMS,Dynamic Kernel Module Support,可以帮我们维护内核外的这些驱动程序,在内核版本变动之后可以自动重新生成新的模块。 |
DKMS全称是Dynamic Kernel Module Support,它可以帮我们维护内核外的这些驱动程序,在内核版本变动之后可以自动重新生成新的模块。
430.50是安装驱动的版本,可进入/usr/src查看nvidia-430.50的文件夹获取版本号
重启后输入nvidia-smi就应该正常输出了。如果还是不好使,可以重启后重新运行上述命令再重启。
如果仍不好使,说明该内核下无法编译显卡驱动,解决方法是内核降级为原来版本,具体方法参考如何降级/切换 Ubuntu 系统 Linux 内核启动版本
- 查看系统可用内核
grep menuentry /boot/grub/grub.cfg |
可以看到子选项:
Ubuntu, with Linux 5.3.0-62-generic
- 修改内核启动版本
使用vim编辑grub文件:
sudo vim /etc/default/grub |
GRUB_DEFAULT=0 // 0表示系统当前启动的内核序号
修改为想要启动的内核版本对应子选项:
GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 5.3.0-62-generic"
注意,>
两侧没有空格,这个原博客有错误。
- 更新 Grub
sudo update-grub |
- 查看系统当前运行内核信息
uname -r(或-a) |
容器硬盘ZFS扩容
LXD 初始化的时候会对 ZFS 进行空间分配,但是随着时间的推移仍有扩容的需求。当容器变得很卡的时候,有可能就是 ZFS 分配的空间已满。
输入下面的命令可以对 ZFS 进行扩容,以扩容512GB为例:
sudo truncate -s +512G /var/snap/lxd/common/lxd/disks/lxd.img |
其中,lxd.img 为初始化时定义的容器名称,不知道容器名称的可以通过下述命令查看 sudo ls /var/snap/lxd/common/lxd/disks/
总结
至此,多人使用的 GPU 服务器就搭建完成了,当需要新建容器时,只需要完成以下几步:
- 从快照中新建容器;
lxc exec <container> bash
进入容器;- 更改容器的 IP;
- 更改容器的用户名、密码;
- 新增共享目录;
这在以后可以编写脚本,使得新建操作更容易。