docker的介绍网上有很多,对于深度学习的认来讲,docker的好处就是直接把环境分享给他人,他人不需要再配置环境了。
比如我有一个目标检测的项目,我想分享给朋友,那么他首先需要在自己的电脑上配置好显卡驱动、CUDA、CuDNN,在拿到我的项目后,还需要安装各种依赖库,最后代码还不一定跑起来。
如果我是用了docker环境进行项目配置,我只需要将环境打包好后分享给朋友。他只需要安装好显卡驱动就行,什么cuda、pytorch之类的都在我分享的环境了。
实际上github很多项目都提供了docker方式,但是我们更多还是自己配置环境和安装对应的pytorch版本,自己调试代码尝试复现作者的项目。如果你使用了docker,分分钟就能将作者的环境在自己的电脑上复现,非常便捷。
在本文,我将介绍docker的基本使用,以及总结可能遇到的bug的解决方法。
在dockerhub中,有很多现成的镜像可用,本文中会使用anibali/pytorch:1.10.2-cuda11.3-ubuntu20.04分享的镜像。
该镜像是ubuntu20+cuda11.3+pytorch1.10.2,根据描述(ENV NVIDIA_REQUIRE_CUDA=cuda>=11.3 brand=tesla,driver>=418,driver<419 driver>=450)我们只需要本机满足显卡驱动版本即可。其他版本的镜像可以去该作者的主页,他制作了非常多的镜像。
windows有桌面docker,所以安装过程比较简单,按照普通软件安装即可。
安装成功后,启动Docker Desktop软件,等待docker引擎启动后,我们就可以在cmd中使用docker命令了。比如执行sudo docker ps -a
查看容器,如果没有报错证明ok了,当然现在我们还没有创建容器也不会输出什么有用的,不报错就行。
潜在的bug可能会与WSL有关,需要windows电脑安装有WSL,遇到这类bug可以参看网上的教程。
ubuntu可以通过apt安装,比较简单:
sudo apt-get update
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
然后在终端执行:sudo docker ps -a
查看容器,如果没有报错证明ok了,当然现在我们还没有创建容器也不会输出什么有用的,不报错就行。
现在docker安装好了,我们拉取一个上面这个配置好cuda和pytorch的镜像:
docker pull anibali/pytorch:1.10.2-cuda11.3-ubuntu20.04
因为我之前下过,所以显示Already exists。等待镜像拉取完成,如果成功了,我们可以查询所有镜像:
docker image ls
镜像我们有了,现在我们可以创建容器了。
容器就像是一个运行起来虚拟机,我们使用同一个镜像创建不同的容器,就像使用同一个windows系统安装盘(镜像),给不同电脑装系统,这些运行起来的电脑就是容器。注意容器的改变不会影响镜像,如果你在容器里部署好的项目,需要将容器导出为新的镜像分享即可。
创建容器的命令是:
docker run -it --gpus all --name container_name anibali/pytorch:1.10.2-cuda11.3-ubuntu20.04 /bin/bash
在这个命令中,run是创建容器的指令,-it是交互式终端,因为创建的容器就相当于一个本机中的linux服务器,我们可以通过终端与容器交互。–gpus all这个就是使用本机的gpu,–name是给新建容器取个名字, anibali/pytorch:1.10.2-cuda11.3-ubuntu20.04就是要使用的镜像,/bin/bash是指定bash。
可以看到创建成功后,我们就直接进入了容器中,处于容器的/app目录下。
现在我们可以像使用云服务器那样部署自己的项目了,比如在本机终端中(非容器中)使用docker cp 将本机中的项目复制到容器中的/app目录下。
现在我们假设你已经在容器中部署好自己的项目了,比如通过python train.py就可以训练,你想让你的朋友也能体验这个项目,那就需要将你的容器打包为镜像分享。
将容器打包为镜像为命令是:
docker commit -m "some information" <容器ID>
容器ID可以通过docker ps -a
查询到,该命令可以查询所有容器信息。
image_name:version就是你自己定义的镜像的名字和版本号。
现在在本地已经有新镜像了,分享有两种:直接将镜像打包tar分享,或者上传到云,类似本文使用热心作者的镜像那样。
打包导出的命令:docker save image_naem:version -o output_name.tar
-o就是output,也就是将镜像保存到什么文件。如果不报错在当前目录下能够看到你的镜像tar文件。
他人收到这个tar后,可以通过docker load -i xxx.tar
完成加载,如果成功了可以通过docker image ls
查看到该镜像。
云仓库可以自己去弄一个,当然dockerhub也可以免费上传分享,前提是你有一个dockerhub的账号。
首先在终端需要登录你的账号:docker login
根据提示填写账号和密码。
首先给新镜像打tag:
docker tag new_image:version username/new_image:version
比如sudo docker tag pt_test_image:0 thgpddl/pt_test_image:0
注意,请将thgpddl替换为自己的用户名,然后我们就可以看到新tag的镜像(实际上发现tag的和之前的镜像只是名字不一样,其ID是一样的)
然后开始上传:docker push thgpddl/pt_test_image:0
然后等待上传成功后(不过很可能出现408网络问题,可以尝试开启代理试试),在dockerhub就可以看到上传的镜像了。
在另一个安装了docker的电脑上使用分享的镜像很简单。如果新镜像上传到了dockerhub,则和拉取镜像一样:docker pull username/image_name:version
。
如果是通过save保存到本地的,镜像是一个tar包,则可以在新电脑上通过:docker load -i new_image.tar加载。
注意:因为windows的docker底层使用的WSL,和Linux不一样,所以在一个平台制作导出的镜像在另一个平台使用时,可能会出现gpu不可用的问题。比如在Ubuntu系统加载Windows平台导出的镜像时,通过docker run --gpus all创建容器后,在容器中使用nvidia-smi会报错:NVIDIA-SMI couldn't find libnvidia-ml.so library in your system. Please make sure that the NVIDIA Display Driver is properly installed and present in your system.Please also try adding directory that contains li PATH.
那么接下来的内容就会详细如何解决跨平台无法使用GPU的问题
首先会出现问题的情况有(同平台是没有问题的,比如linux和linux,windows和windows):
取个名字 | 导出镜像的平台 | 使用镜像的平台 | 问题 |
---|---|---|---|
win2linux | Widnows | Ubuntu | 容器内使用 nvidia-smi命令报错:NVIDIA-SMI couldn’t find libnvidia-ml.so library in your system. |
linux2win | Ubuntu | Windows | 创建容器时 使用–gpus参数就会报错:libnvidia-ml.so.1- file exists- unknown |
我在windows制作的镜像在ubuntu加载后,无法使用nvidia-smi命令并报错NVIDIA-SMI couldn’t find libnvidia-ml.so library in your system.
,那么我们需要先确认是不是本节要讨论的问题,所以我们要在容器中查询:
lspci | grep -i nvidia
(需要安装sudo apt update && sudo apt install pciutils),可以看到存在显卡2208(十六进制),然后在显卡查询查询2208得知是3080ti,就是我的显卡。cat /proc/driver/nvidia/version
,可以看到在容器中是可以查询到宿主机的显卡驱动的,证明没问题。现在请根据自己是哪一个跨平台情况按步骤解决把
本质问题如图,解决办法就是重新建立软链接,不过方法多样而已。
在linux加载来自windows的镜像后,进入容器后使用nvidia-smi命令会报错:
首先查询软链接情况:ldconfig
,看到很多libnvidia和libcuda软链接为empty,所以我们目标就是重新建立软链接
进入目录:cd /usr/lib/x86_64-linux-gnu
,通过find -name "libnvidia-ml.so"
查询显卡驱动515.43.04
,请记住这个。
建立libnvidia-ml.so
的软链接:
# libnvidia-ml.so.515.43.04 -> libnvidia-ml.so.1
sudo rm -f libnvidia-ml.so.1
sudo ln -s libnvidia-ml.so.515.43.04 libnvidia-ml.so.1
建立libcuda.so
的软链接
# libcuda.so.515.43.04->libcuda.so.1
sudo rm -f libcuda.so.1
sudo ln -s libcuda.so.515.43.04 libcuda.so.1
515.43.04
是我的本机驱动,请改为自己的。
测试一下:
可以看到nvidias-mi命令可以用了,然后在python中导入torch后创建一个张量放到cuda上去没有报错,说明cuda可用了。
Dockfile就是创建镜像的一种方法,这里大概原理就是在镜像1的基础上,生成镜像2,而在生成过程中自动建立libnvidia-ml.so
和libcuda.so
的软链接。
注意,与上面方法不同在于,上面的方法是从镜像1创建容器后,到容器里手动创建软链接,然后该容器就可以使用cuda了。本节方法是在镜像1上创建软链接,得到新的镜像,然后就可以直接从镜像创建cuda可用的容器。
那么现在,比如我现在要在linux下使用来自windows的镜像thgpddl/win_1050:0,首先建立createlink.sh文件,内容如下:
# 查找libnvidia-ml.so.driver_version文件,并保存driver_version版本号
libnvidia_ml_file=$(find /usr/lib/x86_64-linux-gnu/ -name 'libnvidia-ml.so.*'|grep -v libnvidia-ml.so.1)
driver_version=${libnvidia_ml_file#/usr/lib/x86_64-linux-gnu/libnvidia-ml.so.}
echo "[success]:find GPU driver version=$driver_version"
# 删除libnvidia-ml.so.driver_version.1原本存在的软链接,并且新建软链接:libnvidia-ml.so.1.driver_version -> libnvidia-ml.so.1
rm -f /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.1
ln -s "$libnvidia_ml_file" /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.1
echo "[success]:create ls: libnvidia-ml.so.1.$driver_version -> libnvidia-ml.so.1"
# 删除libcuda.so.driver_version原本存在的软链接,并且新建软链接:libcuda.so.1.driver_version -> libcuda.so.1
rm -f /usr/lib/x86_64-linux-gnu/libcuda.so.1
ln -s "/usr/lib/x86_64-linux-gnu/libcuda.so.1.$driver_version" /usr/lib/x86_64-linux-gnu/libcuda.so.1
echo "[success]:create ls: libcuda.so.1.$driver_version -> libcuda.so.1 -> libcuda.so"
然后在同目录下新建Dockerfile文件(D大写且没有后缀),内容:
FROM thgpddl/win_1050:0 # 优先从docker image找,没有就从dockerhub找
WORKDIR /app # 创建工作目录/app
COPY ./createlink.sh . # 将本机的createlink.sh复制到镜像中的/app目录下,注意后面有个点,代表WORKDIR文件夹
USER root # 切换root用户
RUN cd /app && ./createlink.sh # 进入 /app执行createlink.sh
然后创建新镜像:sudo docker build -t new_image_name .
注意最后的点,是指Dockerfile所在的目录(终端应当进入Dockerfile和createlink.sh所在的目录)
该Dockerfile大概意思就是使用thgpddl/win_1050:0作为基础镜像,然后在镜像中创建/app文件夹,然后将createlink.sh复制到镜像里,切换到root用户,进入/app目录并执行createlink.sh自动创建软链接。至此得到软链接已经创建的新镜像了。
在实际使用中,createlink.sh是不用更改的,如果你想分享你的镜像,那么直接给一个文件夹,其中包含createlink.sh和Dockerfile文件,Dockerfile第一行改为你的镜像即可。
在windows加载来自linux的镜像后,进入容器后使用nvidia-smi命令会报错:
从最后一行大概是说创建这个libnvidia-ml.so.1失败,因为它已经存在。实际上这就是win2linux是我们创建的软链接,可以在windows上它不能覆盖,否则应该能自动覆盖正常运行了。既然windows的docker不能自动覆盖掉,我们帮它删除即可。
基本原理如下,其实无论下面哪种方法都是要删掉两个so软链接。
当然很多时候镜像发布者没有考虑到这个问题,作者在ubuntu下使用–gpus配置好环境后直接发布了,所以我们需要自己去删除libnvidia-ml.so.1和libcuda.so.1文件。
因为启动命令使用–gpus all使用gpu,所以会调用libnvidia-ml.so.1,那么我们不使用gpu,应该能创建容器,然后进去删掉该文件,然后再docker commit为镜像,然后在新镜像就能使用gpu了(此时没有libnvidia-ml.so.1,docker就能创建成功了)
大概意思如下图,镜像1和镜像2的区别在于镜像2删掉了来自ubuntu的libnvidia-ml.so.1(和libcuda.so.1)
首先不使用gpu创建容器:docker run -it --name container_name image_name /bin/bash
然后删除两个so文件:
(cmd) docker run -it --name container_name image_name /bin/bash
(容器内) sudo rm -f /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.1
(容器内) sudo rm -f /usr/lib/x86_64-linux-gnu/libcuda.so.1
然后形成新镜像:docker commit -m "some information" container_name new_image_name:0
现在我们得到了一个可以在windows下正常使用的镜像了:
docker run -it --gpus all --name container_name new_image_name:0 /bin/bash
上节是在非gpu容器中删除两个so.1文件,然后制作为新镜像就可以使用gpu。实际上我们也可以借助Dockerfile来自动在创建新镜像时删除两个so.1文件,一步到位。
Dockerfile文件的内容:
FROM thgpddl/linux2win:0
USER root
RUN rm -rf /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.1 /usr/lib/x86_64-linux-gnu/libcuda.so.1
然后创建新镜像:sudo docker build -t new_image_name .
注意最后的点,是指Dockerfile所在的目录(终端应当进入Dockerfile所在的目录)
其实该Dockerfile很简单,就是使用基础镜像thgpddl/linux2win:0,然后切换root用户,删除libnvidia-ml.so.1
和libcuda.so.1
,得到新的镜像文件。
因为linux的镜像在windows使用报错是因为创建libnvidia-ml.so.1失败,因为它已经存在
,libnvidia-ml.so.1是在ubuntu中使用gpu创建的。如果ubuntu中不使用gpu,就不会有libnvidia-ml.so.1文件,那么就可以直接在windows上使用而不报错。
其实不用太复杂,我们在ubuntu容器中配置好自己的项目后,然后直接删除两个so.1文件后(确保你不再使用gpu,因为删除后在当前容器就不能使用gpu),直接docker commit导出镜像即可。因为该镜像本身没有两个so.1文件,所以可以直接在Windows或者其他linux中的docker中使用–gpus。
因为ubuntu镜像在windows中报错是因为windows要产生so.1文件,但是ubuntu镜像中存在so.1文件且windows无法强制覆盖。现在好了,我发布镜像时直接打包时删除so.1文件,在win下就不会报错了。
pip install opencv-python-headless
win10 WSL2 Docker 与 Linux Docker
WSL2上Docker打包的镜像迁移到Ubuntu服务器上无法使用GPU
libcuda.so.1: cannot open shared object file: No such file or directory
WSL启动nvidia-docker镜像:报错libnvidia-ml.so.1- file exists- unknown