5-Dockerfile的相关操作
Docker 可以通过解析Dockerfile中的代码说明来自动生成镜像。是一个文本文档,其中包含用户可以在命令行上调用的所有命令来层叠形成镜像。使用用户创建的文档按照层级依次进行多个命令的自动构建镜像的过程
以上是Dockerhub官方对Dockerfile的描述
Dockerfile使用 docker build <PATH>
来进行编译成镜像。创建一个Dockerfile之后,执行
docker build . 就可以针对当前目录的Dockerfile制作镜像了。这里官方给出的一个警告,在当前目录中除了Dockerfile文件和镜像中要用到的其他文件之外,不要存放其他不相关的目录或文件,因为这样会使打包的守护进程将当前文件中的内容全都传输过去执行。会严重影响打包运行的速度。
-t :打包镜像时指定镜像的存储和标记(tag)
-f: 指定作用与上下文的根目录,例如你需要把根目录指定到非当前目录以外的目录中
Dockerfile
的语法格式
FROM
FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
# or
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
描述你的镜像基于哪个镜像来的
--platform 从哪个注册服务器,默认是从Docker-Hub
image 基于的镜像名称例如 ubuntu:18.04
:tag 指定版本号,默认为最新版本latest
MAINTAINER 作者和说明
MAINTAINER <name>
name 描述作者,或者组织等内容例如
MAINTAINER tonystark
RUN
RUN有两种形式
RUN <command>:使用shell的方式
#OR
RUN ["executable", "param1", "param2"] 使用exec的方式
使用shell的方式时,如果命令过长,可以使用 \换行进行,官方给的一个例子
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'
#其结果等价于
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
使用exec方式时,executable是命令,后面的 param1和param2是参数
:ok:Node
param1参数描述必须用双引号而不能用单引号,因为这里exec会将参数解析为 JSON Array
COPY
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
复制当前目录上下文中的文件到镜像目录中
:ok:复制语法
指令从中复制新文件或目录,并将其添加到路径处的容器的文件系统中。
COPY``<src>``<dest>
可以指定多个资源,但文件和目录的路径将被解释为相对于生成上下文的源。
<src>
每个可能都包含通配符,匹配将使用 Go 的文件路径完成。匹配规则。例如:
<src>
要添加以”home"开始的所有文件
COPY home* /mydir/ #/mydir/指镜像中的目录
替换单个匹配的字符
COPY home?.txt /mydir/ #例如home1.txt
注意:warning:
路径必须位于生成上下文中;不能,因为 的第一步是将上下文目录(和子目录)发送到 Docker 守护程序。
<src>``COPY ../something /something``docker build
ADD
ADD 有两种形式:
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
该指令作用是复制新文件、目录或远程文件 URL,并将其添加到路径处的镜像文件系统中。ADD``<src>``<dest>
可以指定多个资源,但如果它们是文件或目录,则它们的路径将被解释为相对于生成上下文的源。<src>
每个可能都包含通配符,匹配将使用 Go 的文件路径完成。匹配规则。例如:<src>
如果是识别压缩格式(标识、gzip、bzip2 或 xz)的本地 tar 存档,则将其解压缩为目录。来自远程URL 的资源不会解压缩。复制或解包目录时,它的行为与 相同,结果是:
<src>``tar -x
- 无论在目标路径上存在什么,
- 源树的内容,在文件基础上解决有利于"2."的冲突。
注意
文件是否标识为识别的压缩格式仅根据文件的内容而不是文件的名称进行。例如,如果一个空文件恰好以此结束,则不会被识别为压缩文件,并且不会生成任何类型的解压缩错误消息,而该文件将简单地复制到目标。
.tar.gz
如果是任何其他类型的文件,则将其及其元数据单独复制。在这种情况下,如果以斜杠结尾,则它将被视为目录,其内容将写入 。
<src>``<dest>``/``<src>``<dest>/base(<src>)
如果直接指定多个资源,或者由于使用了通配符,则必须是目录,并且它必须以斜杠结束。
<src>``<dest>``/
如果不以斜杠结束,则它将被视为常规文件,其内容将写入 。
<dest>``<src>``<dest>
如果目录不存在,则与路径中所有缺少的目录一起创建它。
<dest>
实际上ADD和COPY的区别就在于
1.ADD可以复制网络上的文件添加到镜像COPY只能复制已有的
2.ADD复制过去的压缩文件会解压开,COPY则不会
EXPOSE
EXPOSE <port> [<port>/<protocol>...]
该指令通知 Docker 容器在运行时侦听指定的网络端口。可以指定端口是侦听 TCP 还是 UDP,如果未指定协议,则默认值为 TCP。
默认情况下,假定为 TCP。还可以指定 UDP
EXPOSE 80/udp
如果需要80端口的TCP和UDP,请包括两行
EXPOSE 80/tcp
EXPOSE 80/udp
不过这个配置,允许在运行时进行覆盖
docker run -p 80:80/tcp -p 80:80/udp ...
ENV
ENV <key>=<value> ...
设置环境变量,在生成阶段的所有后续指令中作用。并且在许多情况下可以内联替换。如果不对引号字符进行转义,那么在实际的环境变量中会将引号删除。像命令行解析一样,引号和反斜杠可用于包含值中的空格。
例如
ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffy
WORKDIR
WORKDIR /path/to/workdir
WORKDIR指令为Dockerfile中跟在其后的所有RUN,CMD,ENTRYPOINT,COPY和ADD指令设置工作目录。 如果WORKDIR不存在,即使以后的Dockerfile指令中未使用它也将被创建。WORKDIR指令可在Dockerfile中多次使用。 如果提供了相对路径,则它将相对于上一个WORKDIR指令的路径。 举个例子
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
pwd命令最终输出的是 /a/b/c
ONBUILD
ONBUILD <INSTRUCTION>
ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN, COPY 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。
这里引用别人的一个例子
FROM node:slim
RUN mkdir /app
WORKDIR /app
ONBUILD COPY ./package.json /app
ONBUILD RUN [ "npm", "install" ]
ONBUILD COPY . /app/
CMD [ "npm", "start" ]
当在各个项目目录中,用这个只有一行的 Dockerfile 构建镜像时,之前基础镜像的那三行 ONBUILD 就会开始执行,成功的将当前项目的代码复制进镜像、并且针对本项目执行 npm install,生成应用镜像。
这里有个专门写ONBUILD的文章[引用]
https://blog.csdn.net/supertor/article/details/83057748
简单的理解就是,作为自己的镜像,这个命令后面修饰的是不会执行的,之后依赖了它的镜像,在构建时才会去执行。
USER
USER <user>[:<group>]
指定镜像以什么用户运行
VOLUME
VOLUME ["/data"]
VOLUME指令创建具有指定名称的挂载点,并将其标记为保存来自宿主机霍其他外部安装的磁盘中,值可以为JSON或多个参数的纯字符串。
主机目录在容器运行时声明:主机目录(挂载点)就其性质而言,与主机相关。这是为了保证镜像可移植性,因为无法保证给定的主机目录在所有主机上都可用。因此,不能从 Dockerfile 中装载主机目录。该指令不支持指定参数。创建或运行容器时必须指定装载点。VOLUME``host-dir
CMD
这个指令有三种形式
CMD ["executable","param1","param2"] (exec form, this is the preferred form)
CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
CMD command param1 param2 (shell form)
第一种 executable为指令 param1和param2为参数,这个Docker推荐的方式
第二种,只是作为参数
第三种是以Shell模式的
一个Dockerfile文件中只允许有一行指令,如果写了多行时,最终以最后一样覆盖生效。
CMD的主要目的是为执行中的容器提供默认值。 这些默认值可以包含一个可执行文件,也可以忽略该可执行文件,在这种情况下,您还必须指定ENTRYPOINT指令。
如果使用CMD为ENTRYPOINT指令提供默认参数,则CMD和ENTRYPOINT指令均应使用JSON数组格式指定。同RUN命令一样,引号如果没有被转义,也会被删除。
ENTRYPOINT
ENTRYPOINT ["executable", "param1", "param2"]
语法和CMD类似
区别在于允许配置作为容器运行的参数,例如nginx容器更换端口号
$ docker run -i -t --rm -p 80:80 nginx
命令行参数将以 exec的形式在容器运行的参数后追加,并将覆盖指定的所有元素。这允许将参数传递到入口点,即,将参数传递给入口点。您可以使用标志覆盖指令。
docker run <image>``ENTRYPOINT``CMD``docker run <image> -d``-d``ENTRYPOINT``docker run --entrypoint
可以使用exec的方式设置一个固定的命令和参数,然后在使用镜像启动容器时,对其进行修改
FROM ubuntu:18.04
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
构建镜像
➜ myubuntu docker build -t ubuntu.cloudalibaba.com/tonymark/myubuntu:v7 .
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM ubuntu:18.04
---> 56def654ec22
Step 2/3 : ENTRYPOINT ["top", "-b"]
---> Running in 104a9a882af7
Removing intermediate container 104a9a882af7
---> e9533912943c
Step 3/3 : CMD ["-c"]
---> Running in 364a34ef9117
Removing intermediate container 364a34ef9117
---> b692c7597de0
Successfully built b692c7597de0
Successfully tagged ubuntu.cloudalibaba.com/tonymark/myubuntu:v7
运行镜像
➜ myubuntu docker run -it --rm --name test ubuntu.cloudalibaba.com/tonymark/myubuntu:v7 -H
top - 16:00:17 up 1:42, 0 users, load average: 0.11, 0.10, 0.03
Threads: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.1 us, 0.3 sy, 0.0 ni, 99.5 id, 0.1 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 4015860 total, 3157824 free, 298092 used, 559944 buff/cache
KiB Swap: 4194300 total, 4194300 free, 0 used. 3485136 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 36488 2948 2592 R 0.0 0.1 0:00.03 top
然后查看中top的运行结果
➜ ~ docker exec -it test ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.0 36488 3008 pts/0 Ss+ 16:03 0:00 top -b -H
root 12 0.0 0.0 34404 2756 pts/1 Rs+ 16:03 0:00 ps aux
这里可以看到-b参数是ENTRYPOINT中的固定参数-H则覆盖了CMD中的参数。
因此这里可以得出一个简单的结论
CMD的命令会被Dockerrun的命令覆盖,而ENTRYPOINT的则不会
进一步确认
上面用这个镜像启动的容器,先停止掉,由于启动容器时添加了--rm,因此容器停止运行时会自动删除,这里重启启动容器,去掉-H参数
然后查看top运行的结果
➜ ~ docker exec -it test ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.7 0.0 36488 3032 pts/0 Ss+ 16:06 0:00 top -b -c
可以看到,当run命令不传入参数时,CMD中的内容会被作为ENTRYPOINT的参数。
ARG
ARG <name>[=<default value>]
该指令定义了一个变量,用户可以在构建时使用带有标志的命令将变量传递给构建器。如果用户指定了Dockerfile中未定义的构建参数,则构建会输出警告。
Dockerfile可以包含一个或多个说明。例如
FROM busybox
ARG user1
ARG buildno
# ...
Docker的最佳实践
使用 .dockerignore
在构建docker镜像时,当前目录上下文的中的文件都会被加载进守护进程,如果目录文件过大时会导致响应变慢,构建速度迟缓等问题。因此使用.dockerignore有助于不必要地向守护进程发送大型或敏感文件和目录。
下面是官方给出的示例
# comment
*/temp*
*/*/temp*
temp?
规则 | 行为 |
---|---|
# comment |
以#开头的被标记为注释,解析是回忽略 |
*/temp* |
排除其名称以根的任何直接子目录为起点的文件和目录。例如,纯文件被排除,目录也一样。temp``/somedir/temporary.txt``/somedir/temp
|
*/*/temp* |
排除从比根以下两个级别开始的任何子目录的文件和目录。例如,被排除。temp``/somedir/subdir/temporary.txt
|
temp? |
排除根目录中的文件和目录,其名称为 的单字符扩展名。例如,和 被排除。temp``/tempa``/tempb
|
ARG和FROM的使用
FROM指令支持在该命令之前生命一个变量例如
ARG CODE_VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version
比较简单的例子
这里制作一个NGINX为镜像的例子来回顾一下上边的这些指令
#BASE IMG
FROM centos:8
#MAINTAINER
MAINTAINER 1020952183@qq.com
#将nginx-1.18.0.tar.gz 复制到容器的 /usr/local/src 目录下(ADD命令会针对.gz的文件解压缩)
ADD nginx-1.18.0.tar.gz /usr/local/src
#执行安装命令,安装编译nginx的必要插件
RUN yum install -y gcc pcre pcre-devel openssl openssl-devel gd gd-devel
#同上
RUN yum -y install gcc pcre-devel openssl-devel zlib-devel make
#创建一个用户名为nginx,并设定为不可登录,主要用户nginx(nginx的知识此处不详述)
RUN useradd -M -s /sbin/nologo nginx
#workdir命令相当于 在命令行中执行 cd 切换工作目录命令
WORKDIR /usr/local/src/nginx-1.18.0
#设定编译安装,这里其他都是一些开关和配置项, --prefix是安装之后的路径,下一步设定环境变量要用
RUN ./configure --prefix=/apps/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_v2_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module && make && make install
#根据上面设定的安装目录,添加到环境变量
ENV PATH /apps/nginx/sbin:$PATH
#开放端口号80
EXPOSE 80
#启动nginx
CMD /bin/sh -c 'nginx -g "daemon off;"'
build镜像
➜ dockerfile-nginx docker build -t nginx_my:v1 .
Sending build context to Docker daemon 2.028MB
Step 1/11 : FROM centos:8
---> 0d120b6ccaa8
Step 2/11 : MAINTAINER 1020952183@qq.com
---> Using cache
---> 8aeeb5aabddc
Step 3/11 : ADD nginx-1.18.0.tar.gz /usr/local/src
---> Using cache
---> 14d538a764ba
Step 4/11 : RUN yum install -y gcc pcre pcre-devel openssl openssl-devel gd gd-devel
---> Using cache
---> 104b8457fd6e
Step 5/11 : RUN yum -y install gcc pcre-devel openssl-devel zlib-devel make
---> Using cache
---> f639cf354f39
Step 6/11 : RUN useradd -M -s /sbin/nologo nginx
---> Using cache
---> 5a312ed76886
Step 7/11 : WORKDIR /usr/local/src/nginx-1.18.0
---> Using cache
---> 38ef02059ccb
Step 8/11 : RUN ./configure --prefix=/apps/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_v2_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module && make && make install
---> Using cache
---> 3db4ed3d68c3
Step 9/11 : ENV PATH /apps/nginx/sbin:$PATH
---> Using cache
---> b1dd6f61a612
Step 10/11 : EXPOSE 80
---> Using cache
---> eef5df969ec2
Step 11/11 : CMD /bin/sh -c 'nginx -g "daemon off;"'
---> Running in 584a3d645c8e
Removing intermediate container 584a3d645c8e
---> b872bfa72145
Successfully built b872bfa72145
Successfully tagged nginx_my:v1
我这里由于之前编译过,可以看到 Using cache,表示这里docker在build镜像时使用到了上一次的缓存,如果不想使用缓存可以 加上 --no-cache
查看镜像
➜ dockerfile-nginx docker images | grep nginx
nginx_my v1 b36ebf9dbe5c 2 hours ago 463MB
ubuntu.cloudalibaba.com/tonymark/mynginx v1.1 e35c7e037809 13 days ago 63.2MB
goharbor/nginx-photon v2.1.1 88bf58494701 3 weeks ago 43.6MB
frend/nginx v7 f35646e83998 4 weeks ago 133MB
nginx latest f35646e83998 4 weeks ago 133MB
第一个nginx_my:v1就是用Dockerfile创建的镜像了,启动并运行看看
➜ dockerfile-nginx docker run --rm -d -p8081:80 --name=nginx_my nginx_my:v1
3809946680eafb42b0938dc9114f81efa6be77b33354a712289845523e9a3051
这里-p参数的意思是将冒号左边的端口(本机)映射到冒号右边(容器)的IP中, 访问localhost:8081 就可以看到容器的首页了。
至此,Dockerfile的基础操作就完成了。
参考资料
https://www.jianshu.com/p/10ed530766af[学习Docker之Dockerfile的命令]
https://docs.docker.com/engine/reference/builder/#entrypoint[docker官方文档]
https://www.cnblogs.com/jsonhc/p/7767669.html[docker之Dockerfile实践]
转载请注明出处。祝大家变得更强~!
有疑问加站长微信联系(非本文作者)