WSL下基于Docker的深度学习环境配置
为了兼顾学习和生活,之前在个人电脑上做了双系统,ubuntu用来跑模型,windows来玩游戏。这学期毕设实验做完之后,就没怎么开过ubuntu了。这两天邻近毕业准备做一点康复训练,但ubuntu又不太适合边跑小模型边摸鱼,所以打算在windows下用WSL做一个开发环境。另外这回不想用conda做环境隔离了,就干脆直接配了一套Docker的环境。(真希望所有开源代码实现都能搞个镜像让我直接pull下来啊)
WSL安装
windows侧的WSL安装基本按照官方文档:https://docs.microsoft.com/zh-cn/windows/wsl/install
· 首先安装适用于Linux的windows子系统。Linux发行版本可选,我选用了Ubuntu。
wsl --install -d Ubuntu
安装完成之后可以使用wsl -l -v
查看:
需要注意的是VERSION这一列的值,1/2表示WSL1和WSL2,只有WSL2支持GPU,所以如果是1的话需要手动拉到WSL2。使用wsl --set-version <distro name> 2
进行升级。这边需要开启windows的虚拟机功能。
这里应该还需要去bios里开启虚拟机,我的是微星主板,AMD 3700X,我的修改方式是进入BIOS页面后,找到“OC”——“SVM Mode” 选项,把“Disabled”修改为“Enabled”。完成设置之后,在任务管理器里可以看到
到这一步后,可以在终端输入wsl尝试进入,我在这一步还会有一步报错:
请启用虚拟机平台 Windows 功能并确保在 BIOS 中启用虚拟化。
解决方案是bcdedit /set hypervisorlaunchtype auto
。 输入后再次重启WSL就可以正常进入了。
Windows下CUDA安装
Windows环境下需要安装一下对应的显卡驱动,我忘了是我之前配的环境还是Nvidia Geforce Experience自己给我装的驱动了,我的机子上是已经有对应的驱动了,表现反应是命令行里nvidia-smi是有反馈输出的。
如果是从零开始配置的话可以根据官方文档进行配置:https://docs.nvidia.com/cuda/cuda-installation-guide-microsoft-windows/index.html
Docker 安装 & Nvidia Docker踩坑
最开始安装的是windows下的Docker Desktop,普通的镜像安装进入没有什么问题,但在按照Nvidia官方教程配置之后使用--gpus all这个指令的时候遇到报错
exit status 1, stdout: , stderr: nvidia-container-cli: initialization error: driver error: failed to process request\\n\""": unknown
开始是按照11.7.0的CUDA文档进行的安装:https://docs.nvidia.com/cuda/archive/11.7.0/wsl-user-guide/index.html#installing-nvidia-drivers
wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-wsl-ubuntu.pin sudo mv cuda-wsl-ubuntu.pin /etc/apt/preferences.d/cuda-repository-pin-600 wget https://developer.download.nvidia.com/compute/cuda/11.7.0/local_installers/cuda-repo-wsl-ubuntu-11-7-local_11.7.0-1_amd64.deb sudo dpkg -i cuda-repo-wsl-ubuntu-11-7-local_11.7.0-1_amd64.deb sudo apt-get update sudo apt-get -y install cuda
最后是上面那个报错,这个方案没有走通,但中间碰到了一个小问题也记录一下,主要问题就是在解压deb包的时候由于没有公钥,无法验证签名
W: GPG error: file:/var/cuda-repo-wsl-ubuntu-11-7-local InRelease: NO_PUBKEY FAF69C646FF368B7
当时忙着解决问题,忘记截图了,大概是这么个报错,就是PUBKEY会不一样,解决方案是去请求一个公钥回来,用sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys FAF69C646FF368B7
。对于不同的系统keyserver也不一样。
对比了一下deb包和电脑上的驱动的版本,发现我自己的是11.6.1的CUDA版本,觉得有可能是版本不同的问题,于是去找了11.6.1版本的文档:
https://docs.nvidia.com/cuda/archive/11.6.1/wsl-user-guide/index.html#installing-nvidia-drivers
命令如下:
curl https://get.docker.com | sh distribution=$(. /etc/os-release;echo $ID$VERSION_ID) curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list sudo apt-get update sudo apt-get install -y nvidia-docker2
这版本的安装和11.7.0略微不太一样,需要安装一个nvidia-docker2,跟着安装之后,在service docker stop/start
一步遇到了问题:
docker: unrecognized service
找了很多回答都是建议删除docker重新安装的,之后我尝试了不使用Docker Desktop而在WSL里直接用apt-get install docker.io的,但报错信息一样。之后又找啊找啊找,找到了另一个在WSL下安装linux原生docker的方法,命令如下:
curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh sudo service docker start
这种方法安装后可以通过service docker stop/start
启停docker服务, 之后再根据11.6.1的文档安装nvidia-docker2。用
docker run --gpus all nvcr.io/nvidia/k8s/cuda-sample:nbody nbody -gpu -benchmark
进行测试。可以调用GPU。
VS Code 开发环境配置
由于没有使用Docker Desktop,所以在VS Code里的远程资源管理器里无法找到WSL里启动的容器,为了使开发的过程更加遍历,选择使用ssh进行连接。
这里我拉了一个tensorflow1.4.1+py2.7的镜像,后续都以这个镜像为例。
docker run -it -d --name CAN --net host --gpus all -v /mnt/d/wsl_workspcae:/workspace can:gpu_v3 tail -f /dev/null
-d
使容器在后台运行,tail -f /dev/null
使容器有个事做,免得没事被kill掉。--net host
把里面的端口全都映射出来,这里其实只要映射ssh的22端口即可,我偷懒而且也不跑什么网络相关的项目,就全映射出来了。--gpus all
使容器内能调用GPU。这样就只需要在容器内部开一个ssh服务就可以在windows下的VS Code中进行开发了。
ssh服务配置
和纯Ubuntu环境下一样,只需要安装openssh-server即可
sudo apt update # 更新源 sudo apt install openssh-server # 安装ssh服务 sudo service ssh start/stop/restart
这里需要配置一下/etc/sshd_config 中的PasswordAuthentication,默认应该是no,改成yes,否则外面使用密码连接会有报错。
为了方便外面连接,还做了一个免密登录,主要是通过ssh-keygen生成公钥和密钥,在Docker的~/.ssh文件夹下新建一个authorized_keys,把id_rsa.pub粘到里面,在windows下的C:\Users{username}\.ssh文件夹下放id_rsa文件,之后在对容器内的文件授权,chmod 600 authorized_keys
,chmod 700 .
(当前目录为~/.ssh)。
在/etc/sshd_config中的相关配置如下:
RSAAuthentication yes PubkeyAuthentication yes AuthorizedKeysFile %h/.ssh/authorized_keys
至此就可以不用密码进行ssh连接了。
啊,还有一个是wsl的地址,需要用到:https://www.ohyee.cc/post/note_wsl2_net文章里提到的方法进行获取和自动写入。
ip addr show eth0 | grep 'inet ' | cut -f 6 -d ' ' | cut -f 1 -d '/')
是用来获取容器地址的方法。每次网络变化生成的ip地址都不同,所以需要自动获取并写入,方法如下:
需要修改的文件为hosts(C:\Windows\System32\drivers\etc\hosts),在文件最后写入
123.123.123 winip 123.123.123 wslip
这边开始写入的两个地址没有关系,因为后续会覆写掉。同时需要Windows 设置可写入,右键 host 文件,属性-安全-高级,把 Windows 登录用户给予“完全控制”权限即可。
之后在wsl的 ~/.local.bashrc文件中增加
# WSL export WINIP=$(cat /etc/resolv.conf | grep 'nameserver' | cut -f 2 -d ' ') export WSLIP=$(ip addr show eth0 | grep 'inet ' | cut -f 6 -d ' ' | cut -f 1 -d '/') vim "+:%s/^.*winip/$WINIP\t\twinip/g" "+:%s/^.*wslip/$WSLIP\t\twslip/g" '+:wq' -E /mnt/c/Windows/System32/drivers/etc/hosts
即可完成自动配置。
进入WSL自动启动容器并启动ssh服务
每次想要连接进容器都要先启动wsl,再进入容器开启ssh,这样会显得挺麻烦的。为了方便,在容器内的bashrc和wsl的bashrc分别配置了一些脚本,这样每次进入加载~/.bashrc的时候都会自动进行连接。
WSL侧:
function can_env(){ service docker start sleep 3 docker start CAN sleep 3 docker exec -itd CAN /bin/bash } can_env
加入如下脚本,每次开启wsl会以非交互的形式进入CAN(配置的环境)
容器侧:
function check_ssh(){ SERVICE="ssh" netstat -anp | grep $SERVICE &> /dev/null if [ $? -eq 0 ] then echo "already exists" else echo "starting" service $SERVICE start fi } check_ssh
加入一个服务检测,检测是否启动了ssh服务,若无,启动。
配置完成之后,当启动wsl并加载bashrc之后,系统会进入容器并加载容器内的检测启动脚本,从而实现自动化。