Docker - Dockerfile之FROM、ARG、LABEL、MAINTAINER、RUN、CMD指令详解
Dockerfile是什么
Dockerfile
是一个包含用于组装镜像的所有指令的文本文档。Docker可以使用docker build
命令读取Dockerfile
中的指令自动生成镜像。
Dockerfile
由一行行指令组成,支持以#
开头的注释行。一般的,Dockerfile
分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。
FROM
构建所需要的镜像,就是一个定制化的过程,那么就需要以一个镜像为基础,在其基础上进行修改定制。而FROM指令就是用来指定基础镜像,因此在Dockerfile
中,FROM指令是必备指定,并且必需是第一条指令!
格式
FROM [--platform=<platform>] <image> [AS <name>]FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
FROM指令在Dockerfile
中可以出现多次,这样可以创建多个镜像。每个FROM指令会清除由先前指令创建的任何状态。
通过添加可选的[AS <name>]
可以给予一个新的构建阶段FROM指令进行命名。该名称可以在后续的FROM指令和COPY --from=<name>
中使用,以引用此阶段中构建的镜像。
[:<tag>]
和[@<digest>]
也都是可选的。如果两个都不选择,那么构建器默认情况下将使用latest
标签。如果构建器找不到该tag
或digest
版本,命令则会返回错误。
示例
FROM centosFROM centos:8.3.
除了指定现有的基础镜像以外,Dockerfile
还存在一个特殊的镜像srcatch
,这个镜像是一个虚拟的概念,并不实际存在,它表示一个空白的镜像:
FROM scratch
如果以scratch
作为基础镜像,意味着将不使用任何镜像为基础,接下来所写的指令将作为第一层开始存在。不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见。对Linux下静态编译的程序来说,并不需要其他操作提供其运行时支持,所需的一切库都在可执行文件里了,因此使用scratch
作为基础镜像,可以使镜像的体积更加小巧,之前介绍Docker中关于Image的build
命令时,就使用过scratch
作为基础镜像,将主机上编译好的c
语言程序放入镜像中运行:Docker - Docker Image及Image命令详解
FROM scratchADD hello /CMD ["/hello"]
ARG
Dockerfile
中的ARG指令用以定义构建镜像时需要的参数。
格式
ARG <name>[=<default value>]
ARG指令定义了一个参数,用户可以通过docker build
命令的--build-arg <varname>=<value>
选项将其值传递给构建器(如果ARG指令定义的参数具有默认值,此时默认值会被覆盖)。
默认值
如果ARG指令定义的参数具有默认值(通过设置可选项[=<default value>]
来实现),并且在构建镜像时未传递该参数的值,则构建器将使用默认值。
范围
ARG指令定义的参数从Dockerfile
中定义的行开始生效,而不是从参数使用处开始生效。
FROM centos:7USER ${user:-kaven}ARG userUSER $user
使用该Dockerfile
来构建image
,通过-t
选项来对image
设置name
和tag
。
docker build --build-arg user=kaven_centos -t centos:kaven .
[root@izoq008ryseuupz ~]# docker build --build-arg user=kaven_centos -t centos:kaven .Sending build context to Docker daemon 1.316GBStep 1/4 : FROM centos:7---> 8652b9f0cb4cStep 2/4 : USER ${user:-kaven}---> Running in 0db31a9b2ba2Removing intermediate container 0db31a9b2ba2---> b9cd1bab4371Step 3/4 : ARG user---> Running in 68370ca04defRemoving intermediate container 68370ca04def---> e85d5f280c22Step 4/4 : USER $user---> Running in 357cc35e19faRemoving intermediate container 357cc35e19fa---> 94891bfd473bSuccessfully built 94891bfd473bSuccessfully tagged centos:kaven
查看USER是否设置成功。
docker image inspect centos:kaven
第2
行的USER
为kaven
,因为在随后的第3
行中定义了user
参数,并且通过选项--build-arg
传递该参数的值为kaven_centos
,所以第4
行的USER为kaven_centos
。
不要用ARG指令来保存密码之类的信息,因为通过docker history
命令是能够看到这些信息的。
[root@izoq008ryseuupz ~]# docker history centos:kavenIMAGECREATED CREATED BY SIZECOMMENT94891bfd473b 2 days ago/bin/sh -c #(nop) USER kaven_centos 0B e85d5f280c22 2 days ago/bin/sh -c #(nop) ARG user 0B b9cd1bab4371 2 days ago/bin/sh -c #(nop) USER kaven 0B 8652b9f0cb4c 4 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B <missing> 4 weeks ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B <missing> 4 weeks ago /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4… 204MB
Dockerfile
的每一条指令都会新构建一层,从上面docker history
命令产生的输出也可以看出来。
在通过ARG指令对参数进行定义之前或者没有通过--build-arg <varname>=<value>
选项给定义的参数传递值,对参数的任何使用都会导致一个空字符串。
通过--build-arg <varname>=<value>
选项给未定义的参数传递值,很显然这里并没有给user
参数传递值。
docker build --build-arg xxx=kaven -t kaven .
[root@izoq008ryseuupz ~]# docker build --build-arg xxx=kaven -t kaven .Sending build context to Docker daemon 1.317GBStep 1/4 : FROM centos:7---> 8652b9f0cb4cStep 2/4 : USER ${user:-kaven}---> Using cache---> b9cd1bab4371Step 3/4 : ARG user---> Using cache---> e85d5f280c22Step 4/4 : USER $user---> Running in 20022b5eb237Removing intermediate container 20022b5eb237---> 0dbfbc335abb[Warning] One or more build-args [xxx] were not consumedSuccessfully built 0dbfbc335abbSuccessfully tagged kaven:latest
如果通过--build-arg <varname>=<value>
选项传递了未在Dockerfile
中定义的参数,则构建过程中会输出警告。
[Warning] One or more build-args [xxx] were not consumed
查看刚刚构建的镜像信息。
docker image inspect kaven
确实是空字符串。
LABEL
LABEL指令将一些元数据添加到镜像中。LABEL指令对应一个或多个键值对。
格式
LABEL <key>=<value> <key>=<value> <key>=<value> ...
或
LABEL <key>=<value> LABEL <key>=<value> LABEL <key>=<value> ...
Dockerfile
的每一条指令都会新构建一层,所以,上面的多条LABEL指令可以写成一条指令,使用\
符号进行换行操作:
LABEL <key>=<value> \<key>=<value> \<key>=<value> ...
示例
LABEL maintainer="kaven"LABEL version="1.0"LABEL description="This is Kaven's blog."
LABEL maintainer="kaven" version="1.0" description="This is Kaven's blog."
LABEL maintainer="kaven" \version="1.0" \description="This is Kaven's blog."
所以LABEL指令的作用也很明显了,就是给需要构建的镜像添加一些元数据,用来描述这个镜像的元数据信息。
MAINTAINER (Deprecated)
已弃用。
MAINTAINER <name>
MAINTAINER指令用于设置创建镜像的作者的标识。但LABEL指令比MAINTAINER指令灵活得多,应该使用LABEL指令,因为LABEL指令可以设置所需的任何元数据,并且可以很容易查看这些元数据,例如使用docker inspect
命令。
比如:
MAINTAINER "kaven"
可以换成:
LABEL maintainer="kaven"
RUN
构建镜像时执行的命令。
格式
RUN <command>
(shell
格式)RUN ["executable", "param1", "param2"]
(exec
格式)
RUN指令将在当前镜像顶部的新层中执行任何命令并提交结果。生成的镜像将用于Dockerfile
的下一步。
分层运行指令和生成提交符合Docker的核心概念,在Docker中提交是很轻量的,可以从镜像历史的任何一点来创建容器,就像源代码管理一样。
RUN apt-get updateRUN ["/bin/bash", "-c", "echo hello"]
RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache
选项,如:docker build --no-cache
。
对于一些编译、软件的安装和更新等操作,无需分成好几层来操作,这样会使得镜像非常臃肿,拥有非常多的层,不仅仅增加了构建部署的时间,也很容易出错。例如:
RUN buildDeps=apt-get update && mkdir -p /usr/src/redis && apt-get purge -y --auto-remove $buildDeps
这里仅仅使用了一条RUN指令,所以只会新建一层。可以使用&&
符号将多个命令分割开,使其先后执行。此时,一条RUN指令有可能会变得非常长,可以使用\
进行换行操作。
刚刚编写的RUN指令,会发现在指令的结尾处添加了清理工作的命令。镜像是多层存储,每一层存储的东西不会在下一层删除,会一直跟随着镜像。因此在镜像构建时,一定要确保每一层只添加真正需要的东西,任何无关的东西都应该被清理掉。
CMD
格式
CMD ["executable","param1","param2"]
(exec
格式,官方推荐的方式)。CMD ["param1","param2"]
(提供给ENTRYPOINT
指令的默认参数)。CMD command param1 param2
(shell
格式)。
Dockerfile
中只会有一条有效的CMD指令。如果有多条CMD指令,则只有最后一条CMD指令生效。
Docker不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,就需要指定运行的程序及参数。
所以,CMD指令的主要用途是为启动容器时指定运行的程序及参数,而RUN指令用于指定镜像构建时所要执行的命令。
示例
CMD echo "This is kaven's blog." CMD ["/bin/bash", "-c", "echo This is kaven's blog."]
当使用了ENTRYPOINT指令时,CMD指令的性质将会发生改变:CMD指令中的内容将会以参数形式传递给ENTRYPOINT指令,如下:
FROM ubuntuENTRYPOINT ["rm", "docker"]CMD ["-rf"]
其实,它真正执行的命令将会是:
rm docker -rf
Docker - Dockerfile之ENV、ENTRYPOINT、VOLUME、ONBUILD、STOPSIGNAL指令详解
例子
ARG CENTOS_VERSION=8.3.FROM centos:$CENTOS_VERSIONLABEL maintainer="kaven" \description="This is Kaven's blog." \version="centos.kaven.1.0" \name="CentOS Base Image" \date="0-12-15"CMD ["/bin/bash"]RUN yum install -y /pub/epel/epel-release-latest-8.noarch.rpmRUN yum updateRUN yum install -y mysql
使用Dockerfile
来构建image
,并且通过-t
选项来对image
设置name
和tag
。
docker build -t centos:kaven.1 .
[root@izoq008ryseuupz ~]# docker build -t centos:kaven.1 .Sending build context to Docker daemon 1.308GBStep 1/7 : ARG CENTOS_VERSION=8.3.Step 2/7 : FROM centos:$CENTOS_VERSION---> 300e315adb2fStep 3/7 : LABEL maintainer="kaven"description="This is Kaven's blog."version="centos.kaven.1.0"name="CentOS Base Image"date="0-12-15"---> Using cache---> 72b4d13e9df0Step 4/7 : CMD ["/bin/bash"]---> Using cache---> 69305c7a0a74Step 5/7 : RUN yum install -y /pub/epel/epel-release-latest-8.noarch.rpm---> Using cache---> 2f0cb9c6f0edStep 6/7 : RUN yum update---> Using cache---> 473cf8a6416eStep 7/7 : RUN yum install -y mysql---> Running in 4e5e446fe864Last metadata expiration check: 0:01:30 ago on Tue Dec 15 08:49:57 .Dependencies resolved.===========================================================================================Package Arch VersionRepo Size===========================================================================================Installing:mysql x86_64 8.0.21-1.module_el8.2.0+493+63b41e36 appstream 12 MInstalling dependencies:libedit x86_64 3.1-23.0329cvs.el8baseos102 kmariadb-connector-c-config noarch 3.0.7-1.el8appstream 13 kmysql-common x86_64 8.0.21-1.module_el8.2.0+493+63b41e36 appstream 148 kEnabling module streams:mysql 8.0Transaction Summary===========================================================================================Install 4 PackagesTotal download size: 12 MInstalled size: 63 MDownloading Packages:(1/4): mariadb-connector-c-config-3.0.7-1.el8.n 440 kB/s | 13 kB00:00 (2/4): mysql-common-8.0.21-1.module_el8.2.0+493 3.1 MB/s | 148 kB00:00 (3/4): mysql-8.0.21-1.module_el8.2.0+493+63b41e 33 MB/s | 12 MB00:00 (4/4): libedit-3.1-23.0329cvs.el8.x86_64.rp 60 kB/s | 102 kB00:01 --------------------------------------------------------------------------------Total 2.9 MB/s | 12 MB00:04warning: /var/cache/dnf/appstream-02e86d1c976ab532/packages/mariadb-connector-c-config-3.0.7-1.el8.noarch.rpm: Header V3 RSA/SHA256 Signature, key ID 8483c65d: NOKEYCentOS Linux 8 - AppStream 1.6 MB/s | 1.6 kB00:00 Importing GPG key 0x8483C65D:Userid: "CentOS (CentOS Official Signing Key) <security@>"Fingerprint: 99DB 70FA E1D7 CE22 7FB6 4882 05B5 55B3 8483 C65DFrom : /etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficialKey imported successfullyRunning transaction checkTransaction check succeeded.Running transaction testTransaction test succeeded.Running transactionPreparing :1/1 Installing : libedit-3.1-23.0329cvs.el8.x86_64 1/4 Installing : mariadb-connector-c-config-3.0.7-1.el8.noarch2/4 Installing : mysql-common-8.0.21-1.module_el8.2.0+493+63b41e36.x8 3/4 Installing : mysql-8.0.21-1.module_el8.2.0+493+63b41e36.x86_644/4 Running scriptlet: mysql-8.0.21-1.module_el8.2.0+493+63b41e36.x86_644/4 Verifying : mariadb-connector-c-config-3.0.7-1.el8.noarch1/4 Verifying : mysql-8.0.21-1.module_el8.2.0+493+63b41e36.x86_642/4 Verifying : mysql-common-8.0.21-1.module_el8.2.0+493+63b41e36.x8 3/4 Verifying : libedit-3.1-23.0329cvs.el8.x86_64 4/4 Installed:libedit-3.1-23.0329cvs.el8.x86_64 mariadb-connector-c-config-3.0.7-1.el8.noarch mysql-8.0.21-1.module_el8.2.0+493+63b41e36.x86_64 mysql-common-8.0.21-1.module_el8.2.0+493+63b41e36.x86_64 Complete!Removing intermediate container 4e5e446fe864---> 5b689502d907Successfully built 5b689502d907Successfully tagged centos:kaven.1
查看镜像的详细信息。
docker image inspect centos:kaven.1
查看所有镜像,很明显镜像构建成功了。
docker images
[root@izoq008ryseuupz ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEcentos kaven.1 5b689502d907 4 minutes ago 347MBcentos 8.3. 300e315adb2f 7 days ago209MB
基于构建的镜像创建centos.test.1
容器。
docker run -it --name centos.test.1 centos:kaven.1
[root@izoq008ryseuupz ~]# docker run -it --name centos.test.1 centos:kaven.1[root@48ef31528900 /]# lsbin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var[root@48ef31528900 /]# exit exit
Dockerfile
中的FROM、ARG、LABEL、MAINTAINER、RUN、CMD指令就介绍到这里。
写博客是博主记录自己的学习过程,如果有错误,请指正,谢谢!