Dockerfile 完全知识图谱(速查手册)

1️⃣ 核心概念与执行时机(务必先看这里)

  • 构建时docker build 执行)→ “装修施工期”
  • 执行指令:RUN, COPY, ADD, WORKDIR
  • 产出物:生成一个静态的镜像文件(.tar 压缩包)
  • 运行时docker run 执行)→ “入住使用期”
  • 执行指令:CMD, ENTRYPOINT
  • 产出物:启动一个动态的容器进程(提供服务)

2️⃣ 常用指令详解(按出现顺序排列)

指令作用⚠️ 关键细节/避坑指南
FROM指定基础镜像(起点)推荐 alpine / slim 瘦身版;可写多个(多阶段构建)
WORKDIR创建并切换工作目录(cd务必写在 COPYRUN 之前,路径不存在会自动创建
COPY从本地复制文件到镜像推荐用 COPY 而非 ADD变动频繁的代码务必放最后(利用缓存)
RUN构建时执行的命令常用于安装依赖(apt-get, npm install);多条用 && 连接减少层数
EXPOSE声明容器监听的端口仅作文档说明,不会自动映射端口,运行时仍需 -p
CMD运行时的默认启动命令可被 docker run 末尾命令覆盖;必须用数组格式 ["exec", "param"]
ENTRYPOINT运行时不可覆盖的主命令配合 CMD 使用:ENTRYPOINT ["java"] + CMD ["-jar"]

3️⃣ 性能优化黄金法则(面试/实战必考)

3.1 充分利用构建缓存(Layer Caching)

  • 核心口诀依赖配置放前面,业务代码放后面
  • 理由package.json / requirements.txt / pom.xml 极少改动;源码天天变。
  • 错误做法COPY . .RUN npm install(改一行代码就得重装所有依赖,巨慢)
  • 正确做法COPY package.json .RUN npm installCOPY . .(没改依赖就复用缓存,秒过)

3.2 必须写 .dockerignore 文件

  • 作用:构建时不让本地大文件(如 node_modules, .git, target)发给 Docker 引擎
  • 内容示例
  node_modules
  .git
  *.log
  .env
  target/
  dist/

3.3 基础镜像瘦身

  • node:18-alpine(约 50MB)vs node:18(约 900MB)
  • python:3.10-slim vs python:3.10
  • openjdk:17-slim vs openjdk:17

3.4 合并 RUN 命令(减少镜像层数)

  • ❌ 差:RUN apt-get update + RUN apt-get install + RUN apt-get clean
  • ✅ 好:RUN apt-get update && apt-get install -y xxx && apt-get clean

4️⃣ 多阶段构建(Multi-stage Build)——Java/Go 必用

4.1 核心机制

  • 在 Dockerfile 中写 多个 FROM(多个独立的“施工阶段”)
  • 只有最后一个 FROM 及其内容进入最终镜像,前面的全部被踢出最终镜像(但保留在本地构建缓存中)

4.2 数据传递语法

COPY --from=阶段名称 /源路径 /目标路径

4.3 为什么用它?

  • Java:把 1GB 的 JDK+Maven 留在构建阶段,最终镜像只放 100MB 的 JRE + Jar 包
  • Go:把 Go 工具链留在构建阶段,最终镜像只放 10MB 的二进制文件(甚至可以基于 scratch 空镜像)

5️⃣ 各语言专属标准模板(拿来即用)

5.1 Python(解释型)

FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]

5.2 Node.js(解释型)

FROM node:18-alpine
WORKDIR /app
COPY package*.json .
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
# 注:若为 Vue/React 前端项目,需在 COPY . . 后加 RUN npm run build

5.3 Java Spring Boot(编译型,需多阶段)

# 阶段1:编译
FROM maven:3.8-openjdk-17 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests

# 阶段2:运行(只复制 jar)
FROM openjdk:17-slim
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]

5.4 Go(编译型,多阶段 + scratch)

# 阶段1:编译
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum .
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp .

# 阶段2:运行(空镜像)
FROM scratch
WORKDIR /app
COPY --from=builder /app/myapp .
EXPOSE 8080
CMD ["./myapp"]

6️⃣ 常见问题与误区(FAQ)

疑问标准答案
CMD 是构建时执行吗?❌ 不是!CMDdocker run执行;构建时执行的是 RUN
package*.json 会把多个 JSON 都安装吗?会复制过去,但 npm install 只认 package.json,其余忽略
多阶段构建中,第一个 FROM 去哪了?被踢出最终镜像(docker images 看不见),但保留在构建缓存中供下次复用
EXPOSE 不映射端口怎么办?运行时必须加 -p 主机端口:容器端口EXPOSE 只是说明书
为什么我的镜像那么大?1. 没用 alpine 基础镜像;2. 没合并 RUN 命令;3. 没清理包管理器缓存
编译型语言(Java/Go)编译在哪步做?必须在 docker build(构建时) 完成,绝不能在运行时编译

7️⃣ 常用操作命令速查

场景命令
构建镜像docker build -t 镜像名:标签 .
指定 Dockerfile 路径docker build -f ./path/Dockerfile -t 镜像 .
传入构建参数docker build --build-arg VERSION=1.0 -t 镜像 .
仅构建到某个阶段(调试)docker build --target builder -t 镜像 .
运行容器docker run -d -p 8080:8080 --name 容器名 镜像名
进入容器调试docker exec -it 容器名 /bin/sh (Alpine 用 sh
查看镜像构建历史(找臃肿层)docker history 镜像名
清理构建缓存(释放硬盘)docker builder prune

💡 终极心法:
docker build 是“造房子”,装水电(装依赖)和搞装修(编译);docker run 是“按开关”,亮灯(启服务)。写 Dockerfile 时,时刻问自己:“这行命令是造房子时干,还是入住后干?”——区分清楚,你就出师了。

%title插图%num

相关文章 推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注