文章目录
- 前言
- Dockerfile 案例
- docker build
-
- 1. 基本构建
- 2. 指定 Dockerfile 路径
- 3. 设置构建时变量
- 4. 不使用缓存
- 5. 删除中间容器
- 6. 拉取最新基础镜像
- 7. 静默输出
- 完整示例
- docker run
- DockerFile 入门
-
- syntax指定构造器
- FROM基础镜像
- RUN命令
- 注释
- COPY复制
- ENV设置环境变量
- EXPOSE暴露端口
- CMD启动应用程序
- CMD 和 RUN的区别
- ENTRYPOINT最终启动命令
- ENTRYPOINT 与 CMD 的区别
- 总结
前言
Dockerfile
是一个文本文件,其中包含了若干个命令,用户可以调用这些命令来构建一个镜像。通过这个文件,开发者能够定义应用程序运行环境的所有细节,从基础操作系统的选择到需要安装的软件包,再到启动服务所需的配置。
Dockerfile
不仅是自动化构建的基础,也是实现持续集成和持续部署(CI/CD
)流程的关键组成部分。它使得开发团队能够在一致的环境中开发、测试和部署应用,从而减少“在我机器上能跑”的问题。此外,Dockerfile
促进了微服务架构的发展,让每个服务都可以独立打包成容器,易于管理和扩展。
Dockerfile 案例
Dockerfile
有个和其他文件与众不同的点,就是他没有后缀,他全部的名字就叫做Dockerfile
而不是Dockerfile.txt
或者Dockerfile.yml
先来看一个Dockerfile
的案例:
# 使用指定的基础镜像
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
基本语法
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
,可以使用以下命令:
docker build -t my-app:latest .
这里的 . 表示当前目录。
2. 指定 Dockerfile 路径
如果你的 Dockerfile
不在当前目录下,可以使用 -f 选项指定其路径:
docker build -t my-app:latest -f path/to/Dockerfile .
3. 设置构建时变量
你可以使用 --build-arg
选项传递构建时变量。假设你的 Dockerfile
中使用了 ARG
指令:
ARG BUILD_DATE
RUN echo "Build date: $BUILD_DATE"
你可以这样构建镜像:
docker build -t my-app:latest --build-arg BUILD_DATE=$(date) .
4. 不使用缓存
如果你希望构建时不使用缓存,可以使用 --no-cache
选项:
docker build -t my-app:latest --no-cache .
5. 删除中间容器
默认情况下,构建完成后会删除中间容器。如果你想明确指定这一点,可以使用 --rm
选项:
docker build -t my-app:latest --rm .
6. 拉取最新基础镜像
如果你希望在构建前始终从远程仓库拉取最新版本的基础镜像,可以使用 --pull
选项:
docker build -t my-app:latest --pull .
7. 静默输出
如果你只想输出最终的镜像 ID
,可以使用 --quiet
或 -q
选项:
docker build -q -t my-app:latest .
完整示例
假设你有一个复杂的 Dockerfile
,并且需要设置多个构建时变量,可以这样构建:
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 容器
:
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指定构造器
# 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基础镜像
# 使用指定的基础镜像
FROM registry.gitee-ai.local/base/iluvatar-corex:3.2.0-bi100
这块就是告诉docker
以 这个镜像为基础构建一个新镜像,大家可以这么理解,需要装修得要毛坯房吧,而这个基础的镜像就是毛坯房,一般我们使用 FROM Linux系统
这样的写法,因为我们自己构建的镜像其实本质也是运行在一个系统中的,就像我们上面的案例:
# 使用指定的基础镜像
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
这个镜像就是一个新镜像,类比我们结构图中的 新镜像A
RUN命令
就是案例中的
# 添加 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目录
下
# 复制当前目录下的所有文件到容器内的/app目录下
COPY . /app
如果你要指定某个文件的话,例如jar包
可以这样写:
COPY ./masiyi.jar /app
# 或者这样
COPY masiyi.jar /app
ENV设置环境变量
这个命令设置环境变量,如果熟悉shell
命令的同学有福了,就类似那种的设置
VARIABLE_NAME=value
而dockerfile
使用 ENV
作为标识,例如在生产过程中你可以这样写:
ENV MAX_HEAP 2048m
ENV MIN_HEAP 1024m
CMD java -jar -Xms${MIN_HEAP} -Xmx${MAX_HEAP} masiyi.jar
这样就可以使用变量来控制jvm
的大小了
EXPOSE暴露端口
这块也很好理解,如果你是web
服务,需要一个端口,这里只需要和你的web
服务保持一样就好了
CMD启动应用程序
这里是容器启动时所调用的命令,例如上面的
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
如下:
CMD ["echo", "Hello, World!"]
你可以通过以下命令覆盖 CMD
:
docker run my-image echo "Hello, Docker!"
4. 多条指令的行为
RUN:
- 你可以有多个
RUN
指令,每个RUN
指令都会创建一个新的层。为了减少镜像的层数,建议将多个相关的命令合并为一个RUN
指令,使用&&
连接多个命令。 - 例如:
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
CMD:
你只能有一个
CMD
指令。如果Dockerfile
中有多个CMD
指令,只有最后一个CMD
会生效。例如:
CMD ["echo", "First command"]
CMD ["echo", "Second command"] # 只有这一条会生效
示例
使用 RUN
安装软件
# 安装 curl
RUN apt-get update && apt-get install -y curl
使用 CMD
启动应用程序
# 启动 Java 应用
CMD ["java", "-jar", "app.jar"]
ENTRYPOINT最终启动命令
不知道大家注意到一个细节没有,那就是dockerfile
中的命令基本都是大写,那么最后一个要介绍一个命令就是ENTRYPOINT
ENTRYPOINT
是 Dockerfile
中的一个重要指令,用于定义容器启动时执行的主命令。与 CMD
不同,ENTRYPOINT
提供了一种更灵活的方式来指定容器的默认行为,并且可以与 CMD
结合使用以提供更多的灵活性。
例如上面的案例:
# 指定启动命令
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
提供额外的参数时。
# 使用 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
中的参数会被覆盖。 - 例如:
- 如果你在
docker 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
的语法远远不值这么简单,那么更多的信息可以参考官方文档:https://docs.docker.com/reference/dockerfile/