Dockerfile详解

116 阅读4分钟

Dockerfile详解

Dockerfile是一个组合映像命令的文本,Docker通过dockerfile中的指令自动生成镜像。

编写规则


  • 文件名必须是 Dockerfile
  • Dockerfile中所用的所有文件一定要和Dockerfile文件在同一级父目录下
  • Dockerfile中相对路径默认都是Dockerfile所在的目录
  • Dockerfile中一能写到一行的指令,一定要写到一行,因为每条指令都被视为一层,层多了执行效率就慢
  • Dockerfile中指令大小写不敏感,但指令都用大写(约定俗成)
  • Dockerfile 非注释行第一行必须是FROM
  • Dockerfile 工作空间目录下支持隐藏文件(.dockeringore),类似于git.gitingore

指令详解


FROM         # 基础镜像,一切从这里开始构建
MINTAINER     # 镜像是谁写的,姓名+邮箱
LABEL        # 现在推荐写 LABEL,代替上面的那个
RUN             # 镜像构建的时候需要运行的命令
ADD             # 步骤:如果要加一个tomcat镜像,就是加这个tomcat压缩包!1、添加内容 2、添加同目录
WORKDIR          # 镜像的工作目录
VOLUME       # 挂载的目录
EXPOSE       # 保留的端口配置
CMD             # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代。
ENTRYPOINT    # 指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD          # 当构建一个被继承的DockerFile 时,这时就会运行ONBUILD的指令,触发别的指令。
COPY         # 类似ADD,将我们的文件拷贝到镜像中
ENV             # 构建的时候设置环境变量
FROM:基础镜像
FROM <image>:<tag> [as other_name]      # tag可选;不写默认是latest版
  • FROMDockerfile 文件开篇第一个非注释行代码
  • 用于为镜像文件构建过程指定基础镜像,后续的指令都基于该基础镜像环境运行
  • 基础镜像可以是任何一个镜像文件
  • as other_name是可选的,通常用于多阶段构建(有利于减少镜像大小)
  • 使用是通过--from other_name使用,例如COPY --from other_name
LABEL:镜像描述信息
LABEL author="RobertChao<xxxxxxxxxx@qq.com>"
LABEL describe="create test image"# 或
LABEL author="RobertChao<xxxxxxxxxx@qq.com>" describe="create test image"# 或
LABEL author="RobertChao<xxxxxxxxxx@qq.com>" \
      describe="create test image"
  • LABEL指令用来给镜像以键值对的形式添加一些元数据信息
  • 可以替代MAINTAINER指令
  • 会集成基础镜像中的LABELkey相同会被覆盖
MAINTAINER:添加作者信息
MAINTAINER RobertChao<xxxxxxxxxx@qq.com>
COPY:从构建主机复制文件到镜像中
COPY <src> <dest>
​
COPY ["<src>", "<src>", ... "<dest>"]
  • <src>:要复制的源文件或目录,支持通配符
    • <src>必须在build所在路径或子路径下,不能是其父目录
    • <src>是目录。其内部的文件和子目录都会递归复制,但目录本身不会被复制
    • 如果指定了多个<src>或使用了通配符,这<dest>必须是一个目录,且必须以/结尾
  • <dest>:目标路径,即镜像中文件系统的路径
    • <dest>如果不存在会自动创建,包含其父目录路径也会被创建
ADD:从构建宿主机复制文件到镜像中
ADD <src> <dest>

ADD ["<src>","<src>"... "<dest>"]
  • <src>如果是一个压缩文件(tar),被解压为一个目录,通过URL下载一个文件不会被解压。
  • <src>如果是多个,或使用了通配符,则<dest>必须是以/结尾的目录,否则<src>会被作为一个普通文件,<src>的内容将被写入到<dest>
WORKDIR:设置工作目录
WORKDIR /opt
  • 使用上类似于cd命令,进入到opt文件目录。之后执行的所有操作命令都在当前目录中进行。
  • 目录不存在会自动创建
  • 一个DockerfileWORKDIR可以出现多次,其路径也可以为相对路径,相对路径是基于前一个WORKDIR路径
ENV:设置镜像中的环境变量
# 一次设置一个
ENV <key> <value>

# 一次设置多个
ENV <key>=<value> <key1>=<value1> <key2>=<value2> .....
  • linux系统中使用环境变量的方式是一样的
echo $varname
echo ${varname}
echo ${varname:-default value}           # 设置一个默认值,如果varname未被设置,值为默认值
echo ${varname:+default value}           # 设置默认值;不管值存不存在都使用默认值
USER:设置启动容器的用户
# 使用用户名
USER testuser
​
# 使用用户的UID
USER UID
RUN:镜像构建时执行的命令
# 语法1,shell 形式
RUN command1 && command2

# 语法2,exec 形式
RUN ["executable","param1","[aram2]"]

# 示例
RUN echo 1 && echo 2 

RUN echo 1 && echo 2 \
    echo 3 && echo 4

RUN ["/bin/bash","-c","echo hello world"]
  • RUN 在下一次建构期间,会优先查找本地缓存,若不想使用缓存可以通过--no-cache解除

    • docker build --no-cache
  • RUN 指令指定的命令是否可以执行取决于基础镜像

    • shell形式

      • 默认使用/bin/sh -c执行后面的command
      • 可以使用 && 或 `` 连接多个命令
    • exec形式

      • exec形式被解析为JSON序列,这意味着必须使用双引号 ""
      • 与 shell 形式不同,exec 形式不会调用shell解析。但exec形式可以运行在不包含shell命令的基础镜像中
      • 例如:RUN ["echo","$HOME"] ;指令$HOME并不会被解析,必须RUN ["/bin/sh","-c","echo $HOME"]
EXPOSE:为容器打开指定的监听端口以实现与外部通信
EXPOSE <port>/<protocol>
​
EXPOSE 80
EXPOSE 80/http
EXPOSE 2379/tcp
  • <port>:端口号
  • <protocol>:协议类型,默认TCP协议,tcp/udp/http/https
  • docker run -p 80:2379
VOLUME:实现挂载功能,将宿主机目录挂载到容器中
VOLUME ["/data"]                    # [“/data”]可以是一个JsonArray ,也可以是多个值
​
VOLUME /var/log 
VOLUME /var/log /opt
  • docker run -v /suzhuji /jingxiang
CMD:为容器设置默认启动命令或参数
# 语法1,shell形式
CMD command param1 param2 ...
​
# 语法2,exec形式
CMD ["executable","param1","param2"]
​
# 语法3,还是exec形式,不过仅设置参数
CMD ["param1","param2"]
  • CMD运行结束后容器将终止,CMD可以被docker run后面的命令覆盖
  • 一个Dockerfile只有顺序向下的最后一个CMD生效

语法1,shell形式,默认/bin/sh -c

  • 此时运行为shell的子进程,能使用shell的操作符(if环境变量? *通配符等)
  • 注意: 进程在容器中的 PID != 1,这意味着该进程并不能接受到外部传入的停止信号docker stop

语法2,exec形式CMD ["executable","param1","param2"]

  • 不会以/bin/sh -c运行(非shell子进程),因此不支持shell的操作符
  • 若运行的命令依赖shell特性,可以手动启动CMD ["/bin/sh","-c","executable","param1"...]

语法3,exec形式CMD ["param1","param2"]

  • 一般结合ENTRYPOINT指令使用
ENTRYPOINT:用于为容器指定默认运行程序或命令
# 语法1,shell形式
ENTRYPOINT command# 语法2,exec形式
ENTRYPOINT ["/bin/bash","param1","param2"]
  • 主要用于指定启动的父进程
  • ENTRYPOINT设置默认命令不会被docker run命令行指定的参数覆盖,指定的命令行会被当做参数传递给ENTRYPOINT指定的程序。
  • docker run命令的 --entrypoint选项可以覆盖ENTRYPOINT指令指定的程序
  • 一个Dockerfile中可以有多个ENTRYPOINT,但只有最后一个生效
  • ENTRYPOINT主要用于启动父进程,后面跟的参数被当做子进程来启动
ARG:指定环境变量用于构建过程
ARG name[=default value]
​
ARG arg_name
ARG arg1_name=robert
  • ARG指令定义的参数,在构建过程以docker build --build-arg arg_name=robert 形式赋值

  • ARG中没有设置默认值,构建时将抛出警告:[Warning] One or more build-args..were not consumed

  • Docker默认存在的ARG参数,可以在--build-arg时直接使用

    • HTTP_PROXY/http_proxy/HTTPS_PROXY/https_proxy/FTP_PROXY/ftp_proxy/NO_PROXY/no_proxy
ONBUILD:为镜像添加触发器
ONBUILD <dockerfile_exec> <param1> <param2>
​
ONBUILD RUN mkdir mydir
  • 对于使用Dockerfile构建的镜像并不会生效,只有当其他Dockerfile以当前镜像作为基础镜像时被触发
  • 例如:Dockfile A 构建了镜像A,Dockfile B中设置FROM A,此时构建镜像B是会运行ONBUILD设置的指令
STOPSINGAL:设置停止时要发送给PID=1进程的信号
STOPSIGNAL signal
  • 默认的停止信号为:SIGTERM,也可以通过docker run -s指定
  • 主要目的是为了让容器内的应用程序在接收到signal之后可以先做一些事情,实现容器的平滑退出,如果不做任何处理,容器将在一段时间之后强制退出,会造成业务的强制中断,时间默认是10s
HEALTHCHECK:指定容器健康检查命令
HEALTHCHECK [OPTIONS] CMD command# 示例
HEALTHCHECK --interval=5s --timeout=3s \
    CMD curl -fs http://localhost/ || exit 1        # 如果执行不成功返回1
  • 出现多次,只有最后一次生效

  • OPTIONS选项

    • --interval=30:两次健康检查的间隔,默认为 30 秒;
    • --timeout=30:健康检查命令运行的超时时间,超过视为失败,默认30秒;
    • --retries=3:指定失败多少次视为unhealth,默认3次
  • 返回值

    • 0:成功; 1:失败; 2:保留

创建镜像


Dockerfile需要一个基础镜像,取决于第一行的FROM

FROM scratch:表示使用一个空镜像

镜像centos:alpine

# 基础镜像
FROM centos:alpine
LABEL author="RobertChao<xxxxxxxxxx@qq.com>" describe="create test image"# 添加 JDK 与 TOMCAT8 到指定文件路径
ADD jdk8.tar.gz /usr/local
ADD tomcat8 /usr/local
​
# ENV 设置环境变量
ENV LANG en_US.UTF-8
ENV LC_ALL en_US.UTF-8
ENV JAVA_HOME /usr/local/jdk8
ENV CATALINA_HOME /usr/local/tomcat8
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin
# EXPOSE 映射端口
EXPOSE 8080
# 容器启动时执行命令
CMD ["/usr/local/tomcat8/bin/catalina.sh", "run"]