元数据卡
- 前置知识:镜像 vs 容器的概念(上一页)
- 预计时间:12 分钟
- 完成标志:能写一个 Dockerfile 把自己应用打包成镜像
你的进度
"你从 Docker Hub 拿别人的蓝图造了房子。"工坊主人点点头。"但你要在里面跑你自己的东西——JDK 17、Maven、你的 Spring Boot 应用。别人给你画好的蓝图可不包含这些。"
"所以我得自己画一张蓝图?"
"对。"他从桌上拿起一张白纸。"你想让别人在任何一台电脑上都能复刻你的环境?那就把步骤写下来——用 Docker 能读懂的格式。"
Dockerfile 是什么
Dockerfile 就是你的施工图。Docker 读这个文件,按照每一行的指示,一层一层造出镜像。
想象你有一个 Java 项目,需要 JDK 17。你要把这个环境打包成一个自包含的箱子。在项目根目录创建一个文件叫 Dockerfile(注意没有扩展名):
# 基础镜像:从带有 JDK 17 的官方镜像开始
FROM eclipse-temurin:17-jdk
# 创建应用目录
WORKDIR /app
# 把当前目录下的文件复制到容器里的 /app
COPY . .
# 告诉 Docker 这个容器要暴露 8080 端口
EXPOSE 8080
# 容器启动时运行的命令
CMD ["java", "-jar", "my-app.jar"]每一行的含义:
| 指令 | 作用 | 类比 |
|---|---|---|
FROM | 选一个基础镜像 | 决定地基是什么 |
WORKDIR | 设置工作目录 | 划一块区域 |
COPY | 把文件复制进镜像 | 搬材料进去 |
EXPOSE | 声明容器会用的端口 | 预留门洞 |
CMD | 容器启动时运行的默认命令 | 定好最后一步怎么做 |
# 把蓝图变成镜像
docker build -t my-app:v1 .
# 从镜像启动容器
docker run -d -p 8080:8080 --name my-app my-app:v1docker build -t my-app:v1 . 的意思是:在当前目录(.)找 Dockerfile,按步骤构建镜像,取名叫 my-app:v1。
优化的 Dockerfile:层缓存
你刚烧完一个镜像,工坊主人走过来看了一眼。"等一下,"他说,"你这个蓝图每次改代码就要重新下载所有依赖——连那些没变过的包都要重新下载一遍。这不是浪费炭火吗?"
Dockerfile 是按层(layer)构建的。每一层变更会导致后面所有层重新构建。你应该把不变的东西放在前面,变的东西放在后面:
FROM eclipse-temurin:17-jdk
WORKDIR /app
# 先把依赖描述文件复制进去——依赖通常不变
COPY pom.xml .
RUN mvn dependency:resolve
# 再复制代码——只有代码变了才会触发这层之后的构建
COPY src ./src
RUN mvn package -DskipTests
EXPOSE 8080
CMD ["java", "-jar", "target/my-app-1.0.jar"]"改动少的在前,改动多的在后"——这是 Dockerfile 优化的第一原则。依赖通常不常变,代码经常变,所以把 COPY pom.xml 和 RUN mvn dependency:resolve 放在前面。
常见陷阱:我改了代码,但镜像好像没更新?
如果你改了 pom.xml 或 src/ 里的代码但镜像没变——很可能是 Docker 用了缓存层。加 --no-cache 强制重构建:
docker build --no-cache -t my-app:v2 .旅人笔记
Dockerfile 是你的施工图。FROM 选地基,WORKDIR 划区域,COPY 搬材料,CMD 定启动咒语。不变的东西放前面,变的东西放后面,靠层缓存省时间。
→ 下一步:Docker Compose——管理多个服务
单个容器太简单了。真实项目不只是一个 Java 应用——它还有数据库、缓存、消息队列。