镜像的存储
当你在创建本地没有所需镜像的容器时,或你在执行 pull 拖拽镜像时,dockerd 守护进程将为你分层拖拽与存储镜像。
➜ ~ docker image pull httpd
Using default tag: latest
latest: Pulling from library/httpd
68ced04f60ab: Already exists
35d35f1e0dc9: Pull complete
8a918bf0ae55: Pull complete
d7b9f2dbc195: Pull complete
d56c468bde81: Pull complete
Digest: sha256:946c54069130dbf136903fe658fe7d113bd8db8004de31282e20b262a3e106fb
Status: Downloaded newer image for httpd:latest
docker.io/library/httpd:latest
有一层已存在,新拖拽下来了四层,就这样你得到了最新版的 httpd。这些层都存储在 /var/lib/docker/ 名为 <storage driver> 的目录下,我这里的存储驱动是 overlay2,
ls /var/lib/docker/overlay2 -lh
存储驱动(storage driver)处理着层与层之间的相互作用,
docker info
命令查看你的类型,例如下图中我的建构在本地文件系统之上的存储驱动为 overlay2,
Dockerfile 包含着构建 Docker 镜像所需要的全部指令,因此 Docker 可通过读取该文本文件来自动构建你需要的镜像。
Docerkfile 中的每一条指令,都会为镜像创建一只读层(read-only),且各层堆叠存储着前一层的变化。只有当你进行 run 操作启动一新的容器时,你才会为该容器的顶层,创建一可写层(writable layer),此时容器方可将其变化写入其中。这一层也叫容器层。
Docker 镜像与容器最主要的区别就在于其顶端的可写层。你在容器中做出的新建、修改或是删除操作都将仅仅保存在容器的可写层;同样当你删除容器时,你所删掉的也仅仅是那可写层,其下的只读层亦即镜像不做变化。
所谓分层构建,联合挂载。说明的便是多个容器共享底层的只读层,而只将变化写入到自己独有的容器层。
有赖于 CoW 写时复制 copy-on-write 机制,读时使用镜像中的现有文件,需要修改时才将其复制至容器层中,这保证了容器层将尽可能小。
操作
前文中,你尝试了通过命令行来创建 httpd 的镜像,
Docker 入门 | 1 容器化 vs 虚拟化与 Docker 基本操作
这在以 Dockerfile 的方式创建前,还要先说到 docker build 命令,
ls /var/lib/docker/overlay2 -lh
你可以查到它的帮助手册;当你敲击 docker build 命令时,你目前所在的目录便成为了构建上下文(build context);还记得 docker 的 C/S 架构吗,接着 docker 客户端程序会将构建上下文全部送往 dockerd 守护进程以完成构建。
先为你的构建上下文创建目录,再为 httpd 提供你的 html 展示页面,后创建 Dockerfile 文件,
docker container run -d --name test -p 8080:80 myhttpd
接着就是构建命令了,你要注意后面那个点,就代表着上面说到的构建上下文,
docker build -t myhttpd:v0.1 -t myhttpd:latest .
Docker 客户端首先将你指定的构建上下文送往守护进程,一行一行地识别到你的 Dockerfile 只有两个命令,两个命令也意味着你在 httpd 镜像之上创建了新的两层只读层。最后再将其命名为 myhttpd 并打上了两个标签。
docker container run -d --name test -p 8080:80 myhttpd
使用制作好了的 myhttpd 镜像,以后台运行的方式启动容器,并将其 80 端口发布在主机的 8080 端口上,
Dockerfile
这里细说 Docerkfile 中指令的用法,并在 CentOS 镜像中下载并搭建 httpd 服务,嘿嘿感谢官方的技术支持https://github.com/CentOS/CentOS-Dockerfiles/
先来创建本次的构建上下文吧,
docker build -t myhttpd:v0.1 -t myhttpd:latest .
其上的 Dockerfile 文件符合
INSTRUCTION arguments
格式,本非大小写敏感但约定俗成 Dockerfile 指令全大写。
下为 run-httpd.sh 脚本文件,用以完成清除任务后,并通过 apachectl 脚本在后台启动 httpd 进程,
#!/bin/bash
# Make sure we're not confused by old, incompletely-shutdown httpd
# context after restarting the container. httpd won't start correctly
# if it thinks it is already running.
rm -rf /run/httpd/* /tmp/httpd*
exec /usr/sbin/apachectl -DFOREGROUND
一条命令敲下去之后,
docker container run -d --name test -p 8080:80 myhttpd
我只截取了输出中的重要信息,
Sending build context to Docker daemon 3.072kB
Step 1/7 : FROM centos:7
Step 2/7 : LABEL Vendor="CentOS" License=GPLv2
Step 3/7 : RUN yum -y --setopt=tsflags=nodocs update && yum -y --setopt=tsflags=nodocs install httpd && yum clean all
Step 4/7 : EXPOSE 80
Step 5/7 : ADD run-httpd.sh /run-httpd.sh
Step 6/7 : RUN chmod -v +x /run-httpd.sh
Step 7/7 : CMD ["/run-httpd.sh"]
Successfully built 724a912e8fde
Successfully tagged chttpd:latest
Docker daemon 在对收到的构建上下文检测无误后,第一条 FROM 指令先初始化一个新的构建阶段并为后续命令的执行创建父镜像或可称为基础镜像。Dockerfile 必须要以 FROM 命令开头。
LABEL 指令以键值对的格式为你的镜像添加元数据。再次感谢 CentOS 官方的技术支持,以及 GPLv2 开源许可证。
RUN 指令用于在新的一层运行其后所接的命令,并且将结果提交给镜像后续步骤使用。此处操作意为指定 yum 不生成文档,并且用与逻辑链接只产生一层以尽可能减小镜像大小。
EXPOSE 指令告知 Docker 该容器在运行时所监听的网络端口,默认为 tcp 协议。但此指令与前文用到的 docker run 时的 -p 参数作用不同,EXPOSE 并未发布端口,而更像是一种开发者对使用者选择发布端口的告知。
ADD 指令将源文件复制到镜像的指定文件系统目录下。要注意的是源文件必须包含在构建上下文中——在第一步中构建上下文都已送往了守护进程,目标路径是绝对路径,或者是 WORKDIR 的相对路径。
CMD 指令最主要的目的是为容器运行时设置默认执行程序,
CMD command param1 param2
此与 RUN 指令所用格式相同,称为 shell 格式,在 Linux 上默认通过 /bin/sh -c 运行。CMD 指令在 Docekrfile 中只能出现一次,就算有多个也是最后一个生效。
docker container run [OPTIONS] IMAGE [COMMAND] [ARG...]
如你指定了容器运行时参数,则将覆盖 CMD 指令。