感谢 @田文善 同学供稿

目录

一、需求二、方案(一)docker 配置1、dockerfile 制作2、构建 docker 镜像3、运行 docker 容器4、使用 docker 容器5、环境复用(二)sshfs 配置1、安装 vieux/sshfs 插件2、通过 vieux/sshfs 驱动创建数据卷3、启动容器时指定挂载这个数据卷三、总结

一、需求

想象一下这样的场景,某天,你登上你常用的服务器,打算运行一下你早已开发调试完成的程序,但是和以往不同的是,这一次你一运行,就报了一大堆的错误。你各种排查,最后发现原因是服务器上你程序依赖的库的版本被同事给改了,,而你还不能擅自改回来,因为改动后同事的程序就没法运行了。

另一个场景,某天,你好不容易实现了一个新的想法,兴冲冲地想去训练一下看看,你登上你常用的服务器,发现显卡资源全都被占满了。等?等是不可能等的。于是你马不停蹄地向另一台空闲的服务器上迁移你的项目,等你花了大半天的时间,废了九牛二虎之力,在另一台服务器上重新配置了环境、传输了数据集、拷贝了代码,并做完了完整性校验。然后发现这台服务器又被另一个同事占满了,只剩你面对终端凌乱。

你吸取了教训,在所有的服务器上都配置了环境,放上了常用的数据集和代码,这下哪台服务器空闲我就用哪台总没问题了吧。但是随着时间的推移,你自己也分不清各台服务器上的代码,哪份是最新的,哪份是弃置的了。更严重的是,每台服务器上你都保存了一份数据集,占用了大量的磁盘空间,导致部门服务器的磁盘空间不足,经常要你清理。

上面的场景是我的真实写照,如果你也遇到过以上的一种或多种的问题,或者你只是想拥有一个方便纯粹的开发环境,那可以接着看下去,我们会设计一个开发环境搭建流程,使得我们的开发环境具有以下几个特点:

  1. 沙盒:环境只利用服务器系统的内核与驱动等少数底层资源,不管服务器的系统发生什么变化都不会影响到我们的环境,同样地,不管我们的环境有什么变化,也不会影响到服务器的系统环境;

  2. 一键迁移:如果要迁移我们的项目到一台新服务器上,只需要一两行代码,就可以完全复现我们的环境,并且和原来的环境完全一致,包括我们常用的工具和配置也可以完全还原;

  3. 无冗余:我们的数据和代码只需要保存一份,就可以在各个服务器上调用;

二、方案

大家应该已经猜到了,我们主要用到的工具就是 docker 和 sshfs。

(一)docker 配置

用 docker 配置深度学习开发环境的文章有很多,比如:

  • Docker+VSCode 配置属于自己的炼丹炉

  • PyCharm+Docker:打造最舒适的深度学习炼丹炉

  • Docker,救你于「深度学习环境配置」的苦海

等等。

这些文章的 Docker 配置方式相似,我也从这些文章中受益良多,但是他们的配置方式不能满足我的需求,主要体现在:

  1. 以 root 权限运行:在这些文章里,docker 容器里的进程是用 root 用户运行的,而且这个 root 用户即为服务器的 root,合适的条件下,这些进程有权限控制宿主机中的一切,这是非常危险的;另外,外界查询这些进程的启动用户都为 root,有一定的匿名性,不方便管理;还有以 root 运行进程,经常会导致文件读写权限的问题,很不方便;

  2. 没有图形化转发配置:我是做 CV 的,我的程序在 docker 里运行,有时候我们想通过以图形化展示的方式看看中间结果,但是这些文章中的配置方法没有进行图形化转发;

  3. 复用性不够:这些文章的 docker 配置是在已有的容器中操作的,没有写 dockerfile,时间长了做了哪些操作可能就忘了,不好复现,而且如果容器丢失,所有操作都要再来一遍;

下面就跟着我一起来配置 docker 吧。

docker 和 nvidia-docker 的安装和基本操作,本文不会介绍,请参考官方文档或者其他资料,这里给两个官网链接:docker install、nvidia-docker quickstart。

我们选择 dockerfile 来配置 docker 镜像,这样方便维护我们的镜像。连接 docker 容器的方式我们选择 ssh ,这样可以适配更多的 IDE并且更灵活,使用的时候把 docker 容器当作一个独立的服务器来用就可以了。

1、dockerfile 制作

下面会介绍一下我们的 dockerfile 具体有哪些内容,这么写的目的是什么,如果不想了解的话可以直接拉到最后查看完整的 dockerfile。

(1) 选择镜像基底

我们不会从零搭建我们的镜像,有很多官方的镜像给我们选择,比如对我来说,cuda、cudnn、pytorch是必装的,所以我选择在 pytorch/pytorch:1.5-cuda10.1-cudnn7-devel 这个官方镜像的基础上搭建我们的镜像,这样我们 ubuntu、cuda、cudnn、pytorch、conda 都可以直接用了。dockerfile 写法:

1FROM pytorch/pytorch:1.5-cuda10.1-cudnn7-devel

(2)设置 apt、conda、pip代理

由于众所周知的原因,我们访问国外的源速度很慢,或者有时候都无法连接,所以我们先设置一下代理,在 dockerfile 中写:

1# 设置apt、conda、pip代理镜像2RUN sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list && apt-get clean && \3    /opt/conda/bin/conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ && \4    /opt/conda/bin/conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ && \5    /opt/conda/bin/conda config --set show_channel_urls yes && \6    /opt/conda/bin/pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

这样我们的 apt、conda 和 pip 就可以很快了。

(3)开启终端色彩

运行 docker 容器的时候,终端命令行没有颜色,看的很不舒服啊,这是因为 docker 里默认没有配置 TERM 导致的,所以我们配置一下终端就有颜色了:

1# 开启终端色彩2ENV TERM=xterm-256color

(4)制作完整版 ubuntu

官方的镜像为了控制 docker 的大小,用的是精简版的 ubuntu,很多常用的工具都没有安装,我们既然是作为开发环境来使用,完全可以恢复成完整版的 ubunutu:

1# 制作完整版 ubuntu2RUN export DEBIAN_FRONTEND=noninteractive && \3    bash -c 'yes | unminimize'

(5)apt 安装常用软件

为了用 ssh 连接我们的容器,我们需要安装 openssh-server;为了方便权限管理,我们需要安装 sudo;其他常用的软件可以根据个人习惯在这一步一起安装了。最后 rm -rf /var/lib/apt/lists/* 是为了删掉 apt 的缓存以减小镜像大小。

 1# apt安装常用软件 2RUN apt-get update && apt-get install -y --no-install-recommends \ 3         build-essential \ 4         cmake \ 5         git \ 6         curl \ 7         ca-certificates \ 8         libjpeg-dev \ 9         libpng-dev \10         sudo \11         openssh-server \12         bash-completion \13         vim \14         vim-gnome \15         zsh \16         tmux \17         ranger \18         xsel \19         mediainfo \20         proxychains4 \21         feh \22         apt-transport-https && \23         rm -rf /var/lib/apt/lists/*

(6)设置 ssh X11 转发

为了能转发图形化界面,我们需要修改 /etc/ssh/sshd_config 的设置:

1# 设置X11转发(把/etc/ssh/sshd_config 中的X11Forwarding置为yes,X11UseLocalhost置为no)2RUN sed -i "s/^.*X11Forwarding.*$/X11Forwarding yes/" /etc/ssh/sshd_config && \3    sed -i "s/^.*X11UseLocalhost.*$/X11UseLocalhost no/" /etc/ssh/sshd_config45EXPOSE 22

(7)新建用户替代 root 并用 fixuid 管理 uid

具体的原理可以参考我之前的博客:Docker 容器内用户管理,在这里我直接使用文章中的 dockerfile:

 1# 新建用户并用 fixuid 管理 uid 2ENV USERNAME="docker" 3ENV PASSWD="123456" 4RUN useradd --create-home --no-log-init --shell /bin/zsh ${USERNAME} && \ 5    adduser ${USERNAME} sudo && \ 6    echo "${USERNAME}:${PASSWD}" | chpasswd 7RUN USER=${USERNAME} && \ 8    GROUP=${USERNAME} && \ 9    curl -SsL https://github.com/boxboat/fixuid/releases/download/v0.4.1/fixuid-0.4.1-linux-amd64.tar.gz | tar -C /usr/local/bin -xzf - && \10    chown root:root /usr/local/bin/fixuid && \11    chmod 4755 /usr/local/bin/fixuid && \12    mkdir -p /etc/fixuid && \13    printf "user: $USER\ngroup: $GROUP\n" > /etc/fixuid/config.yml1415USER ${USERNAME}:${USERNAME}

(8)配置环境变量,使ssh连接时 env 也生效

官方的镜像用 ENV 配置了很多的环境变量,我们用 docker run 或者 docker attach 的时候这些环境变量能正常生效,但是当我们用 ssh 连接的时候,这些环境变量的配置就失效了,具体的原因见这篇文章,为了使 ssh 的时候也生效,我们做下面的操作:

1RUN sed -i '$a\export $(cat /proc/1/environ |tr "\\0" "\\n" | xargs)' .zshrc

(9)[可选] 个人常用工具个性化配置

个人的常用工具 zsh、vim、tmux、ranger 有一些个性化配置,我也一起写到 dockerfile 里了,这样每次生成的镜像里都有我用着顺心的工具了。

 1# 安装配置oh-my-zsh 2RUN sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" && \ 3    curl "https://raw.githubusercontent.com/tianws/config/master/zsh/themes/robbyrussell.zsh-theme-server" -o .oh-my-zsh/custom/themes/robbyrussell.zsh-theme && \ 4    git clone https://github.com/zsh-users/zsh-autosuggestions .oh-my-zsh/custom/plugins/zsh-autosuggestions && \ 5    git clone https://github.com/zsh-users/zsh-syntax-highlighting.git .oh-my-zsh/custom/plugins/zsh-syntax-highlighting && \ 6    sed -i "s/^plugins=.*$/plugins=(git colorize cp copydir z zsh-autosuggestions zsh-syntax-highlighting)/" .zshrc 7 8# 配置vim 9RUN curl "https://raw.githubusercontent.com/tianws/config/master/vim/vimrc" -o .vimrc1011# 配置tmux12RUN curl "https://raw.githubusercontent.com/tianws/config/master/tmux/tmux_server.conf" -o .tmux.conf1314# 配置ranger15RUN ranger --copy-config=all && \16    curl "https://raw.githubusercontent.com/tianws/config/master/ranger/rc.conf" -o .config/ranger/rc.conf && \17    curl "https://raw.githubusercontent.com/tianws/config/master/ranger/scope.sh" -o .config/ranger/scope.sh

(10)ENTRYPOINT 和 CMD

最后就是 dockerfile 中的 ENTRYPOINTCMD了,在 docker 容器启动的时候,会自动运行 ENTRYPOINTCMD 的命令,如果 docker run 的时候指定了命令,该命令会作为参数接在 ENTRYPOINT 后,并替换 dockerfile 里 CMD 的命令。

这里我们在 ENTRYPOINT 中配置 fixuid 命令,它会结合 -u 参数帮我们管理用户 uid。在 CMD 命令中默认启动 ssh 服务,并用 zsh 持久化,这样就不用像其他文章中那样手动进入容器里启动 ssh 服务了。

1ENTRYPOINT ["fixuid"]2CMD echo ${PASSWD} | sudo -S service ssh start && /bin/zsh

完整的 dockerfile 见链接。

2、构建 docker 镜像

进入 dockerfile 文件夹,运行 docker build 命令:

1docker build -t pytorch1.5-cuda10.1-cudnn7 -f pytorch1.5-cuda10.1-cudnn7.dockerfile .2# -t:指定镜像的名字3# -f:指定dockerfile4# 如果网络不好,可以配置代理:5## --build-arg http_proxy=http://ip:port6## --build-arg https_proxy=http://ip:port

然后运行 docker images 就可以看到生成的镜像了。

3、运行 docker 容器

在服务器上,运行 docker run 命令:

 1docker run --gpus all --ipc=host  -p 49154:22 -it -u $(id -u):$(id -g) -h dd-docker -v /ssd/tianws:/ssd/tianws --restart=always pytorch1.5-cuda10.1-cudnn7 2# --gpus all:启动 docker 时启用所有的 GPU 3# --ipc=host:增加主机与容器共享内存,pytorch 需要 4# -p 49154:22:将服务器的 49154 端口映射到容器的 22 端口 5# -it:分配 tty 设备支持终端登陆并打开 STDIN,用于控制台交互 6# -u $(id -u):$(id -g):指定容器的用户 7# -h dd-docker:给容器的 hostname 起名为 dd-docker 8# -v /ssd/tianws:/ssd/tianws:将服务器的 /ssd/tianws 目录挂载到容器的 /ssd/tianws 目录 9# --restart=always:总是在容器停止时重启容器10# pytorch1.5-cuda10.1-cudnn7:镜像名

docker run 没有指定命令,所以容器自动执行 dockerfile 里的命令:fixuid /bin/sh -c echo ${PASSWD} | sudo -S service ssh start && /bin/zsh,会自动启动 ssh 服务后进入 zsh 终端。

如果想保持容器运行并退出终端,按 CTRL+P+Q;

如果想重新进入容器的 zsh 终端,执行 docker attach containerID

4、使用 docker 容器

本方案使用 ssh 连接容器,经过上面的配置,我们的容器已经启动并启动容器里的 ssh 服务了(可以在宿主机上通过 docker ps -a 查看容器状态),这个时候,我们就可以把容器当作一个只有你一个用户的服务器来使用了。

在任何能联通宿主机网络的地方(包括宿主机本身),通过 ssh 连接容器:

1ssh -X USERNAME@ip -p port2# 在我们上面的例子中即为:3ssh -X docker@dd -p 49154

然后就可以登陆一台个人专属的“服务器”了,这个服务器已经提前配置好了需要的软件和工具,并且和宿主机的系统环境隔离,无论我们对容器系统环境做什么操作,都不会影响宿主服务器的系统。

而且通过 ssh 的 X11 图形转发,我们甚至可以在本机使用 docker 容器里的 firefox 浏览器。

5、环境复用

经过上面的操作,我们有了一个比较完备的基础镜像,如果想要复用这个基础镜像,有三种方法:

  1. 在要复用的宿主机上再次执行 docker build 命令用 dockerfile 生成镜像即可;

  2. 用 docker save 命令把镜像打包,再在宿主机上用 docker load 加载即可,也可以结合 ssh 和 pv 命令,一个命令完成从一台机器到另一台机器的迁移:

    1# 导出镜像并压缩2docker save  imageName:tag | gzip > imageName-tag.tar.gz3# 加载镜像4gunzip -c  imageName-tag.tar.gz | docker load5# 可以结合 ssh 和 pv 命令,一个命令完成从一个机器将镜像迁移到另一个机器,而且带进度条6docker save  | bzip2 | pv | ssh @ 'cat | docker load'
  3. 利用 docker hub 或者自己搭建的内网 Docker Registry 进行镜像分发;这个教程很多,这里就不赘述了。

有基础镜像还不够,有时候我们需要根据项目,在基础镜像上再加装其他的环境,这个时候也有三种方法:

  1. 修改 dockerfile ,生成新的镜像,复用方法同上:

    这种方式适合改动有普适性的环境,镜像的生成历史明明白白,便于维护;

  2. 直接在容器中改动,再用 docker commit 生成新的镜像,复用方法同上:

    这种方式不用写 dockerfile,比较方便,但相应的,镜像历史不清晰,不便于维护。

  3. 直接在容器中改动,但不生成新的镜像,复用的时候直接将容器快照导入新机器作为新的镜像:

    1# 导出容器并压缩2docker export  | gzip > .tar.gz3# 导入容器为新的镜像4gunzip -c .tar.gz | docker import5# 同样结合 ssh 和 pv 命令,写一个命令完成从一个机器将容器迁移为另一个机器的镜像,而且带进度条6docker export  | bzip2 | pv | ssh @ 'cat | docker import'

    这种方式舍弃了镜像的所有历史信息,只保留当前容器的快照,无法维护,但是这种方式迁移的镜像体积要小很多。

(二)sshfs 配置

上面我们利用 docker,有能力在新服务器上用一两行命令快速生成一套开发环境,这套开发环境自动帮我们配置好了常用的工具,可以直接使用,而且这套环境和宿主机隔离,不用担心和项目环境和宿主机的环境兼容问题。

那接下来就还有一个问题,就是数据和代码复用的问题。我们如何做到数据和代码只需要保存一份,就可以在各个服务器上调用?

举个例子,我的数据和代码都保存在 A 服务器的 /ssd/tianws 目录下,现在我在 B 服务器上用上面的方式启动了 docker 容器,里面有所有需要的开发环境,那我如何在 docker 里调用到 A 服务器上的数据和代码呢?

其实这个问题我之前的一篇文章已经讲的很清楚了,就是利用 sshfs 或者 ntfs,文章链接在这里:炼丹师必备技能 ——Linux 挂载远程目录。

我们可以用文章中的方法,在 docker 容器里挂载 sshfs 或者 ntfs 即可。但是还有更优雅的方法,那就是用 docker 的 vieux/sshfs 插件来实现我们的目标。

1、安装 vieux/sshfs 插件

在 B 服务器上,通过下面的命令安装插件:

1docker plugin install --grant-all-permissions vieux/sshfs

2、通过 vieux/sshfs 驱动创建数据卷

1docker volume create --driver vieux/sshfs \2    -o sshcmd=tianws@server-A:/ssd/tianws \3    -o password=yourpassword \4    dd-ssh-volume56# 查看 docker volume7docker volume ls

3、启动容器时指定挂载这个数据卷

1# 原本 A 服务器上的 docker run 命令:2docker run --gpus all --ipc=host  -p 49154:22 -it -u $(id -u):$(id -g) -h dd-docker --restart=always \3-v /ssd/tianws:/ssd/tianws \4pytorch1.5-cuda10.1-cudnn756# 在 B 服务器上,修改 -v 选项,原本的本地路径更改为 docker volume 名称7docker run --gpus all --ipc=host  -p 49154:22 -it -u $(id -u):$(id -g) -h dd-docker --restart=always \8-v dd-ssh-volume:/ssd/tianws \9pytorch1.5-cuda10.1-cudnn7

现在,A 服务器的 /ssd/tianws 文件夹,就被挂载到 B 服务器 docker 容器里的 /ssd/tianws 目录下了,可以直接当本地文件夹来使用。

三、总结

网上用 docker 来配置开发环境的文章有很多,我也受益良多。但是我在实际的使用过程中,经常遇到权限管理混乱、无法图形可视化、不易维护等等问题,在摸索过程中,我一点点完善了我的 dockerfile,解决了之前遇到的问题,并且结合 sshfs 形成了一套开发环境搭建的 pipeline,用这套流程,开发环境、数据和代码就像放在 U 盘里一样,可以“即插即用“,很方便地用同样的环境调用不同机器的算力。

这套环境搭建完成后理论上可以配合任何你熟悉的远程开发 IDE,只要把 docekr 容器当作一台服务器来配置 IDE 就行了。

这篇文章是我自己的一个总结,在此分享给大家。

如果这篇文章对你有帮助,请点一个赞,如果大家需要的话,我会再更新第二、三篇,分享我常用的 IDE(VS CODE 和 Pycharm)结合这套开发环境的配置和使用方法,搭建一个完整的炼丹炉。

谢谢大家!

docker anaconda_深度学习炼丹炉配置[1] Docker+sshfs环境配置相关推荐

  1. 深度学习降噪方案-RNNoise简介和环境配置

    RNNoise是一个采用音频特征抽取+深度神经网络结合的降噪方案. RNNoise相关基本信息, 请查看 RNNoise学习和翻译系列 目录 RNNoise的基本流程和模块 RNNoise的目录结构介 ...

  2. 深度学习入门篇——一、Pytorch环境配置(GPU:GeForce 940MX)

    安装pytorch的几点注意事项: ①确定你的电脑显卡是否支持cuda.可取网站查询:CUDA Zone - Library of Resources | NVIDIA Developerhttps: ...

  3. Docker+VSCode 配置属于自己的深度学习炼丹炉

    来自 | 知乎 作者 | 常庆丰 地址 | https://zhuanlan.zhihu.com/p/102385239 本文仅作学术分享,若侵权,请联系后台删文处理 序 之所以要写这么一篇介绍的文章 ...

  4. Docker教程-深度学习环境配置

    最近在知乎上刷到一篇文章,标题很有意思叫<Docker,救你于「深度学习环境配置」的苦海>,感兴趣的可以去搜一下.那篇文章主要针对的是一个目前深度学习研究者的痛点:深度学习环境的配置.我在 ...

  5. nvidia docker容器不支持中文的解决办法_用docker搭建深度学习实验环境

    tensorflow和pytorch官方都维护了不同版本的docker镜像.借助docker我们可以方便的搭建起深度学习实验环境. 但是想要在同一个容器内同时拥有tensorflow.pytorch. ...

  6. docker制作深度学习镜像(以windows环境下为例)

    向AI转型的程序员都关注了这个号???????????? 人工智能大数据与深度学习  公众号:datayx 用 Docker 安装深度学习环境,轻量.方便!整个系统大小仅需2~3G,用完还能带着走!一 ...

  7. 基于AI的计算机视觉识别在Java项目中的使用(三) —— 搭建基于Docker的深度学习训练环境

    深度学习在哪里? 我们已然生活在数字时代,一天24小时我们被数字包围.我们生活中的方方面面都在使用数字来表达.传递.存储.我们无时无刻不在接收数字信息,而又无时无刻不在生产数字信息. 在数字世界中,可 ...

  8. Docker部署深度学习服务器,CUDA+cudnn+ssh

    通过Docker来创建多个容器(相当于多个linux系统),每个容器中的CUDA版本之间互相不影响.这样的好处是可以在项目需要时,不改动主机环境的前提下运行多个CUDA版本.同时,也可以为不同的使用人 ...

  9. Centos8安装英伟达显卡驱动并通过docker部署深度学习环境

    20201107 - 每个人的机器和实际需要的环境都不一样,本文只是提供了在自己实验室centos8上的部署过程,部署过程中,没有什么问题.请谨慎参考本篇文章,以免浪费宝贵时间. 0. 引言 之前的时 ...

最新文章

  1. Sublime Text 安装插件的方法
  2. 【编程题目】输入一个单向链表,输出该链表中倒数第 k 个结点
  3. 白话Elasticsearch33-深入聚合数据分析之案例实战bucket + metrics 统计每种颜色电视平均价格
  4. Android的Handler,Looper源码剖析
  5. 千寻和省cors精度对比_使用中海达RTK实战演示千寻cors账号对比省cors网络,验证其测量效果究竟如何...
  6. leetcode 的shell部分4道题整理
  7. 前端学习(1381):多人管理项目1项目管理搭建
  8. 推荐系统——协同过滤
  9. 【项目管理】合同和采购
  10. ecshop商品属性价不打折
  11. 【前沿】详细讲解Transformer新型神经网络在机器翻译中的应用
  12. css实现在一行显示多余部分显示省略号
  13. 魔兽争霸修改器,局域网内使用!防封号!!!!!!!!
  14. 模电(八)放大电路静态工作点的稳定性
  15. HackingLab基础关
  16. 访问使用https协议的网址时,浏览器地址栏没有显示“安全锁”的标志的问题
  17. Landsat8卫星介绍
  18. Text to image论文精读PDF-GAN:文本生成图像新度量指标SSD Semantic Similarity Distance
  19. 【Demllie航天】火箭方程
  20. ctfshow web入门 {45-54}

热门文章

  1. 利用.NET绘图技术制作水晶按钮控件(转)
  2. C#笔记之又谈装箱与拆箱(boxing and unboxing)
  3. Win10 中将网页转换成pdf的简便方法
  4. 观察者模式(发布--订阅模式)
  5. Victor and World(spfa+状态压缩dp)
  6. 研华外触发实验PCI1714板卡安装事项
  7. 在SQL Server查询分析器里查询Excel文件数据
  8. 设计模式笔记 16. Mediator 中介者模式(行为型模式)
  9. mysql 40014无效,Oracle问题-ORA-14400:插入的分区关键字未映射到任何分区
  10. Python框架篇之Django(路由系统URL、视图函数views)