Docker学习——三大组件的应用(二) 顶

一、使用 Docker 镜像

Docker 运行容器前需要本地存在对应的镜像,如果本地不存在该镜像,Docker 会从镜像仓库下载该镜像。 下面将介绍更多关于镜像的内容,包括:从仓库获取镜像; 管理本地主机上的镜像; 介绍镜像实现的基本原理。

获取镜像

获取镜像
从 Docker 镜像仓库获取镜像的命令是 docker pull 。其命令格式为:
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
具体的选项可以通过 docker pull --help 命令看到,这里我们说一下镜像名称的格式。

Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号] 。默认地址是 Docker Hub。
仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名> 。对于 Docker Hub,如果不给出用户名,则默认为 library ,也就是官方镜像。

比如:
$ docker pull ubuntu:16.04
16.04: Pulling from library/ubuntu
bf5d46315322: Pull complete
9f13e0ac480c: Pull complete
e8988b5b3097: Pull complete
40af181810e7: Pull complete
e6f7c7e5c03e: Pull complete
Digest: sha256:147913621d9cdea08853f6ba9116c2e27a3ceffecf3b492983ae97c3d643fbbe
Status: Downloaded newer image for ubuntu:16.04
上面的命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub 获取镜像。而镜像名称是 ubuntu:16.04 ,因此将会获取官方镜像 library/ubuntu 仓库中标签为 16.04 的镜像。

从下载过程中可以看到我们之前提及的分层存储的概念,镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。下载过程中给出了每一层的 ID 的前 12 位。并且下载结束后,给出该镜像完整的 sha256 的摘要,以确保下载一致性。
在使用上面命令的时候,你可能会发现,你所看到的层 ID 以及 sha256 的摘要和这里的不一样。这是因为官方镜像是一直在维护的,有任何新的 bug,或者版本更新,都会进行修复再以原来的标签发布,这样可以确保任何使用这个标签的用户可以获得更安全、更稳定的镜像。            

运行
有了镜像后,我们就能够以这个镜像为基础启动并运行一个容器。以上面的 ubuntu:16.04 为例,如果我们打算启动里面的 bash 并且进行交互式操作的话,可以执行下面的命令。
$ docker run -it --rm \
ubuntu:16.04 \
bash
root@e7009c6ce357:/# cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.5 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.5 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial

docker run 就是运行容器的命令
-it :这是两个参数,一个是 -i :交互式(interactive)操作,一个是 -t 终端(terminal)。我们这里打算进入bash 执行一些命令并查看返回结果,因此我们需要交互式终端。
--rm :这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm 。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 --rm 可以避免浪费空间。
ubuntu:16.04 :这是指用 ubuntu:16.04 镜像为基础来启动容器。
bash :放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 bash 。

进入容器后,我们可以在 Shell 下操作,执行任何所需的命令。这里,我们执行了 cat  /etc/os-release ,这是 Linux 常用的查看当前系统版本的命令,从返回的结果可以看到容器内是 Ubuntu 16.04.4 LTS 系统。

最后我们通过 exit 退出了这个容器。

列出镜像

列出镜像
要想列出已经下载下来的镜像,可以使用 docker image ls 命令。
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest 5f515359c7f8 5 days ago 183 MB
nginx latest 05a60462f8ba 5 days ago 181 MB
mongo 3.2 fe9198c04d62 5 days ago 342 MB
<none> <none> 00285df0df87 5 days ago 342 MB
ubuntu 16.04 f753707788c5 4 weeks ago 127 MB
ubuntu latest f753707788c5 4 weeks ago 127 MB
ubuntu 14.04 1e0c3dd64ccd 4 weeks ago 188 MB
列表包含了 仓库名 、 标签 、 镜像 ID 、 创建时间 以及 所占用的空间 。
镜像 ID 则是镜像的唯一标识,一个镜像可以对应多个标签。  

镜像体积
如果仔细观察,会注意到,这里标识的所占用空间和在 Docker Hub 上看到的镜像大小不同。
比如, ubuntu:16.04 镜像大小,在这里是 127 MB ,但是在 Docker Hub 显示的却是 50MB 。这是因为 Docker Hub 中显示的体积是压缩后的体积。
在镜像下载和上传过程中镜像是保持着压缩状态的,因此 Docker Hub 所显示的大小是网络传输中更关心的流量大小。而docker image ls 显示的是镜像下载到本地后,展开的大小,准确说,是展开后的各层所占空间的总和,因为镜像到本地后,查看空间的时候,更关心的是本地磁盘空间占用的大小。
另外一个需要注意的问题是, docker image ls 列表中的镜像体积总和并非是所有镜像实际硬盘消耗。
由于 Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层。
由于 Docker 使用 Union FS,相同的层只需要保存一份即可,因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多。

你可以通过以下命令来便捷的查看镜像、容器、数据卷所占用的空间。   
$ docker system df
TYPE                TOTAL               ACTIVE              SIZE                RECLAIMABLE
Images              2                   1                   174.7MB             116MB (66%)
Containers          4                   1                   0B                  0B
Local Volumes       3                   3                   395.6kB             0B (0%)
Build Cache         0                   0                   0B                  0B

虚悬镜像
上面的镜像列表中,还可以看到一个特殊的镜像,这个镜像既没有仓库名,也没有标签,均为 <none> 。:
<none> <none> 00285df0df87 5 days ago 342 MB
这个镜像原本是有镜像名和标签的,原来为 mongo:3.2 ,随着官方镜像维护,发布了新版本后,重新 docker pull mongo:3.2 时, mongo:3.2 这个镜像名被转移到了新下载的镜像身上,而旧的镜像上的这个名称则被取消,从而成为了 <none> 。
除了 docker pull 可能导致这种情况, docker build 也同样可以导致这种现象。由于新旧镜像同名,旧镜像名称被取消,从而出现仓库名、标签均为 <none> 的镜像。
这类无标签镜像也被称为 虚悬镜像(dangling image) ,可以用下面的命令专门显示这类镜像:
$ docker image ls -f dangling=true
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 00285df0df87 5 days ago 342 MB
一般来说,虚悬镜像已经失去了存在的价值,是可以随意删除的,可以用下面的命令删除。
$ docker image prune    

中间层镜像
为了加速镜像构建、重复利用资源,Docker 会利用 中间层镜像。所以在使用一段时间后,可能会看到一些依赖的中间层镜像。
默认的 docker image ls 列表中只会显示顶层镜像,如果希望显示包括中间层镜像在内的所有镜像的话,需要加 -a 参数。
$ docker image ls -a  
这样会看到很多无标签的镜像,与之前的虚悬镜像不同,这些无标签的镜像很多都是中间层镜像,是其它镜像所依赖的镜像。这些无标签镜像不应该删除,否则会导致上层镜像因为依赖丢失而出错。
实际上,这些镜像也没必要删除,因为之前说过,相同的层只会存一遍,而这些镜像是别的镜像的依赖,因此并不会因为它们被列出来而多存了一份,无论如何你也会需要它们。
只要删除那些依赖它们的镜像后,这些依赖的中间层镜像也会被连带删除。列出部分镜像不加任何参数的情况下, docker image ls 会列出所有顶级镜像,但是有时候我们只希望列出部分镜像。
 docker image ls 有好几个参数可以帮助做到这个事情。

根据仓库名列出镜像
$ docker image ls ubuntu
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 16.04 f753707788c5 4 weeks ago 127 MB
ubuntu latest f753707788c5 4 weeks ago 127 MB
ubuntu 14.04 1e0c3dd64ccd 4 weeks ago 188 MB

列出特定的某个镜像,也就是说指定仓库名和标签    
$ docker image ls ubuntu:16.04
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 16.04 f753707788c5 4 weeks ago 127 MB

除此以外, docker image ls 还支持强大的过滤器参数 --filter ,或者简写 -f 。
之前我们已经看到了使用过滤器来列出虚悬镜像的用法,它还有更多的用法。
比如,我们希望看到在mongo:3.2 之后建立的镜像,可以用下面的命令:
$ docker image ls -f since=mongo:3.2
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest 5f515359c7f8 5 days ago 183 MB
nginx latest 05a60462f8ba 5 days ago 181 MB
想查看某个位置之前的镜像也可以,只需要把 since 换成 before 即可。
此外,如果镜像构建时,定义了 LABEL ,还可以通过 LABEL 来过滤。
$ docker image ls -f label=com.example.version=0.1
...
以特定格式显示

默认情况下, docker image ls 会输出一个完整的表格,但是我们并非所有时候都会需要这些内容。
比如,刚才删除虚悬镜像的时候,我们需要利用 docker image ls 把所有的虚悬镜像的 ID 列出来,然后才可以交给 docker image rm 命令作为参数来删除指定的这些镜像,这个时候就用到了 -q 参数。
$ docker image ls -q
5f515359c7f8
05a60462f8ba
fe9198c04d62
00285df0df87
f753707788c5
f753707788c5
1e0c3dd64ccd
--filter 配合 -q 产生出指定范围的 ID 列表,然后送给另一个 docker 命令作为参数,从而针对这组实体成批的进行某种操作的做法在 Docker 命令行使用过程中非常常见,不仅仅是镜像,将来我们会在各个命令中看到这类搭配以完成很强大的功能。因此每次在文档看到过滤器后,可以多注意一下它们的用法。
另外一些时候,我们可能只是对表格的结构不满意,希望自己组织列;或者不希望有标题,这样方便其它程序解析结果等,这就用到了 Go 的模板语法。  
比如,下面的命令会直接列出镜像结果,并且只包含镜像ID和仓库名:
$ docker image ls --format "{{.ID}}: {{.Repository}}"
5f515359c7f8: redis
05a60462f8ba: nginx
fe9198c04d62: mongo
00285df0df87: <none>
f753707788c5: ubuntu
f753707788c5: ubuntu
1e0c3dd64ccd: ubuntu
或者打算以表格等距显示,并且有标题行,和默认一样,不过自己定义列:
$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"
IMAGE ID REPOSITORY TAG
5f515359c7f8 redis latest
05a60462f8ba nginx latest
fe9198c04d62 mongo 3.2
00285df0df87 <none> <none>
f753707788c5 ubuntu 16.04
f753707788c5 ubuntu latest
1e0c3dd64ccd ubuntu 14.04

删除本地镜像

删除本地镜像
如果要删除本地的镜像,可以使用 docker image rm 命令,其格式为:
$ docker image rm [选项] <镜像1> [<镜像2> ...]
用 ID、镜像名、摘要删除镜像
其中, <镜像> 可以是 镜像短 ID 、 镜像长 ID 、 镜像名 或者 镜像摘要 。
比如我们有这么一些镜像:
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 0584b3d2cf6d 3 weeks ago 196.5 MB
redis alpine 501ad78535f0 3 weeks ago 21.03 MB
docker latest cf693ec9b5c7 3 weeks ago 105.1 MB
nginx latest e43d811ce2f4 5 weeks ago 181.5 MB
我们可以用镜像的完整 ID,也称为 长 ID ,来删除镜像。使用脚本的时候可能会用长 ID,但是人工输入就太累了,所以更多的时候是用 短 ID 来删除镜像。 docker image ls 默认列出的就已经是短 ID 了,
一般取前3个字符以上,只要足够区分于别的镜像就可以了。

比如这里,如果我们要删除 redis:alpine 镜像,可以执行:
$ docker image rm 501
Untagged: redis:alpine
Untagged: redis@sha256:f1ed3708f538b537eb9c2a7dd50dc90a706f7debd7e1196c9264edeea521a86d
Deleted: sha256:501ad78535f015d88872e13fa87a828425117e3d28075d0c117932b05bf189b7
Deleted: sha256:96167737e29ca8e9d74982ef2a0dda76ed7b430da55e321c071f0dbff8c2899b
Deleted: sha256:32770d1dcf835f192cafd6b9263b7b597a1778a403a109e2cc2ee866f74adf23
Deleted: sha256:127227698ad74a5846ff5153475e03439d96d4b1c7f2a449c7a826ef74a2d2fa
Deleted: sha256:1333ecc582459bac54e1437335c0816bc17634e131ea0cc48daa27d32c75eab3
Deleted: sha256:4fc455b921edf9c4aea207c51ab39b10b06540c8b4825ba57b3feed1668fa7c7

我们也可以用 镜像名 ,也就是 <仓库名>:<标签> ,来删除镜像。
$ docker image rm centos
Untagged: centos:latest
Untagged: centos@sha256:b2f9d1c0ff5f87a4743104d099a3d561002ac500db1b9bfa02a783a46e0d366c
Deleted: sha256:0584b3d2cf6d235ee310cf14b54667d889887b838d3f3d3033acd70fc3c48b8a
Deleted: sha256:97ca462ad9eeae25941546209454496e1d66749d53dfa2ee32bf1faabd239d38

当然,更精确的是使用 镜像摘要 删除镜像。
$ docker image ls --digests
REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
node slim sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228 6e0c4c8e3913 3 weeks ago 214 MB
$ docker image rm node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228
Untagged: node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228
Untagged 和 Deleted
如果观察上面这几个命令的运行输出信息的话,你会注意到删除行为分为两类,一类是Untagged ,另一类是 Deleted 。我们之前介绍过,镜像的唯一标识是其 ID 和摘要,而一个镜像可以有多个标签。
因此当我们使用上面命令删除镜像的时候,实际上是在要求删除某个标签的镜像。所以首先需要做的是将满足我们要求的所有镜像标签都取消,这就是我们看到的 Untagged 的信息。
因为一个镜像可以对应多个标签,因此当我们删除了所指定的标签后,可能还有别的标签指向了这个镜像,如果是这种情况,那么 Delete 行为就不会发生。所以并非所有的 docker rmi 都会产生删除镜像的行为,有可能仅仅是取消了某个标签而已。

当该镜像所有的标签都被取消了,该镜像很可能会失去了存在的意义,因此会触发删除行为。镜像是多层存储结构,因此在删除的时候也是从上层向基础层方向依次进行判断删除。
镜像的多层结构让镜像复用变动非常容易,因此很有可能某个其它镜像正依赖于当前镜像的某一层。这种情况,依旧不会触发删除该层的行为。
直到没有任何层依赖当前层时,才会真实的删除当前层。这就是为什么,有时候会奇怪,为什么明明没有别的标签指向这个镜像,但是它还是存在的原因,也是为什么有时候会发现所删除的层数和自己 docker pull 看到的层数不一样的源。
除了镜像依赖以外,还需要注意的是容器对镜像的依赖。如果有用这个镜像启动的容器存在(即使容器没有运行),那么同样不可以删除这个镜像。
之前讲过,容器是以镜像为基础,再加一层容器存储层,组成这样的多层存储结构去运行的。
因此该镜像如果被这个容器所依赖的,那么删除必然会导致故障。如果这些容器是不需要的,应该先将它们删除,然后再来删除镜像。


用 docker image ls 命令来配合
像其它可以承接多个实体的命令一样,可以使用 docker image ls -q 来配合使用 docker
image rm ,这样可以成批的删除希望删除的镜像。我们在“镜像列表”章节介绍过很多过滤镜像
列表的方式都可以拿过来使用。
比如,我们需要删除所有仓库名为 redis 的镜像:
$ docker image rm $(docker image ls -q redis)
或者删除所有在 mongo:3.2 之前的镜像:
$ docker image rm $(docker image ls -q -f before=mongo:3.2)
充分利用你的想象力和 Linux 命令行的强大,你可以完成很多非常赞的功能。

CentOS/RHEL 的用户需要注意的事项
在 Ubuntu/Debian 上有 UnionFS 可以使用,如 aufs 或者 overlay2 ,而 CentOS 和 RHEL的内核中没有相关驱动。因此对于这类系统,一般使用 devicemapper 驱动利用 LVM 的一些机制来模拟分层存储。这样的做法除了性能比较差外,稳定性一般也不好,而且配置相对复杂。
Docker 安装在 CentOS/RHEL 上后,会默认选择 devicemapper ,但是为了简化配置,其 devicemapper 是跑在一个稀疏文件模拟的块设备上,也被称为 loop-lvm 。这样的选择是因为不需要额外配置就可以运行 Docker,这是自动配置唯一能做到的事情。
但是 loop-lvm的做法非常不好,其稳定性、性能更差,无论是日志还是 docker info 中都会看到警告信息。官方文档有明确的文章讲解了如何配置块设备给 devicemapper 驱动做存储层的做法,这类做法也被称为配置 direct-lvm 。
除了前面说到的问题外, devicemapper + loop-lvm 还有一个缺陷,因为它是稀疏文件,所以它会不断增长。用户在使用过程中会注意到/var/lib/docker/devicemapper/devicemapper/data 不断增长,而且无法控制。很多人会希望删除镜像或者可以解决这个问题,结果发现效果并不明显。
原因就是这个稀疏文件的空间释放后基本不进行垃圾回收的问题。因此往往会出现即使删除了文件内容,空间却无法回收,随着使用这个稀疏文件一直在不断增长。所以对于 CentOS/RHEL 的用户来说,在没有办法使用 UnionFS 的情况下,一定要配置direct-lvm 给 devicemapper ,无论是为了性能、稳定性还是空间利用率。或许有人注意到了 CentOS 7 中存在被 backports 回来的 overlay 驱动,不过 CentOS 里的这个驱动达不到生产环境使用的稳定程度,所以不推荐使用。

二、操作 Docker 容器

容器是 Docker 又一核心概念。 简单的说,容器是独立运行的一个或一组应用,以及它们的运行态环境。对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用。 下面将具体介绍如何来管理一个容器,包括创建、启动和停止等。

启动容器

启动容器
启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态( stopped )的容器重新启动。
因为 Docker 的容器实在太轻量级了,很多时候用户都是随时删除和新创建容器。

新建并启动
所需要的命令主要为 docker run 。
例如,下面的命令输出一个 “Hello World”,之后终止容器。
$ docker run ubuntu:14.04 /bin/echo 'Hello world'
Hello world
这跟在本地直接执行 /bin/echo 'hello world' 几乎感觉不出任何区别。

下面的命令则启动一个 bash 终端,允许用户进行交互。
$ docker run -t -i ubuntu:14.04 /bin/bash
root@af8bae53bdd3:/#
其中, 
-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, 
-i则让容器的标准输入保持打开。

在交互模式下,用户可以通过所创建的终端来输入命令,例如
root@af8bae53bdd3:/# pwd
/
root@af8bae53bdd3:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:
1、检查本地是否存在指定的镜像,不存在就从公有仓库下载
2、利用镜像创建并启动一个容器
3、分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
4、从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
5、从地址池配置一个 ip 地址给容器
6、执行用户指定的应用程序
7、执行完毕后容器被终止

启动已终止容器
可以利用 docker container start 命令,直接将一个已经终止的容器启动运行。
容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必需的。除此之外,并没有其它的资源。可以在伪终端中利用 ps 或 top 来查看进程信息。
root@ba267838cc1b:/# ps
PID TTY TIME CMD
1 ? 00:00:00 bash
11 ? 00:00:00 ps
可见,容器中仅运行了指定的 bash 应用。这种特点使得 Docker 对资源的利用率极高,是货真价实的轻量级虚拟化。

后台运行

后台运行(detached)
更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。
此时,可以通过添加 -d 参数来实现。
下面举两个例子来说明一下。
如果不使用 -d 参数运行容器。
$ docker run ubuntu:17.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
hello world
hello world
hello world
hello world
容器会把输出的结果 (STDOUT) 打印到宿主机上面

如果使用了 -d 参数运行容器。
$ docker run -d ubuntu:17.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
77b2dc01fe0f3f1265df143181e7b9af5e05279a884f4776ee75350ea9d8017a
此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面(输出结果可以用docker logs 查看)。
注: 容器是否会长久运行,是和 docker run 指定的命令有关,和 -d 参数无关。
使用 -d 参数启动后会返回一个唯一的 id,也可以通过 docker container ls 命令来查看容器信息。
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
77b2dc01fe0f ubuntu:17.10 /bin/sh -c 'while tr 2 minutes ago Up 1 minute agitated_wright
要获取容器的输出信息,可以通过 docker container logs 命令。
$ docker container logs [container ID or NAMES]
hello world
hello world
hello world
. . .

进入容器

进入容器
在使用 -d 参数时,容器启动后会进入后台。
某些时候需要进入容器进行操作,包括使用 docker attach 命令或 docker exec 命令,推荐大家使用 docker exec 命令,原因会在下面说明。
attach 命令
docker attach 是 Docker 自带的命令。下面示例如何使用该命令。
$ docker run -dit ubuntu
243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
243c32535da7 ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds nostalgic_hypatia
$ docker attach 243c
root@243c32535da7:/#
注意: 如果从这个 stdin 中 exit,会导致容器的停止。
exec 命令
-i -t 参数
docker exec 后边可以跟多个参数,这里主要说明 -i -t 参数。
只用 -i 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。
当 -i -t 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。            
$ docker run -dit ubuntu
69d137adef7a8a689cbcb059e94da5489d3cddd240ff675c640c8d96e84fe1f6
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
69d137adef7a ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds zealous_swirles
$ docker exec -i 69d1 bash
ls
bin
boot
dev
...
$ docker exec -it 69d1 bash
root@69d137adef7a:/#
如果从这个 stdin 中 exit,不会导致容器的停止。这就是为什么推荐大家使用 docker exec 的原因。
更多参数说明请使用 docker exec --help 查看。

导出和导入容器

导出和导入容器
导出容器
如果要导出本地某个容器,可以使用 docker export 命令。
$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7691a814370e ubuntu:14.04 "/bin/bash" 36 hours ago Exited(0) 21 hours ago test
$ docker export 7691a814370e > ubuntu.tar
这样将导出容器快照到本地文件。
导入容器快照
可以使用 docker import 从容器快照文件中再导入为镜像,例如
$ cat ubuntu.tar | docker import - test/ubuntu:v1.0
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
test/ubuntu v1.0 9d37a6082e97 About a minute ago 171.3MB
此外,也可以通过指定 URL 或者某个目录来导入,例如
$ docker import http://example.com/exampleimage.tgz example/imagerepo
注:用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker
import 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历
史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,
体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。

删除容器

删除容器
可以使用 docker container rm 来删除一个处于终止状态的容器。例如
$ docker container rm trusting_newton
trusting_newton
如果要删除一个运行中的容器,可以添加 -f 参数。Docker 会发送 SIGKILL 信号给容器。

清理所有处于终止状态的容器
用 docker container ls -a 命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,
用下面的命令可以清理掉所有处于终止状态的容器。
$ docker container prune

三、访问仓库

仓库( Repository )是集中存放镜像的地方。 一个容易混淆的概念是注册服务器( Registry )。实际上注册服务器是管理仓库的具体服务器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜像。

从这方面来说,仓库可以被认为是一个具体的项目或目录。例如对于仓库地址dl.dockerpool.com/ubuntu 来说, dl.dockerpool.com 是注册服务器地址, ubuntu 是仓库名。大部分时候,并不需要严格区分这两者的概念。

Docker Hub
目前 Docker 官方维护了一个公共仓库 Docker Hub,其中已经包括了数量超过 15,000 的镜像。大部分需求都可以通过在 Docker Hub 中直接下载镜像来实现。

注册
你可以在 https://cloud.docker.com 免费注册一个 Docker 账号。
登录
可以通过执行 docker login 命令交互式的输入用户名及密码来完成在命令行界面登录Docker Hub。
你可以通过 docker logout 退出登录。

拉取镜像
你可以通过 docker search 命令来查找官方仓库中的镜像,并利用 docker pull 命令来将它下载到本地。
例如以 centos 为关键词进行搜索:
[root@iZwz9eftauv7x69f5jvi96Z ~]# docker search centos
NAME                               DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
centos                             The official build of CentOS.                   4873                [OK]                
ansible/centos7-ansible            Ansible on Centos7                              119                                     [OK]
jdeathe/centos-ssh                 CentOS-6 6.10 x86_64 / CentOS-7 7.5.1804 x86…   99                                      [OK]
consol/centos-xfce-vnc             Centos container with "headless" VNC session…   66                                      [OK]
imagine10255/centos6-lnmp-php56    centos6-lnmp-php56                              47                                      [OK]
tutum/centos                       Simple CentOS docker image with SSH access      43                                      
centos/mysql-57-centos7            MySQL 5.7 SQL database server                   40                                      
gluster/gluster-centos             Official GlusterFS Image [ CentOS-7 +  Glust…   37                                      [OK]
openshift/base-centos7             A Centos7 derived base image for Source-To-I…   35                                      
centos/postgresql-96-centos7       PostgreSQL is an advanced Object-Relational …   33                                      
centos/python-35-centos7           Platform for building and running Python 3.5…   32                                      
kinogmt/centos-ssh                 CentOS with SSH                                 22                                      [OK]
openshift/jenkins-2-centos7        A Centos7 based Jenkins v2.x image for use w…   18                                      
centos/php-56-centos7              Platform for building and running PHP 5.6 ap…   16                                      
pivotaldata/centos-gpdb-dev        CentOS image for GPDB development. Tag names…   8                                       
openshift/wildfly-101-centos7      A Centos7 based WildFly v10.1 image for use …   6                                       
openshift/jenkins-1-centos7        DEPRECATED: A Centos7 based Jenkins v1.x ima…   4                                       
darksheer/centos                   Base Centos Image -- Updated hourly             3                                       [OK]
pivotaldata/centos-mingw           Using the mingw toolchain to cross-compile t…   2                                       
pivotaldata/centos                 Base centos, freshened up a little with a Do…   2                                       
blacklabelops/centos               CentOS Base Image! Built and Updates Daily!     1                                       [OK]
pivotaldata/centos7-test           CentosOS 7 image for GPDB testing               0                                       
pivotaldata/centos7-build          CentosOS 7 image for GPDB compilation           0                                       
smartentry/centos                  centos with smartentry                          0                                       [OK]
pivotaldata/centos-gcc-toolchain   CentOS with a toolchain, but unaffiliated wi…   0  
可以看到返回了很多包含关键字的镜像,其中包括镜像名字、描述、收藏数(表示该镜像的受关注程度)、是否官方创建、是否自动创建。

OFFICIAL  官方的镜像说明是官方项目组创建和维护的,
AUTOMATED 资源允许用户验证镜像的来源和内容。 

根据是否是官方提供,可将镜像资源分为两类。
一种是类似 centos 这样的镜像,被称为基础镜像或根镜像。这些基础镜像由 Docker 公司创建、验证、支持、提供。这样的镜像往往使用单个单词作为名字。
还有一种类型,比如 ansible/centos7-ansible  镜像,它是由 Docker 的用户创建并维护的,往往带有用户名称前缀。可以通过前缀 username/ 来指定使用某个用户提供的镜像,比如 ansible 用户。
另外,在查找的时候通过 --filter=stars=N 参数可以指定仅显示收藏数量为 N 以上的镜像。
下载官方 centos 镜像到本地。
[root@iZwz9eftauv7x69f5jvi96Z ~]# docker pull centos
Using default tag: latest
latest: Pulling from library/centos
aeb7866da422: Pull complete 
Digest: sha256:67dad89757a55bfdfabec8abd0e22f8c7c12a1856514726470228063ed86593b
Status: Downloaded newer image for centos:latest
推送镜像 
用户也可以在登录后通过 docker push 命令来将自己的镜像推送到 Docker Hub。 
以下命令中的 username 请替换为你的 Docker 账号用户名。 
$ docker tag ubuntu:17.10 username/ubuntu:17.10
$ docker image ls
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
ubuntu                17.10               e211a66937c6        3 months ago        100MB
wuweixiang/ubuntu     17.10               e211a66937c6        3 months ago        100MB

[root@iZwz9eftauv7x69f5jvi96Z ~]# docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: wuweixiang
Password: 
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
[root@iZwz9eftauv7x69f5jvi96Z ~]# docker push wuweixiang/ubuntu:17.10
The push refers to repository [docker.io/wuweixiang/ubuntu]
b3c5147f2ed1: Mounted from library/ubuntu 
41f50d293d3a: Mounted from library/ubuntu 
5251addbe073: Mounted from library/ubuntu 
d5ce7f246067: Mounted from library/ubuntu 
f1b91f8d903c: Mounted from library/ubuntu 
17.10: digest: sha256:9c4bf7dbb981591d4a1169138471afe4bf5ff5418841d00e30a7ba372e38d6c1 size: 1357

$ docker search wuweixiang
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
wuweixiang/ubuntu
自动创建
自动创建(Automated Builds)功能对于需要经常升级镜像内程序来说,十分方便。
有时候,用户创建了镜像,安装了某个软件,如果软件发布新版本则需要手动更新镜像。
而自动创建允许用户通过 Docker Hub 指定跟踪一个目标网站(目前支持 GitHub 或BitBucket)上的项目,一旦项目发生新的提交或者创建新的标签(tag),Docker Hub 会自动构建镜像并推送到 Docker Hub 中。
要配置自动创建,包括如下的步骤:
1、创建并登录 Docker Hub,以及目标网站;
2、在目标网站中连接帐户到 Docker Hub;
3、在 Docker Hub 中 配置一个自动创建;
4、选取一个目标网站中的项目(需要含 Dockerfile )和分支;
5、指定 Dockerfile 的位置,并提交创建。
之后,可以在 Docker Hub 的 自动创建页面 中跟踪每次创建的状态。
私有仓库
有时候使用 Docker Hub 这样的公共仓库可能不方便,用户可以创建一个本地仓库供私人使用。

docker-registry 是官方提供的工具,可以用于构建私有的镜像仓库。
本文内容基于 dockerregistryv2.x 版本。

安装运行 docker-registry
容器运行
你可以通过获取官方 registry 镜像来运行。
$ docker run -d -p 5000:5000 --restart=always --name registry registry
这将使用官方的 registry 镜像来启动私有仓库。默认情况下,仓库会被创建在容器的/var/lib/registry 目录下。你可以通过 -v 参数来将镜像文件存放在本地的指定路径。
例如下面的例子将上传的镜像放到本地的 /opt/data/registry 目录。
$ docker run -d \
-p 5000:5000 \
-v /opt/data/registry:/var/lib/registry \
registry
在私有仓库上传、搜索、下载镜像
创建好私有仓库之后,就可以使用 docker tag 来标记一个镜像,然后推送它到仓库。例如
私有仓库地址为 127.0.0.1:5000 。
先在本机查看已有的镜像。
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
ubuntu latest ba5877dc9bec 6 weeks ago 192.7 MB
使用 docker tag 将 ubuntu:latest 这个镜像标记为 127.0.0.1:5000/ubuntu:latest 。

格式为 docker tag IMAGE[:TAG] [REGISTRY_HOST[:REGISTRY_PORT]/]REPOSITORY[:TAG] 。
$ docker tag ubuntu:latest 127.0.0.1:5000/ubuntu:latest
$ docker image ls
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
127.0.0.1:5000/ubuntu   latest              ea4c82dcd15a        2 weeks ago         85.8MB
ubuntu                  latest              ea4c82dcd15a        2 weeks ago         85.8MB

使用 docker push 上传标记的镜像。
$ docker push 127.0.0.1:5000/ubuntu:latest
The push refers to repository [127.0.0.1:5000/ubuntu]
76c033092e10: Pushed 
2146d867acf3: Pushed 
ae1f631f14b7: Pushed 
102645f1cf72: Pushed 
latest: digest: sha256:6b9eb699512656fc6ef936ddeb45ab25edcd17ab94901790989f89dbf782344a size: 1150

用 curl 查看仓库中的镜像。
$ curl 127.0.0.1:5000/v2/_catalog
{"repositories":["ubuntu"]}
这里可以看到 {"repositories":["ubuntu"]} ,表明镜像已经被成功上传了。
先删除已有镜像,再尝试从私有仓库中下载这个镜像。

$ docker image rm 127.0.0.1:5000/ubuntu:latest
$ docker pull 127.0.0.1:5000/ubuntu:latest
Pulling repository 127.0.0.1:5000/ubuntu:latest
ba5877dc9bec: Download complete
511136ea3c5a: Download complete
9bad880da3d2: Download complete
25f11f5fb0cb: Download complete
ebc34468f71d: Download complete
2318d26665ef: Download complete
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
127.0.0.1:5000/ubuntu:latest latest ba5877dc9bec 6 weeks ago 192.7 MB
注意事项
如果你不想使用 127.0.0.1:5000 作为仓库地址,比如想让本网段的其他主机也能把镜像推送到私有仓库。你就得把例如 192.168.199.100:5000 这样的内网地址作为私有仓库地址,这时你会发现无法成功推送镜像。
这是因为 Docker 默认不允许非 HTTPS 方式推送镜像。我们可以通过 Docker 的配置选项来取消这个限制,或者查看下一节配置能够通过 HTTPS 访问的私有仓库。
Ubuntu 14.04, Debian 7 Wheezy
对于使用 upstart 的系统而言,编辑 /etc/default/docker 文件,在其中的 DOCKER_OPTS中增加如下内容:
DOCKER_OPTS="--registry-mirror=https://registry.docker-cn.com --insecure-registries=192.168.199.100:5000"
重新启动服务。
$ sudo service docker restart
Ubuntu 16.04+, Debian 8+, centos 7
对于使用 systemd 的系统,请在 /etc/docker/daemon.json 中写入如下内容(如果文件不存
在请新建该文件)
{
"registry-mirror": [
"https://registry.docker-cn.com"
],
"insecure-registries": [
"192.168.199.100:5000"
]
}
注意:该文件必须符合 json 规范,否则 Docker 将不能启动。
其他
对于 Docker for Windows 、 Docker for Mac 在设置中编辑 daemon.json 增加和上边一样的
字符串即可。
私有仓库高级配置
上一节我们搭建了一个具有基础功能的私有仓库,本小节我们来使用 Docker Compose 搭建一个拥有权限认证、TLS 的私有仓库。
新建一个文件夹,以下步骤均在该文件夹中进行。
准备站点证书
如果你拥有一个域名,国内各大云服务商均提供免费的站点证书。你也可以使用 openssl 自
行签发证书。
这里假设我们将要搭建的私有仓库地址为 docker.domain.com ,下面我们介绍使用 openssl
自行签发 docker.domain.com 的站点 SSL 证书。
第一步创建 CA 私钥。
$ openssl genrsa -out "root-ca.key" 4096
第二步利用私钥创建 CA 根证书请求文件。
$ openssl req \
-new -key "root-ca.key" \
-out "root-ca.csr" -sha256 \
-subj '/C=CN/ST=Shanxi/L=Datong/O=Your Company Name/CN=Your Company Name Doc
ker Registry CA'
以上命令中 -subj 参数里的 /C 表示国家,如 CN ; /ST 表示省; /L 表示城市或者
地区; /O 表示组织名; /CN 通用名称。
第三步配置 CA 根证书,新建 root-ca.cnf 。
[root_ca]
basicConstraints = critical,CA:TRUE,pathlen:1
keyUsage = critical, nonRepudiation, cRLSign, keyCertSign
subjectKeyIdentifier=hash
第四步签发根证书。

$ openssl x509 -req -days 3650 -in "root-ca.csr" \
-signkey "root-ca.key" -sha256 -out "root-ca.crt" \
-extfile "root-ca.cnf" -extensions \
root_ca
第五步生成站点 SSL 私钥。
$ openssl genrsa -out "docker.domain.com.key" 4096
第六步使用私钥生成证书请求文件。
$ openssl req -new -key "docker.domain.com.key" -out "site.csr" -sha256 \
-subj '/C=CN/ST=Shanxi/L=Datong/O=Your Company Name/CN=docker.domain.com'
第七步配置证书,新建 site.cnf 文件。
[server]
authorityKeyIdentifier=keyid,issuer
basicConstraints = critical,CA:FALSE
extendedKeyUsage=serverAuth
keyUsage = critical, digitalSignature, keyEncipherment
subjectAltName = DNS:docker.domain.com, IP:127.0.0.1
subjectKeyIdentifier=hash
第八步签署站点 SSL 证书。
$ openssl x509 -req -days 750 -in "site.csr" -sha256 \
-CA "root-ca.crt" -CAkey "root-ca.key" -CAcreateserial \
-out "docker.domain.com.crt" -extfile "site.cnf" -extensions server
这样已经拥有了 docker.domain.com 的网站 SSL 私钥 docker.domain.com.key 和 SSL 证书
docker.domain.com.crt 。
新建 ssl 文件夹并将 docker.domain.com.key docker.domain.com.crt 这两个文件移入,删
除其他文件。
配置私有仓库
私有仓库默认的配置文件位于 /etc/docker/registry/config.yml ,我们先在本地编辑
config.yml ,之后挂载到容器中。

version: 0.1
log:
accesslog:
disabled: true
level: debug
formatter: text
fields:
service: registry
environment: staging
storage:
delete:
enabled: true
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
auth:
htpasswd:
realm: basic-realm
path: /etc/docker/registry/auth/nginx.htpasswd
http:
addr: :443
host: https://docker.domain.com
headers:
X-Content-Type-Options: [nosniff]
http2:
disabled: false
tls:
certificate: /etc/docker/registry/ssl/docker.domain.com.crt
key: /etc/docker/registry/ssl/docker.domain.com.key
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
生成 http 认证文件
$ mkdir auth
$ docker run --rm \
--entrypoint htpasswd \
registry \
-Bbn username password > auth/nginx.htpasswd
将上面的 username password 替换为你自己的用户名和密码。
编辑 docker-compose.yml

version: '3'
services:
registry:
image: registry
ports:
- "443:443"
volumes:
- ./:/etc/docker/registry
- registry-data:/var/lib/registry
volumes:
registry-data:
修改 hosts
编辑 /etc/hosts
docker.domain.com 127.0.0.1
启动
$ docker-compose up -d
这样我们就搭建好了一个具有权限认证、TLS 的私有仓库,接下来我们测试其功能是否正
常。
测试私有仓库功能
登录到私有仓库。
$ docker login docker.domain.com
尝试推送、拉取镜像。

$ docker pull ubuntu:17.10
$ docker tag ubuntu:17.10 docker.domain.com/username/ubuntu:17.10
$ docker push docker.domain.com/username/ubuntu:17.10
$ docker image rm docker.domain.com/username/ubuntu:17.10
$ docker pull docker.domain.com/username/ubuntu:17.10
如果我们退出登录,尝试推送镜像。
$ docker logout docker.domain.com
$ docker push docker.domain.com/username/ubuntu:17.10
no basic auth credentials
发现会提示没有登录,不能将镜像推送到私有仓库中。
注意事项
如果你本机占用了 443 端口,你可以配置 Nginx 代理,这里不再赘述。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏zhisheng

Docker 入门(二):从实例来看 Dockerfile 和 Compose

上一文中提到,每次对容器做了改动,如果不把它 commit 成新的镜像,在删除容器后,数据也会丢失,有没有什么更好的办法来保存数据呢?使用数据卷就可以解决这个问...

3745
来自专栏散尽浮华

Docker容器学习梳理--基础环境安装

以下是centos系统安装docker的操作记录 1)第一种方法:采用系统自带的docker安装,但是这一般都不是最新版的docker 安装epel源 [roo...

2785
来自专栏你不就像风一样

史上最全面的Docker构建工具使用教程

Docker 是一个开源的应用容器引擎,基于 Go 语言] 并遵从Apache2.0协议开源。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可...

1422
来自专栏jmeter高手高高手

Docker基础操作

   Docker是一个虚拟环境容器,可以将你的环境、代码、配置文件等一并打包到这个容器中,并发布和应用到任意平台中。比如,你在本地部署了git,jenkins...

1343
来自专栏python3

Docker安装

安装之前,我们首先确保自己的linux系统内核版本高于3.10,并且系统是64位,才能体验Docker。所以我用的是Centos7.3

3182
来自专栏散尽浮华

Docker容器学习梳理--日常操作总结

使用Docker已有一段时间了,今天正好有空梳理下自己平时操作Docker时的一些命令和注意细节: Docker 命令帮助 $ sudo docker Com...

4295
来自专栏糊一笑

小白学Docker之基础篇

系列文章: 小白学Docker之基础篇 小白学Docker之Compose 小白学Docker之Swarm PS: 以下是个人作为新手小白学习docker的笔记...

4974
来自专栏PHP技术大全

docker 镜像与容器的导入导出操作实践

公司有一台测试服务器,网速比较慢,特别是下载一些国外站点镜像的时候,而我本机则比较快,还有梯子,所以在思考一个问题;是否能在我本地把镜像下载下来,然后复制到测试...

1951
来自专栏互联网开发者交流社区

SpringBoot与docker

1192
来自专栏田飞雨的专栏

Docker 使用指南 (一)—— 基本操作

Docker 的基础是 Linux 容器(LXC)等技术。在 LXC 的基础上 Docker 进行了进一步的封装,让用户不需要去关心容器的管理,使得操作更为简便...

9.3K3

扫码关注云+社区