Docker 镜像

在前一篇的介绍中,我们知道镜像是 Docker 的三大组件之一。

Docker 运行容器前需要本地存在对应的镜像,如果本地不存在该镜像,Docker 会从镜像仓库下载该镜像。

 

获取镜像

之前提到过,Docker Hub 上有大量的高质量的镜像可以用,这里我们就说一下怎么获取这些镜像。

从 Docker 镜像仓库获取镜像的命令是 docker pull。其命令格式为:

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

Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub。

仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。
比如:

docker pull ubuntu:18.04

// 18.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:18.04

上面的命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub 获取镜像。而镜像名称是 ubuntu:18.04,因此将会获取官方镜像 library/ubuntu 仓库中标签为 18.04 的镜像。

从下载过程中可以看到分层存储的概念,镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。下载过程中给出了每一层的 ID 的前 12 位。并且下载结束后,给出该镜像完整的 sha256 的摘要,以确保下载一致性。

 

列出镜像

要想列出已经下载下来的镜像,可以使用 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

列表包含了 仓库名、标签、镜像 ID、创建时间 以及 所占用的空间。

 

删除本地镜像

如果要删除本地的镜像,可以使用 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 镜像,可以执行:

# 使用镜像ID删除
docker image rm 501
# 使用镜像名删除
docker image rm centos

# 用 docker image ls 命令来配合
# 删除所有镜像名为redis的镜像
docker image rm $(docker image ls -q redis)
# 删除所有在 mongo:3.2 之前的镜像
docker image rm $(docker image ls -q -f before=mongo:3.2)

 

Docker容器

容器是 Docker 又一核心概念。

简单的说,容器是独立运行的一个或一组应用,以及它们的运行态环境。对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用。

 

新建容器并启动

有了镜像后,我们就能够以这个镜像为基础启动并运行一个容器。

docker run {image-id}

例如,下面的命令输出一个 “Hello World”,之后终止容器。

docker run ubuntu:18.04 /bin/echo 'Hello world'
// Hello world 

这跟在本地直接执行 /bin/echo ‘hello world’ 几乎感觉不出任何区别。

下面的命令则启动一个 bash 终端,允许用户进行交互,并在使用完后自动删除容器。

docker run -it --rm ubuntu:18.04 /bin/bash
root@af8bae53bdd3:/# 

-it:这是两个参数,一个是 -i:交互式操作(让容器的标准输入保持打开),一个是 -t 终端(Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上)。如果打算进入 bash 执行一些命令并查看返回结果,就需要交互式终端。

–rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 –rm 可以避免浪费空间。

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

当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:

  • 检查本地是否存在指定的镜像,不存在就从公有仓库下载
  • 利用镜像创建并启动一个容器
  • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从地址池配置一个 ip 地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被终止

容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必需的。除此之外,并没有其它的资源。可以在伪终端中利用 ps 或 top 来查看进程信息。

root@ba267838cc1b:/# ps
//    PID TTY          TIME CMD
//      1 ?        00:00:00 bash
//     11 ?        00:00:00 ps

可见,容器中仅运行了指定的 bash 应用。这种特点使得 Docker 对资源的利用率极高,是货真价实的轻量级虚拟化。

 

后台运行

更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加 -d 参数来实现。

下面举两个例子来说明一下。

如果不使用 -d 参数运行容器。

docker run ubuntu:18.04 /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:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
// 77b2dc01fe0f3f1265df143181e7b9af5e05279a884f4776ee75350ea9d8017a

此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面(输出结果可以用 docker logs 查看)。

注: 容器是否会长久运行,是和 docker run 指定的命令有关,和 -d 参数无关。

使用 -d 参数启动后会返回一个唯一的 id。

要获取容器的输出信息,可以通过 docker container logs 命令。

docker container logs [container ID or NAMES]
// hello world
// hello world
// hello world
// . . .

 

列出容器

可以通过 docker container ls 命令来查看容器信息。

docker container ls
// CONTAINER ID  IMAGE         COMMAND               CREATED        STATUS       PORTS NAMES
// 77b2dc01fe0f  ubuntu:18.04  /bin/sh -c 'while tr  2 minutes ago  Up 1 minute        agitated_wright

 

启动/停止/重启容器

docker container start/stop/restart {container-id}

此外,当 Docker 容器中指定的应用终结时,容器也自动终止。

例如对于只启动了一个终端的容器,用户通过 exit 命令或 Ctrl+d 来退出终端时,所创建的容器立刻终止。

终止状态的容器可以用 docker container ls -a 命令看到。例如

docker container ls -a
// CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
// ba267838cc1b ubuntu:18.04 "/bin/bash" 30 minutes ago Exited (0) About a minute ago trusting_newton
// 98e5efa7d997 training/webapp:latest "python app.py" About an hour ago Exited (0) 34 minutes ago backstabbing_pike

处于终止状态的容器,可以通过 docker container start 命令来重新启动。

此外,docker container restart 命令会将一个运行态的容器终止,然后再重新启动它。

 

进入容器

在使用 -d 参数时,容器启动后会进入后台。

某些时候需要进入容器进行操作,可以使用 docker attach或者docker exec 命令,区别是使用attach连接后一旦exit,容器就会停止,因此推荐使用exec命令。

docker attach ba26
docker exec -it ba26 bash

 

导出容器

如果要导出本地某个容器,可以使用 docker export 命令。

docker container ls -a
// CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
// 7691a814370e ubuntu:18.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.3 MB

此外,也可以通过指定 URL 或者某个目录来导入,例如

docker import http://example.com/exampleimage.tgz example/imagerepo

注:用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker import 来导入一个容器快照到本地镜像库。

这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。

 

删除容器

可以使用 docker container rm 来删除一个处于终止状态的容器。例如

docker container rm trusting_newton

如果要删除一个运行中的容器,可以添加 -f 参数。Docker 会发送 SIGKILL 信号给容器。

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

docker container prune

永远不要认为我们可以逃避,

我们的每一步都决定着最后的结局,

我们的脚这正在走向我们选定的终点。

——米兰·昆德拉