目录
2. 使用 非上下文路径下的Dockerfile 构建镜像(不推荐)
五、Docker多阶段构建入门(multi-stage builds)
一、Dockerfile的基本知识
1、什么是 Dockerfile?
Dockerfile 是一个用来构建自定义镜像的文本文件,它的文本内容由一行行指令语句组成,并且支持已 # 开头的注释行。
当命令较长时可以使用 \(反斜杠)符号来换行,使用 &&符号连接命令。
2、指令语句
指令语句可以大致分为两种:配置指令和操作指令
指令不区分大小写。但是,使用中我们约定大写,以便更轻松地将它们与参数区分开。
这些指令基本上都可以在 docker run命令中使用用相关参数来覆盖掉 Dockerfile相关的值,docker run 的参数优先级高。
指令语句的基础知识:
- 每条保留字指令都必须是大写字母并且后面要跟随至少一个参数
- 指令按照从上到下,顺序执行
- # 表示注释
- 每条指令的执行都会创建一个新的镜像层,注意:过多无意义的层,会造成镜像膨胀过大。
3、什么是上下文路径?
上下文路径:指 docker 在构建镜像时,如果需要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将该路径下的所有内容打包。
解析:由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。
如果未说明最后一个参数,那么默认上下文路径就是 Dockerfile 所在的位置。
注意:上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。
4、使用 Dockerfile 创建镜像的步骤
使用起来很简单,关键指令 + 参数。灵活使用就得熟悉各种指令的含义和多练理解。大致流程如下:
1. 新建一个空目录作为上下文目录,(如:/root/Dockerfiletest)。
2. 在上下文目录中,新建一个名为 Dockerfile文件,并编写指令内容,一般我们默认把 Dockerfile文件放到上下文目录中。
[root@centos7 ~]# mkdir /root/Dockerfiletest
[root@centos7 ~]# cd /root/Dockerfiletest
[root@centos7 Dockerfiletest]# vim Dockerfile
[root@centos7 Dockerfiletest]# cat Dockerfile
# 基础镜像
FROM centos:7
# 创建数据卷
VOLUME ["vdata"]
# 镜像被docker run命令创建并启动容器后执行的命令
使用默认的Dockerfile文件文件名为Dockerfile,以及会将其置于镜像构建上下文目录中。
3. 使用 docker build命令构建/创建新镜像放到本地镜像仓库
[root@centos7 Dockerfiletest]# docker build -t build_image:1.0 .
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM centos:7
---> 7e6257c9f8d8
Step 2/3 : VOLUME ["vdata"]
---> Running in a3e2e87151f9
Removing intermediate container a3e2e87151f9
---> abc8515e4b91
Step 3/3 : CMD ["/bin/bash"]
---> Running in b4a931b41f0b
Removing intermediate container b4a931b41f0b
---> 1f2cfb33aaed
Successfully built 1f2cfb33aaed
Successfully tagged build_image:1.0
[root@centos7 Dockerfiletest]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
build_image 1.0 1f2cfb33aaed About a minute ago 203MB
-t 选项:表示给最终生成的镜像起个名称,“ name:tag”格式的标签
后面那个 . 点儿,表示将当前目录的上下文路径中使用Dockerfile构建,也可使用 -f /root/Dockerfiletest/Dockerfile 来指定
4. 使用 Dockerfile文件新建的镜像来创建并启动容器,run正常ok。
[root@centos7 Dockerfiletest]# docker run -it build_image:1.0
[root@962e7ea2ad26 /]# ls
anaconda-post.log dev home lib64 mnt proc run srv tmp var
bin etc lib media opt root sbin sys usr vdata
二、Dockerfile - 配置指令
具体指令查看官方文档:https://docs.docker.com/engine/reference/builder/#usage
1、FROM指令
FROM指令:指定所创建镜像的基础镜像。
基本语法:
FROM <image> [AS <name>]
FROM <image>[:<tag>] [AS <name>]
FROM <image>[@<digest>] [AS <name>]
其中:
<tag>和<digest> 是可选项,如果没有选择,那么默认值为 latest。
AS <name> 可选的名称。该名称可以在多阶段构建FROM时,COPY --from=<name>说明中使用,以引用此阶段中构建的映像。
一般情况下,Dockerfile文件中的第一条指令就是FROM指令。同时意味着接下来所写的指令将作为镜像的第一层开始。
在 Docker Store 上有非常多的高质量的官方镜像,都可以直接拿来作为我们创建的镜像的基础镜像,服务镜像,如 nginx、redis、mongo、mysql、httpd、php、tomcat 等;方便开发、构建、运行各种编程语言的镜像,如 node、openjdk、python、ruby、golang 等。基础的操作系统镜像,如 ubuntu、debian、centos、fedora、alpine 等等。
Docker 还存在一个特殊的镜像,名为 scratch 空白的镜像。这个镜像是虚拟的概念,并不实际存在。
如果你以 scratch 为基础镜像的话,表示你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。不以任何系统为基础,直接将可执行文件(所需的一切库都已经在可执行文件里)复制进镜像的做法并不罕见,比如centos,ubuntu等等。可以让镜像体积更加小巧。
2、ARG指令
ARG指令:定义创建镜像过程中使用的环境变量。
基本语法:ARG <name> [=<default value>] 。
在执行docker build命令时, 可以通过 --build-arg <参数名>=<值>来覆盖Dockerfile中定义的变量值,Dockerfile 文件中 ${变量名}来取值。
ARG 设置的环境变量仅对 Dockerfile 内有效,docker build构建成功后,构建好的镜像内不存在此环境变量。
[root@centos7 Dockerfiletest]# cat Dockerfile
ARG version=7
# 基础镜像
FROM centos:${version}
# 创建数据卷
VOLUME ["vdata1","vdata2"]
# 镜像被docker run命令创建并启动容器后执行的命令
CMD ["/bin/bash"]
[root@centos7 Dockerfiletest]# docker build --build-arg version=6 -t build_arg:1.0 .
[root@877dc4bf58ae /]# docker run -it build_arg:1.0
[root@06dfd4359482 /]# cat /etc/issue
CentOS release 6.10 (Final)
Kernel \r on an \m
3、LABEL指令
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 \
注意:
1、键值对中等号两边不能有空格
2、每条指令的执行都会创建一个新的镜像层,所以,最好把多个标签合并成一个LABEL指令。
4、EXPOSE指令
EXPOSE指令:指定当前容器对外暴露出的端口。
通知 Docker容器在运行时监听指定的网络端口。可以指定端口是侦听TCP还是UDP,如果未指定协议,则默认值为TCP。例如:EXPOSE 80/tcp 8008/udp 8888
基本语法:
EXPOSE <port> [<port>/<protocol>...]
注意:
EXPOSE指令仅仅只是声明端口,并不会自动完成端口映射。
如果要映射端口出来,在启动容器时可以使用 -P或 -p参数来映射一个或多个端口。
查看 tomcat,redis等官方容器的Dockerfile文件:
5、ENV 指令
ENV 指令:用来在构建镜像过程中设置环境变量。在镜像生成过程中会被后续RUN指令使用,并且通过镜像启动的容器中也会存在。
基本语法:
ENV <key> <value>
ENV <key>=<value> ...
ENV指令有两种形式:
- ENV <key> <value>会将一个变量设置为一个值。第一个空格之后的整个字符串将被视为<value>,包括空格字符。该值将为其他环境变量解释,因此如果不对引号字符进行转义,则将其删除。
- ENV <key>=<value> ...允许一次设置多个变量。引号和反斜杠可用于在值中包含空格。
在执行docker run 命令时, 可以通过 --env <key>=<value> 来覆盖Dockerfile中定义的变量值。
例如:
ENV myName="John Doe" myDog=Rex\ The\ Dog \ myCat=fluffy
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy
注意:当一条ENV指令中同时为多个环境变量赋值并且值也是从环境变量读取时,会为变量都赋值后再更新。
例如:下面的指令:
ENV key1=value2
ENV key1=value1 key2=${key1}
最终结果为key1=value1 key2=value2
实例:
[root@centos7 Dockerfiletest]# cat Dockerfile
ARG version=7
# 基础镜像
FROM centos:${version}
# 添加多个标签信息
LABEL version="1.0" author="赵云" date="2020-09-20" description="Message ..."
# 环境变量
ENV APP_VERSION=1.0.0 APP_HOME=/usr/local/app author="赵云"
# 创建数据卷
VOLUME ["vdata1","vdata2"]
# 镜像被docker run命令创建并启动容器后执行的命令
CMD ["/bin/bash"]
// 省略构建和启动
[root@2b4be9773226 /]# echo $APP_HOME
/usr/local/app
6、VOLUME指令
VOLUME指令:创建具有指定名称的匿名数据卷挂载点。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。
基本语法:
VOLUME ["<mountpoint>",...]
该值可以是JSON数组,也可以是VOLUME ["/var/log/"]具有多个参数的纯字符串,例如:VOLUME /var/log或VOLUME /var/log /var/db。
例如:
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
# 创建数据卷
VOLUME ["vdata1","/myvol"]
该Dockerfile生成一个镜像,该镜像会在docker run启动时创建一个新的挂载点/myvol并将该greeting文件复制到新创建的卷中。
7、WORKDIR指令
WORKDIR指令:指定工作目录。指定在创建容器后,终端默认登陆进来的工作目录,一个落脚点。
基本语法:WORKDIR <工作目录路径>。
可以在 docker run命令中用 -w参数覆盖掉WORKDIR指令的设置。
作用:
使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),后面的指令工作的当前目录就是这个指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。
使用docker run 启动容器后,默认进入的目录,也会是这个指定的目录。
示例:
[root@centos7 Dockerfiletest]# cat Dockerfile
ARG version=7
# 基础镜像
FROM centos:${version}
# 添加多个标签信息
LABEL version="1.0" author="赵云" date="2020-09-20" description="Message ..."
# 环境变量
ENV APP_VERSION=1.0.0 APP_HOME=/usr/local/app author="赵云"
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
# 创建数据卷
VOLUME ["vdata1","/myvol"]
WORKDIR /mydir
RUN echo "hello dsfajflll" > hello.txt
# 镜像被docker run命令创建并启动容器后执行的命令
CMD ["/bin/bash"]
[root@centos7 Dockerfiletest]# docker run -it build_iamge:1.0
[root@437b38a71a47 mydir]# ls
hello.txt
[root@437b38a71a47 mydir]# cat hello.txt
hello dsfajflll
[root@437b38a71a47 mydir]# ls /
anaconda-post.log dev home lib64 mnt myvol proc run srv tmp var
bin etc lib media mydir opt root sbin sys usr vdata1
[root@437b3
执行命令 "hello dsfajflll" > hello.txt 时的当前目录就是 /mydir。
8、USER指令
USER指令:用于指定执行后续命令的用户和用户组。
基本语法:
USER <user>[:<group>]
USER <UID>[:<GID>]
USER指令设置运行映像时以及用于任何映像时使用的用户名(或UID)以及可选的用户组(或GID)这类命令的身份。
USER指令只是切换到后续命令执行的用户而已,这个用户必须是事先建存在,否则无法切换。
示例:推荐使用第三方的 gosu命令(需要下载 gosu)来切换用户。官方Redis镜像。
9、ENTRYPOINT指令
ENTRYPOINT指令:指定一个容器启动时要运行的命令。与 CMD 指令类似,目的都是在指定容器启动程序及参数。
基本语法:ENTRYPOINT有两种形式:
exec形式,这是优选的形式:ENTRYPOINT ["executable", "param1", "param2"]
shell形式:ENTRYPOINT command param1 param2
如果运行 docker run 时使用了 --entrypoint 选项,会覆盖Dockerfile中有ENTRYPOINT。
一般情况下ENTRYPOINT和CMD是结合起来用使用。当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令。
优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。
注意:和CMD一样,每个Dockerfile中只能有一个ENTRYPOINT,当指定多个时,只有最后一个起效。
例如:让镜像变成像命令一样使用
下面的Dockerfile可以写成这个命令:# docker run --entrypoint=/bin/ls centos:7 -l /tmp
相当于在启动容器后执行了:/bin/ls -l /tmp
FROM centos:7
ENTRYPOINT ["/bin/ls"]
CMD ["-l", "/tmp"]
例如:应用运行前的准备工作
启动容器就是启动主进程,但是,有时候启动主进程之前,需要做一些准备工作。
这些准备工作是和容器 CMD 无关的,无论 CMD 是什么,都需要事先进行预处理的工作。
这种情况下,可以写一个脚本,然后放入 ENTRYPOINT 中去执行,而这个脚本会将接到的参数(也就是 <CMD>)放在最后执行,CMD的参数是主进程。
查看 官方Reids镜像:
可以看到其中为了 redis 服务创建了 redis 用户,并在最后指定了 ENTRYPOINT 为 docker-entrypoint.sh 脚本。
假设启动容器的命令是:自己可以分析下那些准备工作
docker run --name myredis -idt -v /root/redis/:/usr/local/etc/redis/ redis:6 redis-server /usr/local/etc/redis/redis.conf
10、ONBUILD指令
ONBUILD指令:用于延迟构建命令的执行。
基本语法:ONBUILD <其它指令>
ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN, COPY 等,而这些指令,在本次构建镜像的过程中不会被执行。只有当使用本次构建的镜像作为基础镜像,去构建下一个新的镜像的时才会被执行。
简单来说,Dockerfile 文件中,所有指令都是为了定制当前镜像而准备的(除了ONBUILD指令),只有 ONBUILD指令是为了帮助别人定制镜像而准备的。
示例:
ONBUILD RUN mkdir /dir222
[root@centos7 Dockerfiletest]# docker build -t build_iamge:1.0 .
[root@centos7 Dockerfiletest]# docker run -it build_iamge:1.0
[root@904eb0379bfa mydir]# ls /
anaconda-post.log dev home lib64 mnt myvol proc run srv tmp var
bin etc lib media mydir opt root sbin sys usr vdata1
dir222 目录是没有被创建的,然后使用 build_iamge:1.0作为基础镜像在构建镜像,在FROM指令执行之后,就会执行ONBUILD指令指定的命令。
[root@centos7 Dockerfiletest]# cat Dockerfile
FROM build_iamge:1.0
[root@centos7 Dockerfiletest]# docker build -t build_iamge:2.0 .
[root@centos7 Dockerfiletest]# docker run -it build_iamge:2.0
[root@de18dd0f9073 mydir]# ls /
anaconda-post.log dev etc lib media mydir opt root sbin sys usr vdata1
bin dir222 home lib64 mnt myvol proc run srv tmp var
11、STOPSIGNAL指令 - 不常用
STOPSIGNAL指令:设置将被发送到容器退出的系统调用信号(默认信号值是SIGTERM)。该信号可以是与内核syscall表中的位置匹配的有效无符号数字(例如9),也可以是格式为SIGNAME的信号名称(例如:STOPSIGNAL SIGKILL)。
基本语法:STOPSIGNAL 信号
这个指令和执行 docker stop命令有点类似,执行docker stop命令时,docker首先会向容器内的当前主程序发送一个SIGTERM信号,用于容器内程序的退出。容器在收到SIGTERM后不会马上退出, 而 stop命令会在等待一段时间(默认是10s)后,再向容器发送SIGKILL信号,将容器杀死,变为退出状态。
12、HEALTHCHECK指令
HEALTHCHECK指令:健康检查,用于指定某个程序或者指令来监控 docker 容器服务的运行状态。
基本语法:有两种形式:
HEALTHCHECK [OPTIONS] CMD <命令>:设置检查容器健康状况的命令
HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以禁用从基本映像继承的任何运行状况检查
支持下列选项:
--interval=<间隔>:两次健康检查的间隔,默认为 30 秒;
--timeout=<时长>:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;
--retries=<次数>:当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次。
HEALTHCHECK指令用于告诉Docker如何测试容器以检查其是否仍在正常工作。
这样可以检测到诸如Web服务器陷入无限循环并且无法处理新连接的情况,即使服务器进程仍在运行。
指定容器的运行状况检查后,除了其正常状态外,它还具有运行状况。此状态最初为starting。只要运行状况检查通过,它将变为healthy。如果在一定数量的连续失败之后,它变为unhealthy。
注意:
HEALTHCHECK指令只可以出现一次,如果写了多个,只有最后一个生效。
CMD后面的命令,格式分为两种: shell 格式 和 exec 格式。
命令退出的返回值(状态),决定了该次容器健康检查的状态。可能的值为:
- 0:成功-容器健康且可以使用
- 1:失败/不健康-容器无法正常工作
- 2:保留-请勿使用此退出代码
示例:
[root@centos7 Dockerfiletest]# cat Dockerfile
FROM nginx:1.19.0
RUN apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s CMD curl -fs http://localhost/ || exit 1
[root@centos7 Dockerfiletest]# docker build -t build_iamge:1.0 .
设置每5秒检查一次,健康检查命令超过3秒没响应就视为失败,并使用 curl -fs http://localhost/ || exit 1 作为健康检查命令。
curl 命令的:-f参数将不输出错误信息。-s不输出内容。
&&:用来执行条件成立后执行的命令
||:用来执行条件不成立后的执行命令
[root@centos7 Dockerfiletest]# docker run -d --name "nginxweb" -p 80:80 build_iamge:1.0
2848e4bc93d57c3a8128ee9602f9bcfc3e36df27ba8748708f0eb6070f4f6d4e
[root@centos7 Dockerfiletest]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2848e4bc93d5 build_iamge:1.0 "/docker-entrypoint.…" 4 seconds ago Up 54 seconds (starting) 0.0.0.0:80->80/tcp nginxweb
[root@centos7 Dockerfiletest]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2848e4bc93d5 build_iamge:1.0 "/docker-entrypoint.…" 15 seconds ago Up 54 seconds (healthy) 0.0.0.0:80->80/tcp nginxweb
13、SHELL指令 - 不常用
SHELL指令:指定其他命令使用 shell形式时的默认 shell类型。
基本语法:SHELL ["executable", "parameters"]
注意:在Linux上,默认值是["/bin/sh", "-c"]。在Windows 上,默认值["cmd", "/S", "/C"]。
三、Dockerfile - 操作指令
1、RUN 运行指定命令。
RUN指令:容器构建时需要运行的命令,实际上运行命令并提交结果。
基本语法:有2种形式:
RUN <command>(shell形式,命令在shell中运行,默认情况,Linux在/bin/sh -c或Windows在cmd /S /C上运行)
RUN ["executable", "param1", "param2"]( exec 格式,必须使用双引号(“),不要使用单引号('))
指定使用其他shell终端类型时可以使用 shell形式实现。
每条RUN指令将在当前镜像基础上执行指定命令,并提交为新的镜像层。
当命令较长时可以使用 \(反斜杠)符号来换行,使用 &&符号连接命令
例如:RUN ["/bin/bash","-c","echo hello"]
例如:
RUN apt-get install -y libsnappy-dev zliblg-dev libbz2-dev \
&& rm -rf /var/cache/apt \
&& rm -rf /var/lib/apt/lists/*
2、CMD 容器启动命令
CMD指令:用于指定默认的容器主进程的启动的命令。
简单来理解CMD 相当于启动docker时候后面添加的参数。如:docker run -it centos:7 /bin/bash
基本语法:有三种形式:
CMD ["可执行文件", "参数1", "参数2"...](exec形式,这是首选形式)
CMD ["param1","param2" ...](作为ENTRYPOINT的默认参数)
CMD <命令>(shell形式)
CMD指令只可以出现一次,如果写了多个,只有最后一个生效。
shell格式和Exec格式区别:
- Exec格式使用exec执行,不会启动shell环境。Exec格式指令会被解析一个JSON数组,因此必须使用双引号(“)不能使用单引号(')。
- shell格式,默认将在shell 终端中运行命令,实际的命令会被包装为 sh -c 的参数的形式进行执行。
比如:CMD echo $HOME 在实际执行中,会将其变更为:CMD [ "sh", "-c", "echo $HOME" ]
RUN实际上运行命令并提交结果;CMD在生成时不执行任何操作,但是指定映像的预期命令。
3、COPY 复制指令
COPY指令:从上下文目录中复制文件或者目录到容器里的指定路径中。
基本语法:
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src1>",... "<dest>"]
[--chown=<user>:<group>]:可选参数,用户改变复制到容器内文件的拥有者和属组。
<src>:源文件或者源目录,可以指定多个资源,但是必须在构建上下文路径内。
可以包含通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:COPY hom* /mydir/
COPY hom?.txt /mydir/
<dest>:容器内的指定路径,必须以斜杠结尾/。
可以是容器中内的绝对路径(推荐),也可以是相对于工作目录的相对路径(工作目录是用 WORKDIR 指令来指定)。路径不存在时会自动创建。
注意:
- 源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。
- 目录本身不被复制,仅其内容被复制。
[root@centos7 Dockerfiletest]# tree
.
├── abc.txt
├── dir1
│ └── dd.txt
└── Dockerfile
实例:复制文件
[root@centos7 Dockerfiletest]# docker build -t copyimage:1 .
[root@centos7 Dockerfiletest]# docker run -it copyimage:1
[root@657c464192cd /]# ls
anaconda-post.log dev home lib64 mnt opt root sbin sys usr
bin etc lib media mydir proc run srv tmp var
[root@657c464192cd /]# cat /mydir/abc.txt
hello
实例:复制目录
[root@centos7 Dockerfiletest]# cat Dockerfile
FROM centos:7
RUN mkdir /mydir
COPY dir1 /mydir
CMD ["/bin/bash"]
[root@centos7 Dockerfiletest]# docker build -t copyimage:1 .
[root@centos7 Dockerfiletest]# docker run -it copyimage:1
[root@20dab72ef433 /]# ls
anaconda-post.log dev home lib64 mnt opt root sbin sys usr
bin etc lib media mydir proc run srv tmp var
[root@20dab72ef433 /]# cat /mydir/dd.txt
adfaf
可以直接一次复制:
[root@centos7 Dockerfiletest]# cat Dockerfile
FROM centos:7
RUN mkdir /mydir
COPY ["abc.txt","dir1","/mydir/"]
CMD ["/bin/bash"]
[root@fd3273cd7528 /]# cd /mydir/
[root@fd3273cd7528 mydir]# ll
total 8
-rw-r--r-- 1 root root 6 Sep 20 06:36 abc.txt
-rw-r--r-- 1 root root 6 Sep 20 06:36 dd.txt
4、ADD 更高级的复制文件
ADD指令:ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。
基本语法:有两种形式:
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
[--chown=<user>:<group>]:可选参数,用户改变复制到容器内文件的拥有者和属组。
<src>:源文件或者源目录,可以指定多个资源,但是必须在构建上下文路径内。
支持远程文件URL,Docker 引擎会试图去下载这个链接的文件放到 <dest> 中。可以包含通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:
COPY hom* /mydir/
COPY hom?.txt /mydir/
<dest>:容器内的指定路径,必须以斜杠结尾/。
可以是容器中内的绝对路径(推荐),也可以是相对于工作目录的相对路径(工作目录是用 WORKDIR 指令来指定)。路径不存在时会自动创建。
注意:
- 源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。
- 目录本身不被复制,仅其内容被复制。
如果 <src>为远程文件 URL时,
下载后的文件权限自动设置为 600,可以增加一层 RUN 指令进行权限调整。
ADD指令不支持身份验证。如果你的URL文件受身份验证保护,可以使用 RUN wget, RUN curl或从容器中使用其他工具。
如果下载的是个压缩包,需要解压缩时,同样需要添加一层 RUN 指令进行解压缩。我们还不如直接使用 RUN 指令方便,然后使用 wget 或者 curl 工具下载,处理权限、解压缩、然后清理无用文件更合理。
因此,这个功能其实并不实用,而且不推荐使用。其他复制文件的情况时,推荐使用 COPY指令,因为 COPY 的语义很明确,就是复制文件而已,而 ADD 则包含了更复杂的功能,其行为也不一定很清晰。
如果 <源路径> 为一个宿主机上的 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去。在某些情况下,这个自动解压缩的功能非常有用,比如官方镜像 ubuntu centos等:
四、docker build 命令
具体查看官方文档:https://docs.docker.com/engine/reference/commandline/build/
docker build 命令用于使用 Dockerfile 文件来创建镜像。如果创建镜像成功,会返回最终镜像的ID。
基本语法:docker build [OPTIONS] PATH | URL | -
选项:
名称,简写 | 默认 | 描述 |
--add-host | 添加自定义主机到IP的映射(host:ip) | |
--build-arg | 设置构建时变量 | |
--cache-from | 视为缓存源的图像 | |
--cgroup-parent | 容器的可选父cgroup | |
--compress | 使用gzip压缩构建上下文 | |
--cpu-period | 限制CPU CFS(完全公平的调度程序)期限 | |
--cpu-quota | 限制CPU CFS(完全公平的调度程序)配额 | |
--cpu-shares , -c | CPU份额(相对重量) | |
--cpuset-cpus | 允许执行的CPU(0-3,0,1) | |
--cpuset-mems | 允许执行的MEM(0-3,0,1) | |
--disable-content-trust | true | 跳过图像验证 |
--file , -f | Dockerfile的名称(默认为“ PATH / Dockerfile”) | |
--force-rm | 始终取出中间容器 | |
--iidfile | 将图像ID写入文件 | |
--isolation | 集装箱隔离技术 | |
--label | 设置图像的元数据 | |
--memory , -m | 内存限制 | |
--memory-swap | 交换限制等于内存加交换:“-1”以启用无限交换 | |
--network | 在构建过程中为RUN指令设置网络模式 | |
--no-cache | 构建映像时不要使用缓存 | |
--output , -o | 输出目的地(格式:type = local,dest = path) | |
--platform | 如果服务器支持多平台,则设置平台 | |
--progress | auto | 设置进度输出的类型(自动,普通,tty)。使用普通显示容器输出 |
--pull | 始终尝试提取图像的较新版本 | |
--quiet , -q | 禁止生成输出并成功打印图像ID | |
--rm | true | 成功构建后删除中间容器 |
--secret | 公开给构建的秘密文件(仅在启用BuildKit的情况下):id = mysecret,src = / local / secret | |
--security-opt | 安全选项 | |
--shm-size | / dev / shm的大小 | |
--squash | 将新构建的层压缩为一个新层 | |
--ssh | SSH代理套接字或要公开给构建的密钥(仅在启用BuildKit的情况下)(格式:default |[=|[,]]) | |
--stream | 流附加到服务器以协商构建上下文 | |
--tag , -t | 名称以及“ name:tag”格式的标签(可选) | |
--target | 设置要构建的目标构建阶段。 | |
--ulimit | Ulimit选项 |
docker build命令从Dockerfile和“上下文”中构建Docker映像。
构建的上下文是位于指定PATH或 URL。
URL参数可以引用三种资源:Git存储库,预打包的tarball上下文和纯文本文件。
1、使用 PATH 构建镜像。
-tag , -t 名称以及“ name:tag”格式的标签(可选)
--file , -f Dockerfile的名称(默认为“ PATH 为上下文路径”)
1. 使用 上下文路径 构建镜像。
默认情况下, Dockerfile放在上下文目录里。指定PATH为 . 符号代表上下文路径。上面示例使用过了。
2. 使用 非上下文路径下的Dockerfile 构建镜像(不推荐)
[root@centos7 Dockerfiletest]# cp ./Dockerfile /root/abc/
[root@centos7 Dockerfiletest]# docker build -t copyimage:1 -f /root/abc/Dockerfile .
2、支持从 URL 构建
比如:可以直接从 Git repo 中构建,注意这个需要依赖git工具,确保Linux安装好git工具。
[root@centos7 git-2.9.5]# docker build https://github.com/twang2218/gitlab-ce-zh.git#:11.0
这行命令指定了构建所需的 Git repo,#后面不写,默认指定的 master 分支,构建目录为 /11.0/,
然后 Docker 就会自己去 git clone 这个项目、切换到指定分支、并进入到指定目录后开始构建。
3、用给定的 tar 压缩包构建
如果所给出的 URL 不是一个 Git仓库,而是个 tar 压缩包归档文件,那么 Docker 引擎会下载这个包,并将其用作构建上下文,自动解压缩构建。
# docker build http://server/context.tar.gz
4、从标准输入中读取 Dockerfile 进行构建:
docker build - < Dockerfile
或
cat Dockerfile | docker build -
如果标准输入传入的是文本文件,则将其视为 Dockerfile,并开始构建。
这种形式由于直接从标准输入中读取 Dockerfile 的内容,任何-f,--file 选项被忽略。在这种情况下,没有上下文。
因此不可以像其他方法那样可以将本地文件 COPY 进镜像之类的事情。
如果标准输入是读取上下文压缩包进行构建:
$ docker build - < context.tar.gz
如果发现标准输入的文件格式是 gzip、bzip2 以及 xz 的话,将会使其为上下文压缩包,直接将其展开并视为上下文,然后开始构建。
重点在1和2 两种形式,后面几个构建方式了解即可。
5、使用 .dockerignore文件
.dockerignore文件:排除从上下文中不需要上传到容器的文件或者目录。
使用 Dockerfile 构建镜像时最好是将 Dockerfile 放置在一个新建的空目录(上下文路径)下,然后将构建镜像所需要的文件添加到该目录中。
在上下文路径下新建一个 .dockerignore 文件来排除与其中的模式匹配的文件和目录。为了匹配,上下文的根被认为是工作目录和根目录。
示例:
[root@centos7 Dockerfiletest]# tree
.
├── aa.file
├── abc.txt
├── dir1
│ ├── a.file
│ └── dd.txt
└── Dockerfile
1 directory, 5 files
[root@centos7 Dockerfiletest]# cat .dockerignore
/dir1*
!*.txt
[root@centos7 Dockerfiletest]# cat Dockerfile
FROM centos:7
COPY [".","/mydir/"]
CMD ["/bin/bash"]
[root@centos7 Dockerfiletest]# docker build -t bulidimage1:1 .
[root@centos7 Dockerfiletest]# docker run -it bulidimage1:1
[root@ef823e49026f /]# ls /mydir/
Dockerfile aa.file abc.txt
五、Docker多阶段构建入门(multi-stage builds)
具体查看官方文档:https://docs.docker.com/develop/develop-images/multistage-build/
从17.05版本以后,新增了Dockerfile多阶段构建。通过多阶段构建,可以在一个Dockerfile 中使用多个 FROM 指令。
每个FROM指令可以使用不同的基础镜像,并且每个指令都开始构建的新阶段,多条 FROM 指令就是多阶段构建。
多个 FROM 指令并不是为了生成多根的层关系,而最后生成的镜像,仍然是以最后一条 FROM 指令生成的镜像为准。
如果把全部构建指令放入在一个 Dockerfile 中,包括项目及其依赖库的编译、测试、打包等流程,可能会带来的一些问题:
- Dockerfile 特别长,可维护性降低
- 镜像层次多,镜像体积较大,部署时间变长
- 源代码存在泄露的风险
多阶段构建可以很方便地将多个彼此依赖的项目通过一个Dockerfile就可轻松构建出期望的容器镜像,而不用担心镜像太大、源码泄露等风险。还可以的构建环境和运行环境进行分离,从而减少镜像层次多,镜像体积较大,部署时间变长,可维护性降低等问题。
示例:以安装nginx做演示
1、使用一个FROM,未优化
[root@centos7 Dockerfiletest]# mkdir /usr/local/nginx
[root@centos7 Dockerfiletest]# ls
Dockerfile nginx-1.18.0.tar.gz
[root@centos7 Dockerfiletest]# cat Dockerfile
FROM centos:7
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
ADD nginx-1.18.0.tar.gz /mnt
RUN yum install -y gcc pcre-devel zlib-devel make
WORKDIR /mnt/nginx-1.18.0
RUN ./configure --prefix=/usr/local/nginx
RUN make
RUN make install
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]
[root@centos7 Dockerfiletest]# docker build -t buildnginx:v1 .
[root@centos7 Dockerfiletest]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
buildnginx v1 4225534fe92d 7 seconds ago 365MB
2、优化:清理中间缓存并尽量减少镜像层数
[root@centos7 Dockerfiletest]# cat Dockerfile
FROM centos:7
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
ADD nginx-1.18.0.tar.gz /mnt
WORKDIR /mnt/nginx-1.18.0
RUN yum install -y gcc pcre-devel zlib-devel make \
&& yum clean all \
&& ./configure --prefix=/usr/local/nginx \
&& make \
&& make install
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]
[root@centos7 Dockerfiletest]# docker build -t buildnginx:v2 .
[root@centos7 Dockerfiletest]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
buildnginx v2 486ef7b26ac2 7 seconds ago 297MB
3、多条 FROM 指令:多阶段构建
[root@centos7 Dockerfiletest]# cat Dockerfile
# nginx安装编译
FROM centos:7 as build_nginx
ADD nginx-1.18.0.tar.gz /mnt
WORKDIR /mnt/nginx-1.18.0
RUN yum install -y gcc pcre-devel zlib-devel make \
&& yum clean all \
&& ./configure --prefix=/usr/local/nginx \
&& make \
&& make install \
&& rm -rf /mnt/nginx-1.18.0
# 创建镜像,将编译好的文件直接拿过来用
FROM centos:7
# 把上面阶段的安装文件复制到本阶段目录中
COPY --from=build_nginx /usr/local/nginx /usr/local/nginx
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]
[root@centos7 Dockerfiletest]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
buildnginx v3 049fa454801c 6 seconds ago 207MB
<none> <none> 8451d367293d 7 seconds ago 285MB
可以发现,文件大小有了变化。