UNRAID折腾笔记(3) - 容器(Docker)与显卡计算
一上来我就直接摊牌,Docker实在是太好玩了,直接上目前配置好的Docker

可以看到上面跑了很多有趣的小项目或者应用,自上一篇笔记已经过了快三天了,这三天清明里面基本上都在捣腾UNRAID相关的内容,本来原计划这篇笔记的标题是UNRAID折腾笔记(3) - 硬盘直通与容器(Docker),但是由于硬盘需要接线,自然会导致原来的网盘暂时无法使用了,如果不能一次性弄完就会很不方便,于是打算先稍微推迟硬盘直通的内容,打算就深度围绕Docker来进行其他项目的迁移。其实从上图已经可以看到,除了NAS网盘的功能之外,其余大部分项目,比如数据库,web服务和mc游戏服务都迁移过来了,而且使用Docker进行管理相当方便,现在就创建于配置相关内容进行说明吧。
容器创建
找到顶部选项卡的DOCKER一栏,然后点击下面一排按钮中“创建容器”开始新的容器配置。

其实一开始还没搞懂docker的时候是相当痛苦,docker有很多种创建方法,最直观的就是在APPS应用商店中,下载基于docker的应用,也是进入这个配置页但是会提供一些配置模版,还有就是通过最原始的docker命令行手动创建,而我这个就是使用web界面进行创建了,可以说Unraid在docker的支持上也是特色之一,而我面对的就是这个没有模版的情况下的web界面创建 其实对docker的研究早在当时写上一篇笔记时就已经开始了,我尝试找了一些Linux系统docker来运行,结果弄了快一天一直都是已停止,就是无法启动,或者说启动之后都没法持续运行。后来进行更多学习才发现docker默认的使用方式其实是单次运行,即在创建容器时指定运行参数,就会直接运行并在完成后结束,如果你没有使用一些参数使其持续运行就会自动退出。
在web界面右上角打开终端,输入docker run --help
,可以看到很多可以指定运行时的参数 docker的入门与使用我就不在这多说了,具体可以参考相关教程,我们需要关注的是以下两个参数:


分别是-i
与-t
这两个参数,这两个参数同时使用会使容器在挂起到后台时保持运行,你可能在教程里看到的参数是-itd
,不过Unriad的docker已经自动为我们添加-d
参数因此就不需要额外添加。要添加这两个参数,需要在容器配置页右上角打开高级视图,然后在额外参数添加参数

但是要注意不要和底下的后置参数搞混,这两个完全不一样,体现在docker命令中一个是添加在容器名之前另一个是在容器名之后,额外参数是传递给docker用于定义容器本身属性的参数,而后置参数是传递给容器的应用影响应用运行的参数。 如果你想像前面我配置好的那些容器一下拥有一个图标,你可以在上述高级视图中的Icon URL:
中填入你想要的图标地址
另外,还有一个常用的功能项是下面的添加其他路径, 端口, 变量, 标签或设备,这里我们可以映射宿主机的路径甚至是设备到容器,也可以将容器内的一些端口映射到宿主机当中实现外部访问

参数配置妥当之后,记得参考第一篇笔记UNRAID折腾笔记(1) - 系统安装与配置配置好docker镜像加速,然后就可以点击确定开始拉取镜像了,我这里拉取的是TensorFlow的镜像,有两三个G,一般的应用镜像不会这么大

容器运行后,点击容器的图标可以看到一个控制台按钮,从这里就可以进入容器内的控制台了,进行应用的部署工作

关于docker的其他内容我就不打算赘述了,因为严格意义来说这并不是Unraid的功能,其余的我也就罗列一下注意事项便可
- 容器的创建与删除
我们刚才进行了一次容器创建操作,容器内部也可以进行数据读写的操作,但并不代表没有主动去删除容器时直接存储在容器内部的数据是安全的,事实上,当容器每发生一次更新或者是你在上方的操作中点击编辑容器,实际上都是先删除原来的容器,然后再按照目前的参数重新新建一个容器。因此直接存储在容器内的数据是会很容易丢失的,此时就需要上面的配置页中添加一个映射路径,将数据写入到宿主机当中实现持久化,那么即使是被删除后重新创建也是完好如初。
- 自定义IP
如果你配置了端口映射相关配置,就可以看到容器列表中端口从容器内部的地址映射到了宿主机上

此时你可能会注意到容器既然有一个自己的ip,那么不同的容器之间能不能直接进行通讯呢?答案当然是能的,除非你在配置页的网络类型里面选了无,不然默认运行在bridge模式,这种情况下就相当于拥有了一个虚拟内网,然后你可以使用--ip xxx
参数来指定容器IP,但是默认网桥是不能指定IP的,你需要自己手动创建一个新的基于bridge模式的网络,这部分可自行查阅相关docker资料。
这里要说的是,既然默认网桥下IP是自动分配的,但是如果需要一些数据库等服务就需要固定IP,在按照docker的教程新建网络之前,需要在Unraid下找到设置-Docker里面,打开保留用户自定义网络,然后就可以保证手动创建的网络在重启之后不会被删除了,记得修改配置需要停止阵列后才能进行

使用GPU加速
在这里,特指的是使用让容器内应用访问NVIDIA显卡实现计算加速,Unraid没有支持AMD显卡的驱动,而Intel集显是通过上面配置设备类型的映射将设备映射进去实现的。
NVIDIA的计算加速需要有相关的工具链支持,所以很显然不能通过简单的映射设备完成,在这里首先需要给Unraid打上显卡驱动补丁。这个打补丁我是从来没成功过,如果你还记得我降级到6.8.2时,使用的其实是集成了显卡补丁的开心版,但由于内核替换了IOMMU补丁所以就没有驱动了。后来只能二选一放弃IOMMU配置上了显卡补丁,在显卡驱动正常的情况下,进入Unriad的控制台(不是容器内的控制台),输入nvidia-smi
就可以看到显卡信息

这么打命令行看显卡状态也不是很直观,那可以尝试去安装GPU Statistics插件,便可以在仪表盘直观看到显卡信息了

那么这样显卡驱动是正常的。接下来我们就需要在容器配置时加入显卡相关的参数。 在这里,我以配置TensorFlow的GPU版本为例,只需在额外参数栏加入--gpus all
即可,就这么简单。

但是实际上,并不是这么无脑加就行了,这里有关于docker版本的要求。你看到的的一些关于显卡加速的教程中,让你加的参数其实是--runtime=nvidia
,这一点与你的docker版本有关,输入docker -v
查看版本

如果你的版本是19.03以下版本,使用--runtime=nvidia
,而如果是19.03及以上版本,使用--gpus all
,如果你无视了这一点直接无脑上--runtime=nvidia
,那么可能就会出现以下的情况,不过可能即使是19.03以下版本也可能出现这种情况

出现这个问题的原因是因为没有找到这个名为nvidia的运行环境,这一点就需要从这个显卡容器创建说起了。 容器诞生之初就是为了无需系统环境依赖,开箱即用,但是对于显卡这类硬件,可能在不同机器上的驱动与工具链都不同,生成容器之后还需要去配置显卡驱动,这样太违背docker的初衷了,于是NVIDIA官方就推出了一个自动集成了相关工具链的容器

实际上,就是对docker的一个封装,你可能在一些关于创建显卡容器的教程中,有提到nvidia-docker这个东西,其实背后就是加了这一层--runtime=nvidia
罢了。尝试在Unraid底下查找nvidia前缀的可执行文件,可以看到,这个nvidia-docker是随着我们的显卡驱动一起安装的。

那么,其实应该只是缺少这个runtime的定义问题,按照相关的教程,在daemon.json中添加相关配置,如果你还有印象的话,第一篇笔记中添加docker镜像加速也是在这里配置的
{
"runtimes": {
"nvidia": {
"path": "nvidia-container-runtime",
"runtimeArgs": []
}
}
}
当然源文件里面还有镜像相关的配置我没写在上面,怎么合并json应该不用我多说了 接下来的的一点是,从nvidia-docker的图里面可以看出,CUDA的驱动是在宿主机的,容器里面只是搭配了工具链,也就是说CUDA的版本和驱动取决于宿主机,换句话来说,容器里面应用运行的环境,也是需要和宿主机提供的环境兼容的。特别是TensorFlow这种与CUDA版本绑定的应用,就没这么好伺候了,如果直接上最新的2.4版本,就会因为CUDA版本过低无法创建容器

很显然,它要求宿主机的CUDA为11以上。给Unraid换驱动就别想了,只得老老实实找个最后支持CUDA10的版本,查表发现好在CUDA10已经相当广泛了,也不需要降很多版本,也好在Unraid的6.8版本其实是去年的版本,如果遇上上古时期的版本,都不敢想会有多难受

于是着手安装2.3版本,在DockerHub上找到原项目,然后点击Tag栏,搜索2.3找到最新的2.3的GPU版


每个版本的镜像都相当庞大

安装看起来很顺利,于是尝试在里面跑了个样例,显卡驱动也正常载入了,但是运行时遇到一个意想不到的问题

既然指的是device的kernel,那可能是硬件支持的问题,好家伙,一查发现是因为显卡太旧了,写这个问题的人显卡是960M,这还能对显卡有要求我是没想到的,于是又尝试降级,终于最终在2.2.2版本成功运行,回想起之前尝试在旧服务器跑结果因为CPU太旧不支持AVX指令集的经历,这次已经顺利很多了(至少不用去一个个下载驱动,CUDA,cuDNN并一个个匹配了)
害,跑TensorFlow只是图一乐,稍微大点的数据集那2G的可怜显存都不够塞牙缝的,另一个waifu2x可能还靠谱点 到这里本篇差不多结束了,因为我没装流媒体平台所以没用那个测试显卡加速,不过效果应该都差不多。这一次学习到了不少关于docker的配置,发现其实docker确实是挺好用的,应用单独隔离这样即使是需要维护也不用整体停机,启停方便而且故障不会蔓延到其他容器。容器本质上还是被隔离跑在宿主机上面的,性能自然是虚拟化的虚拟机没法比,如果在需要网络性能的话,可以用docker的主机模式来给一两个网络性能应用开特例(例如文件管理或者在线播放),最后一提,如果在主机模式的应用可是可以直接访问桥接模式里面的容器的,不过要记得打开设置-Docker-主机访问自定义网络才能允许访问

总的来说,如果不是以后的软路由必须得用到虚拟机,我都不想用虚拟机全部服务塞一块了,ALL IN ONE虽然是服务运行在一个物理机,但是服务却是模块化,可能这就是未来家庭服务器的最终雏形吧。