Dockerfile 文件及指令详解

什么是Dockerfile 文件

  • Dockerfile 文件是用于构建 docker 镜像的脚本文件,由一系列的指令构成。
  • 通过 docker build 命令构建镜像时,Dockerfile 文件 中的指令 会由上到下执行,每条 指令都将会构建出一个镜像层,这就是镜像的分层。
  • 因此,指令越多,层次就越多,构建的镜像就越多,效率就会越低。
    所以,在定义Dockerfile时,能在一个指令内完成的动作,就不要分为两条。

文件名

通常情况下,该文件名为 Dockerfile

docker build 构建命令

【注意】 : 命令最后有个点 . , 表示 在执行该命令的目录下 寻找 Dockerfile 文件。
这个点. 很关键。

shell 复制代码
$ docker build -t 镜像名称:tag . 

指令

基本介绍

  • 指令大小写不敏感,但习惯上全用大写;
  • 指令后至少会携带一个参数;
  • # 号开头的,表示这一行是注释行。

FROM 指令

语法 : FROM <image>[:tag]

例如 : FROM centos:7.9.2009

复制代码
作用 :指定构建新镜像所基于的基础镜像。
* 必须为 Dockerfile 文件的第一条。
* 若 镜像的标签 tag 不写的话,默认就是 latest。
* 若 镜像本地没有的话,会先执行下载。

补充 : 一个scratch 镜像。
* scratch 镜像是一个空镜像,是所有镜像的 Base 镜像。
* scratch 镜像只能在Dockerfile 中被继承,不能pull拉取,不能run,也没有tag。
* scratch 是一个保留字,用户不能作为自己的镜像名称使用。
* 使用 scratch 作为基础镜像意味着你从零开始构建你的镜像,没有预装任何文件、库或运行时环境。
* 这通常用于创建极其精简的容器镜像,尤其是对于静态编译的二进制文件来说非常有用。

MAINTAINER 指令

语法 : MAINTAINER 作者名字 作者邮箱

例如 : MAINTAINER northcastle norcastle@123.com

复制代码
作用 : 标注镜像维护者的姓名和邮箱。
但是,官方现在已经不推荐使用此指令,而是用 【LABEL】 指令代替。

LABEL 指令

语法 : LABEL key1=value1 key2=value2 ...

例如 : LABEL author=northcastle email=northcastle@123.com

复制代码
作用 : 给镜像添加元数据(metadata)。
特点 : 以键值对的方式定义,可以自定义任何的信息;信息会被包含在镜像的json文件中。
查看 : 通过 docker inspect 命令查看镜像元信息时可以查看到自定义的内容。

RUN 指令

语法1 : RUN shell-command

例如 : RUN yum install -y vim
语法2 : RUN ["executable", "param1", "param2", ...]

例如 : RUN ["yum","install","-y","vim"]

复制代码
作用 : 在基于当前构建阶段的镜像上执行命令,并将执行的结果(包括文件系统的变化)保存为新的镜像层。

补充 : 基于 centos 构建镜像时,使用 yum 命令报错的解决方案
在Dockerfile 中添加如下内容:

RUN cd /etc/yum.repos.d/
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' 	/etc/yum.repos.d/CentOS-* 
RUN yum makecache
RUN yum update -y

但是,上述方案有一个缺点:镜像层太大了,非常的臃肿。

WORKDIR 指令

语法 :WORKDIR 工作路径

例如 : WORKDIR /usr/local

复制代码
作用 : 为容器内的文件系统设置一个基础目录,使得后续命令可以基于此目录进行操作。
特点 :
 * 如果指定的目录不存在,WORKDIR 会自动创建该目录。
 * 这个目录会被用作所有 RUN、CMD、ENTRYPOINT、COPY 和 ADD 指令的上下文路径。
 * 可以写多个 WORKDIR 指令。
 * 当写多个时,第一个为绝对路径,后续用相对路径会基于第一个的绝对路径。
 * docker run 的 -w 参数 可以覆盖这个指令置顶的默认工作目录。

ENV 指令

语法1 :ENV key value 单个定义

语法2 :ENV key1=value1 key2=value2 ... 同时定义多个

复制代码
作用:定义环境变量。
 1、在Dockerfile 中可以被后续的指令直接引用,减少重复代码。
 2、运行容器时,可以通过 -e 参数动态的传入环境变量的值,从而使配置更加灵活。
 
 例如:
   ENV wd=/usr/local
   WORKDIR $wd
   CMD ["sh","-c","echo $wd"]

下面是运行容器时覆盖的例子:
* 只有一个环境变量需要覆盖时:
docker run --name xxx -e wd=xxx -it imageName:tag

* 当有多个需要覆盖时,就写 多个 -e k1=v1 -e k2=v2 
docker run --name xxx -e wd=xxx -e wd2=xxx -e wd3=xxx ... -it imageName:tag

注意 : 
-e 参数,并不是所有情况下都能生效。
只有是与容器相关的才会生效,例如 CMD 指令是容器运行的程序入口,会生效。
但 WORKDIR 指令是在构建镜像的时候用,这个对于容器来说就不会生效。

ARG 指令

语法 : ARG 变量名[=默认值]

复制代码
作用 : 定义在构建过程中使用的变量。
使用方式 : 
* 如果有多个变量,则需要写`多个ARG` 指令即可;
* 可以出现在FROM 指令之前,常用于指定 FROM 的基础镜像的版本;
* ARG 定义的变量只能在构建阶段生效,容器运行后不可用。
* 构建时 使用 【--build-arg 变量名=值】 的方式 指定 变量的值,如果有多个变量,则重复写多个即可。

例如 Dockerfile 如下 : 
FROM  centos:centos7.9.2009
ARG wd=/usr # 定义变量
WORKDIR $wd
CMD ["/bin/bash"]

构建命令 : 
docker build -t mycentos:1.0 --build-arg wd=/usr/local . 
此命令即将 变量 wd 的值覆盖成 [/usr/local] 

ADD 指令

语法1 :ADD src dest

语法2 :ADD ["src","dest"]

复制代码
作用: 将 构建上下文中的 文件/目录(src) 复制到 容器中指定的路径中 (dest)。

注意:
* src 可以是URL,会自动下载该路径的资源并添加到镜像中;
* src 可以是压缩文件,复制到容器中后会自动解压;
* src 可以是目录,但不建议是目录,因为会把该目录下所有的内容全都复制到镜像中;
* dest 是一个绝对路径,最后要添加上斜杠[/],表示 一个目录;
* dest 如果没有[/] 则表示将原文件 复制到容器中并重命名为 dest 的名称。

例如 : 
ADD  hello.java /usr/local/javafile/
将 Dockerfile 文件所在目录下的 hello.java 文件 复制到 镜像的 /usr/local/javafile/ 目录下。

特别注意:
【构建上下文限制】:
 * 当您构建一个 Docker 镜像时,您通过 docker build 命令指定一个构建上下文(通常是包含 Dockerfile 的目录)。
 * Docker 客户端会将这个上下文发送给 Docker 守护进程,这意味着 ADD 和 COPY 指令只能访问该上下文内的文件。
 * 因此,虽然 <src> 可以采用相对路径形式来指向上下文内的文件或目录,但它不能直接引用上下文之外的绝对路径。
 
【相对路径 vs 绝对路径】:
* 在 Dockerfile 中,ADD 指令的 <src> 参数通常使用相对于构建上下文的路径。
* 如果您尝试提供一个位于构建上下文外部的绝对路径,比如 /home/user/file.txt,Docker 将无法找到该文件,并导致构建失败。

COPY 指令

语法1 :COPY src dest

语法2 :COPY ["src","dest"]

复制代码
作用 :从主机的文件系统中复制文件或目录到 Docker 镜像中的指定位置。

注意 :
* 不支持自动解压缩 tar 文件;
* 不支持从 URL 下载文件;
* 行为单一,推荐使用。

例如 : 
COPY  .  /usr/loca/hostfile/
将 构建上下文 目录中的所有文件 全都复制到 /usr/local/hostfile/ 目录中去

COPY f1 f2 f3 /usr/local/files/
COPY ["f1","f2","f3","/usr/local/files/"]
支持写多个文件,中间用空格隔开。

COPY *.txt /usr/local/allfiles/
支持通配符 *

EXPOSE 指令

语法1 : EXPOSE port1 port2 ... 默认以 TCP 协议的方式 声明端口号 (可以写多个)

语法2 :EXPOSE port/protocol ... 以 指定protocol协议的方式 声明端口号 (可以写多个)

复制代码
作用 : 声明容器运行时监听的网络端口。
注意 : 
* 它并不直接将端口暴露给主机,而是作为一种文档化手段,告知使用者该容器希望在运行时开放哪些端口。
* 真正的端口映射 需要在 docker run 时 通过 -p 参数指定
*   例如 : docker run --name t1 -p 8081:8080 -d tomcat:8.5.49
*      将主机端口 8081 与 容器端口 8080 进行映射,这样访问主机的 8081 端口即可访问到容器的 8080端口。

CMD 指令(*)

复制代码
作用:
【提供默认的容器启动命令】:
   当用户没有通过 docker run 命令指定任何命令时,将使用 CMD 提供的默认命令来启动容器。
【为 ENTRYPOINT 提供默认参数】:
  如果 Dockerfile 中同时定义了 ENTRYPOINT 和 CMD,那么 CMD 的值会被作为参数传递给 ENTRYPOINT。
  这种组合非常强大,因为它允许你创建一个具有固定入口点(如脚本或应用)的基础镜像,
  并且允许最终用户覆盖默认参数而不必重新定义整个命令。

语法1 :CMD command param1 param2...

例如 :CMD cal -y 2005
command 是 shell 命令, 会在 /bin/sh -c 中运行命令,这意味着它会在一个 shell 环境中执行。
会被 docker run 后面的 命令覆盖,但不支持拼接 ducker run 后面的参数
语法2 : CMD ["executable","param1","param2" ...]

例如:CMD ["cal","-y","2005"]
executable 是可执行文件的路径,后面的是参数;
会被 docker run 后面的 命令覆盖,但不支持拼接 ducker run 后面的参数
语法3 : CMD ["param1","param2",...]

例如:

ENTRYPOINT ["echo"]

CMD ["hello","world"]

ENTRYPOINT 指令中声明的 可执行脚本 提供默认参数
会被 docker run 命令后面的 参数完全覆盖,不支持拼接参数。

ENTRYPOINT 指令(*)

复制代码
作用:配置容器启动时要运行的命令。
* 并且这个命令不容易被覆盖,除非显式地使用 --entrypoint 标志。

与 CMD 指令结合使用时,ENTRYPOINT 可以提供固定的入口点,而 CMD 则可以用来指定默认参数,这些参数可以在运行容器时被用户提供的参数覆盖。
语法1 :ENTRYPOINT command param1 param2...

例如 :ENTRYPOINT cal -y 2005
command 是 shell 命令, 会在 /bin/sh -c 中运行命令,这意味着它会在一个 shell 环境中执行。
会被 docker run 后面的 命令覆盖,但不支持拼接 ducker run 后面的参数
语法2 : ENTRYPOINT ["executable","param1","param2" ...]

例如:ENTRYPOINT ["cal","-y","2005"]
executable 是可执行文件的路径,后面的是参数;
支持 docker run 后面的参数 与 executable 命令进行拼接
【组合 CMD 指令一起使用 】:

例如:

ENTRYPOINT ["echo"]

CMD ["hello","world"]

ENTRYPOINT 指令中声明的 可执行脚本 提供默认参数
会被 docker run 命令后面的 参数完全覆盖,不支持拼接参数。

CMD & ENTRYPOINT 的小结

ONBUILD 指令

语法 : ONBUILD [INSTRUCTION]
[INSTRUCTION] :可以是任何有效的 Dockerfile 指令,如 RUN, COPY, ADD, ENV, 等等。

但 ONBUILD 不支持 FROM, MAINTAINER, ONBUILD 这几个指令作为其参数。

例如 : ONBUILD RUN yum installl -y vim

复制代码
作用 :
* 在当前镜像被用作其他镜像的基础镜像时,触发特定的操作。
* 当一个镜像(我们称它为父镜像)包含 ONBUILD 指令,并且另一个 Dockerfile 使用这个父镜像作为基础镜像(通过 FROM 指令),
  那么在执行 FROM 指令后,立即触发并执行该父镜像中定义的所有 ONBUILD 后面的指令。

VOLUME 指令

语法1 : VOLUME container_path

语法2 : VOLUME ["container_path1","container_path2"...]

复制代码
作用 : 
  在容器内定义多个持久化的挂载点,用于数据的持久化。
  但是,这种方式并不能指定宿主机上对应的持久化目录。

查看 :  
  通过 [docker inspect 容器ID] 命令,可以查看容器的详细信息,
 其中,有一个 Mounts 数组,可以查看到对应的持久化数据卷的详细信息。

补充 :

更推荐使用 docker run --name c1 -v host_path:container_path image:tag 中的 -v 参数的方式进行数据卷的挂载。

这种方式更直观。

build cache

概述

  • Docker Daemon 通过Dockerfile 构建镜像时,当发现即将新构建出的镜像层与本地已经存在的某个镜像层重复时,默认会复用已经存在的镜像层。这种机制称为 docker build cache 机制
  • 此机制不仅加快了镜像的构建过程,同时也大量节省了Docker宿主机的空间。
  • docker build cache 并不是占用内存的 cache,而是一种对磁盘中相应镜像层的检索复用机制。所以无论是关闭Docker引擎,还是重启Docker宿主机,只要该镜像层存在就会复用。

失效的情况

复制代码
1、Dockerfile 本身发生了变化 : 
  从发生变化指令开始,后面的所有指令层全部失效。
  (因为后面的是基于前面的层)
2、Dockerfile 文件内容未变,但是 ADD/COPY 指令所添加/复制的源文件发生了变化,则后续的全部失效。
 (即文件系统变了)
3、RUN 指令的外部依赖发生改变。(也是相当于文件系统变了)
4、明确指定不使用 build cache [--no-cache 参数]
  构建命令 :docker build --no-cache -t  xxx:tag
  清理cache : docker system prune
相关推荐
鹿先森AI探索之路6 小时前
本地部署Dify教程
人工智能·docker·ai
贺贺丿9 小时前
Docker4-容器化企业级应用
linux·nginx·docker·云原生·eureka·tomcat·ssh
Linux运维技术栈10 小时前
从零构建 Node20+pnpm+pm2 环境镜像:基于 Dockerfile 的两种方案及持久化配置指南
运维·docker·容器
chen1108____13 小时前
用 Docker 一键部署 Flask + Redis 微服务
redis·docker·flask
菜鸟是大神16 小时前
【已解决】docker: Error response from daemon: Get “https://registry-1.docker.io/v2/“: net/http: request c
http·docker·容器
kong@react17 小时前
docker安装 Elasticsearch、Kibana、IK 分词器
elasticsearch·docker·jenkins
MurphyStar17 小时前
Ubuntu22.04.5 LTS安装与使用Docker
运维·docker·容器
贺贺丿17 小时前
Docker2-容器应用工具及docker命令
linux·运维·docker·容器·自动化·云计算
亿刀18 小时前
【学习VPN之路】路由表
android·docker