1. 什么是Docker?
docker的介紹網(wǎng)上有很多,對于深度學(xué)習(xí)的認(rèn)來講,docker的好處就是直接把環(huán)境分享給他人,他人不需要再配置環(huán)境了。
比如我有一個(gè)目標(biāo)檢測的項(xiàng)目,我想分享給朋友,那么他首先需要在自己的電腦上配置好顯卡驅(qū)動(dòng)、CUDA、CuDNN,在拿到我的項(xiàng)目后,還需要安裝各種依賴庫,最后代碼還不一定跑起來。
如果我是用了docker環(huán)境進(jìn)行項(xiàng)目配置,我只需要將環(huán)境打包好后分享給朋友。他只需要安裝好顯卡驅(qū)動(dòng)就行,什么cuda、pytorch之類的都在我分享的環(huán)境了。
實(shí)際上github很多項(xiàng)目都提供了docker方式,但是我們更多還是自己配置環(huán)境和安裝對應(yīng)的pytorch版本,自己調(diào)試代碼嘗試復(fù)現(xiàn)作者的項(xiàng)目。如果你使用了docker,分分鐘就能將作者的環(huán)境在自己的電腦上復(fù)現(xiàn),非常便捷。
2. 深度學(xué)習(xí)環(huán)境的基本要求
在本文,我將介紹docker的基本使用,以及總結(jié)可能遇到的bug的解決方法。
在dockerhub中,有很多現(xiàn)成的鏡像可用,本文中會(huì)使用anibali/pytorch:1.10.2-cuda11.3-ubuntu20.04分享的鏡像。
該鏡像是ubuntu20+cuda11.3+pytorch1.10.2,根據(jù)描述(ENV NVIDIA_REQUIRE_CUDA=cuda>=11.3 brand=tesla,driver>=418,driver<419 driver>=450)我們只需要本機(jī)滿足顯卡驅(qū)動(dòng)版本即可。其他版本的鏡像可以去該作者的主頁,他制作了非常多的鏡像。
3. Docker的基本操作
3.1 在Windows上安裝Docker
windows有桌面docker,所以安裝過程比較簡單,按照普通軟件安裝即可。
安裝成功后,啟動(dòng)Docker Desktop軟件,等待docker引擎啟動(dòng)后,我們就可以在cmd中使用docker命令了。比如執(zhí)行sudo docker ps -a
查看容器,如果沒有報(bào)錯(cuò)證明ok了,當(dāng)然現(xiàn)在我們還沒有創(chuàng)建容器也不會(huì)輸出什么有用的,不報(bào)錯(cuò)就行。
潛在的bug可能會(huì)與WSL有關(guān),需要windows電腦安裝有WSL,遇到這類bug可以參看網(wǎng)上的教程。
3.2 在Ubuntu上安裝Docker
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
然后在終端執(zhí)行:sudo docker ps -a
查看容器,如果沒有報(bào)錯(cuò)證明ok了,當(dāng)然現(xiàn)在我們還沒有創(chuàng)建容器也不會(huì)輸出什么有用的,不報(bào)錯(cuò)就行。
3.3 拉取一個(gè)pytorch的鏡像
現(xiàn)在docker安裝好了,我們拉取一個(gè)上面這個(gè)配置好cuda和pytorch的鏡像:docker pull anibali/pytorch:1.10.2-cuda11.3-ubuntu20.04
因?yàn)槲抑跋逻^,所以顯示Already exists。等待鏡像拉取完成,如果成功了,我們可以查詢所有鏡像:docker image ls
3.4 部署自己的項(xiàng)目
鏡像我們有了,現(xiàn)在我們可以創(chuàng)建容器了。
容器就像是一個(gè)運(yùn)行起來虛擬機(jī),我們使用同一個(gè)鏡像創(chuàng)建不同的容器,就像使用同一個(gè)windows系統(tǒng)安裝盤(鏡像),給不同電腦裝系統(tǒng),這些運(yùn)行起來的電腦就是容器。注意容器的改變不會(huì)影響鏡像,如果你在容器里部署好的項(xiàng)目,需要將容器導(dǎo)出為新的鏡像分享即可。
創(chuàng)建容器的命令是:docker run -it --gpus all --name container_name anibali/pytorch:1.10.2-cuda11.3-ubuntu20.04 /bin/bash
在這個(gè)命令中,run是創(chuàng)建容器的指令,-it是交互式終端,因?yàn)閯?chuàng)建的容器就相當(dāng)于一個(gè)本機(jī)中的linux服務(wù)器,我們可以通過終端與容器交互。–gpus all這個(gè)就是使用本機(jī)的gpu,–name是給新建容器取個(gè)名字, anibali/pytorch:1.10.2-cuda11.3-ubuntu20.04就是要使用的鏡像,/bin/bash是指定bash。
可以看到創(chuàng)建成功后,我們就直接進(jìn)入了容器中,處于容器的/app目錄下。
現(xiàn)在我們可以像使用云服務(wù)器那樣部署自己的項(xiàng)目了,比如在本機(jī)終端中(非容器中)使用docker cp 將本機(jī)中的項(xiàng)目復(fù)制到容器中的/app目錄下。
3.5 導(dǎo)出配置好項(xiàng)目的新鏡像
現(xiàn)在我們假設(shè)你已經(jīng)在容器中部署好自己的項(xiàng)目了,比如通過python train.py就可以訓(xùn)練,你想讓你的朋友也能體驗(yàn)這個(gè)項(xiàng)目,那就需要將你的容器打包為鏡像分享。
將容器打包為鏡像為命令是:docker commit -m "some information" <容器ID> <image_name:version>
容器ID可以通過docker ps -a
查詢到,該命令可以查詢所有容器信息。
image_name:version就是你自己定義的鏡像的名字和版本號(hào)。
4. 分享新鏡像
現(xiàn)在在本地已經(jīng)有新鏡像了,分享有兩種:直接將鏡像打包tar分享,或者上傳到云,類似本文使用熱心作者的鏡像那樣。
4.1 將鏡像導(dǎo)出為tar分享給他人
打包導(dǎo)出的命令:docker save image_naem:version -o output_name.tar
-o就是output,也就是將鏡像保存到什么文件。如果不報(bào)錯(cuò)在當(dāng)前目錄下能夠看到你的鏡像tar文件。
他人收到這個(gè)tar后,可以通過docker load -i xxx.tar
完成加載,如果成功了可以通過docker image ls
查看到該鏡像。
4.2 或者將鏡像推送到云倉庫
云倉庫可以自己去弄一個(gè),當(dāng)然dockerhub也可以免費(fèi)上傳分享,前提是你有一個(gè)dockerhub的賬號(hào)。
首先在終端需要登錄你的賬號(hào):docker login
根據(jù)提示填寫賬號(hào)和密碼。
首先給新鏡像打tag:docker tag new_image:version username/new_image:version
比如sudo docker tag pt_test_image:0 thgpddl/pt_test_image:0
注意,請將thgpddl替換為自己的用戶名,然后我們就可以看到新tag的鏡像(實(shí)際上發(fā)現(xiàn)tag的和之前的鏡像只是名字不一樣,其ID是一樣的)
然后開始上傳:docker push thgpddl/pt_test_image:0
然后等待上傳成功后(不過很可能出現(xiàn)408網(wǎng)絡(luò)問題,可以嘗試開啟代理試試),在dockerhub就可以看到上傳的鏡像了。
5. 使用新鏡像
在另一個(gè)安裝了docker的電腦上使用分享的鏡像很簡單。如果新鏡像上傳到了dockerhub,則和拉取鏡像一樣:docker pull username/image_name:version
。
如果是通過save保存到本地的,鏡像是一個(gè)tar包,則可以在新電腦上通過:docker load -i new_image.tar加載。
6. 跨平臺(tái)造成nvidia-smi不可用的問題
注意:因?yàn)閣indows的docker底層使用的WSL,和Linux不一樣,所以在一個(gè)平臺(tái)制作導(dǎo)出的鏡像在另一個(gè)平臺(tái)使用時(shí),可能會(huì)出現(xiàn)gpu不可用的問題。比如在Ubuntu系統(tǒng)加載Windows平臺(tái)導(dǎo)出的鏡像時(shí),通過docker run --gpus all創(chuàng)建容器后,在容器中使用nvidia-smi會(huì)報(bào)錯(cuò):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.
那么接下來的內(nèi)容就會(huì)詳細(xì)如何解決跨平臺(tái)無法使用GPU的問題
首先會(huì)出現(xiàn)問題的情況有(同平臺(tái)是沒有問題的,比如linux和linux,windows和windows):
取個(gè)名字 | 導(dǎo)出鏡像的平臺(tái) | 使用鏡像的平臺(tái) | 問題 |
---|---|---|---|
win2linux | Widnows | Ubuntu |
容器內(nèi)使用 nvidia-smi命令報(bào)錯(cuò):NVIDIA-SMI couldn’t find libnvidia-ml.so library in your system. |
linux2win | Ubuntu | Windows |
創(chuàng)建容器時(shí) 使用–gpus參數(shù)就會(huì)報(bào)錯(cuò):libnvidia-ml.so.1- file exists- unknown |
6.1 確認(rèn)是該問題
我在windows制作的鏡像在ubuntu加載后,無法使用nvidia-smi命令并報(bào)錯(cuò)NVIDIA-SMI couldn’t find libnvidia-ml.so library in your system.
,那么我們需要先確認(rèn)是不是本節(jié)要討論的問題,所以我們要在容器中查詢:
-
顯卡是否存在:
lspci | grep -i nvidia
(需要安裝sudo apt update && sudo apt install pciutils),可以看到存在顯卡2208(十六進(jìn)制),然后在顯卡查詢查詢2208得知是3080ti,就是我的顯卡。 -
顯卡驅(qū)動(dòng)是否存在:
cat /proc/driver/nvidia/version
,可以看到在容器中是可以查詢到宿主機(jī)的顯卡驅(qū)動(dòng)的,證明沒問題。
好了,說明顯卡和顯卡驅(qū)動(dòng)都是正常的,nvida-smi就是不可用
現(xiàn)在請根據(jù)自己是哪一個(gè)跨平臺(tái)情況按步驟解決把
6.2 win2linux問題如何解決?
本質(zhì)問題如圖,解決辦法就是重新建立軟鏈接,不過方法多樣而已。
6.2.1 手動(dòng)創(chuàng)建軟鏈接
在linux加載來自windows的鏡像后,進(jìn)入容器后使用nvidia-smi命令會(huì)報(bào)錯(cuò):
首先查詢軟鏈接情況:ldconfig
,看到很多l(xiāng)ibnvidia和libcuda軟鏈接為empty,所以我們目標(biāo)就是重新建立軟鏈接
進(jìn)入目錄:cd /usr/lib/x86_64-linux-gnu
,通過find -name "libnvidia-ml.so"
查詢顯卡驅(qū)動(dòng)515.43.04
,請記住這個(gè)。
建立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
是我的本機(jī)驅(qū)動(dòng),請改為自己的。
測試一下:
可以看到nvidias-mi命令可以用了,然后在python中導(dǎo)入torch后創(chuàng)建一個(gè)張量放到cuda上去沒有報(bào)錯(cuò),說明cuda可用了。
6.2.2 使用Dockfile自動(dòng)完成
Dockfile就是創(chuàng)建鏡像的一種方法,這里大概原理就是在鏡像1的基礎(chǔ)上,生成鏡像2,而在生成過程中自動(dòng)建立libnvidia-ml.so
和libcuda.so
的軟鏈接。
注意,與上面方法不同在于,上面的方法是從鏡像1創(chuàng)建容器后,到容器里手動(dòng)創(chuàng)建軟鏈接,然后該容器就可以使用cuda了。本節(jié)方法是在鏡像1上創(chuàng)建軟鏈接,得到新的鏡像,然后就可以直接從鏡像創(chuàng)建cuda可用的容器。
那么現(xiàn)在,比如我現(xiàn)在要在linux下使用來自windows的鏡像thgpddl/win_1050:0,首先建立createlink.sh文件,內(nèi)容如下:
# 查找libnvidia-ml.so.driver_version文件,并保存driver_version版本號(hào)
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大寫且沒有后綴),內(nèi)容:
FROM thgpddl/win_1050:0 # 優(yōu)先從docker image找,沒有就從dockerhub找
WORKDIR /app # 創(chuàng)建工作目錄/app
COPY ./createlink.sh . # 將本機(jī)的createlink.sh復(fù)制到鏡像中的/app目錄下,注意后面有個(gè)點(diǎn),代表WORKDIR文件夾
USER root # 切換root用戶
RUN cd /app && ./createlink.sh # 進(jìn)入 /app執(zhí)行createlink.sh
然后創(chuàng)建新鏡像:sudo docker build -t new_image_name .
注意最后的點(diǎn),是指Dockerfile所在的目錄(終端應(yīng)當(dāng)進(jìn)入Dockerfile和createlink.sh所在的目錄)
該Dockerfile大概意思就是使用thgpddl/win_1050:0作為基礎(chǔ)鏡像,然后在鏡像中創(chuàng)建/app文件夾,然后將createlink.sh復(fù)制到鏡像里,切換到root用戶,進(jìn)入/app目錄并執(zhí)行createlink.sh自動(dòng)創(chuàng)建軟鏈接。至此得到軟鏈接已經(jīng)創(chuàng)建的新鏡像了。
在實(shí)際使用中,createlink.sh是不用更改的,如果你想分享你的鏡像,那么直接給一個(gè)文件夾,其中包含createlink.sh和Dockerfile文件,Dockerfile第一行改為你的鏡像即可。
6.3 linux2win問題如何解決?
在windows加載來自linux的鏡像后,進(jìn)入容器后使用nvidia-smi命令會(huì)報(bào)錯(cuò):
從最后一行大概是說創(chuàng)建這個(gè)libnvidia-ml.so.1失敗,因?yàn)樗呀?jīng)存在。實(shí)際上這就是win2linux是我們創(chuàng)建的軟鏈接,可以在windows上它不能覆蓋,否則應(yīng)該能自動(dòng)覆蓋正常運(yùn)行了。既然windows的docker不能自動(dòng)覆蓋掉,我們幫它刪除即可。
基本原理如下,其實(shí)無論下面哪種方法都是要?jiǎng)h掉兩個(gè)so軟鏈接。
6.3.1 在WSL使用時(shí)手動(dòng)刪除軟鏈接
當(dāng)然很多時(shí)候鏡像發(fā)布者沒有考慮到這個(gè)問題,作者在ubuntu下使用–gpus配置好環(huán)境后直接發(fā)布了,所以我們需要自己去刪除libnvidia-ml.so.1和libcuda.so.1文件。
因?yàn)閱?dòng)命令使用–gpus all使用gpu,所以會(huì)調(diào)用libnvidia-ml.so.1,那么我們不使用gpu,應(yīng)該能創(chuàng)建容器,然后進(jìn)去刪掉該文件,然后再docker commit為鏡像,然后在新鏡像就能使用gpu了(此時(shí)沒有l(wèi)ibnvidia-ml.so.1,docker就能創(chuàng)建成功了)
大概意思如下圖,鏡像1和鏡像2的區(qū)別在于鏡像2刪掉了來自u(píng)buntu的libnvidia-ml.so.1(和libcuda.so.1)
首先不使用gpu創(chuàng)建容器:docker run -it --name container_name image_name /bin/bash
然后刪除兩個(gè)so文件:
(cmd) docker run -it --name container_name image_name /bin/bash
(容器內(nèi)) sudo rm -f /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.1
(容器內(nèi)) sudo rm -f /usr/lib/x86_64-linux-gnu/libcuda.so.1
然后形成新鏡像:docker commit -m "some information" container_name new_image_name:0
現(xiàn)在我們得到了一個(gè)可以在windows下正常使用的鏡像了:docker run -it --gpus all --name container_name new_image_name:0 /bin/bash
6.3.2 使用Dockerfile自動(dòng)完成
上節(jié)是在非gpu容器中刪除兩個(gè)so.1文件,然后制作為新鏡像就可以使用gpu。實(shí)際上我們也可以借助Dockerfile來自動(dòng)在創(chuàng)建新鏡像時(shí)刪除兩個(gè)so.1文件,一步到位。
Dockerfile文件的內(nèi)容:
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
然后創(chuàng)建新鏡像:sudo docker build -t new_image_name .
注意最后的點(diǎn),是指Dockerfile所在的目錄(終端應(yīng)當(dāng)進(jìn)入Dockerfile所在的目錄)
其實(shí)該Dockerfile很簡單,就是使用基礎(chǔ)鏡像thgpddl/linux2win:0,然后切換root用戶,刪除libnvidia-ml.so.1
和libcuda.so.1
,得到新的鏡像文件。
6.3.3 不使用–gpus all的容器打包(針對鏡像發(fā)布者)
因?yàn)閘inux的鏡像在windows使用報(bào)錯(cuò)是因?yàn)?code>創(chuàng)建libnvidia-ml.so.1失敗,因?yàn)樗呀?jīng)存在,libnvidia-ml.so.1是在ubuntu中使用gpu創(chuàng)建的。如果ubuntu中不使用gpu,就不會(huì)有l(wèi)ibnvidia-ml.so.1文件,那么就可以直接在windows上使用而不報(bào)錯(cuò)。
其實(shí)不用太復(fù)雜,我們在ubuntu容器中配置好自己的項(xiàng)目后,然后直接刪除兩個(gè)so.1文件后(確保你不再使用gpu,因?yàn)閯h除后在當(dāng)前容器就不能使用gpu),直接docker commit導(dǎo)出鏡像即可。因?yàn)樵撶R像本身沒有兩個(gè)so.1文件,所以可以直接在Windows或者其他linux中的docker中使用–gpus。
因?yàn)閡buntu鏡像在windows中報(bào)錯(cuò)是因?yàn)閣indows要產(chǎn)生so.1文件,但是ubuntu鏡像中存在so.1文件且windows無法強(qiáng)制覆蓋?,F(xiàn)在好了,我發(fā)布鏡像時(shí)直接打包時(shí)刪除so.1文件,在win下就不會(huì)報(bào)錯(cuò)了。文章來源:http://www.zghlxwxcb.cn/news/detail-449640.html
7. 總結(jié)
- WSL的鏡像在Linux下使用需要設(shè)置軟鏈接,Linux鏡像在WSL下使用時(shí)需要?jiǎng)h除軟鏈接。
- libcuda.so.version和libnvidia-ml.so.version,這里的libcuda.so.derver不是安裝cuda生產(chǎn)的,而是安裝驅(qū)動(dòng)生成的,提供該驅(qū)動(dòng)調(diào)用cuda的api。
8. 常見問題debug
- error during connect: in the default daemon configuration on Windows, the docker client must be run with elevated privileges to connectxxxxx:
可能是docker desktop自動(dòng)更新導(dǎo)致了一些問題,重啟docker就好了 - 解決Docker 一直starting 的辦法:解決Docker 一直starting 的辦法
- WIN10家庭版 找不到Hyper-V的解決辦法
- libGL.so.1
一般是安裝使用opencv造成的,直接
pip install opencv-python-headless
- 權(quán)限問題,在run指定參數(shù)–user=root也是可以解決的,但是可能也是文件夾本身需要chmod改權(quán)限
- windows中wsl是默認(rèn)放在c盤的,非常占用磁盤,【轉(zhuǎn)載】Win10/11 更改 WSL Docker Desktop 存儲(chǔ)路徑
- docker 問題
不斷重啟即可,要退出quit那種重啟
Reference
win10 WSL2 Docker 與 Linux Docker
WSL2上Docker打包的鏡像遷移到Ubuntu服務(wù)器上無法使用GPU
libcuda.so.1: cannot open shared object file: No such file or directory
WSL啟動(dòng)nvidia-docker鏡像:報(bào)錯(cuò)libnvidia-ml.so.1- file exists- unknown文章來源地址http://www.zghlxwxcb.cn/news/detail-449640.html
到了這里,關(guān)于基于Docker的深度學(xué)習(xí)環(huán)境NVIDIA和CUDA部署以及WSL和linux鏡像問題的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!