什么是 Dockerfile


  • Dockerfile 为镜像的描述文件
  • 是一个包含用于组合镜像的命令文本文档,是一个脚本文件


Dockerfile 作用


  • 通过读取 Dockerfile 中的指令,按步骤自动生成镜像


解析 Dockerfile

docker build -t 机构/镜像名<:tags> Dockerfile目录

其实在 ​​Docker命令​​ 已经使用过了 Dockerfile 如下图所示:

Dockerfile_ubuntu

命令

  • ​FROM 镜像:标签​​:基准镜像名称与标签
  • ​FROM scratch​​:不依赖任何基准镜像
  • ​MAINTAINER 机构描述​​:维护机构
  • ​WORKDIR 目录名称​​​:切换工作目录相当于​​cd​​ 命令,如果工作目录不存在,会自动创建,尽量使用绝对路径
  • ​ADD 源文件地址 复制的目标地址​​​:将指定的文件或目录复制到镜像的指定目录下,如果指定目录不存在,会自动创建目录,​​ADD test /​​​ 复制到根路径下,​​ADD test.gz /​​ 复制后还会对文件进行解压
  • ​LABEL​​​:其它的描述信息,不会产生功能影响,起方便阅读作用,是你自己想要在里面定义的一些信息,可以去写到​​label​​​ 当中,例如​​LABEL version = "1.0"​​​,​​LABEL description = "描述信息"​
  • ​ENV​​​:设置环境常量,可以提高程序的维护性例如,​​ENV JAVA_HOME /use/local/openjdk11​​​,​​RUN ${JAVA_HOME}/bin/java -jar test.jar​

执行命令

运行命令的方式

Shell 命令方式

RUN yum install -y vim
  • 使用 Shell 执行时,当前 shell 是父进程,生一个子 shell 进程
  • 在子 shell 中执行脚本,执行完毕后,退出子 shell,回到当前 shell
  • 子 shell 退出后,不会对父进程产生影响

Dockerfile_docker_02

Exec 命令方式

RUN ["yum", "install", "-y", "vim"]
  • 会用 Exec 进程替换当前进程,并保持​​pid​​ 不变
  • 执行完毕后,直接退出,并不会退回到之前的进程环境
  • 推荐使用 Exec 方式来执行命令

Dockerfile_ubuntu_03

镜像创建时执行的命令

run

  • 在​​build​​ 构建时执行命令
  • 在构建镜像时执行的命令
  • 对镜像内部的文件或者资源进行调整,一旦镜像被创建以后,就不可以再被修改

容器创建时执行的命令

cmd

  • 容器启动后,执行默认的参数或命令
  • 用于设置默认执行的命令
  • ​Dockfile​​​ 中出现​​多个cmd​​​,只有​​最后一个​​ 才会被执行
  • 如果容器启动时,在启动后添加了命令,则 Dockfile 中添加的 cmd 指定会被忽略
  • 写在 Dockfile 中的 cmd 命令不一定会被执行
CMD ["ps", "-ef"]

entrypoint

  • 容器启动时执行的命令
  • Dockerfile 中只有最后一个​​ENTRYPOINT​​ 会被执行
  • ​ENTRYPOINT​​ 一定会被执行,如果有多个只会执行最后一个
ENTRYPOINT ["ps"]

Dockerfile_缓存_04

Dockerfile 示例

我在 Linux 中的 ​​usr/local​​ 目录当中进行演示,首先创建一个目录如下

mkdir docker-run

Dockerfile_Docker_05

进入到刚刚创建好了 docker-run 目录当中在输入如下命令创建 Dockerfile 文件

vim Dockerfile

Dockerfile_缓存_06

然后加入如下内容

FROM ubuntu:latest
RUN ["echo", "hello-run"]
CMD ["echo", "hello-cmd"]
ENTRYPOINT ["echo", "hello-entrypoint"]

Dockerfile_Docker_07

如上准备工作完毕之后就可以开始构建镜像了输入如下命令开始构建,注意不能直接输入 ​​.​​​ 如果直接输入 ​​.​​ 就成了虚悬镜像了

docker build -t it6666/ubuntu:1.0 .

Dockerfile_ubuntu_08

Dockerfile_ubuntu_09

然后在紧接着以该镜像启动一个容器出来查看效果如下图所示,命令如下,镜像的完整形式应该是镜像的名称加上 TAG 的版本号如下

docker run it6666/ubuntu:1.0

Dockerfile_docker_10

如上的内容其实也演示了一下 CMD 与 ENTRYPOINT 两个结合使用的结果,当两个进行结合使用的时候加了参数之后,后面的 CMD 它就和把加入的参数信息,来去当作是 ENTRYPOINT 的参数使用如下图所示

Dockerfile_Docker_11

如上看了两个进行结合使用的效果我们在将 ENTRYPOINT 去掉来看看 CMD 的效果,经过如上介绍 ​​如果容器启动时,在启动后添加了命令,则 Dockfile 中添加的 cmd 指定会被忽略​​,首先编辑 Dockerfile 文件删除 ENTRYPOINT 这一行内容保存并退出

Dockerfile_docker_12

重新构建镜像 2.0

docker build -t it6666/ubuntu:2.0 .

发现了一个问题,在构建的过程当中发现它使用了缓存的内容,这就是要介绍的一个内容,在构建的过程当中如果该命令已经之前执行过了会放入缓存,下一次在进行执行的时候就会直接去使用缓存当中的,如下

Dockerfile_ubuntu_13

构建完毕之后我们在来验证如上所说的那一点内容,如果没有 ​​ENTRYPOINT​​,在执行时,输入的命令会直接执行如下

Dockerfile_docker_14

镜像分层

  • Docker 镜像是分层构建的,Dockerfile 中每条指令都会新建一层
  • ​Layer​​​ 是按顺序构成的,最底层的​​Layer​​ 是基础镜像(base image)最上层是最终镜像(final image)
  • 当一个镜像被更新或重新构建时,只有更新的层需要修改,其他没有更新的层可以直接复用本地缓存
  • 这就是 Docker 镜像如此快速和轻量级的部分原因,每一层的大小加起来等于最终镜像的大小

Dockerfile_Docker_15

  • Dockerfile 中每条指令都会新建一层,每层只记录本层所做的更改,而这些层都是只读层
  • 启动一个容器,Docker 会在最顶部添加读写层
  • 在容器内做的所有更改,如写日志、修改、删除文件等,都保存到了读写层内,一般称该层为容器层
  • 各个容器相互之间是隔离的
  • 容器(container)和镜像(image)的最主要区别就是容器加上了顶层的读写层。所有对容器的修改都发生在此层,镜像并不会被修改

Dockerfile_ubuntu_16

Step1

产生了一个临时容器,该容器只用于构建,不能直接使用

Step2

  • 每一次运行完了之后,会对上一步进行快照
  • 快照以临时容器的方式来体现,对当时系统环境做一个快照
  • 存档执行的步骤,后续再执行 Dockfile 时,如果有相同的命令,会直接从存档中读取
  • 临时容器就可以被重用

Step3...