如何编写dockerfile

前言

环境:centos7.9 docker version 20.10.14

构建镜像

如何构建一个镜像?我们知道,构建镜像一般有两种方法:
1、手动修改容器内容,比如安装一个工具等等,然后使用docker commit 容器id 镜像:tag 来创建新的镜像;
2、通过在Dockerfile中定义一系列的指令和参数来构建镜像,dockerfile是一个包含用于组合镜像的命令的文本文档,可以使用在命令行中调用任何命令, 然后docker守护进程通过读取Dockerfile中的指令构建生成镜像,docker build命令用于从Dockerfile构建镜像。

Dockerfile的主要组成

Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。

Dockerfile指令

注意:Dockerfile文件必须以Dockerfile为文件名字,这命名是有特殊含义的,后面会讲到。
Dockerfile的指令与两种书写形式,一种是shell格式,另一种是exec格式。
官方文档:https://docs.docker.com/engine/reference/builder/

FROM mysql:5.6						#FROM指令定义基础镜像,FROM指令必须是第一个指令
MAINTAINER Benjamin <qq.com>		#MAINTAINER指令指定维护者信息,此指令为可选
ENV JAVA_HOMR="/usr/local/java"		#ENV定义环境变量,后续指令中可以通过$JAVA_HOME来引用环境变量,ENV定义的变量可以在容器中使用
ARG name="xiaolming"				#ARG也是定义环境变量,与ENV不同的是,ARG定义的环境变量只能在Dockerfile中使用,在容器中不存在
RUN yum install -y httpd			#RUN指令用于运行一个命令,如使用yum安装httpd服务
COPY /file*.txt /file/				#COPY用于复制宿主机文件到容器指定目录,COPY支持通配符
ADD	hello-world /app/				#ADD也是用于复制宿主机文件到容器指定目录,ADD也支持通配符,与COPY不同的是,ADD会自动解压文件
WORKDIR /app/						#WORKDIR指令用于用于设置当前工作目录
USER root							#USER指令用于切换用户
VOLUME	/data						#VOLUME指令用于定义匿名卷,表示将容器内的/data目录挂载为匿名卷到宿主机上
EXPOSE	80 8089/udp					#EXPOSE用于声明容器端口,如果未指定协议,则默认为TCP,仅仅是声明,并没有绑定宿主机端口映射
CMD ["nginx", "-g", "daemon off;"]	#CMD指令的作用是指定容器主进程的默认启动指令
ENTRYPOINT ["/usr/sbin/nginx","-g","daemon off;"]	#ENTRYPOINT指令也是指定容器主进程的默认启动指令,但与CMD有区别。

各个指令详细介绍如下:

FROM mysql:5.6
说明:FROM指令用来指定基础镜像信息,FROM必须为第一个指令,tag是可选的,如果不写tag,会使用latest版本的基础镜像,alpine、busybox镜像
一般可以作为基础镜像。

MAINTAINER Benjamin Yang <133xxxx2282@163.com>	
说明:MAINTAINER 指令用来指定维护者信息,告诉别人谁负责维护它,当然你可以不指定维护者信息。

ENV <key> <value>
ENV <key>=<value>...
ENV  sex "man"
ENV  name="xiaolming"
说明:ENV指令用于定义环境变量,定义好后的变量,在后续的指令中就可以直接使用 $name的形式引用环境变量,同时,ENV定义的变量即能在Dockerfile中使用,还能在容器中使用。

ARG sex "man"
ARG name="xiaolming"
说明:ARG指令也是用于定义环境变量,与ENV定义环境变量相比,ARG定义的环境变量只能在Dockerfile中使用,换句话说,ARG所设置的构建环境的环境变量在将来容器运行时是不会存在的。

RUN <command> (shell格式,该指令在shell中运行,默认情况下/bin/sh -c在Linux运行)
RUN ["executable", "param1", "param2"](exec格式)
RUN ["yum", "install", "httpd"]
RUN yum install -y httpd  && yum -y install wget
说明:RUN指令用来指定操作指令,比如yun安装一个依赖、软件等等,RUN指令有两种方式:一种是直接shell执行,另外一种是exce
注意,每执行一个RUN指令都会生成一层镜像,RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指
定--no-cache参数。
我们为了减少RUN生成的镜像层,可以使用&&符号来将多个RUN指令合起来。

COPY <src>... <dest>
COPY ["<src>", ... "<dest>"]
COPY hello-world /app/		#注意:目标路径/app/一定要加斜杠,不然就表示重命名hello-world文件根app文件了,/app/不存在则自动创建目录
COPY /file*.txt /file/		#COPY支持通配符匹配文件
说明:COPY指令用于复制宿主机文件到容器内指定路径,COPY指令复制tar,tar.gz等格式的文件时,并不会自动解压tar、tar.gz等压缩包,仅复制文
件而已。COPY复制文件时会保留文件的元属性,如文件属组,创建时间,读、写、执行权限等元数据。

ADD <src>... <dest>
ADD	hello-world /app/
ADD https://cdn.mysql.com/archives/mysql-8.0/mysql-8.0.25-linux-glibc2.12-i686.tar.xz /home/mysql/
说明:ADD指令也是复制宿主机文件到容器内指定路径,但ADD比COPY更高级,ADD指令复制tar,tar.gz,gzip,bzip2以及xz的情况下等格式的文件时,
会自动解压压缩包文件,同时ADD指令的<源路径>可以是一个 URL,Docker引擎会试图去下载,然后放置到 <目标路径>。下载后的文件权限自动设置为600 
,如果需要更改权限,需要再增加一层RUN进行权限修改。如果下载的是一个压缩包,一般情况是 RUN 指令,然后使用wget或curl去下载,然后更改权
限、解压、清理下载的源文件。

WORKDIR <dir>
WORKDIR /app/
说明:WORKDIR目录用于设置当前工作目录,ORKDIR /app/就表示进入/app/目录,如果目录不存在则自动创建目录;
WORKDIR亦可以指定的是相对路径,如下:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c 。

USER root
说明:USER指令用于切换用户执行后续操作。

VOLUME	/data
说明:VOLUME指令用于定义匿名卷,表示将容器内的/data目录挂载为一个匿名卷到宿主机上,这个匿名卷卷名由docker自动生成;
ps: 一般不会在 Dockerfile 中用到VOLUME指令,我们更常见的还是在 docker run 的时候指定 -v 参数来指定挂载的数据卷。


EXPOSE <port> [<port>/<protocol>...]
EXPOSE	80 
EXPOSE	80/tcp 8089/udp
说明:EXPOSE指令告诉Docker容器在运行时监听指定的网络端口。如果未指定协议,则默认为TCP。
EXPOSE指令仅仅是声明运行时容器打算使用什么端口,并不会自动在宿主进行端口映射。但是我们还是建议在Dockerfile中使用EXPOSE指令,一是让读者
很清晰的知道该容器暴露的端口,二是当使用docker run -P时可让docker cli自动得知容器端口而免去详细指定宿主机要映射端口的麻烦,-P参数是指随机暴露一个宿主机端口并映射到容器端口。
经验证,如果不写EXPOSE指定容器端口,则docker run -P时将不会创建宿主机端口,docker ps 或docker inspect 都看不到端口相关的信息。

CMD指令有三种格式:
CMD ["executable","param1","param2"] (exec格式,一般推荐使用的格式)
CMD ["param1","param2"] (参数列表格式)
CMD command param1 param2 (shell格式)
CMD nginx -g "daemon off;"
CMD ["nginx", "-g", "daemon off;"]
说明:CMD指令的作用是指定容器主进程的默认启动指令。同时,如果在docker run运行时指定了新的指令行参数则会覆盖这个CMD默认指令。


ENTERYPOINT指令格式:
ENTERYPOINT command (shell模式)
ENTERYPOINT ["executable","param1","param2"] (exec模式)
ENTRYPOINT ["/usr/sbin/nginx","-g","daemon off;"]
ENTERYPOINT不会被docker run 的指令行参数指定的指令所覆盖,而且这些指令行参数会被当作参数送给 ENTERYPOINT 指令指定的程序,
但是,如果运行 docker run 时使用了--entrypoint 选项,将覆盖 entrypoint 指令指定的程序,
使用ENTERYPOINT的优点是:在执行 docker run 的时候可以指定ENTRYPOINT 运行所需的参数。
注意:如果Dockerfile中存在多个ENTRYPOMIT指令,仅最后一个生效。

Dockerfile实战

下面通过编写一个简单的Python网站程序,再编写Dockerfile文件来构建镜像,运行镜像,如下:
注意:Dockerfile文件必须以Dockerfile为文件名字。

[root@node2 ~]# mdkir website									#先创建一个目录
[root@node2 ~]# cd website										#进入目录
[root@node2 website]# docker pull  centos:7.8.2003				#下载一个centos7版本的基础镜像
[root@node2 website]# vim  my_website.py 						#编写我们的网站python程序
#coding:utf8
from flask import Flask
app=Flask(__name__)
@app.route('/')
def hello():
    return "Being single is better than being in an unfaithful relationship."
if __name__=="__main__":
   app.run(host='0.0.0.0',port=8080)								#网站对外暴露的是8080端口

[root@node2 website]# vim Dockerfile								#编写dockerfile文件,文件名是固定,必须是Dockerfile
FROM centos:7.8.2003												#FROM指定基础镜像为centos:7.8.2003
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo		#下载yum源
RUn curl -o /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo				#下载epel源
RUN yum makecache													#建立缓存
RUN yum -y install python3-devel python3-pip						#安装python环境
RUN pip3 install flask												#安装flask
COPY my_website.py /opt/											#把我们的python程序复制到容器的/opt目录
WORKDIR /opt/														#指定当前工作目录是/opt
EXPOSE 8080															#对外暴露端口为8080,因为我们python程序就是8080端口
CMD ["python3","my_website.py"]										#容器启动时运行python程序
[root@node2 website]# 

#构建镜像,--no-cache表示不缓存,镜像是分层的,如果已经构建过了则docker会使用缓存,这里使用--no-cache表示不使用缓存
# -t 指定镜像名称和版本,如果不指定-t ,则构建出来的镜像没有名称和版本号
# 最后一个点.表示当前目录,docker build指令会默认寻找当前目录下名叫Dockerfile的文件进行构建镜像
[root@node2 website]# docker build --no-cache  -t my_website:1.1.1 .
					 
#看到下面的Successfully 表示镜像构建成功				 
Successfully built e72b9af57bca
Successfully tagged my_website:1.1.1
[root@node2 website]# docker images  my_website					#查看镜像
REPOSITORY   TAG        IMAGE ID       CREATED         SIZE
my_website   1.1.1      e72b9af57bca   6 seconds ago   1.05GB

[root@node2 website]# docker run -d -it --name my_website -p 80:8080  my_website:1.1.1	#启动并运行my_website:1.1.1镜像

#查看my_website容器状态,已经是启动了,对外暴露80端口,容器内部端口是8080
[root@node2 website]# docker ps -a
CONTAINER ID   IMAGE              COMMAND                  CREATED          STATUS          PORTS                                   NAMES
1a0f7c714c4b   my_website:1.1.1   "python3 my_website.…"   18 minutes ago   Up 18 minutes   0.0.0.0:80->8080/tcp, :::80->8080/tcp   my_website

验证访问:
[root@node2 ~]# curl http://192.168.44.135/						#访问网站,已经能正确访问了,谷歌浏览器访问也正常
Being single is better than being in an unfaithful relationship.

指令的高级语法讲解

以上的介绍只讲解了我们最常用的指令普通语法,下面将全部讲解指令的语法:
参照官方Dockerfile编写文档:https://docs.docker.com/engine/reference/builder/

转义字符:
1、escape指令用于设置Dockerfile中用于转义的字符。如果未指定,默认转义字符为\
2、转义字符既用于转义行中的字符,也用于转义换行。这允许Dockerfile指令跨越多行。请注意,无论Dockerfile中是否包含转义解析器指令,在RUN命令中都不会执行转义,除非在一行的末尾。
ENV 设置环境变量:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2> ...
ENV  sex "man"
ENV  name="xiaolming" score=100
说明:ENV指令用于定义环境变量,定义好后的变量,在后续的指令ADD、COPY、ENV、EXPOSE、FROM、LABEL、STOPSIGNAL、USER、VOLUME、
WORKDIR、ONBUILD (与上面支持的指令之一结合使用时)中就可以直接使用 $name${name}的形式引用环境变量的值,同时,ENV定义的变量即能在Dockerfile中使用,还能在容器中使用。
env示例:
FROM busybox
ENV  sex "man"
ENV  name="xiaolming"
ENV FOO=/data
WORKDIR $FOO
ADD files/file.txt $FOO
CMD ["sleep","99999"] 
#构建镜像后启动运行进入容器执行env命令就可以看到上面的3个变量了
.dockerignore 文件定义要忽略的文件:
我们知道,在执行docker build命令构建镜像的时候,默认情况下docker CLI会将当前目录下的所有文件都发送给docker守护进程,可以在当前上下文的根目录中定义一个名为.dockerignore的文件,该文件的内容填写哪些文件是不需要发送给docker 守护进程的,简单的说,就是.dockerignore的文件用于定义要忽略的文件,这些文件将不会发送给docker守护进程。
一般的,我们更建议,单独创建一个项目目录,在该目录下不要存放无关的文件。如果实在有一些依赖文件要存放但又不想构建镜像时拖慢构建速度,可以选择使用.dockerignore的文件忽略发送这些文件。
.dockerignore 与 Dockerfile 放在同一路径下,即在当前上下文的根目录。
如果.dockerignore文件中的一行在第1列以#开头,那么该行将被视为注释,在CLI解释之前将被忽略。
.dockerignore 文件的编写示例:
# comment
*/temp*
*/*/temp*
temp?

*/temp*  :排除根目录的任何直接子目录中名称以temp开头的文件和目录。例如,不包括普通文件/somedir/temporary.txt,目录/somedir/temp也不包括在内。
*/*/temp*   :从比根目录低两级的任何子目录中排除以temp开头的文件和目录。例如,/somedir/subdir/temporary.txt将被排除。
temp?  :排除根目录下以“temp”为一个字符扩展名的文件和目录,例如“/tempa”和“/tempb”。

.dockerignore 示例:
[root@docker city]# ll			#当前目录结构
-rw-r--r-- 1 root root 188 96 14:00 Dockerfile
drwxr-xr-x 2 root root  73 96 13:44 files
drwxr-xr-x 2 root root  42 96 14:32 ISO	#ISO目录存在很多大的系统镜像
[root@docker city]# docker build -t test:v1 .	#现在构建镜像,很慢,因为docker cli会发送当前目录下所有文件给docker守护镜像
Sending build context to Docker daemon   4.47GB	#发送了4.47GB
Step 1/9 : FROM busybox
 ---> a416a98b71e2
...........
[root@docker city]# touch .dockerignore			#在当前上下文的根目录创建.dockerignore文件
[root@docker city]# echo "ISO" >> .dockerignore	#文件内容添加一条忽略ISO目录内容
[root@docker city]# docker build -t test:v1 .	#重新构建镜像,现在就不会发送ISO目录给docker守护进程了
Sending build context to Docker daemon   7.68kB	##发送了7.68kB
Step 1/9 : FROM busybox
 ---> a416a98b71e2
...........
FROM 指定基础镜像:
FROM [--platform=<platform>] <image> [AS <name>]
Or
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
Or
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
说明:FROM指令用来指定构建基于哪个基础镜像,FROM必须为第一个指令(ARG除外);
1、在Dockerfile中,ARG是唯一可能位于FROM之前的指令,同时,FROM指令支持任何出现在第一个FROM之前的ARG指令声明的变量;
2、在第一个FROM之前的ARG指令声明的变量仅可以被后续的FROM指令使用,不能被其他指令使用,构建镜像时会报错;
3、FROM可以在单个Dockerfile中出现多次,以创建多个映像,或者将一个构建阶段用作另一个构建阶段的依赖。只需在每个新的FROM指令之前记录提交操作输出的最后一个映像ID。每个FROM指令清除由前一个指令创建的任何状态;
4、通过向FROM指令添加AS别名,可以为新的构建阶段指定一个可选的名称。该名称可用于后续的FROM或COPY --from=<name> 指令引用在此阶段构建的镜像;
5、tag或digest是可选的。如果省略其中任何一个,则构建器默认使用latest。如果构建器找不到latest,则返回一个错误。

使用多FROM指令可以完成多阶段编译。每一条 FROM 指令都是一个构建阶段,多条 FROM 就是多阶段构建,虽然最后生成的镜像只能是最后一个阶段的结果,但是,能够将前置阶段中的文件拷贝到后边的阶段中,这就是多阶段构建的最大意义。
可以查看https://zhuanlan.zhihu.com/p/340086563,里面说明了为什么需要多阶段构建镜像,多节点构建镜像的含义。

FROM多阶段构建镜像:
[root@rancher city]# cat Dockerfile 
ARG version1=1.20
ARG version2=latest
FROM busybox:$version1 as busybox1
WORKDIR /data1
ADD files/file.txt /data1/
FROM nginx:${version2}		#又重新启动一个构建阶段
COPY --from=busybox1 /data1/file.txt  /fileAA.txt   #将上一个构建阶段产生的file.txt文件拷贝过来使用
[root@rancher city]# docker build --force-rm  --no-cache -t test:v1 .
[root@rancher city]# docker run -it test:v1 sh
# ls /fileAA.txt -l
-rw-r--r--. 1 root root 6 Sep  6 06:48 /fileAA.txt
# 

AGR指令的位置:
[root@rancher city]# cat Dockerfile 
ARG version1=1.20
ARG version2=latest
FROM busybox:$version1 as busybox1
WORKDIR /data1
ADD files/file.txt /data1/
FROM nginx:${version2}
COPY --from=busybox1 /data1/file.txt  /fileAA.txt
RUN mkdir $version2
#构建报错,因为$version2时在第一个FROM之前定义的,其他指令不能使用它
[root@rancher city]# docker build --force-rm  --no-cache -t test:v1 .			

[root@rancher city]# cat Dockerfile 
ARG version1=1.20
ARG version2=latest
FROM busybox:$version1 as busybox1
WORKDIR /data1
ADD files/file.txt /data1/
FROM nginx:${version2}
COPY --from=busybox1 /data1/file.txt  /fileAA.txt
ARG version3=latest
RUN mkdir $version3
[root@rancher city]# #构建正常,因为$version3时非第一个FROM之前定义的,其他指令正常使用它
RUN执行一条命令:
说明:RUN指令用来指定操作指令,比如yun安装一个依赖、软件等等,RUN指令有两种方式:一种是直接shell执行,另外一种是exce
注意,每执行一个RUN指令都会生成一层镜像,RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指
定--no-cache参数。
我们为了减少RUN生成的镜像层,可以使用&&符号来将多个RUN指令合起来。

shell格式语法:
RUN <command> (shell格式)
shel格式其实就是我们平时写的shell命令,默认使用的shell是/bin/sh -c参数,其中/bin/sh是shell的路径,-c参数是shell的一个选项,用于指定传入字符串作为要执行的命令,演示如下:
RUN /bin/bash -c 'source $HOME/.bashrc && echo $HOME'  
当然,我们可以不写/bin/bash -c,默认就是/bin/bash -c,如下:
RUN yum install httpd -y && mkdir /data/

exec格式语法:
RUN ["executable", "param1", "param2"](exec格式)
RUN ["yum", "install", "httpd","-y"]
RUN ["/bin/bash", "-c", "yum install httpd -y"]

建议直接使用shell格式即可,也是我们最熟悉的方式。
RUN --mount 挂载文件或卷到容器:
注意:使用RUN --mount ,构建镜像时需要使用BuildKit, BuildKit是 Docker 的一种构建工具,启用BuildKit有两种方法:
1、直接使用DOCKER_BUILDKIT=1环境变量启用,如DOCKER_BUILDKIT=1 docker build xxxx
2、编辑配置文件/etc/docker/daemon.json,添加"features":{"buildkit" : true},重启docker即可生效。

语法:
RUN --mount=[type=<TYPE>][,option=<value>[,option=<value>]...]
其中TYPE可以有5中类型:
这里--mount挂载的类型一共有五种:
bind(default),用于挂载一个上下文目录到容器中
cache,主要用于挂载一个临时目录来缓存编译器和包管理器的目录。
tmpfs,主要用于挂载一个tmpfs
secret,允许构建容器访问诸如私钥之类的安全文件,并且此类文件不会出现在构建好的镜像中,避免密钥外泄。
ssh,允许构建容器通过SSH代理访问SSH密钥,并支持密码短语

RUN --mount=type=bind 示例:
bind类型挂载类型允许将当前上下文的文件或目录绑定到构建容器。默认情况下绑定挂载是只读的。
target:Mount path.
source:Source path in the from. Defaults to the root of the from.
from:Build stage or image name for the root of the source. Defaults to the build context.
rw,readwrite:Allow writes on the mount. Written data will be discarded.

示例:
[root@rancher city]# ls files/
file1.txt  file2.txt  file3.txt  file.txt
[root@rancher city]# cat Dockerfile 
ARG version1=1.20
FROM busybox:$version1 as busybox1
WORKDIR /ref  
RUN --mount=type=bind,source=files,target=/tmp/ cp /tmp/* /home/
CMD ["/bin/sh","-c","sleep 99999999"]
[root@rancher city]# 
#RUN这行的意思是将当前上下文files目录挂载到容器的/tmp/目录,所以这样/tmp/目录下就有文件了,然后容器里cp复制/tmp/目录下全部文件到/home/目录下
[root@rancher city]# DOCKER_BUILDKIT=1  docker build --no-cache  -t busybox2:v2 .
[root@rancher city]# docker run --rm -d --name busybox2 busybox2:v2
[root@rancher city]# docker exec -it 6154 ls /home/
file.txt   file1.txt  file2.txt  file3.txt
[root@rancher city]# docker exec -it 6154 cat /home/file.txt
AAAAA

说明,注意:
1、无法直接通过bind类型挂载宿主机的目录,因为bind类型用于挂载一个上下文目录到容器中,默认from参数也是当前上下文目录,所以可以省略from参数;
2、使用RUN --mount=type=bind挂载后,在容器运行阶段,我们将无法访问目录的挂载,所以我们需要使用cp将文件复制到容器其他目录;
3、同样无法在一个RUN指令中获取到另一个RUN指令挂载的目录,如下面两行,构建镜像时将显示找不到文件:
RUN --mount=type=bind,source=files,target=/tmp/
RUN cp /tmp/* /home/

#其他bind类型这里不讲
# RUN --network 设置控制命令运行的网络环境
语法:
RUN --network=<TYPE>
TYPE有以下几种类型:
default (默认)	默认网络,命令在构建的默认网络中运行
none	无网络
host	运行在宿主机环境网络

示例:
FROM python:3.6
ADD mypackage.tgz wheels/
RUN --network=default pip install --find-links wheels mypackage
RUN --network=none pip install --find-links wheels mypackage
RUN --network=host pip install --find-links wheels mypackage
#CMD 指定容器主进程的默认启动指令
CMD指令有3中格式:
CMD ["executable","param1","param2"] (exec格式,一般推荐使用的格式,必须是双引号,不能是单引号)
CMD ["param1","param2"] (参数列表格式,作为ENTRYPOINT指令的默认参数)
CMD command param1 param2 (shell格式)
例如:
CMD ["nginx", "-g", "daemon off;"]		#exec格式
CMD nginx -g "daemon off;"				#shell格式

说明:CMD指令的作用是指定容器主进程的默认启动指令。同时,如果在docker run运行时指定了新的指令行参数则会覆盖这个CMD默认指令。
CMD指令还可以仅作为参数列表,即为作为ENTRYPOINT指令的默认参数,为ENTRYPOINT指令传递默认参数。
只能有一个CMD命令,如果你定义了多个CMD,那么只有最好一个CMD命令生效。
CMD的主要目的是为执行容器提供默认值。这些默认值可以包含可执行文件,也可以省略可执行文件,在这种情况下,您还必须指定ENTRYPOINT指令。
如果CMD用于为ENTRYPOINT指令提供默认参数,则CMD和ENTRYPOINT指令都应使用JSON数组格式指定。
与shell形式不同,exec形式不调用命令shell。这意味着正常的shell处理不会发生。例如,CMD ["echo","$HOME"]不会对$HOME进行变量替换。如果你想要shell处理,那么要么使用shell形式,要么直接执行shell,例如:CMD ["sh","-c","echo $HOME"]。当使用exec表单并直接执行shell时,就像shell表单的情况一样,是shell在进行环境变量的扩展,而不是docker。
如果你使用shell形式的CMD,那么<command>将在/bin/sh -c中执行:
FROM ubuntu
CMD echo "This is a test." | wc -
LABEL  将元数据添加到镜像
LABEL <key>=<value> <key>=<value> <key>=<value> ...
LABEL指令将元数据添加到镜像中。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."
然后使用docker image inspect --format='{{json .Config.Labels}}'  busybox2:v2 | python -m json.tool
 命令就可以看到设置的这些标签信息。
一个镜像可以有多个标签。可以像上面那样在一行上指定多个标签。在Docker 1.10之前,这减少了最终图像的大小,但现在不再是这样了。您仍然可以选择在一条指令中指定多个标签,如下两种方式均可:
LABEL multi.label1="value1" multi.label2="value2" other="value3"
LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"
注意:请确保使用双引号,而不是单引号。特别是当你使用字符串插值时(例如LABEL示例="foo-$ENV_VAR"),单引号将按原样取字符串,而不拆包变量的值。
镜像会继承父镜像的标签,当镜像设置了与父镜像相同的标签,则会覆盖父镜像的标签值。
#MAINTAINER (deprecated)指定作者信息
语法:
MAINTAINER SvenDowideit@home.org.au
MAINTAINER指令用于设置镜像的Author字段,Author字段可用于指定维护者邮箱等信息,此指令现已废弃,改用LABEL指令是一个更灵活的版本。
docker inspect  busybox2:v2 -f '{{json .Author}}'
要设置与MAINTAINER字段对应的标签,可以使用:
LABEL org.opencontainers.image.authors="SvenDowideit@home.org.au"
EXPOSE 暴露端口
EXPOSE <port> [<port>/<protocol>...]
EXPOSE指令通知Docker容器在运行时监听指定的网络端口。可以指定端口监听TCP还是UDP,如果不指定协议,默认监听TCP。
注意:EXPOSE指令实际上并不发布端口。它的作用是作为构建镜像的人员和运行容器的人员之间的一种说明文档,不是说明该镜像容器暴露的是哪些端口。要在运行容器时实际对外暴露容器端口,可以在docker运行中使用-p参数或-P参数。

默认情况下,EXPOSE使用TCP。你也可以指定UDP:
EXPOSE 80/udp
要在TCP和UDP上都暴露,包括两行:
EXPOSE 80/tcp
EXPOSE 80/udp

在指定了EXPOSE 暴露端口,这样docker run -P 随机的分配的端口就会映射到容器里EXPOSE暴露的指定端口上。当然,还是建议使用-p直接指定端口。
ENV 设置变量
语法格式:
ENV <key>=<value> ...  		#这种有等于号的,可以多个变量放在一行(建议使用这种语法)
或
ENV <key> <value> 			#这种没有等于号,只能一行放一个变量(不建议使用,官方可能在未来抛弃这种写法)

示例:
ENV MY_NAME="John Doe" MY_CAT=fluffy
ENV key1 values1
ENV key2 "values2"

注:ENV设置的变量可以在Dockerfile中使用,而且该变量也会保存在镜像中,换句话说变量会注入容器中,即容器中可以使用该变量。
ARG也是设备变量,但是ARG设置的镜像只能在Dockerfile中使用,即容器中不会有ARG设置的变量。
#查看镜像中的变量
docker inspect  busybox2:v2 -f '{{json .Config.Env}}' | python -m json.tool
#  ADD 复制文件
ADD有两种格式:
ADD [--chown=<user>:<group>] [--chmod=<perms>] [--checksum=<checksum>] <src>... <dest>
ADD [--chown=<user>:<group>] [--chmod=<perms>] ["<src>",... "<dest>"]
可以指定多个"<src>",如果它们是文件或目录,它们的路径将被解释为相对于构建上下文的当前路径,也就是说不要写绝对路径,要复制的文件或目录必须实在当前上下文路径。
"<dest>"就是目标路径,要么写绝对路径,不写那就是容器里面的当前工作目录,即WORKDIR指令设置的当前工作目录,如下:
ADD test.txt relativeDir/		#test.txt文件将被复制到当前工作目录的relativeDir目录下
ADD test.txt /absoluteDir/		#test.txt文件将被复制到/absoluteDir/目录下
可以使用通配符匹配,如下:
ADD hom* /mydir/				#*号表示匹配0个或多个字符
ADD hom?.txt /mydir/			#?号表示匹配一个字符

#当目录路径是一个目录时,目录本身不复制, 即复制目录下的内容
ADD files/ /tmp/ 				#ADD复制目录会将files目录下的文件复制到/tmp/目录下的,目录本身不复制

#目标路径有斜杠和没有斜杠的区别,比较下面两个的区别
WORKDIR /data/nginx						#创建了两级目录
ADD http://nginx.org/download/nginx-1.24.0.tar.gz /data/nginx/	#从URL下载文件到/data/nginx目录下,所以/data/nginx目录里面就有nginx-1.24.0.tar.gz文件
ADD http://nginx.org/download/nginx-1.24.0.tar.gz /data/nginx	#等价于上面这条,因为nginx本身就是目录,所以没有斜杠也是正常的
ADD http://nginx.org/download/nginx-1.24.0.tar.gz /data/nginx/nginx	#从URL下载文件到/data/nginx目录下并命名为nginx文件

#ADD会自动解压本地的tar归档类型的文件,但不会解压URL下载的文件
ADD http://nginx.org/download/nginx-1.24.0.tar.gz /data/nginx/	#从URL下载文件到/data/nginx目录下,ADD不会自动解压URL下载的文件,所以/data/nginx目录里面还是nginx-1.24.0.tar.gz文件

ADD nginx-1.24.0.tar.gz /data/nginx/	#ADD自动解压本地复制的文件,即容器里面解压后就存在/data/nginx/nginx-1.24.0目录

#以上,强烈建议目标路径以斜杠结尾,如 ADD http://nginx.org/download/nginx-1.24.0.tar.gz /data/nginx/
ADD --link 与COPY --link相同,见COPY --link
COPY 复制文件
COPY有两种格式:
COPY [--chown=<user>:<group>] [--chmod=<perms>] <src>... <dest>
COPY [--chown=<user>:<group>] [--chmod=<perms>] ["<src>",... "<dest>"]
COPY指令用于将文件或者目录从<src>复制到容器中的文件系统,位于<dest>路径。可以指定多个<src>资源,但是文件和目录的路径将被解释为相对于构建上下文的源。每个<src>可以包含通配符,匹配将使用Go的filepath.Match规则进行。

比如,你想添加所有以"hom"开始的文件,你可以这样做:
COPY hom* /mydir/
在下面的例子中,?被替换为任何单个字符,例如"home.txt":
COPY hom?.txt /mydir/
可以指定多个"<src>",如果它们是文件或目录,它们的路径将被解释为相对于构建上下文的当前路径,也就是说不要写绝对路径,要复制的文件或目录必须实在当前上下文路径。
"<dest>"就是目标路径,要么写绝对路径,不写那就是容器里面的当前工作目录,即WORKDIR指令设置的当前工作目录,如下:
COPY test.txt relativeDir/		#test.txt文件将被复制到当前工作目录的relativeDir目录下
COPY test.txt /absoluteDir/		#test.txt文件将被复制到/absoluteDir/目录下
可以使用通配符匹配,如下:
COPY hom* /mydir/				#*号表示匹配0个或多个字符
COPY hom?.txt /mydir/			#?号表示匹配一个字符

#当目录路径是一个目录时,目录本身不复制, 即复制目录下的内容
COPY files/ /tmp/ 				#ADD复制目录会将files目录下的文件复制到/tmp/目录下的,目录本身不复制

#目标路径有斜杠和没有斜杠的区别,比较下面两个的区别
WORKDIR /data/nginx						#创建了两级目录
COPY nginx-1.24.0.tar.gz /data/nginx/	#/data/nginx目录里面就有nginx-1.24.0.tar.gz文件
COPY nginx-1.24.0.tar.gz /data/nginx	#等价于上面这条,因为nginx本身就是目录,所以没有斜杠也是正常的
COPY nginx-1.24.0.tar.gz /data/nginx/nginx	#后面一个nginx是一个普通文件并非目录,所以意思就是复制nginx-1.24.0.tar.gz文件到容器并命名为nginx文件
#注意:CPOY不会解压tar归档文件,对于CPOY而言,源文件是什么样,复制进去就是什么样,这点是与ADD最大的区别
COPY --from 从 from 指定的构建阶段中寻找源文件
FROM busybox:1.20 as busybox1
WORKDIR /data1
ADD files/file.txt /data1/
FROM nginx:1.18		#又重新启动一个构建阶段
COPY --from=busybox1 /data1/file.txt  /fileAA.txt   #将上一个构建阶段产生的file.txt文件拷贝过来使用
COPY --link
不懂
ENTRYPOINT 指定容器启动后执行的命令
ENTRYPOINT 有2种格式:
exec格式:ENTRYPOINT ["executable", "param1", "param2"]
shell 格式:ENTRYPOINT command param1 param2
exec表单被解析为JSON数组,这意味着您必须在单词周围使用双引号,而不是单引号
只有Dockerfile中最后一个ENTRYPOINT指令才会生效。
示例:
FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值