在预备课程当中,我们已经初步介绍过如何使用 Docker 启动 Tensorflow 环境,并把 Docker 内部的 jupyter notebook 端口暴露给本机。其中介绍的主要命令包括
docker run -it -p 8888:8888 tensorflow/tensorflow
- 用于以某个 image 为模板,启动一个 container (注意区分 image 和 container 的概念)
docker ps
,docker ps -a
- 分别用于查看正在运行的 container,以及所有的 container(包括已经停止的)
docker start -i container_id
- 其中 container_id 是通过
docker ps -a
命令看到的,用于启动一个已经停止的 container docker stop container_id
- 停止一个正在运行的 container
大家在使用 docker 的时候,可以把 image 理解成一张『操作系统』的光盘,container 就是一台用这张光盘装好系统的『虚拟计算机』。
常见的一个误区,是不断用 docker run
重新建立 container。这样会消耗一些硬盘资源,长时间之后,在 docker ps -a
中会看到大量停止的 container。正确的方法应该是用 docker start -i container_id
来『启动』之前停止的 container。
大家可能发现,Docker 在镜像已经下载好之后,建立 container 的速度非常快。因此 Docker 相对常规的虚拟机更加轻量级。使用 Docker 可以避免很多环境不一致问题,同时保证你的宿主操作系统干净。
一些同学可能会发现,虽然 docker start -i
可以重新『启动』之前的 container,但是 docker start
的参数似乎不如 docker run
丰富,比如我想增加一个端口映射,或者多 mount 本地的一个目录。这时候会发现 start
没有办法增加这些参数
docker start -p 1234:1234 -i container_id
- 这样运行,会显示
unknown shorthand flag: 'p' in -p1234:1234
这个其实是目前 docker 的一些限制,那么假如我想要增加一个端口,或者 mount 一个新目录。是不是就得重新用 docker run
生成一个新的 container 呢?这样的话,已有的环境可能用了一段时间,积累的东西也比较多了,似乎有点浪费。
Docker 提供了一个命令,可以把自己当前运行的某个容器,变成一个新的镜像。如果自己折腾过系统安装的同学,应该用过 Ghost,这个软件可以把当前某个硬盘备份成一个 image 文件,然后我们把这个 image 文件刻录到光盘里,就可以带着这个光盘到处『复制』我们自己的操作系统了。
Docker 提供的这个命令是 docker commit container_id image_name
。 container_id
通过 docker ps -a
查看。注意要先 stop 这个 container,才能 commit 为一个 image。
commit
成功之后,可以通过 docker images
去看系统中已有的 image,就会发现自己刚刚建设好的 image 了。
有了新 image 之后,用这个新 image 作为参数重新 run
,就可以以新的参数启动 container 了。这时候加端口映射,mount 本地目录都不是问题。
- Tips: 通过提供多个 -p, -v 参数,可以分别映射多个端口,mount 多个目录。 如
docker start -p 1234:1234 -p 8888:8888 -i container_id
- Tips: 可以不断地 commit 到同一个 image 名字上,新的 commit 会覆盖旧的
为了节省资源,一般的 Docker 镜像都安装了一个非常简洁的系统。官方的 tensorflow 镜像是基于 ubuntu 的,其 image 构建的逻辑感兴趣的话可以参考 https://github.com/tensorflow/tensorflow/blob/master/tensorflow/tools/docker/Dockerfile
假设我们希望把自己的开发环境变得更强大,希望长期用这个 container 做一些开发的话,我们可以在这个简洁的系统上动一些手术。
常规来说,一个 Container 只会执行一个主程序,我们的 tensorflow 容器执行的是 jupyter notebook。
如果要执行更多的程序,比如进入容器的 bash,可以用 exec 的方式执行
docker exec -it container_id echo hello
docker exec -it container_id /bin/bash
进入之后,就可以用 ubuntu 的一些命令安装自己需要的软件。默认的 apt 镜像用的是国外的服务器,下载软件会比较慢。我们可以换成网易的镜像。
vi /etc/apt/sources.list
。把文件内容更换成
deb http://mirrors.163.com/ubuntu/ trusty main restricted universe multiverse deb http://mirrors.163.com/ubuntu/ trusty-security main restricted universe multiverse deb http://mirrors.163.com/ubuntu/ trusty-updates main restricted universe multiverse deb http://mirrors.163.com/ubuntu/ trusty-proposed main restricted universe multiverse deb http://mirrors.163.com/ubuntu/ trusty-backports main restricted universe multiverse deb-src http://mirrors.163.com/ubuntu/ trusty main restricted universe multiverse deb-src http://mirrors.163.com/ubuntu/ trusty-security main restricted universe multiverse deb-src http://mirrors.163.com/ubuntu/ trusty-updates main restricted universe multiverse deb-src http://mirrors.163.com/ubuntu/ trusty-proposed main restricted universe multiverse deb-src http://mirrors.163.com/ubuntu/ trusty-backports main restricted universe multiverse
之后执行 apt-get update
,然后就可通过 apt-get install
来安装软件,如 apt-get install vim git
- Tips: 如果想在
docker exec
之后使用 tmux, screen 之类的终端软件, 目前似乎 docker 有问题不支持 ( lh),可以参见下面直接docker run
启动 bash 的方案。
假设我们更进一步,把容器当成一个 linux
来用。即启动时不直接启动 tensorflow,而是进入 bash,应该怎么做呢?
首先 commit 来将现在的 container 做成 image,然后重新启动一个新的 container。这时在启动时在尾部加一个新参数 /bin/bash
docker run -it -p 8888:8888 image_name /bin/bash
这样就更改了 container 的启动命令。在这个 bash 之下,你可以自由地做各种事情。而且这个 bash 里面可以启动 tmux, screen 之类的多终端工具。
这样这个环境启动的就是 bash 而不是 jupyter notebook 了。这时候需要自行运行 jupter notebook
来启动服务。
如果需要将一台机器里的容器『转移』到另一台机器,可以先把容器 commit 成镜像,然后将镜像导出成压缩文件。
docker save image_name | gzip -c > image_name.tgz
在另一台机器读取
gunzip -c image_name.tgz | docker load