Docker 制作镜像Dockerfile和commit操作
构建镜像
构建镜像主要有两种方式:
使用docker commit命令从运行中的容器提交为镜像;
使用docker build命令从 dockerfile 构建镜像。
首先介绍下如何从运行中的容器提交为镜像。我依旧使用 busybox 镜像举例,使用以下命令创建一个名为 busybox 的容器并进入 busybox 容器。
$ docker run --rm --name=busybox -it busybox sh
执行完上面的命令后,当前窗口会启动一个 busybox 容器并且进入容器中。在容器中,执行以下命令创建一个文件并写入内容:
/ # touch hello.txt && echo "i love docker. " > hello.txt
此时在容器的根目录下,已经创建了一个 hello.txt 文件,并写入了 "i love docker. "。
下面,我们新打开另一个命令行窗口,运行以下命令提交镜像:
$ docker commit busybox busybox:hello
sha256:cbc6406aaef080d1dd3087d4ea1e6c6c9915ee0ee0f5dd9e0a90b03e2215e81c
然后使用上面讲到的docker image ls命令查看镜像:
$ docker image ls busybox repository tag image id created size busybox hello cbc6406aaef0 2 minutes ago 1.22mb busybox latest 018c9d7b792b 4 weeks ago 1.22mb
此时我们可以看到主机上新生成了 busybox:hello 这个镜像。
通过对比显然使用dockerfile的docker build更好。docker commit的缺点如下:
需要在容器内操作麻烦,效率低。
这一点也是最重要的,其他人或者过一段时间后自己也不知道这个镜像是怎么做出来的,但是使用dockerfile构建的镜像,我们看到是执行了apt-get install命令。
第二种方式是最重要也是最常用的镜像构建方式:dockerfile。dockerfile 是一个包含了用户所有构建命令的文本。通过docker build命令可以从 dockerfile 生成镜像。
使用 dockerfile 构建镜像具有以下特性:
dockerfile 的每一行命令都会生成一个独立的镜像层,并且拥有唯一的 id
dockerfile 的命令是完全透明的,通过查看 dockerfile 的内容,就可以知道镜像是如何一步步构建的
dockerfile 是纯文本的,方便跟随代码一起存放在代码仓库并做版本管理
看到使用 dockerfile 的方式构建镜像有这么多好的特性,你是不是已经迫不及待想知道如何使用了。别着急,我们先学习下 dockerfile 常用的指令。
dockerfile 指令 | 指令简介 |
---|---|
from | dockerfile 除了注释第一行必须是 from ,from 后面跟镜像名称,代表我们要基于哪个基础镜像构建我们的容器。 |
run | run 后面跟一个具体的命令,类似于 linux 命令行执行命令。 |
add | 拷贝本机文件或者远程文件到镜像内 |
copy | 拷贝本机文件到镜像内 |
user | 指定容器启动的用户 |
entrypoint | 容器的启动命令 |
cmd | cmd 为 entrypoint 指令提供默认参数,也可以单独使用 cmd 指定容器启动参数 |
env | 指定容器运行时的环境变量,格式为 key=value |
arg | 定义外部变量,构建镜像时可以使用 build-arg = 的格式传递参数用于构建 |
expose | 指定容器监听的端口,格式为 [port]/tcp 或者 [port]/udp |
workdir | 为 dockerfile 中跟在其后的所有 run、cmd、entrypoint、copy 和 add 命令设置工作目录。 |
看了这么多指令,感觉有点懵?别担心,我通过一个实例让你来熟悉它们。这是一个 dockerfile:
from centos:7 copy nginx.repo /etc/yum.repos.d/nginx.repo run yum install -y nginx expose 80 env host=mynginx cmd ["nginx","-g","daemon off;"]
第一行表示我要基于 centos:7 这个镜像来构建自定义镜像。这里需要注意,每个 dockerfile 的第一行除了注释都必须以 from 开头。
第二行表示拷贝本地文件 nginx.repo 文件到容器内的 /etc/yum.repos.d 目录下。这里拷贝 nginx.repo 文件是为了添加 nginx 的安装源。
第三行表示在容器内运行yum install -y nginx命令,安装 nginx 服务到容器内,执行完第三行命令,容器内的 nginx 已经安装完成。
第四行声明容器内业务(nginx)使用 80 端口对外提供服务。
第五行定义容器启动时的环境变量 host=mynginx,容器启动后可以获取到环境变量 host 的值为 mynginx。
第六行定义容器的启动命令,命令格式为 json 数组。这里设置了容器的启动命令为 nginx ,并且添加了 nginx 的启动参数 -g 'daemon off;' ,使得 nginx 以前台的方式启动。
镜像的实现原理
其实 docker 镜像是由一系列镜像层(layer)组成的,每一层代表了镜像构建过程中的一次提交。下面以一个镜像构建的 dockerfile 来说明镜像是如何分层的。
from busybox
copy test /tmp/test
run mkdir /tmp/testdir
上面的 dockerfile 由三步组成:
第一行基于 busybox 创建一个镜像层;
第二行拷贝本机 test 文件到镜像内;
第三行在 /tmp 文件夹下创建一个目录 testdir。
这里我的 docker 使用的是 overlay2 文件驱动,进入到/var/lib/docker/overlay2目录下使用tree .命令查看产生的镜像文件:
$ tree . # 以下为 tree . 命令输出内容 |-- 3e89b959f921227acab94f5ab4524252ae0a829ff8a3687178e3aca56d605679 | |-- diff # 这一层为基础层,对应上述 dockerfile 第一行,包含 busybox 镜像所有文件内容,例如 /etc,/bin,/var 等目录 ... 此次省略部分原始镜像文件内容 | `-- link |-- 6591d4e47eb2488e6297a0a07a2439f550cdb22845b6d2ddb1be2466ae7a9391 | |-- diff # 这一层对应上述 dockerfile 第二行,拷贝 test 文件到 /tmp 文件夹下,因此 diff 文件夹下有了 /tmp/test 文件 | | `-- tmp | | `-- test | |-- link | |-- lower | `-- work |-- backingfsblockdev |-- bec6a018080f7b808565728dee8447b9e86b3093b16ad5e6a1ac3976528a8bb1 | |-- diff # 这一层对应上述 dockerfile 第三行,在 /tmp 文件夹下创建 testdir 文件夹,因此 diff 文件夹下有了 /tmp/testdir 文件夹 | | `-- tmp | | `-- testdir | |-- link | |-- lower | `-- work ...
通过上面的目录结构可以看到,dockerfile 的每一行命令,都生成了一个镜像层,每一层的 diff 夹下只存放了增量数据,如图 2 所示。
分层的结构使得 docker 镜像非常轻量,每一层根据镜像的内容都有一个唯一的 id 值,当不同的镜像之间有相同的镜像层时,便可以实现不同的镜像之间共享镜像层的效果。
总结一下, docker 镜像是静态的分层管理的文件组合,镜像底层的实现依赖于联合文件系统(unionfs)。充分掌握镜像的原理,可以帮助我们在生产实践中构建出最优的镜像,同时也可以帮助我们更好地理解容器和镜像的关系。
总结
到此,相信你已经对 docker 镜像这一核心概念有了较深的了解,并熟悉了 docker 镜像的常用操作(拉取、查看、“重命名”、删除和构建自定义镜像)及底层实现原理。
镜像操作命令:
拉取镜像,使用 docker pull 命令拉取远程仓库的镜像到本地 ;
重命名镜像,使用 docker tag 命令“重命名”镜像 ;
查看镜像,使用 docker image ls 或 docker images 命令查看本地已经存在的镜像;
删除镜像,使用 docker rmi 命令删除无用镜像 ;
构建镜像,构建镜像有两种方式。第一种方式是使用 docker build 命令基于 dockerfile 构建镜像,也是我比较推荐的镜像构建方式;第二种方式是使用 docker commit 命令基于已经运行的容器提交为镜像。
镜像的实现原理:
镜像是由一系列的镜像层(layer )组成,每一层代表了镜像构建过程中的一次提交,当我们需要修改镜像内的某个文件时,只需要在当前镜像层的基础上新建一个镜像层,并且只存放修改过的文件内容。分层结构使得镜像间共享镜像层变得非常简单和方便。
以上这篇docker 制作镜像dockerfile和commit操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。