天天看点

Docker - Dockerfile之ADD、COPY、WORKDIR、USER、EXPOSE指令详解

Docker - Dockerfile之ADD、COPY、WORKDIR、USER、EXPOSE指令详解

ADD

ADD指令用于将文件添加到容器中。

格式

  • ​ADD <src> ... <dest>​

    ​。
  • ​ADD ["<src>",... "<dest>"]​

    ​ 。

ADD指令和COPY指令的格式和性质基本一致,只不过ADD指令是在COPY指令的基础上增加了一些功能。例如,ADD指令指定的源路径可以是一个远程URL,Docker引擎会自动将远程URL的文件下载到目标路径下,例如:

ADD https://www.baidu.com/index.html /index/      

使用​

​docker build​

​​命令构建镜像,然后使用​

​docker run​

​​命令创建并启动容器,会发现在容器根目录下的​

​index​

​​文件夹下有了​

​index.html​

​文件。

如果源路径是本地的一个​

​tar​

​压缩文件时,ADD指令复制该压缩文件到目标路径下会自动将其进行解压,如下:

ADD kaven.tar /kaven/      

压缩格式为​

​gzip​

​​、​

​bzip2​

​​以及​

​xz​

​的情况下,ADD指令都会将其解压。

但网络上的压缩文件不会被自动解压,所以还需要额外增加一条RUN指令进行解压。所以,在这种情况下,还不如只用一条RUN指令,使用​

​curl​

​​或者​

​wget​

​工具进行下载,并更改权限,然后进行解压,最后清理无用文件。

源路径是一个远程URL时,会自动将远程URL的文件下载到目标路径下,但是其文件权限被自动设置成了​

​600​

​(只有拥有者有读写权限),如果这并不是你想要的权限,那么你还需要额外增加一条RUN指令进行更改。

ADD指令从源路径​

​<src>​

​​(宿主路径或远程URL)中复制新文件,并将它们添加到镜像文件系统中指定的目标路径​

​<dest>​

​​,​

​<src>​

​​可以指定多个资源,但是如果它们是文件或目录,则将其路径解释为相对于构建上下文的路径。每个​

​<src>​

​​都可能包含通配符,并且匹配将使用Go的​

​filepath.Match​

​规则完成。例如:

要添加所有以​

​hom​

​开头的文件:

ADD hom* /mydir/      

在下面的示例中,​

​?​

​​被替换为任何单个字符,例如​

​home.txt​

​:

ADD hom?.txt /mydir/      

添加包含特殊字符(例如​

​[​

​​ 和​

​]​

​​)的文件或目录时,需要按照Golang规则对这些路径进行转义,以防止将它们视为匹配模式。例如,要添加名为​

​arr[0].txt​

​的文件,请使用以下命令:

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

ADD指令遵守以下规则:

  • ​<src>​

    ​​路径必须在构建的上下文中,不能使用​

    ​../something/something​

    ​这种路径,因为Docker构建镜像的第一步是将上下文目录(和子目录)发送到Docker守护进程。
  • 如果​

    ​<src>​

    ​​是一个URL,并且​

    ​<dest>​

    ​​没有以斜杠结尾,则会从该URL下载一个文件并将其复制到​

    ​<dest>​

    ​。
  • 如果​

    ​<src>​

    ​​是一个URL,​

    ​<dest>​

    ​​以斜杠结尾,则会从URL推断出文件名并将文件下载到​

    ​<dest>/<filename>​

    ​​。例如,​

    ​ADD https://www.baidu.com/index.html /​

    ​​将创建文件​

    ​/index.html​

    ​​。URL必须有一个文件路径,以便在这种情况下可以发现合适的文件名(​

    ​https://www.baidu.com​

    ​就不符合要求)。
  • 如果​

    ​<src>​

    ​是一个目录,则复制目录的全部内容,包括文件系统元数据(目录本身不被复制,仅其内容被复制)。
  • 如果直接或由于使用通配符指定了多个​

    ​<src>​

    ​​资源,则​

    ​<dest>​

    ​必须是一个目录,并且必须以斜杠结尾。
  • 如果​

    ​<dest>​

    ​不存在,则会连同路径中所有不存在的目录一起被创建。

COPY

COPY指令将从上下文目录中的指定路径下的文件或文件夹复制到新的一层的镜像内的指定路径。

格式

  1. ​COPY <src>... <dest>​

    ​。
  2. ​COPY ["<src>",... "<dest>"]​

    ​。

功能类似ADD指令,但是不会自动解压文件,也不能访问网络资源。

源路径可以有多个,直接指定或者使用通配符,并且匹配将使用Go的​

​filepath.Match​

​规则完成。ADD指令中已经介绍过了,这里就不赘述了。

目标路径是容器内的绝对路径,也可以是工作目录下的相对路径,工作目录可以使用WORKDIR指令进行指定(下面会介绍该指令)。目标路径不需要事先创建,Docker会自动创建所需的文件目录。使用COPY指令会将源路径的文件的所有元数据进行复制,比如读、写、指定全选、时间变更等。如果源路径是一个目录,那么会将整个目录复制到容器中,包括文件系统元数据。

除了关于源路径是远程URL的一些规则外,ADD指令遵守的规则,COPY指令也都遵守。

当使用本地目录为源目录时,推荐使用COPY指令。

WORKDIR

使用WORKDIR指令来指定工作目录(或者称为当前目录),以后各层操作的当前目录就是为WORKDIR指令指定的目录,如果该目录不存在,WORKDIR指令会自动创建该目录。

格式

  • ​WORKDIR /path/to/workdir​

    ​。

类似于​

​cd​

​命令。

为后续的RUN、CMD、ENTRYPOINT等指令指定工作目录。

可以使用多个WORKDIR指令,后续指令如果参数是相对路径,则会基于之前WORKDIR指令指定的路径。例如

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd      

输出将是​

​/a/b/c​

​。

WORKDIR指令可以通过​

​docker run​

​​命令中的​

​-w​

​选项来进行覆盖。

-w, --workdir string                 Working directory inside the container(容器内的工作目录)      

USER

格式

  • ​USER <user>[:<group>]​

    ​。
  • ​USER <UID>[:<GID>]​

    ​。

USER指令用于指定容器执行程序的用户身份,默认是​

​root​

​,例如:

[root@izoq008ryseuupz ~]# docker run -it --name user_root centos
[root@c2cb41f2f972 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var      

USER指令可以通过​

​docker run​

​​命令中的​

​-u​

​选项进行覆盖。

-u, --user string                    Username or UID (format: <name|uid>[:<group|gid>])      

查看有哪些用户。

vim /etc/passwd      
Docker - Dockerfile之ADD、COPY、WORKDIR、USER、EXPOSE指令详解

通过​

​docker run​

​​命令中的​

​-u​

​​选项指定用户为​

​bin​

​。

docker run -it --name user_bin -u bin centos      
[root@izoq008ryseuupz etc]# docker run -it --name user_bin -u bin centos
bash-4.4$ ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
bash-4.4$ exit
exit      

EXPOSE

格式

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

    ​。

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

EXPOSE指令实际上并不发布端口。它的功能只是构建镜像的人和使用镜像的人之间对于要发布哪些端口的一种声明。要在运行容器时实际发布端口,需要使用​

​docker run​

​​命令的​

​-p​

​​或​

​-P​

​选项来映射一个或多个端口:​​Docker - Docker初级网络配置 - 端口映射​​。

默认情况下,EXPOSE指令假定协议为TCP。也可以指定UDP,例如:

EXPOSE 80/udp      

端口同时在TCP和UDP上公布的话,需要以下两行:

EXPOSE 80/tcp
EXPOSE 80/udp      

在这种情况下,如果在​

​docker run​

​​命令中使用​

​-P​

​​选项,则该端口对TCP暴露一次,同样对UDP也暴露一次。​

​-P​

​选项会在主机上使用随机的端口进行映射,因此对于TCP和UDP,映射的宿主端口将不同。

无论EXPOSE指令设置如何,都可以在运行时使用​

​-p​

​选项覆盖它们,例如:

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

EXPOSE指令是声明运行时容器服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。

在​

​Dockerfile​

​中这样声明有两个好处:

  1. 帮助镜像使用者更好的了解这个镜像的服务端口。
  2. 使用随机端口映射时,也就是使用​

    ​docker run -P​

    ​命令时,会自动随机映射EXPOSE指令指定的端口。

要将EXPOSE指令和在运行时使用​

​-p <host-port>:<container-port>​

​​区分开来,​

​-p​

​选项是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而EXPOSE指令仅仅是声明哪些端口可能需要被映射而已,并不会自动进行端口映射。

继续阅读