ADD

ADD

ADD有两种形式:

ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]

对于包含空格的路径,需要使用后一种形式。

NOTE:

--chown功能仅在用于构建Linux容器的Dockerfiles上受到支持,在Windows容器上不起作用。由于用户和组所有权概念无法在LinuxWindows之间进行转换,因此使用/etc/passwd/etc/group用户名和组名转换为ID限制了该功能,使得该功能仅适用于基于Linux OS的容器。

ADD指令从复制新文件,目录或远程文件URL,并将它们添加到镜像文件系统中的路径中。

每个都可以包含通配符,并且匹配根据Gofilepath.Match规则进行。例如:

要添加所有以"hom"开头的文件:

ADD hom* /mydir/

在下面的示例中,?可以被替换为任何单个字符,例如"home.txt"

ADD hom?.txt /mydir/

是绝对路径,或相对于WORKDIR的路径,源文件将被复制到目标容器所在文件系统的路径中。

下面的示例使用相对路径,并将"test.txt“添加到 /relativeDir/

ADD test.txt relativeDir/

而此示例使用了绝对路径,并向/absoluteDir/添加了"test.txt"

ADD test.txt /absoluteDir/

在添加包含特殊字符的文件或目录(如 [和]) 时,需要按照Golang规则对这些路径进行转义,以防止它们被视为匹配模式。例如,要添加名为arr[0].txt的文件,请使用以下文件:

ADD arr[[]0].txt /mydir/

附go代码示例

函数声明:
func Match(pattern, name string) (matched bool, err error)

函数作用:
如果name匹配shell文件名模式匹配字符串,Match函数返回真(注意:Match要求匹配整个name字符串,不是它的一部分。如果pattern语法错误时,会返回"syntax error in pattern"的错误)

可用的匹配字符如下:

    '*'                                  匹配0或多个非/的字符
    '?'                                  匹配1个非/的字符
    '[' [ '^' ] { character-range } ']'  字符组(必须非空)(支持三种格式[abc],[^abc],[a-c])
    c                                    匹配字符c(c != '*', '?', '\\', '['
    '\\' c                               匹配字符c(可上面c的区别是 可以支持字符 * ? \\ [的匹配)

测试实例:
* 字符测试实例

    //* 匹配0或多个非/的字符
    path.Match("*", "a")            // true    
    path.Match("*", "sefesfe/")     // false
? 字符测试实例

    //?匹配一个非/的字符
    path.Match("a?b", "aab")    // true
    path.Match("a?b", "a/b")    // false
[] 格式测试实例

    path.Match("[abc][123]", "b2")        // true

    path.Match("[abc][1-3]", "b2")        // true

    path.Match("[abc][^123]", "b2")        // false

    path.Match("[abc][^123]", "b4")        // true
字符或者特殊用途字符(  \\   ?  *   [ )测试实例

    path.Match("a\\\\", "a\\")    // true
    path.Match("a\\[", "a[")        // true
    path.Match("a\\?", "a?")        // true
    path.Match("a\\*", "a*")        // true

所有新文件和目录均以值为0UIDGID创建,除非通过可选的--chown指定用户名组名UID/GID组合,来为添加的内容指定所有权(ownership)--chown允许使用用户名组名字符串的格式,或直接使用整数形式UIDGID。如果使用不带组名用户名或不带GIDUID的形式,则会使用与UID相同的值来作为GID。如果提供了用户名组名,则会根据容器根文件系统中的/etc/passwd/etc/group文件来将用户名组名name的形式转换为整数的形式。以下示例显示了--chown的有效定义:

ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/

示例:

ARG VERSION=7

FROM centos:7

# RUN useradd -u 9000 Yang; groupadd -g 9100 ChangZhou; groupadd -g 9200 JiangSu

ADD --chown=Yang:ChangZhou main.go /tmp/
ADD --chown=Yang:9100 home?.txt /tmp/
ADD --chown=9000:JiangSu home??.txt /tmp/

CMD ["/bin/echo/", "building finished"]

通过上述Dockerfile构建镜像时,会产生下面这样的错误。

unable to convert uid/gid chown string to host mapping: can't find gid for group ChangZhou: no such group: ChangZhou

虽然我们对ADD指令的定义没有任何问题,但是我们忽略了一个问题。由于我们在指令中使用的用户名和组名,以及用户ID组ID在系统中并不存在,所以构建镜像时,无法从/etc/passwd和/etc/group中找到相关记录(在Linux中创建用户时,用户的记录会被保存在/etc/passwd中,而组的记录会被保存在/etc/group文件中),因此构建失败。

如何解决上述问题?

ARG VERSION=7

FROM centos:7

RUN useradd -u 9000 Yang; groupadd -g 9100 ChangZhou; groupadd -g 9200 JiangSu

ADD --chown=Yang:ChangZhou main.go /tmp/
ADD --chown=Yang:9100 home?.txt /tmp/
ADD --chown=9000:JiangSu home??.txt /tmp/

CMD ["/bin/echo/", "building finished"]

通过在ADD指令之前,创建好ADD指令所需的用户和组,就能够解决上述问题。这里我们通过RUN指令来执行这些创建任务。

如果容器的根文件系统不包含/etc/passwd/etc/group文件,并且以用户组名的形式使用--chown,那么关于ADD构建的操作将会失败。使用数值ID形式的用户,则不需要查询且不会去依赖容器根文件系统的内容。

如果是一个URL形式的远端文件,目标文件将拥有600的文件权限。如果被检索的远端文件拥有一个HTTP Last-Modified头部,那么来自头部的时间戳将被用于设置目标文件的mtime。然而,像在ADD期间被处理的任意其他文件那样,mtime不会被用来决定文件是否被更改和缓存是否应该被更新。

NOTE:

在构建镜像时,如果通过标准输入来传递Dockerfile(docker build - < somefile),则不会存在构建上下文,因此Dockerfile中只能使用基于URLADD指令。我们也可以通过标准输入传递一个压缩文件:(docker build - < archive.tar.gz), 在压缩文件根目录下的Dockerfile和余下的一些文件将被作为构建上下文。

如果URL文件需要进行身份认证,那么我们需要使用RUN wgetRUN curl或者使用容器内部的其他工具来获取文件,因为ADD指令不支持身份认证功能。

NOTE:

如果<src>中的内容被更改了,在构建镜像时,遇到的第一个ADD指令会使后续指令的缓存失效。这其中还包含了RUN指令的缓存。查看Dockerfile Best Practice guide - Leverage build cache来获取更多信息。

ADD指令遵照以下规则:

  • <src>路径必须包含在构建上下文之中;不能使用ADD ../something /something, 因为docker build的第一步就是将上下文目录(及其子目录)发送给docker守护进程。

  • 如果<src>是一个URL,并且<dest>并没有以斜线结尾,那么将会从URL下载一个文件然后拷贝<dest>。

  • 如果<src>是一个URL,并且<dest>以斜线结尾,那么docker会根据URL推断出文件名,然后文件会下载到<dest>/<filename>。例如,ADD http://example.com/foobar / 将创建一个/foobar文件。URL必须包含一个资源路径,以便可以通过这个路径找到适当的文件名。(http://example.com将无法执行)。

NOTE:

目录本身并不会被拷贝,仅仅只是拷贝目录中的内容。

  • 如果<src>是一个能够被识别的本地压缩文件(例如identitygzipbzip2xz),那么它会被解压成一个目录。来自远端URL的资源无法被解压。当一个目录被拷贝或者解压时,它与tar -x命令有着相同的行为。

NOTE:

文件是否被识别为压缩格式的文件仅基于文件的内容,而不是文件的名称。例如,如果一个空文件碰巧以.tar.gz结尾,这个文件不会被识别为压缩文件,也不会生成任何类型的减压错误消息,而是将这个文件简单地复制到目的地。

  • 如果<src>是任意其他类型的文件,它和它的元数据会被单独拷贝。在这种情况下,如果<dest>以斜线结尾,它会被当作目录,<src>的内容会被写入到<dest>/base(<src>)

  • 如果通过直接或使用通配符的方式指定了多个<src>资源,那么<dest>必须是一个目录,并且必须以斜线结尾。

  • 如果<dest>没有以斜线结尾,那么它会被当成一个正常的文件,而<src>的内容会被写入到这个文件中。

  • 如果<dest>不存在,那么会在它的路径下创建所有缺失的目录。

Last updated