当前位置 博文首页 > RtxTitanV的博客:通过Dockerfile构建Docker镜像

    RtxTitanV的博客:通过Dockerfile构建Docker镜像

    作者:[db:作者] 时间:2021-07-21 09:37

    Dockerfile是Docker用来构建镜像的文本文件,包含自定义的指令和格式。可以通过docker build命令利用Dockerfile构建镜像。Dockerfile提供了一系列统一的资源配置语法指令,开发人员可以根据需求定制Dockerfile,然后使用这份Dockerfile文件进行自动化镜像构建,简化了构建镜像的复杂过程,同时Dockerfile与镜像配合使用,使Docker在构建时可以充分利用镜像的功能进行缓存,大大提升了Docker的使用效率。

    本文主要对Dockerfile指令和使用Dockerfile构建镜像进行简单总结。

    一、Dockerfile基本结构

    Dockerfile由一行行命令语句组成,支持以#开头的注释行。一般Dockerfile主体分为基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令四个部分。下面是从Docker Hub上拿来的nginx的一个Dockerfile的例子,可以对Dockerfile的基本结构有个大体的了解,我们在编写自己的Dockerfile时也可以参考Docker Hub上优秀镜像的Dockerfile,通过优秀镜像的Dockerfile来学习和总结经验来编写高效的Dockerfile文件。

    # 指定所构建镜像的基础镜像
    FROM debian:buster-slim
    
    # 指定生成镜像的元数据标签信息
    LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
    
    # 指定环境变量
    ENV NGINX_VERSION   1.18.0
    ENV NJS_VERSION     0.4.0
    ENV PKG_RELEASE     1~buster
    
    # 运行指定命令
    RUN set -x \
    # create nginx user/group first, to be consistent throughout docker variants
        && addgroup --system --gid 101 nginx \
        && adduser --system --disabled-login --ingroup nginx --no-create-home --home /nonexistent --gecos "nginx user" --shell /bin/false --uid 101 nginx \
        && apt-get update \
        && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates \
        && \
        NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
        found=''; \
        for server in \
            ha.pool.sks-keyservers.net \
            hkp://keyserver.ubuntu.com:80 \
            hkp://p80.pool.sks-keyservers.net:80 \
            pgp.mit.edu \
        ; do \
            echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
            apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \
        done; \
        test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
        apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
        && dpkgArch="$(dpkg --print-architecture)" \
        && nginxPackages=" \
            nginx=${NGINX_VERSION}-${PKG_RELEASE} \
            nginx-module-xslt=${NGINX_VERSION}-${PKG_RELEASE} \
            nginx-module-geoip=${NGINX_VERSION}-${PKG_RELEASE} \
            nginx-module-image-filter=${NGINX_VERSION}-${PKG_RELEASE} \
            nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-${PKG_RELEASE} \
        " \
        && case "$dpkgArch" in \
            amd64|i386) \
    # arches officialy built by upstream
                echo "deb https://nginx.org/packages/debian/ buster nginx" >> /etc/apt/sources.list.d/nginx.list \
                && apt-get update \
                ;; \
            *) \
    # we're on an architecture upstream doesn't officially build for
    # let's build binaries from the published source packages
                echo "deb-src https://nginx.org/packages/debian/ buster nginx" >> /etc/apt/sources.list.d/nginx.list \
                \
    # new directory for storing sources and .deb files
                && tempDir="$(mktemp -d)" \
                && chmod 777 "$tempDir" \
    # (777 to ensure APT's "_apt" user can access it too)
                \
    # save list of currently-installed packages so build dependencies can be cleanly removed later
                && savedAptMark="$(apt-mark showmanual)" \
                \
    # build .deb files from upstream's source packages (which are verified by apt-get)
                && apt-get update \
                && apt-get build-dep -y $nginxPackages \
                && ( \
                    cd "$tempDir" \
                    && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \
                        apt-get source --compile $nginxPackages \
                ) \
    # we don't remove APT lists here because they get re-downloaded and removed later
                \
    # reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
    # (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)
                && apt-mark showmanual | xargs apt-mark auto > /dev/null \
                && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
                \
    # create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)
                && ls -lAFh "$tempDir" \
                && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \
                && grep '^Package: ' "$tempDir/Packages" \
                && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
    # work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
    #   Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
    #   ...
    #   E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages  Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
                && apt-get -o Acquire::GzipIndexes=false update \
                ;; \
        esac \
        \
        && apt-get install --no-install-recommends --no-install-suggests -y \
                            $nginxPackages \
                            gettext-base \
                            curl \
        && apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \
        \
    # if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
        && if [ -n "$tempDir" ]; then \
            apt-get purge -y --auto-remove \
            && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \
        fi
    
    # forward request and error logs to docker log collector
    RUN ln -sf /dev/stdout /var/log/nginx/access.log \
        && ln -sf /dev/stderr /var/log/nginx/error.log
    
    # make default server listen on ipv6
    RUN sed -i -E 's,listen       80;,listen       80;\n    listen  [::]:80;,' \
            /etc/nginx/conf.d/default.conf
    
    # 指定镜像内服务所监听的端口
    EXPOSE 80
    
    # 指定所创建镜像启动的容器接收退出的信号值
    STOPSIGNAL SIGTERM
    
    # 指定启动容器时默认执行的命令
    CMD ["nginx", "-g", "daemon off;"]
    

    二、Dockerfile格式

    Dockerfile的格式如下:

    # Comment
    INSTRUCTION arguments
    

    其中指令(INSTRUCTION)不区分大小写,但是为了与参数区分,通常情况大写。Docker会按顺序运行Dockerfile中的指令。Dockerfile必须以FROM指令开头。它可以在解析器指令(parser directives),注释(comments)和全局范围参数(globally scoped ARGs)之后。FROM指令具体指定你想要构建的镜像的父镜像(Parent Image)。FROM指令只能在一个或多个声明在Dockerfile中的FROM行中使用的参数即ARG指令之前。
    Docker将以开头的行视为注释,除非该行是有效的解析器指令(parser directive)。在一行中的任何其他位置使用标记都将被视为参数,它允许下面这样的语句。还有注释中不支持行连续字符(\)。

    # Comment
    RUN echo 'we are running some # of cool things'
    

    三、解析器指令(Parser directives)

    解析器指令是可选的,并且会影响Dockerfile中后续行的处理方式。解析器指令不会添加层到构建的镜像中,也不会显示为一个构建步骤。解析器指令以#directive = value的形式编写为特殊类型的注释,单个指令只能使用一次。
    一旦一个注释,空行或者构建指令被执行。Docker就不会再寻找解析器指令。相反它会将任何符合解析器指令的格式视为注释,并且不会去尝试验证它是否是一个解析器指令,因此所有的解析器指令都必需位于Dockerfile的顶部。
    解析器指令不区分大小写,但是它们通常是小写的并且在每个解析器指令后会使用空行分隔,解析器指令不支持行连续字符(\),解析器指令允许非断行空格字符,根据上述规则,以下示例均不是有效的解析器指令:
    使用行连续字符(\)无效:

    # direc \
    tive=value
    

    出现两次无效:

    # directive=value1
    # directive=value2
    FROM ImageName
    

    出现在构建器指令后被视为注释:

    FROM ImageName
    # directive=value
    

    出现在注释后被视为注释而不是解析器指令:

    # About my dockerfile
    # directive=value
    FROM ImageName
    

    未被识别的未知指令会被视为注释,而出现在其后的有效指令也会被视为注释:

    # unknowndirective=value
    # knowndirective=value
    

    解析器指令支持syntaxescape

    1.syntax

    格式

    # syntax=[remote image reference]

    示例
    # syntax=docker/dockerfile
    # syntax=docker/dockerfile:1.0
    # syntax=docker.io/docker/dockerfile:1
    # syntax=docker/dockerfile:1.0.0-experimental
    # syntax=example.com/user/repo:tag@sha256:abcdef...
    

    只有使用BuildKit后端时才启用此功能,syntax指令设定用于构建当前Dockerfile的Dockerfile构建器的位置。BuildKit后端允许无缝使用以Docker镜像的形式分发并在容器沙箱环境中执行的构建器的外部实现。
    自定义的Dockerfile实现能够:

    • 自动获取错误修正而无需更新守护程序
    • 确保所有用户都使用相同的实现来构建Dockerfile
    • 使用最新功能而不更新守护程序
    • 试用新的测试性的或第三方的功能
    正式发布(Official releases)

    Docker分发了可用于docker/dockerfile在Docker Hub上的仓库下构建Dockerfile的镜像的正式版本。这里有稳定版(stable)和测试版(experimental)两个发布新镜像的渠道。
    稳定版渠道遵循的版本控制示例如下:

    • docker/dockerfile:1.0.0 - 只允许不可变版本 1.0.0
    • docker/dockerfile:1.0 - 允许版本 1.0.*
    • docker/dockerfile:1 - 允许版本 1.*.*
    • docker/dockerfile:latest - 稳定渠道发布的最新版本

    测试版渠道在正式发布时使用稳定版渠道中主要和次要组成部分的增量版本控制的示例如下:

    • docker/dockerfile:1.0.1-experimental - 只允许不可变版本
      1.0.1-experimental
    • docker/dockerfile:1.0-experimental - 1.0版本之后最新发布的测试版
    • docker/dockerfile:experimental - 测试渠道发布的最新版本

    我们可以根据需求选择合适的发布渠道,如果只想修正错误,则应使用docker/dockerfile:1.0,如果想从测试功能中受益,则应使用测试版渠道,如果正在使用测试版渠道,则较新的版本可能无法向后兼容,因此建议使用不可变的完整版本。

    2.escape

    格式

    # escape=\ (backslash)# escape=` (backtick)

    escape指令设定在Dockerfile中作为转义字符的字符,如果未指定,默认转义字符为\
    转义字符既用于转义行中的字符,也用于转义换行符,这允许Dockerfile指令跨越多行。注意,无论escape解析器指令是否包含在Dockerfile中,都不会在RUN命令中执行转义,除非在行尾。
    将转义字符设置为`Windows上特别有用,其中\是目录路径分隔符。`与Windows PowerShell一致。
    通过以下示例可以对escape指令做一个简单的了解,该示例在Windows系统环境中会运行失败,第二行末尾的第二个\将被解释为换行符的转义符,而不是第一个\的转义目标,类似的第三行结尾的 \ 将会被作为换行符处理,这个dockerfile的结果就是第二行和第三行被认为是单个指令:

    FROM microsoft/nanoserver
    COPY testfile.txt c:\\
    RUN dir c:\
    

    上面的一个解决方案是使用/作为COPY指令和dir的目标。但是这种语法对于Windows上的路径来说并不自然,并且最坏的情况是由于Windows上的所有命令都不支持/作为路径分隔符,因此容易出错。
    但是通过添加escape解析器指令,以下Dockerfile会在Windows上按照预期的方式的成功使用自然路径执行,具体方式如下:

    # escape=`
    
    FROM microsoft/nanoserver
    COPY testfile.txt c:\
    RUN dir c:\
    

    四、环境替换(Environment replacement)

    ENV声明的环境变量也可以在Dockerfile的某些指令中作为参数使用,还可以处理转义,将字符串中的类似变量的语法包含在语句中。
    Dockerfile中使用$variable_name${variable_name}标注环境变量具有相同的效果,并且括号语法通常用来解决没有空格的变量名称的问题,例如${foo}_bar
    ${variable_name}语法还支持以下指定的一些标准bash修饰符,下面的word在所有情况下可以是包括其他环境变量的任何字符串:

    • ${variable:-word}:如果设置了variable,结果将是该值,如果未设置variable,结果将是word
    • ${variable:+word}:如果设置了variable,结果将是word,否则结果是空字符串 。

    我们还可以通过在变量前添加\来进行转义。例如\$foo\${foo},将分别转换为$foo${foo}文字,下面是一个应用示例,之后显示的是解析后的表示:

    FROM busybox
    ENV foo /bar
    WORKDIR ${foo}   # WORKDIR /bar
    ADD . $foo       # ADD . /bar
    COPY \$foo /quux # COPY $foo /quux
    

    另外Dockerfile中支持环境变量的有以下指令:ADDCOPYENVEXPOSEFROMLABELSTOPSIGNALUSERVOLUMEWORKDIRONBUILD
    注意:环境变量替换将在整个指令中为每个变量使用相同的值。例如:

    ENV abc=hello
    ENV abc=bye def=$abc
    ENV ghi=$abc
    

    def的值为hello不是bye,然而ghi的值为bye,这是因为它不是将abc设置为bye的相同指令的一部分。

    五、构建指令

    Dockerfile的格式上文已经总结过了,其中用于构建镜像的构建指令一般格式为INSTRUCTION arguments,而Dockerfile中一般有以下构建指令:FROMLABELMAINTAINEREXPOSEENVENTRYPOINTVOLUMEUSERWORKDIRARGONBUILDSTOPSIGNALHEALTHCHECKSHELLRUNCMDADDCOPY。下面将对这些指令进行简单总结。

    1.FROM

    格式

    FROM [--platform=<platform>] <image> [AS <name>]

    FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]

    FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

    功能描述

    FROM指令初始化一个新的构建阶段并为后续指令设置基础镜像。因此有效的Dockerfile必须以FROM指令开头,其中基础镜像可以是任何有效镜像,而通过从公有仓库中拉取镜像来启动它尤其容易。

    选项

    --platform选项在FROM引用了多平台镜像的情况下可以用于指定镜像所属的平台。例如linux/amd64linux/arm64windows/amd64,默认会使用发起构建请求的目标平台,也可以在该选项的值中使用全局构建参数。

    注意

    ARGDockerfile中唯一可以在FROM之前的指令。

    FROM可以在单个Dockerfile中多次出现以创建多个镜像,或者使用一个构建阶段作为另一个构建阶段的依赖项。这只需在每个新的FROM指令之前记下提交输出的最后一个镜像ID。每个FROM指令会清除先前指令创建的任何状态。

    通过将AS name添加到FROM指令可以将可选的名称赋予到新的构建阶段,该名称可以在后续的FROMCOPY --from=<name|index>指令中使用,以引用此阶段构建的镜像。

    tagdigest的值是可选的,如果省略其中任何一个,构建器默认采用latest标签,如果找不到tag值,构建器将返回错误。

    ARG与FROM间的相互作用

    FROM指令支持在第一个FROM之前发生的任何ARG指令声明的变量。示例如下:

    ARG  CODE_VERSION=latest
    FROM base:${CODE_VERSION}
    CMD  /code/run-app
    
    FROM extras:${CODE_VERSION}
    CMD  /code/run-extras
    

    FROM之前声明的ARG在构建阶段之外,因此在FROM之后的任何指令中都不能使用它。要使用在第一个FROM之前声明的ARG的默认值,需在构建阶段内使用没有值的ARG指令。示例如下:

    ARG VERSION=latest
    FROM busybox:$VERSION
    ARG VERSION
    RUN echo $VERSION > image_version
    

    2.LABEL

    格式

    LABEL <key>=<value> <key>=<value> <key>=<value> ...

    功能描述

    LABEL指令将元数据添加到生成的镜像中,采用键值对的形式。

    注意

    想要在LABEL值中包含空格需要像在命令行解析中一样使用引号和反斜杠。示例如下:

    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,可以在一行中指定多个label。在Docker 1.10之前,这减小了最终镜像的大小,但现在不再是这样了。我们仍然可以选择在单个指令中指定多个label,有以下两种方式:

    LABEL multi.label1="value1" multi.label2="value2" other="value3"
    
    LABEL multi.label1="value1" \
          multi.label2="value2" \
          other="value3"
    

    基础或父镜像(FROM指定镜像)中包含的label将继承到新构建的镜像,如果label已存在但具有不同的值,则最近应用的值将覆盖任何之前设置的值。

    使用docker image inspect命令可以查看镜像的label,使用--format选项可以只显示label,例如命令docker image inspect --format='' myimage

    3.MAINTAINER[已弃用(deprecated)]

    格式

    MAINTAINER <name>

    功能描述

    MAINTAINER指令设置生成镜像的作者(Author)字段。不过LABEL指令使用起来比MAINTAINER更灵活,可以设置我们所需的任何元数据并且能够轻松查看这些元数据,所以比起MAINTAINER指令更推荐使用LABEL指令。要设置与MAINTAINER字段对应的label,可以使用下面示例中的LABEL指令:

    LABEL maintainer="SvenDowideit@home.org.au"
    

    MAINTAINER字段可以通过docker inspect与其他label一起显示出来。

    4.EXPOSE

    格式

    EXPOSE <port> [<port>/<protocol>...]

    功能描述

    EXPOSE指令通知Docker容器在运行时监听指定的网络端口,可以指定端口是监听TCP还是UDP,如果未指定网络协议,默认监听TCP。

    注意

    EXPOSE指令只是起到声明作用,并不会自动完成端口映射,也就是实际上不会发布端口。

    如果想要在运行容器时实际发布端口,即完成端口映射,需要在docker run即创建运行容器时使用-p具体指定一个或多个主机端口与容器端口间的映射,或者使用-P,对容器中被监听的每个端口,Docker都将自动分配一个主机临时端口与之完成映射。

    默认情况EXPOSE指定端口是监听TCP,也可以指定为UDP,例如markup EXPOSE 80/udp。如果要指定端口是同时监听TCP和UDP,需要包括两行指令,下面是一个示例,在这种情况如果将-Pdocker run一起使用,则端口将对TCP和UDP各暴露一次,由于-P使用的是Docker自动分配的主机临时端口,所以在TCP和UDP上与EXPOSE指定端口映射的主机端口不相同。

    EXPOSE 80/tcp
    EXPOSE 80/udp
    

    无论EXPOSE如何设置,在运行时都可以使用-p进行覆盖。示例如下:

    docker run -p 80:80/tcp -p 80:80/udp ...
    

    5.ENV

    格式

    ENV <key> <value>

    ENV <key>=<value> ...

    功能描述

    ENV指令指定一个环境变量<key>的值<value>,该值将存在于构建阶段中所有后续指令的环境中,并且可以在许多时候进行内部替换。

    注意

    ENV指令有两种形式,第一种ENV <key> <value>设置单个变量的值,第一个空格后面的整个字符串包括空格字符都将被视为<value>,因为该值将针对其他环境变量进行解释,在未对其进行转义的情况下将删除引号字符。示例如下:

    ENV myName John Doe
    ENV myDog Rex The Dog
    ENV myCat fluffy
    

    第二种形式ENV <key>=<value> ...允许一次设置多个变量,注意与第一种形式不同的是第二种形式在语法中使用等号(=)。与命令行解析一样,引号和反斜杠可用于在值内包含空格。示例如下,该例在最终生成的镜像中与上例一样会产生相同的结果。

    ENV myName="John Doe" myDog=Rex\ The\ Dog \
        myCat=fluffy
    

    从生成的镜像运行容器时使用ENV设置的环境变量将保持不变,可以使用docker inspect来查看环境变量的值,还可以使用docker run --env <key>=<value>来改变指定环境变量的值。

    环境变量的持久性可能会导致意想不到的副作用。例如设置ENV DEBIAN_FRONTEND noninteractive可能会使基于Debian的镜像上的apt-get用户感到困惑。要为单个命令设置值,可以使用RUN <key>=<value> <command>

    6.ENTRYPOINT

    格式

    ENTRYPOINT指令有两种形式:

    • ENTRYPOINT ["executable", "param1", "param2"](exec形式,推荐形式)
    • ENTRYPOINT command param1 param2(shell形式)
    功能描述

    ENTRYPOINT指令指定镜像的默认入口命令,该入口命令会在启动容器时作为根命令执行,所有传入值作为该命令的参数。

    注意

    使用exec形式时docker run <image>传入的命令行参数会被附加到ENTRYPOINT所有元素之后,并将覆盖CMD指定的所有元素。这允许将参数传递给入口点,即docker run <image> -d-d参数传递给入口点。使用docker run --entrypoint可以覆盖ENTRYPOINT指令。

    使用shell形式时ENTRYPOINT指令会防止任何CMDrun命令行参数被使用,但缺点是ENTRYPOINT将作为/bin/sh -c的子命令启动,它不传递信号。这意味着进程在容器中的PID不是1并且不会接收Unix信号,所以命令进程将不会从docker stop <container>中接收到SIGTERM信号,即在通过docker stop的形式停止容器的时候接收不到停止信号将会导致异常终止。

    Dockerfile中只有最后一个ENTRYPOINT指令才会生效。

    exec形式将被解析为JSON数组,所以必须使用"来包围单词而不是'

    exec形式不会调用命令shell,也就不会发生正常的shell处理,例如ENTRYPOINT [ "echo", "$HOME" ]不会对$HOME进行变量替换。如果需要shell处理,可以使用shell形式或者直接执行shell,例如ENTRYPOINT [ "sh", "-c", "echo $HOME" ]。当使用exec形式并直接执行shell时,跟shell形式的情况一样是执行环境变量扩展的shell而不是docker。

    如果CMD在基础镜像中被设置过,设置ENTRYPOINT将会重置CMD为一个空值,这种情况下CMD必须在当前镜像中设置一个值。

    CMD与ENTRYPOINT间的相互作用

    CMDENTRYPOINT指令都定义了运行容器时执行的命令,以下为它们之间协作的规则。

    1. Dockerfile应至少指定一个CMDENTRYPOINT命令。
    2. 使用容器作为可执行文件时应定义ENTRYPOINT
    3. CMD应该作为ENTRYPOINT命令定义默认参数或在容器中执行特定命令的方法。
    4. 使用备用参数运行容器时CMD将被覆盖。

    下表展示了不同的ENTRYPOINTCMD组合执行的命令:

    .No ENTRYPOINTENTRYPOINT exec_entry p1_entryENTRYPOINT [“exec_entry”, “p1_entry”]
    No CMDerror, not allowed/bin/sh -c exec_entry p1_entryexec_entry p1_entry
    CMD [“exec_cmd”, “p1_cmd”]exec_cmd p1_cmd/bin/sh -c exec_entry p1_entryexec_entry p1_entry exec_cmd p1_cmd
    CMD [“p1_cmd”, “p2_cmd”]p1_cmd p2_cmd/bin/sh -c exec_entry p1_entryexec_entry p1_entry p1_cmd p2_cmd
    CMD exec_cmd p1_cmd/bin/sh -c exec_cmd p1_cmd/bin/sh -c exec_entry p1_entryexec_entry p1_entry /bin/sh -c exec_cmd p1_cmd

    7.VOLUME

    格式

    VOLUME ["/data"]

    功能描述

    VOLUME指令创建一个具有指定名称的挂载点,并将其标记为从本机主机或其他容器保存的外部挂载卷,该值可以是JSON数组或具有多个参数的普通字符串。使用docker run命令会用任何存在于基础镜像内指定位置的数据初始化新创建的卷。

    注意

    以下是关于Dockerfile中的卷的注意事项:

    基于Windows容器的卷:使用基于Windows的容器时,容器中卷的目标必须是一个不存在的或空目录和C:以外的驱动器这两者之一。

    从Dockerfile中更改卷:如果任何构建步骤在声明后更改卷内的数据,那么这些更改将被丢弃。

    JSON格式:将列表解析为JSON数组必须使用"而不是'

    主机目录在容器运行时被声明:主机目录(挂载点)本质上是依赖于主机的。这是为了保持镜像的可移植性,因为不能保证给定的主机目录在所有主机上都可用,因此无法从Dockerfile中挂载主机目录。VOLUME指令不支持指定host-dir参数,在创建或运行容器时必须指定挂载点。

    8.USER

    格式

    USER <user>[:<group>]

    USER <UID>[:<GID>]

    功能描述

    USER指令设置用户名或UID以及可选的用户组或GID,当运行镜像以及在Dockerfile中紧接着的所有RUNCMDENTRYPOINT指令时会使用该指定用户。

    注意

    当为用户指定用户组时,用户将只有指定的用户组的成员身份,任何其他已配置的用户组的成员身份将被忽略。

    当用户没有主用户组时将使用root组运行镜像或下一条指令。

    在Windows上,如果用户不是内置帐户,则必须先创建用户,这可以通过作为Dockerfile一部分调用的net user命令来完成。示例如下:

    FROM microsoft/windowsservercore
    # 在容器中创建Windows用户
    RUN net user /add patrick
    # 为后续命令设置用户名
    USER patrick
    

    9.WORKDIR

    格式

    WORKDIR /path/to/workdir

    功能描述

    WORKDIR指令为Dockerfile中的任何RUNCMDENTRYPOINTCOPYADD指令设置工作目录。如果WORKDIR不存在,即使它没有在任何后续Dockerfile指令中被使用,也将创建它。

    注意

    WORKDIR指令可以在Dockerfile中多次使用,如果提供了相对路径,则这个工作目录会基于之前WORKDIR指令指定的路径。示例如下,该Dockerfile最终的pwd命令输出为/a/b/c

    WORKDIR /a
    WORKDIR b
    WORKDIR c
    RUN pwd
    
    
    下一篇:没有了