Docker File构建镜像
https://docs.docker.com/engine/reference/builder/
关于DockerFile
在Docker中创建镜像最常用的方式,就是使用Dockerfile。Dockerfile是一个Docker镜像的描述文件,我们可以理解成火箭发射的A、B、C、D…的步骤。Dockerfile其内部包含了一条条的指令,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
Dockerfile结构大致分为四个部分:
- 基础镜像信息
- 维护者信息
- 镜像操作指令
- 容器启动时执行指令
Dockerfile每行支持一条指令,每条指令可带多个参数,支持使用以#号开头的注释。下面会对上面使用到的一些常用指令做一些介绍。
Dockerfile常用指令
指令 | 对象 | 含义 |
---|---|---|
FROM | 镜像 | 指定新镜像所基于的镜像,必须为第一条指令 |
MAINTAINER | 名字 | 新镜像的维护人信息 |
RUN | 命令 | 在所基于的镜像上执行命令,并提交到新镜像中 |
EXPOSE | 端口号 | 指定新镜像加载到Docker时开启的端口号 |
ENV | 环境变量 变量值 | 设置一个环境变量的值,会被后面的RUN使用 |
ADD | 源文件/目录 目标文件/目录 | 将源文件复制到目标文件,源文件要与Docker位于同一目录下,或者为一个URL |
COPY | 源文件/目录 目标文件/目录 | 将本地主机上的源文件/目录复制到目标地点,源文件/目录要与Dockerfile在同一目录下 |
VOLUME | ["目录"] | 在容器中创建一个挂载点 |
USER | 用户名 /UID | 指定运行容器时的用户 |
WORKDIR | 路径 | 为后续的RUN、CMD、ENTRYPOINT指定工作目录 |
ONBUILD | 命令 | 指定所生成的镜像作为一个基础镜像时所要运行的命令 |
CMD | ["要运行的程序","参数1","参数2"] | 指定启动容器时运行的命令或脚本,只能有一条CMD命令,多条时只有最后一条被执行 |
来一张通俗易懂的全景图:
FROM
指明构建的新镜像是来自于哪个基础镜像
FROM <repository>[:<tag>]
#较为安全
FROM <repository>@<digest>
#<repository>:指定作为base image的名称
#<tag>:base image的标签,为可选性,省略时默认为latest
#<digest>:哈希码
例如:
FROM centos:7
MAINTAINER(depreacted)
指明镜像维护着及其联系方式(一般是邮箱地址),已被LABEL所替代
MAINTAINER<author's detail>
例如:
MAINTAINER "MiaosenG <MiaosenG@MiaosenG.com>"
不过,MAINTAINER并不推荐使用,更推荐使用LABEL来指定镜像作者,例如:
LABEL maintainer="MiaosenG <MiaosenG@MiaosenG.com>"
RUN
#第一种格式<command>通常是一个shell命令,且以"/bin/sh -c"运行
#此进程在容器的PID不为1,当使用docker stop <container>命令停止容器时,此进程接收不到SIGTERM信号
RUN <command>
#第二种格式中的参数是一个JSON格式数组,其中<executable>为要运行命令,<paramN>为传递命令选项或参数
RUN ["<executable>","<param1>","<param2>"]
#此格式不会以"/bin/sh -c"运行,无法使用变量替换或通配符等shell操作符,可写成
RUN ["/bin/sh" "-c","<exrcutable>","<param1>"]
构建镜像时运行的Shell命令,针对于基础镜像,仅可使用基础镜像环境,和命令;
容器构建时,会启动临时容器执行 run命令;执行完成后生成镜像
例如:
RUN ["yum", "install", "httpd"]
RUN yum install httpd
jason数组中,要是用双引号
CMD
#意义等同RUN
CMD <command>
CMD ["<executable>","<param1>","<param2>"]
#第三种用于ENTRYPOINT指令提供默认参数
CMD ["<param1>","<param2>"]
启动容器时执行的Shell命令,针对镜像默认运行命令,多个CMD仅最后一个生效
例如:
CMD ["-C", "/start.sh"]
CMD ["/usr/sbin/sshd", "-D"]
CMD /usr/sbin/sshd -D
EXPOSE
EXPOSE <port>[/<protocol>][ <port>[/<protocol>]...]
#<protocol>用于指定传输层协议,可为tcp或udp,默认为TCP
声明容器运行的(待暴露)默认服务端口,但运行容器时不会暴露该端口;需要配合docker run -P
命令,会暴露DockerFile内写定的全部端口
例如:
EXPOSE 11211/udp 11211/tcp
ENV
ENV <key> <value>
ENV <key>=<value>...
设置环境内环境变量,可被文件中位于其后的其他指令所调用
例如:
#单一变量赋值
ENV MYSQL_ROOT_PASSWORD 123456
#多变量赋值
ENV JAVA_HOME=/usr/local/jdk1.8.0_45
#使用
ENV DOC_ROOT=/data/web/html/
WEB_SERVER_PACKAGE="nginx-1.15.2"
COPY index.html ${DOC_ROOT:-/data/web/html} #设定默认值
ADD ${WEB_SERVER_PACKAGE}.tar.gz /usr/local/src/
第一种格式,<key>之后所有内容均被视为<value>
第二种格式,<value>中包含空格,可以利用
进行转义或换行,也可以加
""
进行表示
可配合docker run -e
设定变量值使用,在初始化运行容器时改变默认设定变量
ADD
拷贝文件或目录到镜像中,例如:
ADD <src>...<dest>
ADD ["<src>"..."<dest>"]
支持URL路径,用法同COPY
#展开文件
ADD nginx-1.15.2.tar.gz /usr/local/src/
#仅下载不解压
ADD http://nginx.org/download/nginx-1.15.2.tar.gz /usr/local/src/
如果<src>为URL
- <dest>不以
/
结尾,则<src>指定的文件将被下载并直接被创建为<dest>;- <dest>以
/
结尾,则文件名URL指定的文件将被直接下载并保存为<dest>/<filename>如果<src>是一个
- 本地系统上的压缩格式的tar文件;他将会被展开为一个目录,类似于
tar -x
- URL获取的tar文件;将不会被展开
如果<src>为多个,或期间使用了通配符,则<dest>必须是一个以
/
的目录路径;如果不以
/
结尾,则其被视作一个普通文件,<src>的内容将被直接写入到<dest>
COPY
拷贝主机文件或目录到镜像中
COPY <src>...<dest>
COPY ["<src>"..."<dest>"]
#<src>:需要复制的源文件,支持通配符
#<dest>:目标路径;建议使用绝对路径;否则,COPY指定则以WORKDIR为其其实路径
用法同ADD,只是不支持自动下载和解压,例如:
COPY index.html /data/web/html
COPY yum.repos.d /etc/yum.repos.d
<src>必须是build上下文中的路径,不能是其父目录中的文件
如果<src>是目录,则其内部文件或子目录会被递归复制,但<src>目录自身不会被复制
如果指定了多个<src>,或在<src>中使用了通配符,则<dest>必须是一个目录,且必须以
/
结尾如果<dest>实现不存在,他将会被自动创建,这包括其父目录路径
ENTRYPOINT
ENTRYPOINT <command>
ENTRYPOINT ["<executable>","<param1>","<param2>"]
启动容器时执行的Shell命令,同CMD类似,只是由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且,这些命令行参数会被当作参数传递给ENTRYPOINT指定指定的程序,例如:
ENTRYPOINT ["/bin/bash", "-C", "/start.sh"]
ENTRYPOINT /bin/bash -C '/start.sh'
PS:Dockerfile文件中也可以存在多个ENTRYPOINT指令,但仅有最后一个会生效。
除非使用docker run --entrypoint string
才可强制覆盖dockerfile内有ENTRYPOINT命令
当CMD与ENTRYPOINT同时出现在Dockerfile中;CMD使用第三种写法,为ENTRYPOINT指令提供默认参数
CMD ["/bin/httpd","-f","-h","/data/web/html"]
ENTRYPOINT ["/bin/sh","-c"]
但当使用docker run
对容器内部传参或输入指令,会对CMD进行覆盖,而不会覆盖ENTRYPOINT
VOLUME
VOLUME <mountpoint>
VOLUME ["<mountpoint>"]
指定容器挂载点到宿主机自动生成的目录或其他容器
例如:
VOLUME ["/data/mysql/"]
PS:一般不会在Dockerfile中用到,更常见的还是在docker run的时候指定-v数据卷。
USER
USER <UID>|<UserName>
为RUN、CMD和ENTRYPOINT执行Shell命令指定运行用户,默认容器中使用root用户
例如:
USER <user>[:<usergroup>]
USER <UID>[:<UID>]
USER miaoseng
UID可以为任意数字,但实践中其必须为/etc/passwd
(容器)中某用户的有效UID,否则docker run
命令将失败
WORKDIR
WORKDIR <dirpath>
为RUN、CMD、ENTRYPOINT以及COPY和AND设置工作目录,理解为cd
命令
例如:
WORKDIR /usr/local/src/
ADD nginx-1.15.2.tar.gz ./
#等效于
ADD nginx-1.15.2.tar.gz /usr/local/src/
HEALTHCHECK
HEALTHCHECK [options] CMD command
告诉Docker如何测试容器以检查它是否仍在工作,即健康检查,例如:
HEALTHCHECK --interval=5m --timeout=3s --retries=3
CMD curl -f http:/localhost/ || exit 1
其中,一些选项的说明:
- --interval=DURATION (default: 30s):每隔多长时间探测一次,默认30秒
- -- timeout= DURATION (default: 30s):服务响应超时时长,默认30秒
- --start-period= DURATION (default: 0s):服务启动多久后开始探测,默认0秒
- --retries=N (default: 3):认为检测失败几次为宕机,默认3次
一些返回值的说明:
- 0:容器成功是健康的,随时可以使用
- 1:不健康的容器无法正常工作
- 2:保留不使用此退出代码
SHELL
修改shell类型
#Linux
SHELL ["/bin/sh","-c"]
#Windows
SHELL ["cmd","/S","/C"]
STOPSIGNAL
定义停止信号
STOPSIGNAL signal
ARG
AGE <name>[=<default value>]
在构建镜像时,指定一些参数,例如:
FROM centos:7
ARG author="MiaosenG <miaosen@MiaosenG.com>"
LABEL maintainer="${author}"
这时,我们在docker build时可以带上自定义参数user了,如下所示:
docker build --build-arg author=Tony Dockerfile .
ONBUILD
ONBUILD <INSTRUCTION>
用于在Dockerfile中定义一个触发器,延迟触发;当其他用户,基于含有ONBUILD镜像构建新镜像,再次构建并运行build时执行命令;留下后门
ONBUILD不可自我嵌套,且不会触发FROM和MAINTAINER指令;通常使用RUN和ADD,使用ADD,COPY时可能缺少指定文件;无法拷贝
CMD与RUN区别
指令 | CMD | RUN |
---|---|---|
区别 | 基于Dockerfile构建出新映像文件启动一个容器时 | 运行于映像文件构建过程中 |
可被docker run命令选项进行覆盖,针对镜像默认运行命令 | 使用基础镜像环境,和命令 | |
可存在多个CMD仅最后一个生效 | 可运行多个 |
例如:
Dockerfile1
vim Dockerfile
FROM busybox
LABEL maintainer="MiaosenG <miaoseng@miaoseng.com>" app="http"
ENV WEB_DOC_ROOT="/data/web/html/"
RUN mkdir -p $ENV WEB_DOC_ROOT && echo '<h1>hello busybox httpd server</h1>' > $ENV WEB_DOC_ROOT/indec.html
CMD /bin/httpd -f -h ${WEB_DOC_ROOT}
运行后查看容器内PID=1进程为/bin/httpd -f -h /data/web/html/
Dockerfile2
vim Dockerfile
FROM busybox
LABEL maintainer="MiaosenG <miaoseng@miaoseng.com>" app="http"
ENV WEB_DOC_ROOT="/data/web/html/"
RUN mkdir -p $ENV WEB_DOC_ROOT && echo '<h1>hello busybox httpd server</h1>' > $ENV WEB_DOC_ROOT/indec.html
CMD ["/bin/httpd","-f","-h","${WEB_DOC_ROOT}"]
运行后报错httpd:can't change directory to '${WEB_DOC_ROOT}': No such file or directory