构建镜像
前面我们使用各种镜像进行测试演示,很多情况下我们是需要自己的镜像,满足自己业务需要的镜像,这就需要我们能够定制自己需要的镜像,构建 Docker 镜像有以下两种方法 。
- 使用 docker commit 命令 。
- 使用 docker build 命令和 Dockerfile 构建文件 。
1、使用 commit 命令构建
docker commit 命令是创建新镜像最直观的方法,其过程包含三个步骤:
- 运行容器;
- 修改容器;
- 将容器保存为新的镜像 。
1.1 运行一个要进行修改的容器
root@ubuntu:~# docker run -ti ubuntu /bin/bashroot@733a4b080491:/#1.2 安装 Apache 软件包
root@733a4b080491:/# apt-get update... ...root@733a4b080491:/# apt-get install -y apache2... ...我们启动了一个容器,并在里面安装了 Apache。我们将会拿这个容器作为一个 Web 服务器来运行,我们需要把它保存下来,这样就不用每次都运行这个步骤了 。
1.3 提交定制容器
root@ubuntu:~# docker ps -aCONTAINER IDIMAGECOMMANDCREATEDSTATUSPORTSNAMES733a4b080491ubuntu"/bin/bash"11 minutes agoExited (0) 5 seconds agosuspicious_mestorfroot@ubuntu:~# docker commit 733a4b080491 wzlinux/ubuntu_with_apachesha256:902ac2c87147fefc5b70c741ce9550dcda426cea9f824f442d5cc2744bdc90aeroot@ubuntu:~# docker imagesREPOSITORYTAGIMAGE IDCREATEDSIZEwzlinux/ubuntu_with_apache latest902ac2c8714733 seconds ago261MBubuntulatest20c44cd7596f10 days ago123MB可以看到,我们使用 docker commit 提交了修改过的容器,从 size 上可以看到镜像因为安装软件而变大了,docker commit 提交的只是创建容器的镜像与容器的当前状态之间有差异的部分,这使得该更新非常轻量 。
以上演示了如何用 docker commit 创建新镜像 。然而,Docker 并不建议用户通过这种方式构建镜像 。因为这是一种手工创建镜像的方式,容易出错,效率低且可重复性弱 。比如要在 debian base 镜像中也加入 apache,还得重复前面的所有步骤 。更重要的:使用者并不知道镜像是如何创建出来的,里面是否有恶意程序 。也就是说无法对镜像进行审计,存在安全隐患 。
不过,为了对 Docker 有一个更全面的了解,我们还是要了解一下如何使用 docker commit 构建 Docker 镜像 。因为即便是用 Dockerfile(推荐方法)构建镜像,底层也 docker commit 一层一层构建新镜像的 。学习 docker commit 能够帮助我们更加深入地理解构建过程和镜像的分层结构 。
2、使用 Dockerfile 构建
Dockerfile 使用基本的基于DSL(Domain Specific Language)语法的指令来构建一个 Docker 镜像,我们推荐使用 Dockerfile 方法来代替 docker commit,因为通过前者构建镜像更具备可重复性、透明性以及幂等性 。
一旦有了 Dockerfile,我们就可以使用 docker build 命令基于该 Dockerfile 中的指令构建一个新的镜像 。
2.1 我们的第一个 Dockerfile
用 Dockerfile 创建上面的 ubuntu_with_apache,内容如下 。
# Version 0.0.1FROM ubuntuRUN sed -i 's/archive.ubuntu.com/cn.archive.ubuntu.com/g' /etc/apt/sources.listRUN sed -i 's/security.ubuntu/cn.archive.ubuntu/g' /etc/apt/sources.listRUN apt-get -y update && apt-get -y install apache2EXPOSE 80执行 docker build 命令时,Dockerfile 中的所有指令都会被执行并且提交,并且在该命令成功结束后返回一个新镜像 。
root@ubuntu:~/sample# docker build -t ubuntu_with_apache_dockerfile .①Sending build context to Docker daemon 6.144kB②Step 1/5 : FROM ubuntu③ ---> 20c44cd7596fStep 2/5 : RUN sed -i 's/archive.ubuntu.com/cn.archive.ubuntu.com/g' /etc/apt/sources.list ---> Running in bac6dc3b900f ---> c66ad94ad8a4Removing intermediate container bac6dc3b900fStep 3/5 : RUN sed -i 's/security.ubuntu/cn.archive.ubuntu/g' /etc/apt/sources.list ---> Running in 5158558b6403 ---> 0a4c480147c5Removing intermediate container 5158558b6403Step 4/5 : RUN apt-get -y update && apt-get -y install apache2④ ---> Running in f547ce7a1b39⑤ …… …… ---> 118bde35120a⑥Removing intermediate container f547ce7a1b39⑦Step 5/5 : EXPOSE 80 ---> Running in e546786de05b ---> f55d7b07365bRemoving intermediate container e546786de05bSuccessfully built f55d7b07365b⑧Successfully tagged ubuntu_with_apache_dockerfile:latest

文章插图
① 运行 docker build 命令,-t 将新镜像命名为 ubuntu-with-apache-dockerfile,命令末尾的 . 指明 build context 为当前目录 。Docker 默认会从 build context 中查找 Dockerfile 文件,我们也可以通过 -f 参数指定 Dockerfile 的位置 。
② 从这步开始就是镜像真正的构建过程 。首先 Docker 将 build context 中的所有文件发送给 Docker daemon 。build context 为镜像构建提供所需要的文件或目录 。
Dockerfile 中的 ADD、COPY 等命令可以将 build context 中的文件添加到镜像 。此例中,build context 为当前目录 /sample,该目录下的所有文件和子目录都会被发送给 Docker daemon 。
所以,使用 build context 就得小心了,不要将多余文件放到 build context,特别不要把 /、/usr 作为 build context,否则构建过程会相当缓慢甚至失败 。
③ Step 1:执行 FROM,将 ubuntu 作为 base 镜像 。ubuntu 镜像 ID 为 452a96d81c30 。
④ Step 4:执行 RUN,安装 apache,具体步骤为 ⑤ ~ ? 。
⑤ 启动 ID 为 e38bc83df8b1 的临时容器,在容器中通过 apt-get 安装 apache 。
⑥ 安装成功后,将容器保存为镜像,其 ID 为 fbc9af08328d 。这一步底层使用的是类似 docker commit 的命令 。
⑦ 删除临时容器 02a4f3243dda 。
⑧ 镜像构建成功 。
通过 docker images 查看镜像信息 。
root@ubuntu:~/sample# docker imagesREPOSITORYTAGIMAGE IDCREATEDSIZEubuntu_with_apache_dockerfile latestf55d7b07365b27 minutes ago261MBwzlinux/ubuntu_with_apachelatest902ac2c87147About an hour ago 261MBubuntulatest20c44cd7596f10 days ago123MB2.2 查看镜像分成结构
ubuntu_with_apache_dockerfile 是通过在 base 镜像的顶部添加几个新的镜像层而得到的 。

文章插图
上图是从原文中拷贝的,下图是在我的电脑上面实验得到的数据,IMAGE的ID不同,但是其他都是相同的 。

文章插图
查看我本机的Ubuntu的IMAGE历史如下:

文章插图
从输出的结果可以看出来,每个命令都会生成一个镜像层 。
docker history 会显示镜像的构建历史,也就是 Dockerfile 的执行过程 。
ubuntu_with_apache_dockerfile 与 ubuntu 镜像相比,确实只是多了几层,Dockerfile 中的每个指令都会创建一层,docker history 也向我们展示了镜像的分层结构,每一层由上至下排列 。
2.3 镜像的缓存特性
由于每一步的构建过程都会将结果提交为镜像,所以 Docker 的构建镜像过程就显得非常聪明 。它会将之前的镜像层看作缓存 。
比如我们把 EXPOSE 80 改为 EXPOSE 8080 。
root@ubuntu:~/sample# docker build -t ubuntu_with_apache_8080 .Sending build context to Docker daemon 6.144kBStep 1/5 : FROM ubuntu ---> 20c44cd7596fStep 2/5 : RUN sed -i 's/archive.ubuntu.com/cn.archive.ubuntu.com/g'/etc/apt/sources.list ---> Using cache ---> c66ad94ad8a4Step 3/5 : RUN sed -i 's/security.ubuntu/cn.archive.ubuntu/g' /etc/apt/sources.list ---> Using cache ---> 0a4c480147c5Step 4/5 : RUN apt-get -y update && apt-get -y install apache2 ---> Using cache ---> 118bde35120aStep 5/5 : EXPOSE 8080 ---> Running in c89f8210c56a ---> ac88967e578eRemoving intermediate container c89f8210c56aSuccessfully built ac88967e578eSuccessfully tagged ubuntu_with_apache_8080:latest我们可以看到,之前的指令都是一样的,所以 docker 直接利用之前的缓存,只构建我们更改的指令,新的镜像层如下 。

文章插图
如果我们希望在构建镜像时不使用缓存,可以在 docker build 命令中加上 --no-cache 参数 。
Dockerfile 中每一个指令都会创建一个镜像层,上层是依赖于下层的 。无论什么时候,只要某一层发生变化,其上面所有层的缓存都会失效 。也就是说,如果我们改变 Dockerfile 指令的执行顺序,或者修改或添加指令,都会使缓存失效 。比如我们在前面添加指令 MAINTAINER wzlinux "admin@wzlinux.com" 。如下:
# Version 0.0.1FROM ubuntuMAINTAINER wzlinux "admin@wzlinux.com"RUN sed -i 's/archive.ubuntu.com/cn.archive.ubuntu.com/g' /etc/apt/sources.listRUN sed -i 's/security.ubuntu/cn.archive.ubuntu/g' /etc/apt/sources.listRUN apt-get -y update && apt-get -y install apache2EXPOSE 80然后使用docker进行构建,查看其过程 。
root@ubuntu:~/sample# docker build -t ubuntu_with_apache_author .Sending build context to Docker daemon 6.144kBStep 1/6 : FROM ubuntu ---> 20c44cd7596fStep 2/6 : MAINTAINER wzlinux "admin@wzlinux.com" ---> Running in 637bb3457407 ---> 829b24531d69Removing intermediate container 637bb3457407Step 3/6 : RUN sed -i 's/archive.ubuntu.com/cn.archive.ubuntu.com/g' /etc/apt/sources.list ---> Running in 416ae8aefb61 ---> 84643fe8447aRemoving intermediate container 416ae8aefb61Step 4/6 : RUN sed -i 's/security.ubuntu/cn.archive.ubuntu/g' /etc/apt/sources.list ---> Running in 58d8375fd5c3 ---> 1cb5776982d3Removing intermediate container 58d8375fd5c3Step 5/6 : RUN apt-get -y update && apt-get -y install apache2 ---> Running in 0514a7d04814 …… ……Processing triggers for sgml-base (1.26+nmu4ubuntu1) ... ---> 30eb21527feeRemoving intermediate container 0514a7d04814Step 6/6 : EXPOSE 80 ---> Running in 476ca5f98886 ---> 30672998f3d0Removing intermediate container 476ca5f98886Successfully built 30672998f3d0Successfully tagged ubuntu_with_apache_author:latest

文章插图
从输出的结果生成了很多新的镜像层,缓存已经失效 。
2.4 调试 Dockerfile
包括 Dockerfile 在内的任何脚本和程序都会出错 。有错并不可怕,但必须有办法排查,那我们测试一下在构建的过程中指令出现错误怎么办,比如我们把第二个sed指令写错了,写错了sd 。
# Version 0.0.1FROM ubuntuMAINTAINER wzlinux "admin@wzlinux.com"RUN sed -i 's/archive.ubuntu.com/cn.archive.ubuntu.com/g' /etc/apt/sources.listRUN sd -i 's/security.ubuntu/cn.archive.ubuntu/g' /etc/apt/sources.listRUN apt-get -y update && apt-get -y install apache2EXPOSE 80执行 docker build,如下 。

文章插图
Dockerfile 在执行第四步 RUN 指令时失败 。我们可以利用第三步创建的镜像 84643fe8447a 进行调试,方式是通过 docker run -it 启动镜像的一个容器 。
root@ubuntu:~/sample# docker run -ti 84643fe8447a /bin/bashroot@422ecce78664:/# sdbash: sd: command not found其实我们肯定不会傻到连 sd 不存在也不知道,我这里只是作为一个例子,其他更难的排错方法我们就使用这种方式 。
2.5 Dockerfile 指令
FROM
指定 base 镜像 。
【docker使用Dockerfile构建镜像的方法】MAINTAINER
设置镜像的作者,可以是任意字符串 。
COPY
将文件从 build context 复制到镜像 。
COPY 支持两种形式:
COPY src destCOPY ["src", "dest"]
注意:src 只能指定 build context 中的文件或目录 。
ADD
与 COPY 类似,从 build context 复制文件到镜像 。不同的是,如果 src 是归档文件(tar, zip, tgz, xz 等),文件会被自动解压到 dest 。
ENV
设置环境变量,环境变量可被后面的指令使用 。例如:
ENV MY_VERSION 1.3RUN apt-get install -y mypackage=$MY_VERSIONEXPOSE
指定容器中的进程会监听某个端口,Docker 可以将该端口暴露出来 。
VOLUME
将文件或目录声明为 volume 。
WORKDIR
为后面的 RUN, CMD, ENTRYPOINT, ADD 或 COPY 指令设置镜像中的当前工作目录 。
RUN
在容器中运行指定的命令 。
CMD
容器启动时运行指定的命令 。
Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效 。CMD 可以被 docker run 之后的参数替换 。
ENTRYPOINT
设置容器启动时运行的命令 。
Dockerfile 中可以有多个 ENTRYPOINT 指令,但只有最后一个生效 。CMD 或 docker run 之后的参数会被当做参数传递给 ENTRYPOINT 。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持考高分网 。
- 春季老年人吃什么养肝?土豆、米饭换着吃
- 三八妇女节节日祝福分享 三八妇女节节日语录
- 老人谨慎!选好你的“第三只脚”
- 校方进行了深刻的反思 青岛一大学生坠亡校方整改校规
- 脸皮厚的人长寿!有这特征的老人最长寿
- 长寿秘诀:记住这10大妙招 100%增寿
- 春季老年人心血管病高发 3条保命要诀
- 眼睛花不花要看四十八 老年人怎样延缓老花眼
- 香槟然能防治老年痴呆症? 一天三杯它人到90不痴呆
- 老人手抖的原因 为什么老人手会抖
