Dockerfile不会写?于是我花十分钟看了这篇文章

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搞混淆,我们来了解一下他们有什么区别

CMDRUN 是 Dockerfile 中用于指定命令的两个不同指令,它们在 Docker 镜像构建和容器运行过程中扮演着不同的角色。

CMDRUN的区别

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 如下:

      dockerfile 复制代码
      CMD ["echo", "Hello, World!"]

      你可以通过以下命令覆盖 CMD

      sh 复制代码
      docker run my-image echo "Hello, Docker!"
4. 多条指令的行为
  • RUN:

    • 你可以有多个 RUN 指令,每个 RUN 指令都会创建一个新的层。为了减少镜像的层数,建议将多个相关的命令合并为一个 RUN 指令,使用 && 连接多个命令。

    • 例如:

      dockerfile 复制代码
      RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
  • CMD:

    • 你只能有一个 CMD 指令。如果 Dockerfile 中有多个 CMD 指令,只有最后一个 CMD 会生效。

    • 例如:

      dockerfile 复制代码
      CMD ["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

这是他的一个最基础的用法,但是ENTRYPOINTCMD 可以一起使用,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 中的参数会被覆盖。

    • 例如:

      arduino 复制代码
      docker run my-image another-app.jar

      这将执行

      复制代码
      java -jar another-app.jar

ENTRYPOINTCMD 的区别

特性 ENTRYPOINT CMD
执行时机 容器启动时执行 容器启动时执行,默认参数可以被覆盖
是否可覆盖 不可覆盖,除非使用 --entrypoint 选项 可以通过 docker run 命令行参数覆盖
用途 定义容器的主命令,适合创建可执行容器 提供默认参数或命令,适合提供默认行为
docker run 的关系 docker run 参数会作为 ENTRYPOINT 的参数传递 docker run 参数会覆盖 CMD 中的默认参数

总结

至此,我们已经学到了dockerfile中最常用的命令,至于其他的都是作为特殊需求或者更定制化的dockerfile脚本去使用,而这篇文章中的命令基本可以作为企业中的dockerfile使用,当然,dockerfile的语法远远不值这么简单,那么更多的信息可以参考官方文档:docs.docker.com/reference/d...

相关推荐
喝养乐多长不高30 分钟前
Spring Web MVC基础理论和使用
java·前端·后端·spring·mvc·springmvc
大数据追光猿37 分钟前
【大数据】服务器上部署Apache Paimon
大数据·服务器·docker·架构·apache
莫轻言舞1 小时前
SpringBoot整合PDF导出功能
spring boot·后端·pdf
钱叁壹1 小时前
修改docker为国内源
docker·容器·eureka
Violet_Stray1 小时前
【Ollama】docker离线部署Ollama+deepseek
docker·部署·ollama·deepseek
大唐锦绣1 小时前
Docker下Gogs设置Webhook推送Spug,踩坑记录与解决方案
运维·docker·容器·gogs
玄武后端技术栈1 小时前
什么是死信队列?死信队列是如何导致的?
后端·rabbitmq·死信队列
free慢2 小时前
Docker组件详解:核心技术与架构分析
docker·eureka·架构
老兵发新帖3 小时前
NestJS 框架深度解析
后端·node.js
码出钞能力4 小时前
对golang中CSP的理解
开发语言·后端·golang