一、使用 Docker 镜像
Docker 运行容器前需要本地存在对应的镜像,如果本地不存在该镜像,Docker 会从镜像仓库下载该镜像。 下面将介绍更多关于镜像的内容,包括:从仓库获取镜像; 管理本地主机上的镜像; 介绍镜像实现的基本原理。
获取镜像
获取镜像从 Docker 镜像仓库获取镜像的命令是 docker pull 。其命令格式为:docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]具体的选项可以通过 docker pull --help 命令看到,这里我们说一下镜像名称的格式。Docker 镜像仓库地址:地址的格式一般是 <域名 ip> [:端口号] 。默认地址是 Docker Hub。仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名> / <软件名> 。对于 Docker Hub,如果不给出用户名,则默认为 library ,也就是官方镜像。比如:$ docker pull ubuntu:16.0416.04: Pulling from library/ubuntubf5d46315322: Pull complete9f13e0ac480c: Pull completee8988b5b3097: Pull complete40af181810e7: Pull completee6f7c7e5c03e: Pull completeDigest: sha256:147913621d9cdea08853f6ba9116c2e27a3ceffecf3b492983ae97c3d643fbbeStatus: 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 \bashroot@e7009c6ce357:/# cat /etc/os-releaseNAME="Ubuntu"VERSION="16.04.5 LTS (Xenial Xerus)"ID=ubuntuID_LIKE=debianPRETTY_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=xenialUBUNTU_CODENAME=xenialdocker 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 lsREPOSITORY TAG IMAGE ID CREATED SIZEredis latest 5f515359c7f8 5 days ago 183 MBnginx latest 05a60462f8ba 5 days ago 181 MBmongo 3.2 fe9198c04d62 5 days ago 342 MB00285df0df87 5 days ago 342 MBubuntu 16.04 f753707788c5 4 weeks ago 127 MBubuntu latest f753707788c5 4 weeks ago 127 MBubuntu 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 dfTYPE TOTAL ACTIVE SIZE RECLAIMABLEImages 2 1 174.7MB 116MB (66%)Containers 4 1 0B 0BLocal Volumes 3 3 395.6kB 0B (0%)Build Cache 0 0 0B 0B虚悬镜像上面的镜像列表中,还可以看到一个特殊的镜像,这个镜像既没有仓库名,也没有标签,均为 。: 00285df0df87 5 days ago 342 MB这个镜像原本是有镜像名和标签的,原来为 mongo:3.2 ,随着官方镜像维护,发布了新版本后,重新 docker pull mongo:3.2 时, mongo:3.2 这个镜像名被转移到了新下载的镜像身上,而旧的镜像上的这个名称则被取消,从而成为了 。除了 docker pull 可能导致这种情况, docker build 也同样可以导致这种现象。由于新旧镜像同名,旧镜像名称被取消,从而出现仓库名、标签均为 的镜像。这类无标签镜像也被称为 虚悬镜像(dangling image) ,可以用下面的命令专门显示这类镜像:$ docker image ls -f dangling=trueREPOSITORY TAG IMAGE ID CREATED SIZE 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 ubuntuREPOSITORY TAG IMAGE ID CREATED SIZEubuntu 16.04 f753707788c5 4 weeks ago 127 MBubuntu latest f753707788c5 4 weeks ago 127 MBubuntu 14.04 1e0c3dd64ccd 4 weeks ago 188 MB列出特定的某个镜像,也就是说指定仓库名和标签 $ docker image ls ubuntu:16.04REPOSITORY TAG IMAGE ID CREATED SIZEubuntu 16.04 f753707788c5 4 weeks ago 127 MB除此以外, docker image ls 还支持强大的过滤器参数 --filter ,或者简写 -f 。之前我们已经看到了使用过滤器来列出虚悬镜像的用法,它还有更多的用法。比如,我们希望看到在mongo:3.2 之后建立的镜像,可以用下面的命令:$ docker image ls -f since=mongo:3.2REPOSITORY TAG IMAGE ID CREATED SIZEredis latest 5f515359c7f8 5 days ago 183 MBnginx 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 -q5f515359c7f805a60462f8bafe9198c04d6200285df0df87f753707788c5f753707788c51e0c3dd64ccd--filter 配合 -q 产生出指定范围的 ID 列表,然后送给另一个 docker 命令作为参数,从而针对这组实体成批的进行某种操作的做法在 Docker 命令行使用过程中非常常见,不仅仅是镜像,将来我们会在各个命令中看到这类搭配以完成很强大的功能。因此每次在文档看到过滤器后,可以多注意一下它们的用法。另外一些时候,我们可能只是对表格的结构不满意,希望自己组织列;或者不希望有标题,这样方便其它程序解析结果等,这就用到了 Go 的模板语法。 比如,下面的命令会直接列出镜像结果,并且只包含镜像ID和仓库名:$ docker image ls --format "{ {.ID}}: { {.Repository}}"5f515359c7f8: redis05a60462f8ba: nginxfe9198c04d62: mongo00285df0df87: f753707788c5: ubuntuf753707788c5: ubuntu1e0c3dd64ccd: ubuntu或者打算以表格等距显示,并且有标题行,和默认一样,不过自己定义列:$ docker image ls --format "table { {.ID}}\t{ {.Repository}}\t{ {.Tag}}"IMAGE ID REPOSITORY TAG5f515359c7f8 redis latest05a60462f8ba nginx latestfe9198c04d62 mongo 3.200285df0df87 f753707788c5 ubuntu 16.04f753707788c5 ubuntu latest1e0c3dd64ccd ubuntu 14.04
删除本地镜像
删除本地镜像如果要删除本地的镜像,可以使用 docker image rm 命令,其格式为:$ docker image rm [选项] <镜像1> [ <镜像2> ...]用 ID、镜像名、摘要删除镜像其中, <镜像> 可以是 镜像短 ID 、 镜像长 ID 、 镜像名 或者 镜像摘要 。比如我们有这么一些镜像:$ docker image lsREPOSITORY TAG IMAGE ID CREATED SIZEcentos latest 0584b3d2cf6d 3 weeks ago 196.5 MBredis alpine 501ad78535f0 3 weeks ago 21.03 MBdocker latest cf693ec9b5c7 3 weeks ago 105.1 MBnginx latest e43d811ce2f4 5 weeks ago 181.5 MB我们可以用镜像的完整 ID,也称为 长 ID ,来删除镜像。使用脚本的时候可能会用长 ID,但是人工输入就太累了,所以更多的时候是用 短 ID 来删除镜像。 docker image ls 默认列出的就已经是短 ID 了,一般取前3个字符以上,只要足够区分于别的镜像就可以了。比如这里,如果我们要删除 redis:alpine 镜像,可以执行:$ docker image rm 501Untagged: redis:alpineUntagged: redis@sha256:f1ed3708f538b537eb9c2a7dd50dc90a706f7debd7e1196c9264edeea521a86dDeleted: sha256:501ad78535f015d88872e13fa87a828425117e3d28075d0c117932b05bf189b7Deleted: sha256:96167737e29ca8e9d74982ef2a0dda76ed7b430da55e321c071f0dbff8c2899bDeleted: sha256:32770d1dcf835f192cafd6b9263b7b597a1778a403a109e2cc2ee866f74adf23Deleted: sha256:127227698ad74a5846ff5153475e03439d96d4b1c7f2a449c7a826ef74a2d2faDeleted: sha256:1333ecc582459bac54e1437335c0816bc17634e131ea0cc48daa27d32c75eab3Deleted: sha256:4fc455b921edf9c4aea207c51ab39b10b06540c8b4825ba57b3feed1668fa7c7我们也可以用 镜像名 ,也就是 <仓库名> : <标签> ,来删除镜像。$ docker image rm centosUntagged: centos:latestUntagged: centos@sha256:b2f9d1c0ff5f87a4743104d099a3d561002ac500db1b9bfa02a783a46e0d366cDeleted: sha256:0584b3d2cf6d235ee310cf14b54667d889887b838d3f3d3033acd70fc3c48b8aDeleted: sha256:97ca462ad9eeae25941546209454496e1d66749d53dfa2ee32bf1faabd239d38当然,更精确的是使用 镜像摘要 删除镜像。$ docker image ls --digestsREPOSITORY TAG DIGEST IMAGE ID CREATED SIZEnode slim sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228 6e0c4c8e3913 3 weeks ago 214 MB$ docker image rm node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228Untagged: node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228Untagged 和 Deleted如果观察上面这几个命令的运行输出信息的话,你会注意到删除行为分为两类,一类是Untagged ,另一类是 Deleted 。我们之前介绍过,镜像的唯一标识是其 ID 和摘要,而一个镜像可以有多个标签。因此当我们使用上面命令删除镜像的时候,实际上是在要求删除某个标签的镜像。所以首先需要做的是将满足我们要求的所有镜像标签都取消,这就是我们看到的 Untagged 的信息。因为一个镜像可以对应多个标签,因此当我们删除了所指定的标签后,可能还有别的标签指向了这个镜像,如果是这种情况,那么 Delete 行为就不会发生。所以并非所有的 docker rmi 都会产生删除镜像的行为,有可能仅仅是取消了某个标签而已。当该镜像所有的标签都被取消了,该镜像很可能会失去了存在的意义,因此会触发删除行为。镜像是多层存储结构,因此在删除的时候也是从上层向基础层方向依次进行判断删除。镜像的多层结构让镜像复用变动非常容易,因此很有可能某个其它镜像正依赖于当前镜像的某一层。这种情况,依旧不会触发删除该层的行为。直到没有任何层依赖当前层时,才会真实的删除当前层。这就是为什么,有时候会奇怪,为什么明明没有别的标签指向这个镜像,但是它还是存在的原因,也是为什么有时候会发现所删除的层数和自己 docker pull 看到的层数不一样的源。除了镜像依赖以外,还需要注意的是容器对镜像的依赖。如果有用这个镜像启动的容器存在(即使容器没有运行),那么同样不可以删除这个镜像。之前讲过,容器是以镜像为基础,再加一层容器存储层,组成这样的多层存储结构去运行的。因此该镜像如果被这个容器所依赖的,那么删除必然会导致故障。如果这些容器是不需要的,应该先将它们删除,然后再来删除镜像。用 docker image ls 命令来配合像其它可以承接多个实体的命令一样,可以使用 docker image ls -q 来配合使用 dockerimage 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 里的这个驱动达不到生产环境使用的稳定程度,所以不推荐使用。 标签> 仓库名> 镜像> 镜像2> 镜像1>
二、操作 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/bashroot@af8bae53bdd3:/#其中, -t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i则让容器的标准输入保持打开。在交互模式下,用户可以通过所创建的终端来输入命令,例如root@af8bae53bdd3:/# pwd/root@af8bae53bdd3:/# lsbin 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:/# psPID TTY TIME CMD1 ? 00:00:00 bash11 ? 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 worldhello worldhello worldhello 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 lsCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES77b2dc01fe0f 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 worldhello worldhello world. . .
进入容器
进入容器在使用 -d 参数时,容器启动后会进入后台。某些时候需要进入容器进行操作,包括使用 docker attach 命令或 docker exec 命令,推荐大家使用 docker exec 命令,原因会在下面说明。attach 命令docker attach 是 Docker 自带的命令。下面示例如何使用该命令。$ docker run -dit ubuntu243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550$ docker container lsCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES243c32535da7 ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds nostalgic_hypatia$ docker attach 243croot@243c32535da7:/#注意: 如果从这个 stdin 中 exit,会导致容器的停止。exec 命令-i -t 参数docker exec 后边可以跟多个参数,这里主要说明 -i -t 参数。只用 -i 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。当 -i -t 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。 $ docker run -dit ubuntu69d137adef7a8a689cbcb059e94da5489d3cddd240ff675c640c8d96e84fe1f6$ docker container lsCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES69d137adef7a ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds zealous_swirles$ docker exec -i 69d1 bashlsbinbootdev...$ docker exec -it 69d1 bashroot@69d137adef7a:/#如果从这个 stdin 中 exit,不会导致容器的停止。这就是为什么推荐大家使用 docker exec 的原因。更多参数说明请使用 docker exec --help 查看。
导出和导入容器
导出和导入容器导出容器如果要导出本地某个容器,可以使用 docker export 命令。$ docker container ls -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES7691a814370e 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 lsREPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZEtest/ubuntu v1.0 9d37a6082e97 About a minute ago 171.3MB此外,也可以通过指定 URL 或者某个目录来导入,例如$ docker import http://example.com/exampleimage.tgz example/imagerepo注:用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 dockerimport 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。
删除容器
删除容器可以使用 docker container rm 来删除一个处于终止状态的容器。例如$ docker container rm trusting_newtontrusting_newton如果要删除一个运行中的容器,可以添加 -f 参数。Docker 会发送 SIGKILL 信号给容器。清理所有处于终止状态的容器用 docker container ls -a 命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,用下面的命令可以清理掉所有处于终止状态的容器。$ docker container prune
三、访问仓库
仓库( Repository )是集中存放镜像的地方。
一个容易混淆的概念是注册服务器( Registry )。实际上注册服务器是管理仓库的具体服务器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜像。从这方面来说,仓库可以被认为是一个具体的项目或目录。例如对于仓库地址dl.dockerpool.com/ubuntu 来说, dl.dockerpool.com 是注册服务器地址, ubuntu 是仓库名。大部分时候,并不需要严格区分这两者的概念。
Docker Hub目前 Docker 官方维护了一个公共仓库 ,其中已经包括了数量超过 15,000 的镜像。大部分需求都可以通过在 Docker Hub 中直接下载镜像来实现。注册你可以在 https://cloud.docker.com 免费注册一个 Docker 账号。登录可以通过执行 docker login 命令交互式的输入用户名及密码来完成在命令行界面登录Docker Hub。你可以通过 docker logout 退出登录。拉取镜像你可以通过 docker search 命令来查找官方仓库中的镜像,并利用 docker pull 命令来将它下载到本地。例如以 centos 为关键词进行搜索:[root@iZwz9eftauv7x69f5jvi96Z ~]# docker search centosNAME DESCRIPTION STARS OFFICIAL AUTOMATEDcentos 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 centosUsing default tag: latestlatest: Pulling from library/centosaeb7866da422: Pull complete Digest: sha256:67dad89757a55bfdfabec8abd0e22f8c7c12a1856514726470228063ed86593bStatus: Downloaded newer image for centos:latest
推送镜像 用户也可以在登录后通过 docker push 命令来将自己的镜像推送到 Docker Hub。 以下命令中的 username 请替换为你的 Docker 账号用户名。 $ docker tag ubuntu:17.10 username/ubuntu:17.10$ docker image lsREPOSITORY TAG IMAGE ID CREATED SIZEubuntu 17.10 e211a66937c6 3 months ago 100MBwuweixiang/ubuntu 17.10 e211a66937c6 3 months ago 100MB[root@iZwz9eftauv7x69f5jvi96Z ~]# docker loginLogin 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: wuweixiangPassword: WARNING! Your password will be stored unencrypted in /root/.docker/config.json.Configure a credential helper to remove this warning. Seehttps://docs.docker.com/engine/reference/commandline/login/#credentials-storeLogin Succeeded[root@iZwz9eftauv7x69f5jvi96Z ~]# docker push wuweixiang/ubuntu:17.10The 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 wuweixiangNAME DESCRIPTION STARS OFFICIAL AUTOMATEDwuweixiang/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 lsREPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZEubuntu 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 lsREPOSITORY TAG IMAGE ID CREATED SIZE127.0.0.1:5000/ubuntu latest ea4c82dcd15a 2 weeks ago 85.8MBubuntu latest ea4c82dcd15a 2 weeks ago 85.8MB使用 docker push 上传标记的镜像。$ docker push 127.0.0.1:5000/ubuntu:latestThe 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:latestPulling repository 127.0.0.1:5000/ubuntu:latestba5877dc9bec: Download complete511136ea3c5a: Download complete9bad880da3d2: Download complete25f11f5fb0cb: Download completeebc34468f71d: Download complete2318d26665ef: Download complete$ docker image lsREPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE127.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 restartUbuntu 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 Docker Registry CA'以上命令中 -subj 参数里的 /C 表示国家,如 CN ; /ST 表示省; /L 表示城市或者地区; /O 表示组织名; /CN 通用名称。第三步配置 CA 根证书,新建 root-ca.cnf 。[root_ca]basicConstraints = critical,CA:TRUE,pathlen:1keyUsage = critical, nonRepudiation, cRLSign, keyCertSignsubjectKeyIdentifier=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,issuerbasicConstraints = critical,CA:FALSEextendedKeyUsage=serverAuthkeyUsage = critical, digitalSignature, keyEnciphermentsubjectAltName = DNS:docker.domain.com, IP:127.0.0.1subjectKeyIdentifier=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.1log:accesslog:disabled: truelevel: debugformatter: textfields:service: registryenvironment: stagingstorage:delete:enabled: truecache:blobdescriptor: inmemoryfilesystem:rootdirectory: /var/lib/registryauth:htpasswd:realm: basic-realmpath: /etc/docker/registry/auth/nginx.htpasswdhttp:addr: :443host: https://docker.domain.comheaders:X-Content-Type-Options: [nosniff]http2:disabled: falsetls:certificate: /etc/docker/registry/ssl/docker.domain.com.crtkey: /etc/docker/registry/ssl/docker.domain.com.keyhealth:storagedriver:enabled: trueinterval: 10sthreshold: 3生成 http 认证文件$ mkdir auth$ docker run --rm \--entrypoint htpasswd \registry \-Bbn username password > auth/nginx.htpasswd将上面的 username password 替换为你自己的用户名和密码。编辑 docker-compose.ymlversion: '3'services:registry:image: registryports:- "443:443"volumes:- ./:/etc/docker/registry- registry-data:/var/lib/registryvolumes:registry-data:修改 hosts编辑 /etc/hostsdocker.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.10no basic auth credentials发现会提示没有登录,不能将镜像推送到私有仓库中。注意事项如果你本机占用了 443 端口,你可以配置 Nginx 代理,这里不再赘述。