It all starts with a Dockerfile. (docker万物始于此)
这是官网对Dockerfile的描述。
系列教程
在介绍Dockerfile之前,推荐小白去恶补一下作者之前发的文章,初学者选学,可以留个赞再走,求求了!!
标题 | 链接 |
---|---|
SpringBoot应用:Docker与Kubernetes全栈实战秘籍 | juejin.cn/post/744068... |
Docker入门之Windows安装Docker初体验 | juejin.cn/post/741565... |
从零开始玩转 Docker:一站式入门指南,带你快速掌握镜像、容器与仓库 | juejin.cn/post/740318... |
面试官让你介绍一下docker,别再说不知道了 | juejin.cn/post/740283... |
Windows 10环境用Docker发布SpringBoot项目 | juejin.cn/post/724197... |
前言
Dockerfile 是一个文本文件,其中包含了若干个命令,用户可以调用这些命令来构建一个镜像。通过这个文件,开发者能够定义应用程序运行环境的所有细节,从基础操作系统的选择到需要安装的软件包,再到启动服务所需的配置。
Dockerfile 不仅是自动化构建的基础,也是实现持续集成和持续部署(CI/CD)流程的关键组成部分。它使得开发团队能够在一致的环境中开发、测试和部署应用,从而减少"在我机器上能跑"的问题。此外,Dockerfile 促进了微服务架构的发展,让每个服务都可以独立打包成容器,易于管理和扩展。
Dockerfile 案例
Dockerfile 有个和其他文件与众不同的点,就是他没有后缀,他全部的名字就叫做Dockerfile 而不是Dockerfile.txt 或者Dockerfile.yml
先来看一个Dockerfile 的案例(之前作者参加gitee比赛的时候成功运行在比赛服务器上的Dockerfile 示例):
csharp
# 使用指定的基础镜像
FROM registry.gitee-ai.local/base/iluvatar-corex:3.2.0-bi100
# 设置工作目录
WORKDIR /app
# 添加 Java 环境
RUN apt-get update && \
apt-get install -y openjdk-8-jdk && \
apt-get clean
# 复制当前目录下的所有文件到容器内的/app目录下
COPY . /app
EXPOSE 7860
# 指定启动命令
ENTRYPOINT ["java","-jar","/app/mergevideo-0.0.1-SNAPSHOT.jar"]
- FROM registry.gitee-ai.local/base/iluvatar-corex:3.2.0-bi100 : 使用指定的基础镜像
iluvatar-corex:3.2.0-bi100
从私有仓库registry.gitee-ai.local
中拉取。 - WORKDIR /app : 设置工作目录为
/app
。 - RUN apt-get update && apt-get install -y openjdk-8-jdk && apt-get clean: 更新包列表并安装 OpenJDK 8 JDK,然后清理缓存。
- COPY . /app : 将当前目录下的所有文件复制到容器内的
/app
目录下。 - EXPOSE 7860: 声明容器监听 7860 端口。
- ENTRYPOINT ["java","-jar","/app/mergevideo-0.0.1-SNAPSHOT.jar"] : 指定容器启动时运行的命令,使用 Java 启动
/app/mergevideo-0.0.1-SNAPSHOT.jar
应用。

而作者写的这个dockerFile几乎覆盖了官方文档中说到了主要几个命令。这里介绍一下: docker build
命令。
docker build
命令可以从 Dockerfile 构建一个新的 Docker 镜像。
docker build
基本语法
sh
docker build [OPTIONS] PATH | URL | -
常用选项
- -t, --tag : 为生成的镜像指定标签(通常是
name:tag
格式)。 - -f, --file : 指定 Dockerfile 的路径(默认是当前目录下的
Dockerfile
)。 - --build-arg: 设置构建时的变量。
- --no-cache: 构建时不使用缓存。
- --rm: 构建完成后删除中间容器(默认是启用的)。
- --pull: 始终尝试从远程仓库拉取最新版本的基础镜像。
- -q, --quiet: 只输出构建的镜像 ID。
示例
1. 基本构建
假设你的 Dockerfile 位于当前目录下,并且你想为生成的镜像打上标签 my-app:latest
,可以使用以下命令:
sh
docker build -t my-app:latest .
这里的 .
表示当前目录。
2. 指定 Dockerfile 路径
如果你的 Dockerfile 不在当前目录下,可以使用 -f
选项指定其路径:
sh
docker build -t my-app:latest -f path/to/Dockerfile .
3. 设置构建时变量
你可以使用 --build-arg
选项传递构建时变量。假设你的 Dockerfile 中使用了 ARG
指令:
dockerfile
ARG BUILD_DATE
RUN echo "Build date: $BUILD_DATE"
你可以这样构建镜像:
sh
docker build -t my-app:latest --build-arg BUILD_DATE=$(date) .
4. 不使用缓存
如果你希望构建时不使用缓存,可以使用 --no-cache
选项:
sh
docker build -t my-app:latest --no-cache .
5. 删除中间容器
默认情况下,构建完成后会删除中间容器。如果你想明确指定这一点,可以使用 --rm
选项:
sh
docker build -t my-app:latest --rm .
6. 拉取最新基础镜像
如果你希望在构建前始终从远程仓库拉取最新版本的基础镜像,可以使用 --pull
选项:
sh
docker build -t my-app:latest --pull .
7. 静默输出
如果你只想输出最终的镜像 ID,可以使用 --quiet
或 -q
选项:
sh
docker build -q -t my-app:latest .
完整示例
假设你有一个复杂的 Dockerfile,并且需要设置多个构建时变量,可以这样构建:
sh
docker build -t my-app:latest \
--build-arg BUILD_DATE=$(date) \
--build-arg VERSION=1.0.0 \
--no-cache \
--pull \
-f path/to/Dockerfile \
.
我们的build命令都会根据DockerFile 里面的内容来一步一步构建我们的镜像,那么介绍了build命令,再来认识一下build之后构建的镜像如何运行
docker run
运行一个 Docker 容器:
shell
docker run -d -p 8999:8999 aijava:1.0
docker run
:这是运行 Docker 容器的命令。-d
:表示在后台运行容器。-p 8999:8999
:将宿主机的 8999 端口映射到容器的 8999 端口。aijava:1.0
:这是要运行的镜像名称和标签。
最后,我们就来认识一下DockerFile里面的命令所代表的意思了
DockerFile 入门
Docker 镜像由层组成。每个层都是构建的结果 指令。层按顺序堆叠,每个层都是 表示应用于上一层的更改的增量。

syntax指定构造器
bash
# syntax=docker/dockerfile:1
# 使用指定的基础镜像
FROM registry.gitee-ai.local/base/iluvatar-corex:3.2.0-bi100
这个是DockerFile中一个特殊的注释
# syntax=docker/dockerfile:1
注释告诉 Docker 使用 BuildKit 构建器来解析和构建 Dockerfile。BuildKit 是 Docker 的下一代构建工具,旨在提高 Docker 镜像构建的性能和可靠性。它引入了许多新的特性和改进,使其成为比传统 Docker 构建器更强大的工具。
写了这个注释可以让我们利用 BuildKit 的高级特性,还可以提高构建性能和灵活性。如果不写这个注释,Docker 将使用传统的构建器构建镜像
FROM基础镜像
bash
# 使用指定的基础镜像
FROM registry.gitee-ai.local/base/iluvatar-corex:3.2.0-bi100
这块就是告诉docker以 这个镜像为基础构建一个新镜像,大家可以这么理解,需要装修得要毛坯房吧,而这个基础的镜像就是毛坯房,一般我们使用 FROM Linux系统
这样的写法,因为我们自己构建的镜像其实本质也是运行在一个系统中的,就像我们上面的案例:
csharp
# 使用指定的基础镜像
FROM registry.gitee-ai.local/base/iluvatar-corex:3.2.0-bi100
# 设置工作目录
WORKDIR /app
# 添加 Java 环境
RUN apt-get update && \
apt-get install -y openjdk-8-jdk && \
apt-get clean
在ubuntu的镜像基础上,安装Java 环境,而这里的结构图就像这样:

这样是不是大家就很好理解这句话了:层按顺序堆叠,每个层都是 表示应用于上一层的更改的增量(Docker images consist of layers. Each layer is the result of a build instruction in the Dockerfile. Layers are stacked sequentially, and each one is a delta representing the changes applied to the previous layer.)。
而 registry.gitee-ai.local/base/iluvatar-corex:3.2.0-bi100 这个镜像就是gitee大赛中官方打的一个新镜像,类比我们结构图中的 新镜像A
RUN命令
就是作者案例中的
bash
# 添加 Java 环境
RUN apt-get update && \
apt-get install -y openjdk-8-jdk && \
apt-get clean
这个命令会在上面的镜像的基础上运行一些命令,例如作者这里写的就是安装jdk环境,因为再官方提供的那个镜像是没有jdk环境的,所以运行了这个命令之后,在这个镜像中就有了Java应用所需要的jdk环境了
#注释
这个就对应开发者来说太熟悉了,就是注释,例如上面的 # 添加 Java 环境
写了这个dockerfile在构建的过程中就会跳过这个注释,很好理解
COPY复制
这个语法是会将你与dockerfile同路径的文件复制到一个新的目录下面,而新的目录你可以理解为linux中的目录,例如 /
就是可以理解为新镜像中的根目录,例如上面作者的案例就是把所有文件复制到 /app
目录下
bash
# 复制当前目录下的所有文件到容器内的/app目录下
COPY . /app
如果你要指定某个文件的话,例如jar包可以这样写:
bash
COPY ./masiyi.jar /app
# 或者这样
COPY masiyi.jar /app
ENV设置环境变量
这个命令设置环境变量,如果熟悉shell命令的同学有福了,就类似那种的设置
bash
VARIABLE_NAME=value
而dockerfile使用 ENV
作为标识,例如在生产过程中你可以这样写:
bash
ENV MAX_HEAP 2048m
ENV MIN_HEAP 1024m
CMD java -jar -Xms${MIN_HEAP} -Xmx${MAX_HEAP} masiyi.jar
这样就可以使用变量来控制jvm的大小了
EXPOSE暴露端口
这块也很好理解,如果你是web服务,需要一个端口,这里只需要和你的web服务保持一样就好了

CMD启动应用程序
这里是容器启动时所调用的命令,例如上面的
bash
CMD java -jar -Xms${MIN_HEAP} -Xmx${MAX_HEAP} masiyi.jar
最后你在启动这个镜像的时候就会构建一个容器,从而执行这个命令,这样一个jar包就被启动起来了。但是很多同学容易把CMD和上面的RUN搞混淆,我们来了解一下他们有什么区别
CMD
和 RUN
是 Dockerfile 中用于指定命令的两个不同指令,它们在 Docker 镜像构建和容器运行过程中扮演着不同的角色。
CMD
和 RUN
的区别
1. 执行时机
-
RUN
:- 执行时机 : 在 构建镜像时执行。
- 作用: 用于在构建过程中执行命令,通常用于安装软件、配置环境等。
- 结果: 执行的结果会被保存到镜像的层中,成为镜像的一部分。
-
CMD
:- 执行时机 : 在 启动容器时执行。
- 作用: 用于指定容器启动时默认执行的命令或参数。
- 结果: 只在容器启动时生效,不会影响镜像的构建过程。
2. 命令执行方式
-
RUN
:- 每次执行
RUN
指令时,Docker 会创建一个新的中间层,执行该命令,并将结果保存到这个层中。多个RUN
指令会导致多个层的创建。 - 例如,
RUN apt-get update && apt-get install -y curl
会在构建镜像时安装curl
,并且这个安装结果会被保存到镜像中。
- 每次执行
-
CMD
:CMD
指定的命令只在容器启动时执行,不会在构建镜像时执行。- 如果你在运行容器时指定了其他命令(例如通过
docker run
的命令行参数),CMD
中的命令会被覆盖。 - 例如,
CMD ["java", "-jar", "app.jar"]
会在容器启动时运行java -jar app.jar
,但如果你在docker run
时指定了其他命令,CMD
中的命令将不会执行。
3. 可覆盖性
-
RUN
:- 不可覆盖 :
RUN
指令在构建镜像时执行,生成的层是镜像的一部分,无法在运行容器时被覆盖或修改。
- 不可覆盖 :
-
CMD
:-
可覆盖 :
CMD
指令可以在运行容器时被覆盖。你可以通过docker run
命令行参数指定不同的命令来替代CMD
中的默认命令。 -
例如,如果你有一个 Dockerfile 如下:
dockerfileCMD ["echo", "Hello, World!"]
你可以通过以下命令覆盖
CMD
:shdocker run my-image echo "Hello, Docker!"
-
4. 多条指令的行为
-
RUN
:-
你可以有多个
RUN
指令,每个RUN
指令都会创建一个新的层。为了减少镜像的层数,建议将多个相关的命令合并为一个RUN
指令,使用&&
连接多个命令。 -
例如:
dockerfileRUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
-
-
CMD
:-
你只能有一个
CMD
指令。如果 Dockerfile 中有多个CMD
指令,只有最后一个CMD
会生效。 -
例如:
dockerfileCMD ["echo", "First command"] CMD ["echo", "Second command"] # 只有这一条会生效
-
示例
使用 RUN
安装软件
dockerfile
# 安装 curl
RUN apt-get update && apt-get install -y curl
使用 CMD
启动应用程序
dockerfile
# 启动 Java 应用
CMD ["java", "-jar", "app.jar"]
ENTRYPOINT最终启动命令
不知道大家注意到一个细节没有,那就是dockerfile中的命令基本都是大写,那么最后一个要介绍一个命令就是ENTRYPOINT
ENTRYPOINT
是 Dockerfile 中的一个重要指令,用于定义容器启动时执行的主命令。与 CMD
不同,ENTRYPOINT
提供了一种更灵活的方式来指定容器的默认行为,并且可以与 CMD
结合使用以提供更多的灵活性。
例如作者上面的案例:
shell
# 指定启动命令
ENTRYPOINT ["java","-jar","/app/mergevideo-0.0.1-SNAPSHOT.jar"]
这个就会在容器启动的时候执行 java -jar /app/mergevideo-0.0.1-SNAPSHOT.jar
这是他的一个最基础的用法,但是ENTRYPOINT
和 CMD
可以一起使用,CMD
提供的参数会被传递给 ENTRYPOINT
指定的命令。这种组合非常有用,尤其是当你希望容器在启动时执行一个特定的命令,但允许用户通过 docker run
提供额外的参数时。
css
# 使用 ENTRYPOINT 和 CMD 启动 Java 应用
ENTRYPOINT ["java", "-jar"]
CMD ["/app/mergevideo-0.0.1-SNAPSHOT.jar"]
-
解释:
ENTRYPOINT ["java", "-jar"]
指定了容器启动时的主命令是java -jar
。CMD ["/app/mergevideo-0.0.1-SNAPSHOT.jar"]
提供了默认的参数/app/mergevideo-0.0.1-SNAPSHOT.jar
。- 当你运行
docker run my-image
时,容器会执行java -jar /app/mergevideo-0.0.1-SNAPSHOT.jar
。
-
覆盖
CMD
:-
如果你在
docker run
时提供了其他参数,CMD
中的参数会被覆盖。 -
例如:
arduinodocker run my-image another-app.jar
这将执行
java -jar another-app.jar
-
ENTRYPOINT
与 CMD
的区别
特性 | ENTRYPOINT |
CMD |
---|---|---|
执行时机 | 容器启动时执行 | 容器启动时执行,默认参数可以被覆盖 |
是否可覆盖 | 不可覆盖,除非使用 --entrypoint 选项 |
可以通过 docker run 命令行参数覆盖 |
用途 | 定义容器的主命令,适合创建可执行容器 | 提供默认参数或命令,适合提供默认行为 |
与 docker run 的关系 |
docker run 参数会作为 ENTRYPOINT 的参数传递 |
docker run 参数会覆盖 CMD 中的默认参数 |
总结
至此,我们已经学到了dockerfile中最常用的命令,至于其他的都是作为特殊需求或者更定制化的dockerfile脚本去使用,而这篇文章中的命令基本可以作为企业中的dockerfile使用,当然,dockerfile的语法远远不值这么简单,那么更多的信息可以参考官方文档:docs.docker.com/reference/d...
