docker(3、镜像2)1. docker commit 命令构建镜像 2. Dockerfile 构建镜像 3.镜像的缓存特性 4,调试 Dockerfile 5 镜像命名

1、docker commit

docker commit 命令是创建新镜像最直观的方法,其过程包含三个步骤:
1. 运行容器
2. 修改容器 
3. 将容器保存为新的镜像

举个例子:在 ubuntu base 镜像中安装 vim 并保存为新镜像。

1. 第一步, 运行容器安装 vim
-it 参数的作用是以交互模式进入容器,并打开终端。aeddb500ce69 是容器的内部 ID。


2. 保存为新镜像
如果要正常退出不关闭容器,请按Ctrl+P+Q进行退出容器(ctrl+d可以直接退出容器 容器关闭)
在新窗口中查看当前运行的容器。

mystifying_bassi 是 Docker 为我们的容器随机分配的名字。
执行 docker commit 命令将容器保存为镜像。 

新镜像命名为 ubuntu-with-vim。
查看新镜像的属性。

从 size 上看到镜像因为安装了软件而变大了。
从新镜像启动容器,验证 vim 已经可以使用。

以上演示了如何用 docker commit 创建新镜像。然而,Docker 并不建议用户通过这种方式构建镜像。原因如下:
1. 手工创建镜像的方式,容易出错,效率低且可重复性弱。比如要在 debian base 镜像中也加入 vi,还得重复前面的所有步骤。
2. 更重要的:使用者并不知道镜像是如何创建出来的,里面是否有恶意程序。也就是说无法对镜像进行审计,存在安全隐患。

2. Dockerfile 构建镜像

用 Dockerfile 创建上节的 ubuntu-with-vim,其内容则为

[root@localhost ~]# cat Dockerfile 
FROM ubuntu
RUN  apt-get update && apt-get install -y vim 
[root@localhost ~]# 

docker build 命令构建镜像并详细分析

[root@localhost ~]# pwd   ①
/root
[root@localhost ~]# ls    ②
Dockerfile
[root@localhost ~]# docker build -t ubuntu-with-vim-dockerfile .   ③
Sending build context to Docker daemon  48.64kB     ④
Step 1/2 : FROM ubuntu    ⑤
 ---> ccc6e87d482b
Step 2/2 : RUN  apt-get update && apt-get install -y vim    ⑥
 ---> Running in 5b6b069a531b   ⑦
.......
Setting up vim (2:8.0.1453-1ubuntu1.1) ...
 ---> bd5f13ced1fb   ⑧ 
Removing intermediate container 5b6b069a531b   ⑨
Successfully built bd5f13ced1fb    ⑩
Successfully tagged ubuntu-with-vim-dockerfile:latest

① 当前目录为 /root。
② Dockerfile 准备就绪。
③ 运行 docker build 命令,-t 将新镜像命名为 ubuntu-with-vim-dockerfile,命令末尾的 . 指明 build context 为当前目录。Docker 默认会从 build context 中查找 Dockerfile 文件,我们也可以通过 -f 参数指定 Dockerfile 的位置。
④ 从这步开始就是镜像构建过程。 首先 Docker 将 build context 中的所有文件发送给 Docker daemon。build context 为镜像构建提供所需要的文件或目录。Dockerfile 中的 ADD、COPY 等命令可以将 build context 中的文件添加到镜像。此例中,当前目录 /root,该目录下的所有文件和子目录都会被发送给 Docker daemon。
⑤ Step 1:执行 FROM,将 ubuntu 作为 base 镜像。
ubuntu 镜像 ID 为 ccc6e87d482b。
⑥ Step 2:执行 RUN,安装 vim,具体步骤为 ⑦、⑧、⑨。
⑦ 启动 ID 为 5b6b069a531b 的临时容器,在容器中通过 apt-get 安装 vim。
⑧ 安装成功后,将容器保存为镜像,其 ID 为 bd5f13ced1fb。
这一步底层使用的是类似 docker commit 的命令。
⑨ 删除临时容器 5b6b069a531b。
⑩ 镜像构建成功。 

通过 docker images 查看镜像信息

镜像 ID 为5b6b069a531b,与构建时的输出一致。

在上面的构建过程中,我们要特别注意指令 RUN 的执行过程 ⑦、⑧、⑨。Docker 会在启动的临时容器中执行操作,并通过 commit 保存为新的镜像。

查看镜像分层结构

ubuntu-with-vi-dockerfile 是通过在 base 镜像的顶部添加一个新的镜像层而得到的。

 

可以通过 docker history 命令验证

docker history 会显示镜像的构建历史,也就是 Dockerfile 的执行过程。

ubuntu-with-vi-dockerfile 与 ubuntu 镜像相比,确实只是多了顶部的一层 bd5f13ced1fb,由 apt-get 命令创建,大小为 87.9MB。docker history 也向我们展示了镜像的分层结构,每一层由上至下排列。

3、镜像的缓存特性

Docker 会缓存已有镜像的镜像层,构建新镜像时,如果某镜像层已经存在,就直接使用,无需重新创建。
在前面的 Dockerfile 中添加一点新内容,往镜像中复制一个文件:,所以有缓存可用。通过 docker history 可以进一步验证。

[root@localhost ~]# cat  Dockerfile  ①
FROM ubuntu
RUN  apt-get update && apt-get install -y vim
COPY testfile /
[root@localhost ~]# ls
Dockerfile  testfile
[root@localhost ~]# docker build -t ubuntu-with-vi-dockerfile-2 .
Sending build context to Docker daemon  50.18kB
Step 1/3 : FROM ubuntu
 ---> ccc6e87d482b
Step 2/3 : RUN  apt-get update && apt-get install -y vim
 ---> Using cache   ②
 ---> bd5f13ced1fb
Step 3/3 : COPY testfile /   ③
 ---> 16cbe34ae715
Successfully built 16cbe34ae715
Successfully tagged ubuntu-with-vi-dockerfile-2:latest

① 确保 testfile 已存在。
② 重点在这里:之前已经运行过相同的 RUN 指令,这次直接使用缓存中的镜像层 bd5f13ced1fb。
③ 执行 COPY 指令。
其过程是启动临时容器,复制 testfile,提交新的镜像层 16cbe34ae715,删除临时容器。
在 ubuntu-with-vi-dockerfile 镜像上直接添加一层就得到了新的镜像 ubuntu-with-vi-dockerfile-2。

如果我们希望在构建镜像时不使用缓存,可以在 docker build 命令中加上 --no-cache 参数。
Dockerfile 中每一个指令都会创建一个镜像层,上层是依赖于下层的。无论何时,只要某一层发生变化,其上面所有层的缓存都会失效。
也就是说,如果我们改变 Dockerfile 指令的执行顺序,或者修改或添加指令,都会使缓存失效。
举例说明,比如交换前面 RUN 和 COPY 的顺序

虽然在逻辑上这种改动对镜像的内容没有影响,但由于分层的结构特性,Docker 必须重建受影响的镜像层。
root@ubuntu:~# docker build -t ubuntu-with-vi-dockerfile-3 .
Sending build context to Docker daemon 37.89 kB
Step 1 : FROM ubuntu
 ---> f753707788c5
Step 2 : COPY testfile /
 ---> bc87c9710f40
Removing intermediate container 04ff324d6af5
Step 3 : RUN apt-get update && apt-get install -y vim
 ---> Running in 7f0fcb5ee373
Get:1 http://archive.ubuntu.com/ubuntu xenial InRelease [247 kB]
......
从上面的输出可以看到生成了新的镜像层 bc87c9710f40,缓存已经失效。
除了构建时使用缓存,Docker 在下载镜像时也会使用。

例如我们下载 httpd 镜像。

docker pull 命令输出显示第一层(base 镜像)已经存在,不需要下载。
由 Dockerfile 可知 httpd 的 base 镜像为 debian,正好之前已经下载过 debian 镜像

4,调试 Dockerfile

 Dockerfile 在内的任何脚本和程序都会出错,如何排查

 Dockerfile 构建镜像的过程:

  1. 从 base 镜像运行一个容器。

  2. 执行一条指令,对容器做修改。

  3. 执行类似 docker commit 的操作,生成一个新的镜像层。

  4. Docker 再基于刚刚提交的镜像运行一个新容器。

  5. 重复 2-4 步,直到 Dockerfile 中的所有指令执行完毕。

如果 Dockerfile 由于某种原因执行到某个指令失败了,我们也将能够得到前一个指令成功执行构建出的镜像,这对调试 Dockerfile 非常有帮助。我们可以运行最新的这个镜像定位指令失败的原因。

我们来看一个调试的例子。Dockerfile 内容如下:

执行 docker build

Dockerfile 在执行第三步 RUN 指令时失败。我们可以利用第二步创建的镜像 22d31cc52b3e 进行调试,方式是通过 docker run -it 启动镜像的一个容器。

手工执行 RUN 指令很容易定位失败的原因是 busybox 镜像中没有 bash。虽然这是个极其简单的例子,但它很好地展示了调试 Dockerfile 的方法

5 镜像命名

docker build -t ubuntu-with-vi
这里的 ubuntu-with-vi 就是镜像的名字。通过 dock images 可以查看镜像的信息

这里注意到 ubuntu-with-vi 对应的是 REPOSITORY,而且还有一个叫 latest 的 TAG

实际上一个特定镜像的名字由两部分组成:repository 和 tag。

[image name] = [repository]:[tag]

如果执行 docker build 时没有指定 tag,会使用默认值 latest。其效果相当于:

docker build -t ubuntu-with-vi:latest

 Docker Hub 上很多 repository 将 latest 作为最新稳定版本的别名,但这只是一种约定,而不是强制规定。所以我们在使用镜像时最好还是避免使用 latest,明确指定某个 tag,比如 httpd:2.3,ubuntu:xenial

发布了62 篇原创文章 · 获赞 10 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/cojn52/article/details/104351371