前言
Dockerfile 是镜像的“灵魂”。它定义了镜像是怎么生成的。
很多新手写 Dockerfile,往往是“能跑就行”。结果就是:
- 体积巨大: 一个简单的 Spring Boot 应用镜像竟然有 800MB+。
- 构建缓慢: 改了一行代码,重新 build 居然要几分钟。
- 不安全: 镜像里包含了源码和 Maven/Gradle 构建工具。
今天我们就来聊聊,如何编写一个体积小、构建快、更安全的 Dockerfile。我们将以 Java 和 Python 为例进行演示。
核心技巧一:多阶段构建 (Multi-stage Builds) —— Java必备
这是 Docker 17.05 引入的神级功能,特别适合 Java、Go 这种需要编译的语言。
❌ 传统写法(反面教材):在一个镜像里安装 JDK、Maven,把源码拷进去,运行 mvn package,然后启动。后果: 你的镜像里包含了 Maven、大量的 jar 包依赖缓存、源代码……这些在生产运行环境根本不需要!
✅ 优化写法(多阶段构建):我们把过程拆分为两个阶段:
- 构建环境(Build Stage): 负责编译打包,甚至产生垃圾文件也没关系。
- 运行环境(Run Stage): 这是一个纯净的空镜像,我们只从上一阶段把生成的
.jar包“偷”过来。
Java (Spring Boot) 最佳实践 Dockerfile:
Dockerfile
# ================= 第一阶段:构建 (Builder) ================= # 使用包含 Maven 的官方镜像 FROM maven:3.8.6-openjdk-11 AS builder # 设置工作目录 WORKDIR /app # 1. 这一步很关键:先只拷贝 pom.xml,下载依赖 # 这样如果源码变了但依赖没变,Docker 会利用缓存,跳过下载步骤 COPY pom.xml . RUN mvn dependency:go-offline -B # 2. 再拷贝源码,开始打包 COPY src ./src RUN mvn package -DskipTests # ================= 第二阶段:运行 (Runner) ================= # 换一个只有 JRE 的小镜像 (Alpine版本或者Slim版本) FROM openjdk:11-jre-slim WORKDIR /app # 3. 重点:只从第一阶段 (builder) 拷贝生成的 jar 包 COPY --from=builder /app/target/my-app.jar app.jar # 暴露端口 EXPOSE 8080 # 启动命令 ENTRYPOINT ["java", "-jar", "app.jar"]
效果: 镜像体积直接从 800MB (JDK+Maven) 降到了 150MB (JRE only)。
核心技巧二:巧妙利用缓存层 —— Python必备
Docker 构建是分层的。如果某一层没有变动,Docker 就会直接使用缓存(Cache)。原则: 变动越少的东西,越要写在 Dockerfile 的前面。
对于 Python 来说,通常代码变动频繁,但 requirements.txt (依赖库) 变动不频繁。
❌ 传统写法(反面教材):
Dockerfile
FROM python:3.9 WORKDIR /app COPY . . # <--- 坑在这里!先把所有代码拷进去了 RUN pip install -r requirements.txt # <--- 只要代码改一个字,缓存失效,这里就要重新下载所有依赖! CMD ["python", "app.py"]
✅ 优化写法:
Dockerfile
FROM python:3.9-slim WORKDIR /app # 1. 先只拷贝依赖定义文件 COPY requirements.txt . # 2. 安装依赖 (利用缓存) # 只要 requirements.txt 内容没变,这层就会秒过 RUN pip install --no-cache-dir -r requirements.txt # 3. 最后再拷贝源代码 # 只有这层会重新构建 COPY . . CMD ["python", "app.py"]
效果: 修改业务代码后,docker build 几乎是秒级完成,因为不用重新 pip install 了。
核心技巧三:选择更小的基础镜像
在 FROM 指令中,选择合适的基础镜像能事半功倍。
latest(如node:latest): 通常是基于 Debian/Ubuntu 的完整版,体积大,包含很多工具。适合开发调试。slim(如python:3.9-slim): 阉割了一些非必要的工具,体积适中。推荐生产环境使用。alpine(如openjdk:8-jdk-alpine): 极其精简的 Linux 发行版,只有 5MB 左右。
- 注意: Alpine 使用
musl libc而不是glibc,可能会导致某些 C 语言编写的依赖库(如 Python 的 NumPy、Java 的一些 native 库)无法运行或兼容性差。新手慎用,除非你很清楚自己在做什么。
核心技巧四:使用 .dockerignore
这东西和 .gitignore 一样重要。 当你执行 COPY . . 时,如果没有 .dockerignore,Docker 会把你项目里的 .git 目录(巨大)、target 目录、__pycache__、甚至本地的测试日志全部拷进镜像里。
创建 .dockerignore 文件:
Plaintext
.git .idea target *.log __pycache__ Dockerfile
总结
写好 Dockerfile 的 4 个黄金法则:
- 多阶段构建:编译和运行分离(Java/Go)。
- 利用缓存:先拷依赖配置,安装依赖,最后拷源码(Python/Node)。
- 选对底座:优先使用
-slim标签的基础镜像。 - 忽略无用文件:配置
.dockerignore。
掌握这几招,你的镜像不仅传输快、部署快,还能节省大笔的存储费用!