Dockerfile指令详解
Dockerfile 文件详解
用于构建镜像。
常用指令介绍
1、FROM
定制的镜像都是基于 FROM 的镜像,就是基础镜像。
2、RUN
用于执行后面跟着的命令行命令。
在构建过程中**
在镜像中
**执行命令。也就是构建镜像时运行的指令。执行结束后, commit 这一层的修改,构成新的镜像。
有以下俩种格式:
# 1、shell 格式:
RUN <命令行命令>
# 备注: <命令行命令> 等同于,在终端操作的 shell 命令。## 2、exec 格式:
RUN ["可执行文件", "参数1", "参数2"]
# 例如:RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline
简单的例子:
FROM nginx
RUN echo '这是一个本地构建的nginx镜像' > /usr/share/nginx/html/index.html
注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。
例如:
FROM centos
RUN yum -y install wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN tar -xvf redis.tar.gz
以上执行会创建 3 层镜像。可简化为以下格式:
FROM centos
RUN yum -y install wget \&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \&& tar -xvf redis.tar.gz
如上,以 && 符号连接命令,这样执行后,只会创建 1 层镜像。
最简单例子:
$ docker build -t nginx:v3 .
上下文路径说明
上下文路径(就是上面的点 " . "),是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。
解析:由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。
如果未说明最后一个参数,那么
默认上下文路径就是 Dockerfile 所在的位置
。注意:上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。
3、LABEL
添加镜像的元数据,使用键值对的形式。
注意
:
也可以使用
MAINTAINER
,指定Dockerfile的作者/维护者,但是已弃用,推荐使用LABEL
指令。
4、CMD
指定容器创建时的默认命令。(可以被覆盖)
类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:
- CMD 在docker run 时运行。
- RUN 是在 docker build。
作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。
注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。
格式:
CMD <shell 命令>
CMD ["<可执行文件或命令>","<param1>","<param2>",...]
CMD ["<param1>","<param2>",...] # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数
推荐使用第二种格式,执行过程比较明确。第一种格式实际上在运行的过程中也会自动转换成第二种格式运行,并且默认可执行文件是 sh。
5、ENTRYPOINT
设置容器创建时的主要命令。(不可被覆盖)
运行容器时执行的shell命令。
类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。
但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 ENTRYPOINT 指令指定的程序。
优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。
注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。
格式:
ENTRYPOINT ["<executeable>","<param1>","<param2>",...]
可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参,以下示例会提到。
示例:
假设已通过 Dockerfile 构建了 nginx:test 镜像:
FROM nginxENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参
- 不传参运行
$ docker run nginx:test# 容器内会默认运行以下命令,启动主进程。
nginx -c /etc/nginx/nginx.conf
- 传参运行
$ docker run nginx:test -c /etc/nginx/new.conf# 容器内会默认运行以下命令,启动主进程(/etc/nginx/new.conf:假设容器内已有此文件)
nginx -c /etc/nginx/new.conf
6、EXPOSE
声明容器运行时监听的特定网络端口。
仅仅只是声明端口。
作用:
- 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
- 在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
格式:
EXPOSE <端口1> [<端口2>...]
7、ENV
在容器内部设置环境变量。
设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。
格式:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
以下示例设置 NODE_VERSION = 7.2.0 , 在后续的指令中可以通过 $NODE_VERSION 引用:
ENV NODE_VERSION 7.2.0RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"
8、ADD
将文件、目录或远程URL复制到镜像中。
拷贝文件或目录到容器中,如果是URL或压缩包便会自动下载或自动解压。
ADD 指令和 COPY 的使用格类似(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下:
ADD
的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。ADD
的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。
9、COPY
将文件或目录复制到镜像中。
拷贝文件或目录到容器中,跟ADD类似,但不具备自动下载或解压的功能。
复制指令,从上下文目录中复制文件或者目录到容器里指定路径。
格式:
COPY [--chown=<user>:<group>] <源路径1>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
[–chown=:]:可选参数,用户改变复制到容器内文件的拥有者和属组。
<源路径>:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:
COPY hom* /mydir/
COPY hom?.txt /mydir/
<目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。
10、VOLUME
为容器创建挂载点或声明卷。
指定容器挂载点到宿主机自动生成的目录或其他容器。
定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。
作用:
- 避免重要的数据,因容器重启而丢失,这是非常致命的。
- 避免容器不断变大。
格式:
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。
11、WORKDIR
设置后续指令的工作目录。
为 RUN、CMD、ENTRYPOINT、COPY 和 ADD 设置工作目录,就是切换目录。
指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。
docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在。
格式:
WORKDIR <工作目录路径>
12、其它命令
USER、ARG、ONBUILD、STOPSIGNAL、HEALTHCHECK、SHELL
99、实践总结
总结一
-
RUN
我们在使用
Dockfile
构建镜像时,就是通过RUN
来构建镜像层,每执行一行RUN
命令,就会构建一层镜像,其命令也是在该层镜像层中执行的。常用于安装软件包、下载文件等操作。执行时机
就是docker build
命令执行时候,比如:RUN yum install -y net-tools vim
-
CMD
提供容器启动时默认执行的命令或参数,如果运行容器时指定了其他命令,则 CMD 指定的默认命令会被覆盖。
记住
:- 只能有一个 CMD 指令在 Dockerfile 中生效(如果有多个,只有最后一个会起作用)。
- CMD 可以被 docker run 命令行参数覆盖。
CMD 支持三种格式,但最常用的是提供一个默认的可执行文件及其参数:
# 格式:CMD ["executable","param1","param2"]# 执行python文件 CMD ["python", "app.py"]
执行时机
就是docker run
命令执行时候。Tips
:我们知道,镜像就是由多个镜像层堆叠起来,就是由多个
RUN
命令执行构建的。我们启动容器就是在启动我们自己的服务,肯定只有最后的CMD
指令起作用,这样才有意义。再者,我们再执行
docker run
时候,其实也是在构建最后一层镜像,然后添加各种参数,比如-p
、-v
等等之类的。 -
ENTRYPOINT
配置容器启动时要运行的命令,并且这个命令不会被 docker run 命令行参数覆盖。它更倾向于定义容器的主要任务是什么。
- 类似于 CMD,但是更不容易被覆盖,旨在为容器提供一个固定的入口点。
- 使用
--entrypoint
标志可以在运行时覆盖 ENTRYPOINT。
形式:同样支持三种格式,最常见的是使用 JSON 数组来指定命令和参数:
ENTRYPOINT ["executable", "param1", "param2"]# 运行Java ENTRYPOINT ["java", "-jar", "/app/myapp.jar"]
对比
RUN
是在构建阶段执行命令,目的是修改镜像。CMD
提供了容器启动时默认执行的命令,但容易被覆盖。ENTRYPOINT
设定容器的主命令,不易被覆盖,适合明确容器的主要功能。
结合使用:可以将 ENTRYPOINT
和 CMD
结合使用,其中 ENTRYPOINT
定义应用本身,而 CMD
则定义传递给该应用的默认参数。这样,用户可以通过覆盖 CMD
来更改参数,但不改变应用本身。
FROM ubuntu
ENTRYPOINT ["echo", "Hello"]
CMD ["world"]
运行容器时不带额外参数会输出 “Hello world”,但如果运行时指定参数如 docker run <image> User
,则输出 “Hello User”。
总结二
-
COPY
简单地将文件或目录,从主机系统复制到镜像中指定的位置。
- 直观且易于理解,主要用于将本地文件复制到镜像中。
- 支持通配符(如
*.txt
),可以方便地选择多个文件。 不会
自动解压归档文件(如.tar.gz
文件)。- 对于简单的文件复制任务,推荐使用
COPY
。
-
ADD
除了可以完成与
COPY
相同的任务外,还提供了一些额外的功能。- 可以自动解压缩本地的
.tar
、.tar.gz
等格式的归档文件到目标位置(注意:对于远程 URL 下载的归档文件不会自动解压)。 - 支持通过 URL 添加远程文件到镜像中(不过通常不推荐这样做,因为这可能引起构建不稳定的问题,例如网络问题导致构建失败)。
- 如果源路径是一个可识别的 Git 仓库,
ADD
还会尝试克隆该仓库而不是直接复制内容(但同样,这不是最佳实践,建议明确控制依赖项)。
- 可以自动解压缩本地的
对比
- 功能差异:
COPY
更加直接,仅限于将本地文件复制到镜像内;而ADD
提供了更多高级功能,比如自动解压归档文件和支持从远程 URL 获取资源。 - 透明度和安全性:由于
ADD
的一些高级特性可能导致不可预期的行为(如自动解压),因此对于只需简单复制文件的情况,使用COPY
更加透明和安全。 - 性能考虑:
ADD
自动解压归档文件可能会增加不必要的处理时间,尤其是在不需要解压的情况下。
使用建议
-
当你需要将本地文件复制到 Docker 镜像时,除非需要
ADD
的特殊功能(如自动解压或从 URL 加载文件),否则优先选择COPY
。这样可以使 Dockerfile 更加简洁明了,并减少潜在的错误来源。 -
如果需要添加一个
.tar
或.tar.gz
归档文件并希望它被自动解压,或者想要从 URL 添加资源,那么ADD
就是更合适的选择。
总结三
-
EXPOSE
指定容器将监听的网络端口。这个指令并不会实际发布(port publish)端口。
它只是一个**
声明
**,告知使用者该镜像内的服务将会监听哪些端口。在运行时并不会因为这个声明应用就会开启这个端口的服务。
- 可以指定TCP或UDP协议,默认为TCP。
- 实际上暴露端口需要在运行容器时通过
-p
或--publish
参数来完成。
EXPOSE 80/tcp EXPOSE 53/udp
-
VOLUME
创建一个挂载点,用于绑定宿主机的一个目录到容器内,或者指定容器内的数据卷(data volume),主要用于持久化数据或共享数据。
比较重要
如果你仅在 Dockerfile 中使用
VOLUME
指令而不指定宿主机上的具体路径,Docker 会自动为该容器创建一个数据卷,并将其挂载到指定的容器目录。这种情况下,Docker 管理这个数据卷的位置,通常位于宿主机文件系统的特定区域(例如,在 Linux 上可能是/var/lib/docker/volumes/
)。这种方式的好处在于简化了数据持久化的过程,因为你不需要手动指定宿主机的路径,同时也能确保数据独立于容器的生命周期存在。
VOLUME ["/data"] # 或者多路径 VOLUME ["/data1", "/data2"]
- 数据卷允许你在容器之间共享和重用数据。
- 宿主机上的文件或目录可以被挂载到容器中指定的位置,从而使得数据能够在容器重启后仍然存在。
- 使用
-v
或--volume
在运行容器时指定具体的宿主机路径。
比如mysql:
VOLUME /var/lib/mysql
-
WORKDIR
设置工作目录。后续的 RUN, CMD, ENTRYPOINT, COPY 和 ADD 指令都会在这个目录下执行。如果目录不存在,WORKDIR 会帮你创建它。
WORKDIR /path/to/workdir
- 可以多次使用 WORKDIR,路径是相对前面的 WORKDIR 设置进行累加的,也可以直接给出绝对路径。
- 改变工作目录对于组织复杂的构建过程非常有用,并且可以让 Dockerfile 更加清晰易读。
WORKDIR /app RUN npm install COPY . . CMD ["npm", "start"]
完毕。
模板例子
Java服务模板
FROM openjdk:8-jre
MAINTAINER wangxf <1437211236@qq.com>
VOLUME /tmp
WORKDIR /app
COPY /target/your-app-name-server.jar your-app-name-serve.jar
EXPOSE 8090
# ENTRYPOINT ["java", "-Dspring.profiles.active=test", "-jar", "your-app-name-serve.jar"]
ENTRYPOINT ["java", "-jar", "your-app-name-serve.jar"]
实际例子:
FROM dockette/jdk8
MAINTAINER wangxf <1437211236@qq.com>
VOLUME /temp # 用于存放我们的附件
WORKDIR /app
COPY target/app.jar app.jar
EXPOSE 8090
ENTRYPOINT ["java", "-jar", "app.jar"]
# 把当前Dockerfile下target文件下的app复制到容器的工作目录
上面这种是制作镜像的流程,既是把 app.jar 做成一个镜像(虽然上面没有使用 RUN 命令,其实也可以使用),使用命令 docker build -t app:latest .
就可以生成镜像,使用 docker images 查看镜像。然后使用 docker run 去运行镜像,运行命令:
docker run -itd -p 8090:8090 -v /opt/server/ttt:/temp --name app app
# app 是我们生成的镜像名称,默认版本为:app:latest
其实还有一种就是,不使用 docker build -t app:latest .
命令生成镜像,直接运行 jdk8 的基础镜像,然后做目录挂载,直接运行我们的服务,这样就可以不用生成镜像,一定层面上减少存储空间的使用,这种情况就不能使用 Dockerfile 文件,必须使用 docker-compose.yml 文件,或者直接使用 docker run 命令。
-
docker-compose.yml 文件:该文件是运行多个容器服务,或者说管理容器服务,是使用镜像,具体见
Compose介绍
。 -
直接使用 docker run 命令:
docker run -itd -p 8090:8090 -v /opt/server/ttt:/temp -v /opt/server/target:/app -w /app --name app dockette/jdk8 java -jar app.jar
上面就是直接运行我们的 dockette/jdk8 镜像,并不是我们做的镜像,这种方式也可以;
当然还是推荐使用 docker-compose.yml 文件,如果有特殊需求可以先制作镜像(比如安装环境),然后再使用镜像。
完毕。