Docker 命令 之 Dockerfile

摘要

Dockerfile 是什么?

  • Docker可以通过读取 Dockerfile 中的指令来自动构建镜像。

  • Dockerfile 是一个文本文档,其中包含用户可以在命令行上调用以组装镜像的所有指令。

  • 可以在Dockerfile中使用的指令:

指令 中文描述
ADD 将本地或远程的文件/目录添加到镜像中,支持自动解压 .tar 文件和使用 URL 下载远程资源。通常推荐使用 COPY,除非需要这些高级功能。
ARG 定义构建阶段使用的变量,可在 docker build 命令中通过 --build-arg 传入,变量仅在构建时有效,不会保留在最终镜像中。
CMD 指定容器默认执行的命令和参数,容器运行时若未指定命令,则使用该指令设置的命令。如果配置了多个CMD,则只有最后一个生效。和ENTRYPOINT共同使用时,作为传递给ENTRYPOINT的参数。可被 docker run 提供的命令覆盖。
COPY 将构建上下文中的文件或目录复制到镜像中。相比 ADD 更简单、安全,推荐优先使用。
ENTRYPOINT 指定容器启动时的主命令,不容易被 docker run 中的命令覆盖。可与 CMD 配合使用,用于提供默认参数。
ENV 设置环境变量,变量将在构建和容器运行时均可使用。例如:配置应用参数或系统路径等。
EXPOSE 声明容器运行时将开放的端口,仅用于文档说明或与容器编排工具配合,不会自动进行端口映射。
FROM 指定基础镜像,是 Dockerfile 的起点。也可以用于多阶段构建,通过多次使用 FROM 指令创建多个构建阶段。
HEALTHCHECK 定义容器健康检查命令,用于定期检测容器内部服务的健康状态,可结合容器编排系统实现故障自动恢复。
LABEL 为镜像添加键值对形式的元数据,例如版本、维护者、用途说明等,方便镜像管理与自动化处理。
MAINTAINER (已弃用)用于指定镜像维护者信息,推荐改用 LABEL 来设置作者信息。
ONBUILD 定义一个触发指令,当当前镜像作为基础镜像被其他 Dockerfile 使用时自动执行。常用于基础镜像的预设行为。
RUN 执行一条命令并提交结果作为新镜像层,常用于安装软件包、复制文件、设置权限等构建操作。
SHELL 更改 Dockerfile 中后续 RUN、CMD、ENTRYPOINT 等指令的默认 shell(如使用 sh 或 powershell)。
STOPSIGNAL 设置容器终止时发送的系统信号(如 SIGTERM),用于优雅关闭应用。
USER 设置执行后续命令时所使用的用户和用户组,增强容器的安全性,避免使用 root 权限。
VOLUME 定义容器内的挂载点,用于持久化数据或与宿主机/其他容器共享数据。运行容器时可指定挂载路径。
WORKDIR 设置工作目录,相当于执行 cd,用于简化后续命令中的路径。若目录不存在则自动创建。
  • 构建阶段指令:FROM, ARG, ENV, COPY, ADD, RUN, WORKDIR, LABEL, USER, SHELL, ONBUILD

  • 启动配置指令:CMD, ENTRYPOINT, HEALTHCHECK, EXPOSE, VOLUME, STOPSIGNAL

Dockerfile 指令介绍

FROM

  • FROM 指令用于指定基础镜像(base image),是每一个 Dockerfile 中必须的第一条指令。所有后续指令都是基于这个基础镜像构建的。

  • 语法

1
2
3
4
5
FROM <image>[:<tag>] [AS <stage-name>]
# 说明:
# <image>:镜像名称(可以是本地已有的,也可以是从 Docker Hub 或其他镜像仓库拉取的)
# [:<tag>]:镜像标签(可选,默认是 latest)
# [AS <stage-name>]:为该构建阶段指定名称,用于多阶段构建
  • 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 使用最新版本的镜像(默认标签是 latest)
FROM node
# 使用指定的标签
FROM node:16.13.2-alpine
# 使用私有镜像仓库
FROM private.registry.com/my-image:latest

# 使用多阶段构建
FROM golang:1.20 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp

FROM alpine:latest
COPY --from=builder /app/myapp /usr/local/bin/myapp
ENTRYPOINT ["myapp"]
## 这里用了两个阶段:
### builder 阶段用来编译应用;
### alpine 阶段用来打包最终镜像,只包含编译好的二进制文件,减少体积。

WORKDIR

  • WORKDIR 指定了工作目录,即后续所有指令(如 RUN、CMD、ENTRYPOINT、COPY、ADD 等)所运行的当前路径(working directory)。

  • 如果目录不存在,Docker 会自动创建它。

  • 每个 WORKDIR 都会创建一层镜像(layer),所以不要重复设置无意义的路径。

  • 语法

1
2
3
WORKDIR <path>
# 说明:
# <path>:要切换的工作目录,可以是绝对路径或相对路径。
  • 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
# 设置绝对路径
WORKDIR /app

# 连续设置多个工作目录(逐层嵌套)
WORKDIR /var
WORKDIR www
WORKDIR html
# 等同于:
WORKDIR /var/www/html

# 避免在 RUN cd some_dir 后继续执行依赖路径的命令,因为 Docker 每一条指令都是新的 shell 实例,cd 不会跨指令保留,应该改用 WORKDIR。
RUN cd some_dir # 错误
WORKDIR some_dir # 正确

ARG

  • ARG 用于在 构建镜像时(build-time)传入参数。这些参数只在 构建阶段有效,不会出现在最终镜像中,也不会在容器运行时被保留。

  • 语法

1
2
3
4
ARG <name>[=<default_value>]
# 说明:
# <name>:参数名称
# [=<default_value>]:参数的默认值,如果未传入参数,则使用默认值,如果为设置default_value,则构建镜像时必须传递参数,--build-arg
  • 示例

1
2
3
4
5
6
7
8
9
10
# 定义一个参数,在构建镜像时必须传入参数,--build-arg APP_ENV=development
ARG APP_ENV
# 带默认值的 ARG
ARG APP_ENV=development
# 使用参数
RUN echo "当前环境:${APP_ENV}"

# 与 FROM 一起用(从 Docker 17.05 起)
ARG BASE=ubuntu
FROM ${BASE}:22.04

ENV

  • ENV 指令用于在镜像构建过程中定义环境变量(Environment Variables)。这些变量可以在之后的构建步骤(比如 RUN、CMD 等)中使用,也会在容器运行时生效。

  • 语法

1
2
3
4
5
6
7
# 两种语法
ENV <key> <value>
ENV <key>=<value> ...
# 说明:
# <key>:环境变量的名称
# <value>:环境变量的值
# 如果定义多个变量,推荐使用 key=value 的形式。
  • 示例

1
2
3
4
5
6
7
8
9
10
11
12
# 定义一个环境变量
ENV APP_ENV=production
# 定义多个变量
ENV APP_PORT=8080 NODE_ENV=production
# 使用多行格式(提高可读性)
ENV PATH="/usr/local/bin:${PATH}" \
LANG="en_US.UTF-8" \
TZ="Asia/Shanghai"
# 也可以分开定义
ENV PATH="/usr/local/bin:$PATH"
ENV LANG="en_US.UTF-8"
ENV TZ="Asia/Shanghai"
  • 注意:ENV 指令定义的环境变量在构建阶段和运行阶段都会生效,但运行阶段会覆盖构建阶段定义的变量。

LABEL

  • LABEL 用于为镜像添加元数据标签,以 key=value 的形式存在。这些标签可以是作者信息、版本描述、用途说明、构建时间等。

  • 旧的 MAINTAINER 指令现在已被废弃,推荐使用 LABEL 来代替。

  • 每个 LABEL 都会创建一层镜像(layer),推荐一次设置多个。

  • 语法

1
2
3
4
LABEL <key>=<value> [<key>=<value>...]
# 说明:
# <key>:标签的键
# <value>:标签的值
  • 示例

1
2
3
4
5
6
7
8
# 添加一个标签
LABEL maintainer="yourname@example.com"
# 换行格式(推荐),以下标签符合 OCI(Open Container Initiative) 镜像规范
LABEL \
org.opencontainers.image.title="MyApp" \
org.opencontainers.image.description="演示项目" \
org.opencontainers.image.version="1.0.0" \
org.opencontainers.image.authors="zhangsan@example.com"

USER

  • USER 指令用于指定后续指令(如 RUN、CMD、ENTRYPOINT、COPY 等)以哪个用户身份来执行。

  • 默认情况下,Docker 容器中的命令以 root 用户运行,这虽然灵活但不安全。使用 USER 可以让我们切换到普通用户,从而提升容器的安全性,防止潜在的权限滥用。

  • 语法

1
2
3
4
USER <user>[:<group>]
# 说明:
# <user>:用户名或 UID
# [:<group>]:可选,用户组名或 GID
  • 示例

1
2
3
4
5
6
# 设置用户
USER appuser
# 设置用户组
USER appuser:appgroup
# 使用 UID 和 GID
USER 1000:1000
  • 如果基础镜像中没有你想要的用户,需要在 Dockerfile 中手动创建

1
2
3
4
# 创建用户和组
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
# 切换用户
USER appuser

ADD

  • ADD 用于将本地文件或目录、远程文件(URL) 或 压缩包 复制到镜像中的指定位置。

  • 它的功能类似于 COPY,但比 COPY 多几个功能(解压、拉取远程文件等)。

  • 语法

1
2
3
4
ADD <src>... <dest>
# 说明:
# <src>:要复制的文件或目录,可以是本地文件、远程 URL、压缩包等
# <dest>:目标路径
  • 示例

1
2
3
4
5
6
7
8
9
10
# 从本地文件复制
ADD app.jar /app.jar
# 复制多个
ADD app1.jar app2.jar /app/
# 通配符
ADD static-assets/*.html /app/public/
# 从远程 URL 复制
ADD https://example.com/app.jar /app.jar
# 从压缩包中复制
ADD static-assets.tar.gz /app/public/
  • 最佳实践是优先使用 COPY,只有在需要 ADD 的额外功能时才使用它。

COPY

  • COPY 指令用于将主机上的文件或目录复制到镜像的文件系统中。它是构建镜像过程中最常用的数据引入方式之一。

  • 与 ADD 类似,但功能更简单、明确、安全

  • 推荐优先使用 COPY,除非你确实需要 ADD 提供的自动解压或远程下载功能。

  • 语法

1
2
3
4
COPY <src>... <dest>
# 说明:
# <src>:要复制的文件或目录
# <dest>:目标路径
  • 示例

1
2
3
4
5
6
7
8
9
10
# 复制单个文件
COPY app.jar /app.jar
# 复制多个文件
COPY app1.jar app2.jar /app/
# 复制目录
COPY static-assets/ /app/public/
# 通配符
COPY static-assets/*.html /app/public/
# 设置目标文件属主属组
COPY --chown=appuser:appgroup app.jar /app.jar

RUN

  • RUN 指令用于在镜像构建阶段执行命令,结果会被打包进镜像层中。

  • 它可以用于安装依赖、编译代码、运行命令等。

  • 每一条 RUN 指令会创建一层镜像(layer),合并多个命令成一条 RUN,可以减少镜像层数(例如使用 && 串联)。

  • 语法

1
2
3
4
5
# 第一种格式:实际运行的是:/bin/sh -c "<命令字符串>"
RUN <命令字符串>

# 第二种格式
RUN ["可执行文件", "参数1", "参数2", ...]
  • 示例

1
2
3
4
5
6
7
8
9
10
# 命令字符串
RUN apt-get update && apt-get install -y curl
# 构建多个命令(用 && 串联)
RUN apt-get update && \
apt-get install -y python3 && \
rm -rf /var/lib/apt/lists/*


# 可执行文件参数
RUN ["/bin/sh", "-c", "curl https://example.com/app.jar > app.jar"]

CMD

  • CMD 用于指定容器启动时默认执行的命令及其参数。

  • 如果用户在运行容器时没有手动指定其他命令,Docker 就会使用 CMD 提供的内容。

  • 它可以定义多个,但只有最后一个会被使用。

  • 语法

1
2
3
4
5
6
7
# Shell 形式(字符串)
CMD command param1 param2
# 等价于 /bin/sh -c "command param1 param2"


# Exec 形式(数组)
CMD ["executable", "param1", "param2", ...]
  • 示例

1
2
3
4
5
6
7
8
9
# 简单 shell 命令
CMD echo "Hello from container"

# Exec 形式
CMD ["npm", "start"]

# 作为 ENTRYPOINT 的参数
ENTRYPOINT ["python3", "app.py"]
CMD ["--host=0.0.0.0", "--port=8080"]
  • 用户可以覆盖 CMD:

1
docker run <myapp> npm run test

ENTRYPOINT

  • ENTRYPOINT 定义容器启动时执行的主命令,相比 CMD,它不容易被覆盖,更适合制作“专用型”容器(如 nginx、python 脚本等)。

  • 你可以把 ENTRYPOINT 理解为容器的“主程序”,而 CMD 是为它提供的默认“命令行参数”。

  • 语法

1
2
3
4
5
6
# Shell 形式(字符串)
ENTRYPOINT command param1 param2
# 等价于 /bin/sh -c "command param1 param2"

# Exec 形式(数组)
ENTRYPOINT ["executable", "param1", "param2", ...]
  • 示例

1
2
3
4
5
6
7
8
9
# 简单 shell 命令
ENTRYPOINT echo "Hello from container"

# Exec 形式
ENTRYPOINT ["npm", "start"]

# 结合 CMD 使用
ENTRYPOINT ["python3", "app.py"]
CMD ["--host=0.0.0.0", "--port=8080"]

EXPOSE

  • EXPOSE 用于声明容器将会监听的端口,让使用该镜像的人知道应该对外开放哪些端口。

  • ⚠️ 注意:EXPOSE 并不会真的开放端口,只是“声明”这个容器监听了这些端口。

  • 要让端口真正暴露出来,还需要在运行容器时加上 -p 或 --publish 参数。

  • 语法

1
2
3
4
EXPOSE <port> [<port>/<protocol>...]
# 说明:
# <port>:端口号,可以是单个端口号,也可以是范围(如 8080-8085)
# <protocol>:协议,可以是 tcp 或 udp,默认为 tcp
  • 示例

1
2
3
4
EXPOSE 8080
EXPOSE 8080/udp
EXPOSE 8080-8085
EXPOSE 8080 8081 8082 8083 8084 8085

VOLUME

  • VOLUME 指令用于声明一个或多个容器中的挂载点(mount point),用于持久化数据或与宿主机/其他容器共享数据。

  • 语法:

1
2
3
VOLUME ["/path/in/container", ...]
# 路径必须是容器内部的绝对路径
# 可以一次声明一个,也可以是多个。
  • 示例

1
2
3
4
# 声明一个挂载点
VOLUME /app/public
# 声明多个挂载点
VOLUME ["/app/public", "/app/logs"]
  • 当容器运行时,可以将镜像中声明的挂载点映射到宿主机上,从而实现持久化数据。

1
docker run -v /host/path:/app/public myimage
  • 如果你没有手动绑定挂载,Docker 会自动创建一个匿名卷,卷的内容默认保存在宿主机的 /var/lib/docker/volumes 下。

HEALTHCHECK

  • HEALTHCHECK 用来定义容器运行时的健康检查命令,定期检测容器内服务的状态,帮助编排工具(Docker Swarm、Kubernetes 等)判断容器是否健康。

  • 如果健康检查失败,Docker 会将容器标记为 unhealthy,便于自动重启或替换。

  • 语法

1
2
3
4
5
6
7
8
HEALTHCHECK <options> CMD <command>
# 说明:
# <options>:可选项,用于设置健康检查的选项,如超时时间、重试次数等。
# <command>:健康检查命令,可以是任何有效的 shell 命令。
# 必须返回退出码:
# 0 表示健康
# 1 表示不健康
# 2 表示未知
  • 可选参数(OPTIONS)

参数 说明 默认值
--interval=DURATION 两次健康检查之间的时间间隔 30s
--timeout=DURATION 单次检测命令的超时时间 30s
--start-period=DURATION 容器启动后,开始健康检查前的等待时间 0s
--retries=N 连续失败几次后判定容器不健康 3
  • 示例

1
2
# 使用 curl 检测 Web 服务是否响应
HEALTHCHECK --interval=5s --timeout=3s --retries=3 CMD curl -f http://localhost:8080/health || exit 1
  • 运行容器后,可以用命令查看健康状态

1
2
3
4
5
6
7
8
9
# 如果镜像中有健康检查,可以查看容器状态(STATUS)
docker ps
# STATUS 列会显示:
# healthy
# unhealthy
# starting(启动中)

# 查看更详细的信息
docker inspect --format='{{json .State.Health}}' <container-id>

SHELL

  • SHELL 指令用来自定义后续 RUN、CMD 和 ENTRYPOINT 指令所使用的默认 shell 程序和参数。

  • 默认情况下:

    • 在 Linux 镜像中,Docker 使用 /bin/sh -c
    • 在 Windows 镜像中,使用 cmd /S /C
  • 使用 SHELL,你可以替换为其他 shell,如 Bash、PowerShell、zsh 等。

  • 语法

1
SHELL ["executable", "param1", "param2", ...]
  • 示例

1
2
3
4
5
6
7
8
9
10
11
12
# 切换到 bash
SHELL ["bash", "-c"]

# 多次切换
SHELL ["/bin/bash", "-c"]
RUN echo "Hello from Bash" # 在 Bash 中执行
SHELL ["/bin/sh", "-c"]
RUN echo "Now back to sh" # 在 sh 中执行


# Windows
SHELL ["powershell", "-Command"]

STOPSIGNAL

  • STOPSIGNAL 指定当容器收到 docker stop 命令时,发送给容器主进程的信号类型。

  • 默认情况下,Docker 会向容器的主进程发送 SIGTERM 信号,让它有机会优雅地退出(在超时时未退出则发 SIGKILL 强制终止)。

  • 默认情况,大多数程序(如 nginx),不需要设置(默认 SIGTERM)

  • 但有些程序可能需要使用不同的信号,比如 SIGINT、SIGHUP,这时你可以通过 STOPSIGNAL 来修改。

  • 语法

1
2
3
4
5
STOPSIGNAL <signal>
# 说明:
# 其中 <signal> 可以是:
# 信号名称,例如:SIGTERM、SIGKILL、SIGINT、SIGHUP
# 或信号编号,例如:15(等价于 SIGTERM)
  • 示例

1
2
3
4
5
# 这是默认行为,不写也一样。
STOPSIGNAL SIGTERM

# 修改为 SIGINT
STOPSIGNAL SIGINT

ONBUILD

  • ONBUILD 用于定义延迟执行的构建指令,即这些命令不会在当前 Dockerfile 构建时执行,而是在 以当前镜像为基础的子镜像中构建时触发执行。

  • 它的典型用途是:构建一个“通用基础镜像”,让使用者在自己的 Dockerfile 中 FROM 它时自动继承一些操作(比如 COPY、RUN 等)。

  • ONBUILD 是一种设计模式,方便基础镜像作者预先定义“未来子镜像构建时一定要执行的步骤”,而不是在基础镜像中“硬编码”那些步骤。

  • 语法

1
2
3
ONBUILD <INSTRUCTION>
# 说明:
# <INSTRUCTION>:必须是一个合法的 Dockerfile 指令,如 RUN、COPY、ADD、CMD 等(但不能是 FROM, ONBUILD, HEALTHCHECK 等)。
  • 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 基础镜像中使用 ONBUILD
# 文件:Dockerfile.base
FROM node:18
WORKDIR /app
ONBUILD COPY . /app
ONBUILD RUN npm install

# 构建基础镜像
docker build -t my-node-base -f Dockerfile.base .

# 使用基础镜像构建子镜像
# 文件:Dockerfile(子镜像)
FROM my-node-base
CMD ["node", "index.js"]
# 构建子镜像时,等于自动插入了:
COPY . /app
RUN npm install
  • ONBUILD 是一种“构建钩子”机制

    • ONBUILD 可以理解成“钩子”或“触发器”:
      • 在基础镜像构建时不执行
      • 但当某人以这个基础镜像为起点写自己的 Dockerfile,并构建时,这些 ONBUILD 里的指令自动插入执行
    • 这样:
      • 基础镜像只负责定义环境(node、npm版本、系统依赖等),保持轻量
      • 下游项目可以不用写重复的代码复制和安装指令,自动继承基础镜像预定义的构建步骤
      • 代码复制和依赖安装在下游镜像构建时执行,使用自己的上下文(也就是项目代码)
  • ONBUILD 总结

特性 说明
⏱ 延迟执行 构建基础镜像时不会执行,在子镜像构建时触发
✅ 支持指令 例如 RUN, COPY, ADD, CMD, WORKDIR, ENV 等
❌ 不支持 FROM, ONBUILD, HEALTHCHECK, SHELL, STOPSIGNAL
👎 不推荐滥用 会隐藏构建行为,降低可维护性
✅ 推荐场景 团队共享模板、构建“标准开发镜像”

Dockerfile 哪些 指令 会创建新的层

  • 当 Dockerfile 中创建新层的指令内容发生变化时,使用 --no-cache 选项可以确保这些变更被正确应用。

指令 描述
ADD 从 复制文件并自动解压(如果是一个 tar 文件)到容器的 路径。
COPY 从 复制文件到容器的 路径,不会自动解压。
RUN 执行任意命令并在容器中做出更改。每条 RUN 指令都会创建一个新的层。
ENV 设置环境变量。每一行 ENV 指令都会创建一个新的层。
WORKDIR 设置工作目录。每一行 WORKDIR 指令都会创建一个新的层。
VOLUME 创建一个挂载点。每一行 VOLUME 指令都会创建一个新的层。
LABEL 添加元数据标签。每一行 LABEL 指令都会创建一个新的层。

Dockerfile 示例: Spring Boot 应用

  • 目录结构

1
2
3
4
5
6
7
springbootweb/
├── Dockerfile
├── target/
│ └── app.jar
├── static-assets.tar.gz
└── ...

  • Dockerfile: 注意 Dockerfile 中所有关键字都要求大写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 使用轻量级 Alpine 版本的 OpenJDK 17 官方镜像,适合部署 Spring Boot 应用
FROM openjdk:17-alpine

# 设置构建时变量,默认使用构建好的 jar 文件
ARG JAR_FILE=target/app.jar

# 设置运行时环境变量
ENV JAVA_OPTS="-Xms1024M -Xmx1024M -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=128M"
ENV TZ=Asia/Shanghai

# 镜像元信息
LABEL maintainer="yourname@example.com"
LABEL version="1.0.0"
LABEL description="用于部署 Spring Boot 应用的生产级镜像"

# 设置工作目录
WORKDIR /app

# 示例 RUN:安装 curl(用于容器健康检查或调试)
RUN apk add --no-cache curl

# 复制 Spring Boot 构建生成的 jar 包
COPY ${JAR_FILE} app.jar

# 解压静态资源到容器中(ADD 可以自动解压 tar.gz)
ADD static-assets.tar.gz /app/public/

# 声明暴露的应用端口(Spring Boot 默认是 8080)
EXPOSE 8080

# 容器健康检查:访问 actuator 健康端点
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl -f http://localhost:8080/app/actuator/health || exit 1

# 容器终止时优雅关闭(Java 推荐 SIGTERM)
STOPSIGNAL SIGTERM

# 设置容器启动时的主命令(ENTRYPOINT 不会被 docker run 参数覆盖)
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

# CMD 提供默认的运行参数,可以被 docker run 覆盖
# 这里通过 Spring Boot 参数设置启动环境和端口号
CMD ["--spring.profiles.active=app", "--server.port=8080"]
# 这里使用的是 ENTRYPOINT + CMD 的混合模式
# 完整命令: java $JAVA_OPTS -jar app.jar --spring.profiles.active=app --server.port=8080

# 声明一个卷挂载点,运行容器时可将该路径映射到宿主机,实现数据持久化
VOLUME ["/app/logs"]
  • 构建镜像

1
2
# 构建镜像时用 --build-arg 指定构建参数,如果需要多个,就配置多个 --build-arg
docker build --build-arg JAR_FILE=target/app.jar -t springbootweb:latest .
  • 运行容器

1
2
3
4
5
docker run -d --name springbootweb \
-p 8080:8080 \
-e JAVA_OPTS="-Xms512m -Xmx1024m" \
-v /home/centos/logs/app/:/app/logs \
springbootweb:latest

Dockerfile 示例: 多阶段构建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 第一阶段:获取代码
FROM alpine/git AS fetcher
WORKDIR /workspace/application
# 将替换为实际的Git仓库URL和分支/标签
ARG GIT_REPOSITORY=https://gitee.com/hanqunfeng/springbootweb.git
ARG GIT_BRANCH=master
RUN git clone -b ${GIT_BRANCH} ${GIT_REPOSITORY} .

# 第二阶段:使用Maven环境进行构建
FROM maven:3.8.4-openjdk-17 AS builder
WORKDIR /workspace/application
# 从第一阶段复制代码
COPY --from=fetcher /workspace/application .
# 使用Maven清理并打包
RUN mvn clean package -DskipTests

# 第三阶段:创建最终的运行环境
FROM openjdk:17-alpine
WORKDIR /app
# 设置运行时环境变量
ENV JAVA_OPTS="-Xms1024M -Xmx1024M -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=128M"
# 将第二阶段生成的目标文件复制到这里。注意这里假设你的spring boot工程打成的jar名是target/app.jar
COPY --from=builder /workspace/application/target/app.jar app.jar
# 暴露端口(如果需要的话)。请根据实际情况修改端口号
EXPOSE 8080
# 声明一个卷挂载点,运行容器时可将该路径映射到宿主机,实现数据持久化
VOLUME ["/app/logs"]
# 设置容器启动时的主命令(ENTRYPOINT 不会被 docker run 参数覆盖)
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
# CMD 提供默认的运行参数,可以被 docker run 覆盖
# 这里通过 Spring Boot 参数设置启动环境和端口号
CMD ["--spring.profiles.active=app", "--server.port=8080"]
# 这里使用的是 ENTRYPOINT + CMD 的混合模式
# 完整命令: java $JAVA_OPTS -jar app.jar --spring.profiles.active=app --server.port=8080
  • 构建镜像

1
2
# 构建镜像,--no-cache 表示不使用缓存,每次构建都会重新构建
docker build --no-cache -t app:latest .
  • 运行容器

1
2
3
4
5
docker run -d --name app \
-p 8080:8080 \
-e JAVA_OPTS="-Xms512m -Xmx1024m" \
-v /home/centos/logs/app/:/app/logs \
app:latest

Dockerfile 语法检测

1
2
3
4
5
6
7
8
9
# 安装命令
wget -O /usr/local/bin/hadolint https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Linux-x86_64
chmod +x /usr/local/bin/hadolint
hadolint --version
# 检查
hadolint Dockerfile

# docker 运行
docker run --rm -i hadolint/hadolint < Dockerfile