1、Dockerfile初识
镜像的生成途径:Dockerfile 和 基于容器制作
Dockerfile 就是用来构建镜像的源码,它就是一个纯文本文件,中间包含了一些指令,这些指令是在Dockerfile制作镜像时规定使用的指令
Dockerfile 总共就两类语句组成:#Comment(注释信息) 和 INSTRUCTION arguments(指令加其参数),这里的指令为了区分对应的参数,一般使用大写的
Dockerfile 的执行是从上到下依次执行的;第一个非注释行必须是 FROM 指令,指定当前要做的镜像是基于哪个基础镜像来制作的(所有制作镜像都是建立在一个已经存在的镜像基础之上)
2、Dockerfile 工作逻辑
- 在使用 Dockerfile 时候,需要有专用的工作目录;
- 如果要想基于当前目录向镜像中打包文件,那么这些文件做好之后要放在当前工作目录下(引用的文件路径不能是工作目录的父目录,只能是基于当前目录往下走的路径),如果此时想要将一个目录全部放入镜像中,也是可以的
- 如果此时这个目录中存在有几百个文件,但是有几个文件不想放入镜像中,Dockerfile 还支持在当前工作目录中加入 .dockerignore 文件,这样就可以忽略部分指定的文件
- 使用命令 docker build 命令来制作镜像了,做好镜像、打好标签、推导镜像仓库里面就可以使用了;其实这里的 docker build 构建一个镜像的时候,也是隐藏式的启动一个容器,然后在这个容器中执行相关的操作,和人工通过在 docker 容器中执行命令并最后打包镜像的操作没有什么本质区别
3、Dockerfile 指令
3.1、FROM
FROM 指令是最重要的一个且必须为 Dockerfile 文件开篇的第一个非注释行,用于为映射文件构建指定基准镜像,后续的指令运行于此基准镜像所提供的运行环境
实践中,基准镜像可以是任何可用镜像文件,默认情况下,docker build 会在docker 主机上查找指定的镜像文件,在其不存在时,则会从 Docker Hub Registry 上拉取所需的镜像文件
FROM <repository>[:<tag>] 或
FROM <repository>@<digest>
<repository>:指定作为base image的名称
<tag>:base image的标签,为可选项,省略时默认为latest
3.2、MAINTAINER -> LABEL
用于让 Dockerfile 制作者提供本人的详细信息,这个目前已经废弃掉了
Dockerfile 并不限制 MAINTAINER 指令出现的位置,但推荐将其放在 FROM 之后
MAINTAINER "aaa<aaa@126.com>"
这个在 docker 的新版中间使用 LABEL 替换了,LABEL 是让用户为一个镜像指定各种各样的元数据,数据为 key-value 格式
LABEL author="aaa<aaa@126.com>"
其实这两个指令对于做镜像本身来说并没有什么实际的意义
3.3、COPY
用于从 docker 宿主机复制文件至创建的新映像文件
COPY <src> ... <dest> 或
COPY ["<src>",..."<dest>"]
<src>:要复制的源文件或目录,支持使用通配符
<dest>:目表路径,即正在创建的image的文件系统路径;建议为<dest>使用绝对路径,否则,COPY指令则会以WORKDIR为起始路径
在上面的源文件是一个相对路径,目标路径是一个绝对路径
注意:在路径中有空白字符时,通常使用第二种方式
文件复制准则
- <src> 必须是 build 上下文中的路径,不能是其父路径中的文件
- 如果 <src> 是目录,则其内部文件或子目录会被递归复制,但 <src> 目录自身不会被复制
- 如果指定了多个 <src> ,或在 <src> 中使用了通配符,则 <dest> 必须是一个目录,且必须以 / 结尾
- 如果 <dest> 事先不存在,它将会被自动创建,这包括父目录路径
在上面的 index.html ,可以看到和 Dockerfile 是在同一个目录中
下面就可以使用 docker build 来制作镜像了
如上,就创建好了一个镜像
将这个镜像启动,并查看中间的 index.html 文件,可以看到启动的容器中已经存在这个文件了
上面的流程是将一个单独的文件放到创建的镜像指定目录下,下面可以将一个目录中的内容打包进镜像中;这里提一嘴,在Dockerfile中要尽量的少写内容,每写一行的内容对应的就是镜像的一层,所以能够进行合并的就合并为一条指令
3.4、ADD
ADD 指令类似于 COPY 指令,ADD 支持使用 TAR 文件和 URL 路径
ADD <src> ... <dest> 或
ADD ["<src>",..."<dest>"]
操作准则
- 同 COPY 命令
- 如果 <src> 为 URL 且 <dest> 不以 / 结尾,则 <src> 指令的文件将被下载并直接被创建为 <dest> ;如果 <dest> 以 / 结尾,则文件名 URL 指定的文件将被执行下载并保存为 <dest>/<filename>
- 如果 <src> 是一个本地系统上的压缩文件的tar文件,它将被展开为一个目录,其行为类似于 ```tar -x``` 命令;然而,通过 URL 获取到的tar文件将不会自动展开
- 如果 <src> 有多个,或其间接或直接使用了通配符,则 <dest> 必须是一个以 / 结尾的目录路径;如果 <dest> 不以 / 结尾,则其被视为一个普通文件,<src> 的内容将被直接写入 <dest>
可以看到,这里 ADD 后面如果跟上 URL 的时候,先会进行下载的操作,之后将下载好的文件传到构建的镜像中去,当启动这个镜像的时候,可以看到,这个文件是并没有展开的
下面将一个本地tar文件传入到镜像中去
3.5、WORKDIR
用于为 Dockerfile 中的所有 RUN、CMD、ENTRYPOINT、COPY 和 ADD 指定设定工作目录
WORKDIR <dirpath>
在 Dockerfile 文件中,WORKDIR 指令可出现多次,其路径也可为相对路径,不过,其是相对此前一个 WORKDIR 指令指定的路径
另外,WORKDIR 也可调用由 ENV 指定定义的变量
WORKDIR /var/log
WORKDIR $STATEPATH
3.6、VOLUME
用于在image中创建一个挂载点目录,以挂载 Docker Host 上面的卷或其他容器上的卷
VOLUME <mountpoint> 或
VOLUME ["<mountpoint>"]
可以看到,这个容器中的目录已经成功挂载在宿主机上面了
3.7、EXPOSE
用于为容器打开指定要监听的端口以实现与外部通信
EXPOSE <port>[/protocol] [<port>[/protocol]] ...
EXPOSE 指令可一次指定多个端口
EXPOSE 11211/udp 11211/tcp
这里注意,这里在文件中间指定了 EXPOSE 端口,并不是说之后依据这个镜像启动容器就一定就会暴露这个端口,在 ```docker container run``` 命令中,有一个 ```-P``` 选项,加上这个参数在启动容器的时候就会自动将这里 EXPOSE 指定的端口进行对外暴露(-P 表示暴露要镜像中指定要暴露的所有端口)
3.8、ENV
用于为镜像定义所需的环境变量,并可被 Dockerfile 文件中位于其后的其他指令(如 ENV、ADD、COPY等)所调用
调用格式为 ${variable_name} 或 $variable_name
ENV <key> <value> 或
ENV <key>=<value> ...
- 第一种格式中,<key> 之后的所有内容均会被视为 <value> 的组成部分,因此,一次只能设置一个变量
- 第二种格式可以一次设置多个变量,每一个变量为一个 “<key>=<value>” 的键值对,如果 <value> 中包含空格,可以使用反斜线(\)进行转义,也可以通过对 <value> 加引号进行标识;另外,反斜线也可用于续行
定义多个变量时,建议使用第二种格式,以便在同一层中完成所有功能
在 Dockerfile 中定义的所有环境变量,在启动容器之后,在容器中是可以看到的(printenv),在 docker container run 的时候,也是可以为容器设定环境变量的(-e,--env),此时如果在启动的时候设定了一个和 Dockerfile 定义相同的环境变量,后者会冲掉前者定义的环境变量
3.9、RUN
用于指定 docker build 过程中运行的程序,其可以是任何命令
RUN <command> 或
RUN ["<executable>","<param1>","<param2>"]
在第一种格式中,<command> 通常是一个shell命令,并以 /bin/sh -c 来运行它,这意味着此进程在容器中的PID不为1,不能接受 Unix 信号,因此,当使用 docker stop <container> 命令停止容器时,此进程接收不到 SIGTERM 信号
第二种语法格式中的参数是一个json格式的数组,其中 <executable> 为要运行的命令,后面的 <paramN> 为传递给命令的选项或参数;然而,此种格式指定的命令不会以 /bin/sh -c 来发起,因此常见的shell操作如变量替换以及通配符(?,*等)替换将不会进行,不过,如果要运行的命令依赖于此shell特性的话,可以将其替换为类似下面的格式
RUN ["/bin/bash","-c","<executable>","<param1>"]
3.10、CMD
类似于 RUN 指令,CMD 指令也可用于运行任何命令或应用程序,不过,二者的运行时间点不同
- RUN 指令运行于映像文件构建过程中,而 CMD 指令运行于基于 Dockerfile 构建出的新映像文件启动一个容器时
- CMD 指令的首要目的在于为启动的容器指定默认要运行的程序,且其运行结束后,容器也将被终止;不过,CMD 指定的指令其可以被 docker container run 的命令行选项所覆盖
- 在 Dockerfile 中可以存在多个 CMD 指令,但仅有最后一个生效
CMD <command> 或
CMD ["<executable>","param1","param2"] 或
CMD ["param1","param2"]
前两种语法格式的意义同 RUN,第三种则用于为 ENTRYPOINT 指令提供默认参数
3.11、重点
在上面使用到的 CMD 和 RUN 命令,如果后面直接跟上的就是命令,这样执行的命令默认是在shell下执行的,即所启动的进程的父进程是shell,且这样启动的docker并不能接受到 Unix 信号,比如使用 docker stop,就无法关闭docker,这是因为其主进程不唯一(原理是这样,但是docker会自动做替换,将启动的命令作为进程为1启动,这样启动的容器就能够接收 Unix 信号了)
可以看到,在 Dockerfile 中定义的时候,CMD 后面是命令行,正常来看的话,所启动命令的父进程应该是shell,进入容器之后可以看到,启动的命令的进程ID为1,这是docker自身对其进行了替换,这样就能够使用相关的docker命令来对容器进行管理了
3.12、ENTRYPOINT
类似于 CMD 指令的功能,用于为容器指定默认运行程序,从而使得容器像是一个单独的可执行程序
与 CMD 不同的是,由 ENTRYPOINT 启动的程序不会被 docker run 命令行指定的参数所覆盖,而且,这些命令行参数会被当做参数传递给 ENTRYPOINT 指定的程序
这里需要注意:如果在启动容器的时候,使用了 --entrypoint 选项的参数可覆盖 ENTRYPOINT 指令指定的程序
ENTRYPOINT <command>
ENTRYPOINT ["<executable>","<param1>","<param2>"]
docker run 命令传入的命令参数会覆盖 CMD 指令的内容并且附加到 ENTRYPOINT 命令最后作为其参数使用,Dockerfile 文件中也可以存在多个 ENTRYPOINT 指令,但仅有最后一个会生效