用户管理 (管理员权限)

添加docker用户组:

sudo groupadd -g 344 docker

添加用户到用户组:

sudo usermod -a -G 用户组 用户

从用户组中删除用户

gpasswd -d 用户 用户组

镜像的基本操作

列出本地镜像

docker images


各个选项说明:
• REPOSITORY:表示镜像的仓库源(不唯一)
• TAG:镜像的标签(不唯一,可以自己设定)
• IMAGE ID:镜像ID(唯一)
• CREATED:镜像创建时间
• SIZE:镜像大小
同一个镜像ID可以有多个仓库源和标签,如图中红框所示。

查找镜像

我们可以从Docker Hub网站来搜索镜像,Docker Hub网址为:https://hub.docker.com/
我们也可以使用docker search命令来搜索镜像。比如我们需要一个httpd的镜像来作为我们的web服务。我们可以通过docker search命令搜索httpd来寻找适合我们的镜像。

docker search httpd


NAME:镜像仓库源的名称
DESCRIPTION:镜像的描述
OFFICIAL:是否docker官方发布

下载镜像

当我们在本地主机上使用一个不存在的镜像时 Docker 就会自动下载这个镜像。如果我们想预先下载这个镜像,我们可以使用docker pull命令来下载它。
此处以ubuntu:15.10为例,其中15.10为标签,若不写,会默认下载最新的镜像,标签为latest。

docker pull 镜像名(:标签)

设置镜像标签

docker tag 原始镜像名 新镜像名:标签


发现镜像ID为00a10af6cf18的镜像多了一个新的标签 liufan。

删除镜像

当我们删除某一镜像时,会先尝试删除所有指向该镜像的标签,然后删除该镜像本身。
1.若一个镜像有多个标签,我们只想删除已经没用的标签

docker rmi 仓库源(liufan): 镜像标签(lf)

删除前后


我们发现liufan:lf已经被删除

2.彻底删除镜像

docker rmi –f 镜像ID(以8c811b4aec35为例)(不建议-f强制删除)


我们发现8c811b4aec35这个镜像已经被彻底删除(包含所有指向这个镜像的标签)

3.若想删除的镜像有基于它创建的容器存在时,镜像文件是默认无法删除的。(容器会在下面章节有所讲解)

docker run -it --name liufan ubuntu/numpy /bin/bash


我们基于ubuntu/numpy这个镜像创建了一个名为liufan的容器。下面我们退出容器,尝试删除这个镜像,docker会提示有容器在运行,无法删除:

若想强制删除,可使用2中的 docker rmi –f 镜像ID,但不建议这样做,因为有容器依赖这个镜像,强制删除会有遗留问题(强制删除的镜像换了新的ID继续存在系统中)

导入导出镜像

导出
docker save 镜像(busybox) > 存储位置(/home/lf/aa.tar)


已经在对应目录生成压缩文件

先把本地的busybox镜像删除,然后尝试导入刚刚导出的压缩镜像

docker rmi busybox && docker images

导入
docker load < (镜像存储位置)/home/lf/aa.tar


我们发现busybox镜像已经成功导入。

注:当已有的镜像不能满足我们的需求时,我们需要自己制作镜像,主要通过下面2中方式:
1) 通过Dockerfile文件制作镜像(较难)
2) 基于一个原始镜像创建一个容器,在容器里面进行一些操作(安装一些框架或者软件包),然后退出容器,利用commit命令提交生成新的镜像 (简单)

容器基本操作

容器是镜像的一个运行实例,它是基于镜像创建的。

新建容器

docker create -it --name lf tensorflow
docker ps -a


可以看见我们成功创建了一个名为lf,基于tensorflow镜像的容器。

使用docker create 命令新建的容器处于停止状态,可以用如下命令来启动并进入它。

docker start lf
docker attach lf

启动容器

启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(stopped)的容器重新启动。

docker run -it  --name liufan ubuntu/numpy /bin/bash

上述命令等价于先执行docker create,再执行docker start命令。

上面我们以交互模式创建了一个基于ubuntu/numpy镜像,名为liufan的容器。

其中,-i:表示让容器的标准输入保持打开,
-t:让docker分配一个伪终端并绑定到容器的标准输入上,
/bin/bash:不是必要选项,只是在表明创建容器的时候并运行了bash应用,方便我们进入容器内部,不写也可以,不过那就要用其他命令进入容器了。(docker中必须要保持一个进程的运行,要不然整个容器就会退出)

我们可以按Ctrl+d或输入exit命令来退出容器。退出后该容器就会处于终止状态(stopped),可通过3.1中的start和attach重新进入容器。

查看终止删除容器

docker ps // 查看所有正在运行容器

docker ps -a // 查看所有容器

docker ps -a -q // 查看所有容器ID

docker stop containerId // containerId 是容器的ID或者名字,一个或多个

docker rm containerId // containerId 是容器的ID或者名字,一个或多个

docker rm containerId // containerId 是容器的ID或者名字,一个或多个


可以看到lf、wh这两个容器已经被删除

docker stop $(docker ps -a -q) //  stop停止所有容器

docker  rm $(docker ps -a -q) //   remove删除所有容器

注:删除容器时必须保证容器是终止态(stopped),若不是先进行docker stop操作再进行docker rm操作,可以-f强制删除但不建议。

进入容器

1.attach命令

使用attach命令有时候并不方便。当多个窗口同时attach到同一个容器的时候,所有的窗口都会同步显示。当某个窗口因命令阻塞时,其他窗口就无法执行操作了。

2.exec命令
docker自1.3版本起,提供了一个更加方便的工具exec,可以直接在容器内部运行命令,例如进入到刚创建的容器中,并启动一个bash

导入和导出容器

docker run -it --name liufan ubuntu/numpy /bin/bash


我们基于ubuntu/numpy镜像创建了一个名为liufan的容器,下面将它

导出:

docker export 容器名(liufan) > 存储地址(/home/lf/aa.tar)


我们将liufan这个容器导出本地并压缩命名为aa.tar文件。

导入:
先将liufan容器删除在尝试导入

docker stop liufan &&docker rm liufan &&docker ps -a
docker import /home/lf/aa.tar test/ubuntu:lf



我们可以看到刚刚的容器压缩文件已经成功导入,命名为test/ubuntu:lf镜像。
前面第一章中的1.6节中,我们介绍过用docker load命令来导入一个镜像文件,其实这边也可以用docker import命令来导入一个容器到本地镜像库。
两者的区别是:
docker import:丢弃了所有的历史记录和元数据信息,仅保存容器当时的快照状态。在导入的时候可以重新制定标签等元数据信息。
docker load:将保存完整记录,体积较大。

代码实例(以Tensorflow为例)

上面两章我介绍了镜像和容器的关系和它们的一些基本操作,接下来我将介绍如何在创建的容器里面运行我们的代码。
集群上有Tensorflow、Pytorch、Caffe、MXNet等深度学习框架的镜像,此处我已Tensorflow为例,介绍如何在容器里运行我们的代码。

创建容器

docker run -it --name liufan bluesliuf/tensorflow /bin/bash


我们基于bluesliuf/tensorflow这个镜像创建了一个名为liufan的镜像,进入容器ls查看目录列表,发现此时的容器就类似一个Linux环境,默认的用户权限为root权限。
问题:我们的代码和数据集都在本地机器上,如何放到容器内部呢?直接复制困难并且耗时,如果我们的数据集过大。
Docker提供了一种方法:挂载。将我们的本地目录挂载到容器内部,实现本机目录文件和容器目录文件共享。

挂载本地目录到容器

注:查询资料发现不能先创建容器,再挂载本地目录,两者必须同时进行,于是我们重新创建容器并挂载本地目录。
我的代码和数据集都放在本机/home/lf/lf/catdogs目下,下面将它挂载到容器内。

创建容器有2种方式

-v:挂载的命令参数
红色冒号前:本地目录的绝对路径
红色冒号后:容器挂载本地目录的绝对路径
蓝色部分表示容器需要使用GPU 时将显卡驱动映射到容器中,默认参数不用修改,如果不使用GPU 可以不加蓝色部分
name: 创建的容器名
bluesliuf/tensorflow:基于的镜像

不调用GPU(本机):

调用GPU(集群):

可以看见我们已经成功将本地目录挂载到了我们指定的容器内部位置。

运行代码(本机):
注:本地代码里面通常会有数据集的读取路径,一些生成日志文件的存储路径,我们要对它进行修改,换为容器内读取和存储路径。

再去容器内部看,本地的修改已经同步到容器内了。

在本地修改文件和容器内修改文件都行,一处修改两者都会同步修改。但建议在本地修改,因为本地修改起来方便,容器内一般用vim编辑器,较为不便。
在终端输入命令:python 代码文件名(此处我是tr aining.py

不调用GPU(本机):

可以看见代码已经成功运行,并且相应的日志文件也存储到本地目录(容器目录当然也有,两者是同步共享的)
此外,docker还提供了类似screen,可以让容器在后台运行的功能,退出时如果想继续运行:按顺序按【ctrl+p】【ctrl+q】,下次再使用docker attach 或者docker exec进入容器,可以看见我们的程序还在继续运行。例如:

调用GPU(本机):

在后台运行和上面一样,也是利用【ctrl+p】【ctrl+q】

数据卷挂载

Docker针对挂载目录还提供了一种高级的用法。叫数据卷。
数据卷:“其实就是一个正常的容器,专门用来提供数据卷供其它容器挂载的”。感觉它就像是由一个容器定义的一个数据挂载信息。其他的容器启动可以直接挂载数据卷容器中定义的挂载信息。示例如下:

1.创建一个普通的容器,名为wuhao,并将本地的文件目录挂载到了容器,接下来把这个容器当做一个数据卷。

docker run -v /home/lf/lf/catdogs:/var/catdogs --name wuhao bluesliuf/tensorflow /bin/bash

2.再创建一个新的容器,来使用这个数据卷。

docker run -it --volumes-from wuhao --name lf bluesliuf/tensorflow /bin/bash

–volumes-from用来指定要从哪个数据卷来挂载数据。

我们可以发现通过wuhao这个容器(数据卷),我们成功的将本地目录也挂载到了lf这个容器内。

通过数据卷挂载目录更具有优势。
1) 我们只需先创建一个容器并挂载本地目录,将其看成数据卷,当我们其他容器也需要挂载同样目录的时候,我们只需要利用–volumes-from就可以实现。
2) 当我们需要挂载的本地目录发生改变时,我们只需要修改作为数据卷那个容器挂载的本地目录即可(类似一个全局变量),而无须一个个修改其他容器的本地挂载目录。

挂载成功后。运行代码步骤与上面一样。