zoukankan      html  css  js  c++  java
  • Docker 006 Dockerfile 指令

    Docker 006 Dockerfile 指令

    前面我们在构建镜像的时候已经使用了一些Dockerfile 中的指令,比如 FROM、RUN、EXPOSE,其实还有很多其他的指令可供我们使用。

    FROM

    一个有效的 Dockerfile 文件必须以为 FROM 指令开始,他的作用是指定一个新构建镜像的初始镜像,后续其他指令都基于 FROM 指定的镜像。

    # 三种格式
    FROM [--platform=<platform>] <image> [AS <name>]
    FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
    FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
    

    --platform可以用于指定镜像的来源的平台,以防FROM指令引用了一个多平台的镜像,比如linux/amd64, linux/arm64, or windows/amd64等平台,值还可以使用全局参数

    tagdigest值也是可选的,如果不使用的话builder会自动为我们的新镜像分配一个latest作为默认tag.

    可选项AS name可以为新构建的镜像起一个名字,方便记忆,name还可以被用于接下来的FROMCOPY --from=指令引用.

    ARG 指令是唯一一个可能出现在 FROM 之前的指令,

    ARG  CODE_VERSION=latest
    FROM base:${CODE_VERSION}
    CMD  /code/run-app
    
    FROM extras:${CODE_VERSION}
    CMD  /code/run-extras
    
    #######################
    #
    ARG VERSION=latest
    FROM busybox:$VERSION
    ARG VERSION
    RUN echo $VERSION > image_version
    

    Dockerfile 中的FROM指令可以出现多次,出现的每一条 FROM 指定都是一个构建阶段,生成的镜像只能是最后一个阶段的结果,但能将前置阶段的内容拷贝到后边的阶段中。

    RUN

    RUN指令有两种形式:

    RUN <command> # shell形式,命令会在shell中执行,Linux 下默认是/bin/sh -c, win下默认是cmd /S /C
    RUN ["executable", "param1", "param2"]  # exec 形式
    

    RUN指令会在当前镜像层的顶层执行命令,并提交结果,即产生新的一层,新产生的层可以用与 dockerfile 的下一步中。

    exec 形式可以避免破坏 shell 字符串,并使用不包含指定 shell 的基本镜像。

    shell 形式的默认 shell 可以通过 shell 命令进行修改,比如不想使用/bin/sh,想使用/bin/bash,可通过如下形式:

    # 当一行内容太长时,可使用反斜线 进行分割, 依旧认为是一条指令
    RUN /bin/bash -c 'source $HOME/.bashrc;  
    echo $HOME
    
    或者
    RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
    
    或者
    RUN ["/bin/bash", "-c", "echo hello"]  # 推荐使用此形式
    

    注意:

    • exec 形式会被解析为 json 数组,也就是说字符串周围必须是双引号。
    • exec 形式不会调用 shell,意思是说,有些 shell 处理不会发生,例如RUN [ "echo", "$HOME" ]$HOME不会发生变量替换,如果你想要 shell 来做这些处理,可以直接使用 shell 形式或者指定 shell 来执行,例如RUN echo $HOME 或者 RUN [ "sh", "-c", "echo $HOME" ]。 在exec 形式中指定 shell 的时候,变量的解析由 shell 来执行,而不是 docker。

    多次构建时,RUN 指令的 cache 不会自动失效,例如RUN apt-get dist-upgrade -y的缓存会被再次使用,可以使用--no-cache参数让缓存无效,例如:docker build --no-cache

    CMD

    CMD指令用于指定一个容器启动时要运行的命令。类似于 RUN 指令,区别是 RUN 指令是指定镜像被构建时要运行的命令,CMD 是指定容器被启动时要运行的命令。和使用 docker run 命令启动容器时,要运行的命令非常相似,例如:

    $ docker run -it web01 /bin/bash
    # 等价于 Dockerfile 中的
    CMD ["/bin/bash"]
    
    # CMD 指令后的命令还可以加参数
    CMD ["/bin/bash", "-l"]
    

    CMD 支持的种格式

    CMD ["executable","param1","param2"] # 使用 exec 执行,推荐方式;
    CMD command param1 param2 # 在 /bin/sh 中执行,提供给需要交互的应用,可能会导致意外
    

    需要注意的是 docker run 命令可以覆盖 CMD指令,如果在 Dockerfile 中指定了 CMD 指令,同时在 docker run 命令中也指定了运行的命令,那么CMD 指令中的命令会被覆盖。

    还有就是 在 Dockerfile 中只鞥你指定一条 CMD指令,如果指定了多条 CMD指令,那么只有最后一条 CMD指令会被使用,

    ENTRYPOINT

    ENTRYPOINT 指令和 CMD 指令非常相似,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。

    但是, 如果运行 docker run 时使用了 --entrypoint 选项,此选项的参数可当作要运行的程序覆盖 ENTRYPOINT 指令指定的程序。

    优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。

    注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

    ENTRYPOINT ["/usr/sbin/nginx"]
    # 和 CMD 指令一样,可通过数组的方式为命令添加响应的参数
    ENTRYPOINT ["/usr/sbin/nginx", "-t"]
    

    ENTRYPOINT 指令还可以和 CMD 指令搭配使用

    # 假设 Dockerfile 中有如下内容
    ENTRYPOINT ["nginx", "-c"] 
    CMD ["/etc/nginx/nginx.conf"] 
    
    # 默认执行,不传递参数
    $ docker run nginx:test # 这个相当于直接启动一个 nginx 容器
    #此时容器中运行的命令是
    $ nginx -c /etc/nginx/nginx.conf
    
    
    # 指定参数运行,会传递参数
    $ docker run nginx:test -c  /etc/nginx/new.conf
    #此时容器中运行的命令是
    $ nginx -c /etc/nginx/new.conf
    
    

    WORKDIR

    WORKDIR指令的作用是 在从镜像创建一个新容器时,为 RUN,CMD,ENTRPOINT,COPY和ADD指令指定工作目录。可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。

    WORKDIR /a
    WORKDIR b
    WORKDIR c
    RUN pwd
    
    # pwd命令的输出/a/b/c
    

    docker run 命令中的 -w 参数可以覆盖 WORKDIR 中的工作目录

    $ docker run -it -w /var/log ubuntu pwd
    # 此时容器内的工作目录是 /var/log
    

    ENV

    ENV指令用来在镜像的构建过程中设置环境变量。

    # key为变量名, value 为变量的值
    ENV key value   # 设置一个变量
    ENV key1=value1 key2=value2  # 设置多个变量
    

    通过 ENV指令设置的变量可以在后续其他指令中继续使用。

    USER

    USER 指令用来指定镜像会以哪个用户来运行,

    USER nginx
    

    基于该镜像启动的容器会以nginx 用户的身份来运行,还可以实行用户名或UID以及组或者 GID,或者两者的组合

    USER user
    USER user:group
    USER uid
    USER uid:gid
    USER user:gid
    USER uid:group
    

    还可以在 docker run 命令中以-u参数来覆盖改指令的值

    VOLUME

    VOLUME指令用来向基于镜像创建的容器添加卷,一个卷可以是一个或多个容器内特定的目录,该目录可以绕过联合文件系统,并提供以下功能:

    • 卷可以在容器建共享和重用
    • 一个容器不是必须和其他容器共享卷
    • 对卷的修改是立即生效的
    • 对卷的修改不会对更新镜像产生影响
    • 卷会一直存在,直到没有任何容器再使用它

    使用卷功能,可以把数据或某些内容添加到镜像而不是提交到镜像上,还可以在多个容器间共享这些内容。

    # 所有基于此进行启动的容器,都会创建挂载点:/opt/web
    VOLUME ["/opt/web"]
    VOLUME ["路径1", "路径2", ...]
    

    卷服务有有个作用是:

    • 可避免容器不断变大
    • 避免重要数据,因 容器重启而丢失

    ADD

    ADD 指令用来将构建环境下的文件和目录复制到镜像中,ADD指令需要指定源文件位置和目标文件位置两个参数。

    ADD src dst
    
    # 将构建目下下的 sofaware.lic 文件拷贝到 /opt/web/software.lic
    ADD sofaware.lic  /opt/web/software.lic
    ADD http://re2do.com/test.zip /opt/web/test.zip
    
    # 如果源是归档文件,docker 会将其解开,docker在这里的行为和 tar -x 一样
    # 该指令执行后的输入原目标目录+归档文件中的内容,如果目标目录下存在与源文件同名的目录或文件,那么目标目录下的文件或目录不会被覆盖
    # 缺点也很明显,不解压无法复制归档文件
    ADD test.tar.gz /opt/web/test/
    

    源文件可以是一个 URL、或者构建上下文或环境中的文件或者目录,且不能是构建目录或者上下文之外的文件。Docker 通过目的地址的字符判断文件源是目录还是文件,如果以”/“结尾,那么 Docker 就任务源是目录,如果不以”/“结尾,就认为源是文件。

    COPY

    COPY 指令类似于 ADD 指令,区别是,COPY 只从上下文目录中复制文件或目录到指定路径。

    COPY [--chown=<user>:<group>] <src>... <dest>
    COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] # 路径中有空格的需要使用此形式
    # [--chown=<user>:<group>]:可选参数,仅适用于构建linux容器,用户改变复制到容器内文件的拥有者和属组,不指定时属主属组都为 0
    

    把src 中的文件或目录复制到容器中的 dst 路径中,src 中可能包含通配符:

    COPY hom* /mydir/        # 复制所有以 home 开头的文件
    COPY hom?.txt /mydir/    # 问号? 表示任意的单字符
    

    目标路径是绝对路径或者 WORKDIR的相对路径

    COPY test relativeDir/   # 把 test复制到 `WORKDIR`/relativeDir/
    COPY test /absoluteDir/  # 把 test复制到 /absoluteDir/
    

    在复制的文件或目录的路径有有特殊字符时,需要按照 golang 的规则将特殊字符转义,例如复制名为arr [0] .txt的文件:

    COPY arr[[]0].txt /mydir/    # 复制文件"arr[0].txt" 到 /mydir/
    

    LABEL

    LABEL指令用于以键值对的形式为镜像添加元数据,如果值中包含空格,请使用引号和反斜杠,示例:

    # 用法
    LABEL <key>=<value> <key>=<value> <key>=<value> ...
    
    # 示例
    LABEL "com.example.vendor"="ACME Incorporated"
    LABEL com.example.label-with-value="foo"
    LABEL version="1.0"
    LABEL description="This text illustrates 
    that label-values can span multiple lines."
    
    LABEL multi.label1="value1" multi.label2="value2" other="value3"
    
    LABEL multi.label1="value1" 
          multi.label2="value2" 
          other="value3"
    
    # 使用 docker inspect 命令查看镜像中的 label 信息、
    $ docker inspect image_name
    "Labels": {
        "com.example.vendor": "ACME Incorporated"
        "com.example.label-with-value": "foo",
        "version": "1.0",
        "description": "This text illustrates that label-values can span multiple lines.",
        "multi.label1": "value1",
        "multi.label2": "value2",
        "other": "value3"
    },
    

    ARG

    ARG指令用于定义一个变量,在docker build命令使用--build-arg =参数时,可以将使用时传递给运行时的变量,

    一个变量,在构建时,用户可以传给给构建者,当 docker build 使用 --build-arg =参数时,可以将值传递给该变量。

    # Dockerfile 文件部分内容
    FROM ubuntu
    ARG CONT_IMG_VER
    ENV CONT_IMG_VER ${CONT_IMG_VER:-v1.0.0}
    RUN echo $CONT_IMG_VER
    
    # 要执行的 docker build 命令
    $ docker build --build-arg CONT_IMG_VER=v2.0.1 .
    
    # 在这里例子中,ARG 指令中的CONT_IMG_VER变量会被替换为 v2.0.1 ,
    # 然后 ENV 指令做了变量替换,如果CONT_IMG_VER未被设置,则会使用v1.0.0
    
    

    ONBUILD

    当镜像是其他镜像的基础镜像时,使用ONBUILD指令可以添加一条触发指令,以便稍后执行;触发指令会在下游的构建上下文中执行,就好像它已经被插入到下游 Dockfile 的 FROM指令 之后一样。触发指令可以是任何构建指令。

    # 示例:可能添加了 如下内容
    [...]
    ONBUILD ADD . /app/src
    ONBUILD RUN /usr/local/bin/python-build --dir /app/src
    [...]
    

    注意:

    • 不允许出现 ONBUILD ONBUILD 这种形式的 ONBUILD 链
    • ONBUILD指令可能不会触发 FROM 或者MAINTAINER(废弃) 指令

    STOPSIGNAL

    STOPSIGNAL指令用来设置停止容器时发送什么系统调用信号给容器,且这个信号必须是内核系统调用表中合法的数字(例如 9)或者 SIGNAME 格式中的信号名称(如 SIGKILL)。

    # 格式
    STOPSIGNAL signal
    

    HEALTHCHECK

    HEALTHCHECK指令有两种形式 :

    # 第一种形式:通过在容器内部运行一个命令来检查容容器的健康状态
    HEALTHCHECK [OPTIONS] CMD command
    
    # 第二种形式:禁用从基本映像继承的任何健康状态检查
    HEALTHCHECK NONE
    

    在 CMD 之前可以使用的选项如下:

    • --interval=DURATION (default: 30s): ,从容器运行起来开始计时,interval秒进行第一次健康检查,随后每间隔interval秒进行一次健康检查
    • --timeout=DURATION (default: 30s):执行command需要时间,比如curl 一个地址,如果超过timeout秒则认为超时是错误的状态,此时每次健康检查的时间是timeout+interval秒。
    • --start-period=DURATION (default: 0s): 启动时间, 默认 0s, 如果指定这个参数, 则必须大于 0s 。为需要启动的容器提供了初始化的时间段, 在这个时间段内如果检查失败, 则不会记录失败次数。 如果在启动时间内成功执行了健康检查, 则容器将被视为已经启动, 如果在启动时间内再次出现检查失败, 则会记录失败次数。
    • --retries=N (default: 3): 连续检查retries次,如果结果都是失败状态,则认为这个容器是unhealth的

    Dockerfile 中只会有一条HEALTHCHECK指令有效,如果出现多次HEALTHCHECK,那么只有最后一次的会生效。

    关键字 CMD之后的命令和 ENTRYPOINT一样,可以是一个 shell 格式(HEALTHCHECK CMD /bin/check-running)或者exec 格式。

    命令的返回值可以表明容器的健康状态:

    • 0 :成功,容器是健康的,可以使用
    • 1 : 失败,容器没有正确运行
    • 2 :保留,不要使用这个返回值

    例如,每 5 分钟请求一次网站首页,如果 3 秒内无响应就认为失败:

    HEALTHCHECK --interval=5m --timeout=3s 
      CMD curl -f http://localhost/ || exit 1
    

    为了便于调试,该命令在stderr或stdout 的任何输出(UTF-8编码)都会被存储在健康状态中,可使用 docker inspect 命令查询到。此类输出应该简短(当前值保存钱 4096 个字节)。

    当容器的健康状态发生变化时,将以新状态生成一个health_status事件。

    SHELL

    SHELL 指令允许覆盖默认的shell 形式命令的默认 shell,在 Linux 中,默认的 shell 是["/bin/sh", "-c"],Win 上的是["cmd", "/S", "/C"]

    SHELL 指令必须是 JSON 格式:

    # SHELL 指令格式:
    SHELL ["executable", "parameters"]
    

    SHELL 指令在 win 上特别有用;Win 上有两个不同的shell:cmd 和 powershell,以及别用 shell:sh。

    SHELL 指令可以出现多次,每个 SHELL 指令会覆盖前面的 SHELL 指令,并影响后续的所有指令。例如:

    FROM microsoft/windowsservercore
    
    # Executed as cmd /S /C echo default
    RUN echo default
    
    # Executed as cmd /S /C powershell -command Write-Host default
    RUN powershell -command Write-Host default
    
    # Executed as powershell -command Write-Host hello
    SHELL ["powershell", "-command"]
    RUN Write-Host hello
    
    # Executed as cmd /S /C echo hello
    SHELL ["cmd", "/S", "/C"]
    RUN echo hello
    

    当在 Dockerfile 中使用RUN, CMDENTRYPOINT指令的 shell 形式,他们可能会受到 SHELL 指令的影响。

    下面的例子是Win 上常见的形式,可以使用 SHELL 指令进行精简:

    ...
    RUN powershell -command Execute-MyCmdlet -param1 "c:foo.txt"
    ...
    
    # docker 调用的命令将会如下:
    cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:foo.txt"
    
    

    这样做会效率低下,有两个原因。

    • 第一:有一个不必要的 cmd.exe 被调用
    • 第二:使用 shell 格式的 RUN 指令都需要额外的命令前缀:powershell -command

    为了变的更高效,可以采用两种机制之一。

    第一种:使用JSON 形式的 RUN 指令:

    ...
    RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 "c:\foo.txt""]
    ...
    

    第二种:使用 SHELL 指令的 shell 形式。

    # escape=`
    
    FROM microsoft/nanoserver
    SHELL ["powershell","-command"]
    RUN New-Item -ItemType Directory C:Example
    ADD Execute-MyCmdlet.ps1 c:example
    RUN c:exampleExecute-MyCmdlet -sample 'hello world'
    
    # 执行结果如下:
    PS E:dockeruildshell> docker build -t shell .
    Sending build context to Docker daemon 4.096 kB
    Step 1/5 : FROM microsoft/nanoserver
     ---> 22738ff49c6d
    Step 2/5 : SHELL powershell -command
     ---> Running in 6fcdb6855ae2
     ---> 6331462d4300
    Removing intermediate container 6fcdb6855ae2
    Step 3/5 : RUN New-Item -ItemType Directory C:Example
     ---> Running in d0eef8386e97
    
    
        Directory: C:
    
    
    Mode                LastWriteTime         Length Name
    ----                -------------         ------ ----
    d-----       10/28/2016  11:26 AM                Example
    
    
     ---> 3f2fbf1395d9
    Removing intermediate container d0eef8386e97
    Step 4/5 : ADD Execute-MyCmdlet.ps1 c:example
     ---> a955b2621c31
    Removing intermediate container b825593d39fc
    Step 5/5 : RUN c:exampleExecute-MyCmdlet 'hello world'
     ---> Running in be6d8e63fe75
    hello world
     ---> 8e559e9bf424
    Removing intermediate container be6d8e63fe75
    Successfully built 8e559e9bf424
    PS E:dockeruildshell>
    

    SHELL 指令还可以用于修改 shell 的运行方式,例如在 win 上使用SHELL cmd /S /C /V:ON|OFF.

    如果需要备用shell(例如 zsh、csh、tcsh 等),也可以使用 SHELL 指令。

  • 相关阅读:
    flask中程序和请求上下文
    flask的初始化
    git 强制覆盖本地代码
    python编写一个带参数的装饰器
    Android 11 unexpected LOCAL_MODULE_CLASS for prebuilts: FAKE
    systemctl自定义service执行shell脚本时报错:code=exited, status=203/EXEC
    shell应用记录
    ssm在maven项目中的需要的依赖
    swiper 5张卡片轮播图实现效果
    Codeforces 1534 题解
  • 原文地址:https://www.cnblogs.com/resn/p/12501008.html
Copyright © 2011-2022 走看看