作为深度学习算法工程师,除了要跟论文、公式、数据、代码打交道,还有更基础、有时候也是最头疼的就是环境,环境操作的熟练程度对工作效率有重要影响。经过无数次的挖坑踩坑,有必要把常用的环境操作进行总结梳理。
为了支持多个不同的工程,只有一个环境一般是不够的,使用过的多环境工具有anaconda虚拟环境、pipenv、docker等,但在大规模工程实践中,docker在多系统移植、多平台支持和多用户复用和隔离等方面有着非常大的优势,所以应用更加广泛。本文主要介绍docker的基本操作,以及搭建好docker环境后,如何使用本地pycharm远程连接docker环境并在本地浏览器使用tensorboard进行可视化分析。
docker有两个基本概念:镜像(image)和容器(container),二者的关系类似于类和对象,一个是静态且固化的环境文件,一个是运行起来的、可以修改和操作的环境实例。docker的所有操作的前提是已经安装好docker软件,并且存在至少一个基础镜像,基础镜像可以从远程镜像仓拉取或者自行下载。
下面从基于一个基础镜像启动一个新的容器开始:
查看本地镜像的命令是:
docker images
可以看到镜像名称、ID、创建时间、size和标签(TAG),假设有一个镜像名称和标签为image_test:1.0
创建容器的命令有:
docker run
或docker create
二者都是基于一个镜像创建新的容器,区别是docker create只创建不启动,而docker run创建后启动。
注意在docker19版本之前,docker是不支持gpu的,因而无法使用nvidia显卡,如果需要使用nvidia,需要使用nvidia-docker命令,而docker19版本之后不需要。假设我们要创建一个名为container_test的容器并启动它:
nvidia-docker run --name container_test image_test:1.0
如果需要启动后直接进入容器,则需要加上-it参数
如果需要外部访问容器,需要用-p参数设置容器与服务器之间的端口映射。由于后续要使用pycharm(默认22端口)和tensorboard(默认6006端口),所以至少需要设置两个端口映射。
如果需要挂载服务器地址,需要用-v参数设置容器与服务器之间的地址映射。由于需要使用docker运行服务器中/home/my_name/projects下的代码,所以需要将这个地址映射到容器内,假设新建一个/workspace
由于默认给docker分配的共享内存只有64M,所以需要用--shm-size调整大小。
所以完整命令可以写为:
nvidia-docker run --name container_test --shm-size="32g" -v /home/my_name/projects:/workspace -p 40010:22 -p 40011:6006 -it image_test:1.0
要注意这些设置一旦设置后不能修改,只能重新创建一个新的容器。
现在系统中已经存在创建好的容器,部分容器已启动,查看所有启动中的容器的命令是:
docker ps
查看所有容器(包括未启动)的话,加上-a 参数
假设要启动、停止、重启我的容器:
docker start container_test
docker stop container_test
docker restart container_test
进入启动中的容器:(停止的容器不能进入,需要先start)
docker exec -it container_test bash
docker attach container_test
这两个命令都可以进入容器,但是是有区别的。假设已经有一个终端进入容器,exec命令是在另一个终端重新开一个进程,和原进程彼此间互不影响,而attach命令是在另一个终端进入同一个进程,两个终端的信息将同步显示。
退出容器:
exit
ctrl+d
ctrl+p ctri+q
前两个命令退出后容器停止,第三个命令退出后容器仍在运行,此时再进入容器适合使用attach命令。
如果需要代理上网,则应该进行一些docker的代理配置。具体操作可以参见以下博客:
docker代理配置详解_、Dong的博客-CSDN博客
因为docker容器是一个独立的环境,所以像pip源的设置需要重新设置一下,方法一是修改~/.pip/pip.conf文件,如设置为豆瓣源:
[global]
timeout = 60
index-url=http://pypi.douban.com/simple
trusted-host=pypi.douban.com
方法二是直接用命令行设置:(如阿里源)
pip config set global.trusted-host mirrors.aliyun.com
pip config set global.index-url https://mirrors.aliyun.com/simple
另外还有以下备选:
清华大学 :https://pypi.tuna.tsinghua.edu.cn/simple/
中国科学技术大学 :http://pypi.mirrors.ustc.edu.cn/simple/
华中科技大学:http://pypi.hustunique.com/
豆瓣源:http://pypi.douban.com/simple/
腾讯源:http://mirrors.cloud.tencent.com/pypi/simple
华为镜像源:https://repo.huaweicloud.com/repository/pypi/simple/
假设现在已经在容器中进行了很多操作,包括安装了新的工具包、更新了代码等,需要迁移到新的服务器或者提供给其他用户使用,就需要进行容器固化,固化即生成新的镜像。
这里要注意一点,假如希望固化的镜像中保存自己的代码,必须将代码从挂载的地址拷贝到自有地址,因为挂载地址的内容是不会被固化的。
假设现在需要将container_test容器固化为image_test:2.0(不加标签则默认标签为latest),命令为:
docker commit container_test image_test:2.0
如果需要加入一些commit信息,则可用-a输入作者,-m输入提交信息。
这里需要说明的是,因为docker镜像是分层(layer)存储的,每一层依赖上一层,只保存新增信息。假如用commit命令生成镜像,则在原有镜像中加入了一个新的layer,即使这一层的操作是删除了部分内容,整个镜像的体积仍然在增大。一开始看到commit,博主想到git中也有commit操作,并且可以回退,但docker的commit是不存在回退操作的,而是每次commit生成一个新的镜像,原有镜像保持不变。假如不想保存本次commit的修改,只能删除本次保存的镜像,从原有镜像开始重新操作。
commit后可以查看镜像的修改记录:
docker history image_test:2.0
由于使用commit生成镜像会导致镜像变得臃肿,所以工程中更推荐使用DockerFile构建镜像,关于DockerFile的使用方式网上的教程很多,本文不详细展开。
镜像生成后,在系统中仍然是分层存储的,具体的存储方式比较复杂,不能直接拷贝和移植。如果要进行跨服务器拷贝和传输,需要先将镜像导出为tar文件,假设为image_test.tar,命令如下:
docker save -o image_test.tar image_test:2.0
或docker save image_test:2.0 > image_test.tar
则在当前目录生成了image_test.tar文件,可以进行拷贝和传输。
在目标地址可以进行镜像的导入,导入完毕后可以由docker images命令查看到。导入命令为:
docker load -i image_test.tar
或 docker load < image_test.tar
还有一种导出和导入的方式为:
导出:
docker export -o image_test.tar image_test:2.0
或docker export image_test:2.0 > image_test.tar
导入:
docker import image_test.tar 新镜像名
或docker import 新镜像名 < image_test:2.0
export/import和save/load的区别是前者保存的是容器的快照,只包含容器当前信息,不包括任何历史记录,体积比较小,后者保存的是一个分层的文件系统,包含所有历史记录,体积比较大。
在工程项目中,本地镜像经常需要和远程镜像仓进行交互,所以需要使用push(推送)和pull(拉取)操作,同样与git的相关操作比较类似,push和pull的内容都是新修改的layer。
首先需要配置自己的远程镜像仓。
sudo vi /etc/docker/daemon.json
然后在“insecure-registries”列表中加入镜像仓地址,并更新daemon文件和重启docker
sudo systemctl daemon-reload
sudo systemctl restart docker
如果要拉取镜像,默认从Docker Hub中拉取,如果配置了自己的远程镜像仓,可从自己的远程镜像仓拉取:
docker pull centos
docker pull 远程镜像仓地址/镜像名:TAG
如果要推送镜像,需要先给镜像重命名为远程镜像仓地址/镜像名:TAG,然后再推送,如:
docker tag image_test:2.0 远程镜像仓地址/镜像名:TAG
docker push 远程镜像仓地址/镜像名:TAG
我们还可以建立自己的镜像仓,提供给别人拉取,方法是拉取Docker Hub中的registry镜像,并启动一个容器,设置端口映射和地址映射:
docker run --name registry -d -p 5000:5000 --restart=always -v 本地地址:/var/lib/registry registry
自己的IP地址:5000即为自己的镜像仓地址。
docker的基本操作就介绍到这里,下面介绍一下配置好docker环境后如何在本地使用pycharm和tensorboard
首先注意pycharm只有专业版才有SSH功能。可以用pycharm打开并编辑本地代码工程,远程连接docker的解释器,所有的修改都将即时地同步到docker中的映射路径,可以顺畅地使用pycharm各种功能。
docker内需要进行以下操作:
1.启动并进入docker
2.在docker中安装openssh:
apt-get install openssh-server
3.修改ssh_config,把PermitRootLogin改为yes
vi /etc/ssh/sshd_config
4.重启ssh服务:
service ssh restart
5.设置root密码:执行passwd,输入密码
pycharm中需要以下操作:
1.进入tools/deployment/configuration,新建SFTP连接方式,输入连接名
2.在弹出的SSH configuration页面填入远程连接的host地址、端口号、用户名(root)和刚才设置的root密码,点Test Connection按钮,没问题即连接成功。如果直接连到服务器,即填入22端口,如果连到服务器中运行的docker,需要填入本文1.2节创建容器时设置的映射到容器22端口的40010端口。
3.进入File/settings/Project/Python Interpreter,点击解释器的设置按钮,增加新的解释器
4.在弹出的Add Python Interpreter页面中选择SSH Interpreter,再选择Existing environment,选择刚才设置的SSH地址
5.在弹出页面中填入docker中的Interpreter地址。默认为/usr/bin/python,也可以是别的地址,如果docker内使用anaconda装的三方件,可能为/root/anaconda3/bin/python,可以用whereis python命令查看
6.在同页面的Sync folder中填入本地工程地址和docker中同步的地址,确认即可。
注意以上操作在代码运行时实际上跑的是docker中的代码,如果生成新的文件,本地是看不到的,可在tools/deployment选项中手动同步,但本地的修改是自动同步到docker中的。实际使用中可以将较大的数据文件都放在服务器上映射到的docker地址中,只把需要编辑的代码在本地拷贝一份,避免占用太多空间。
另外注意如果程序在运行中,一定要保证docker也在运行状态,一旦docker停止,程序也将停止。
因为tensorboard保存的文件需要在浏览器中查看,而docker一般不支持图形界面显示,所以无法在docker中直接看到tensorboard可视化效果。
解决的方法有两种,一种是在docker外面的服务器上安装tensorboard和chrome浏览器(之前博客介绍过:苹果姐:pytorch使用tensorboard实现可视化总结),但这种方法比较繁琐,因为tensorboard依赖的环境比较多,包括tensorflow/python等。
另一种方法是在服务器或本地做端口映射,如果服务器上没有安装chrome,在本地做端口映射比较方便。
首先在docker中运行:
tensorboard --logdir my_path
假设生成的地址为127.0.0.1:6006(tensorboard默认端口号为6006),则在本地cmd窗口运行:
ssh -L 6006:127.0.0.1: 40011 用户名@服务器IP地址
此处的40011为本文1.2节创建容器时设置的服务器上映射到docker6006端口的端口号。
然后在本地浏览器输入127.0.0.1:6006即可看到tensorboard可视化效果。
以上即为深度学习工程实践中常用的环境操作,希望对大家有所帮助。