1️⃣ 核心概念与执行时机(务必先看这里)
- 构建时(
docker build 执行)→ “装修施工期”
- 执行指令:
RUN, COPY, ADD, WORKDIR
- 产出物:生成一个静态的镜像文件(.tar 压缩包)
- 运行时(
docker run 执行)→ “入住使用期”
- 执行指令:
CMD, ENTRYPOINT
- 产出物:启动一个动态的容器进程(提供服务)
2️⃣ 常用指令详解(按出现顺序排列)
| 指令 | 作用 | ⚠️ 关键细节/避坑指南 |
|---|
FROM | 指定基础镜像(起点) | 推荐 alpine / slim 瘦身版;可写多个(多阶段构建) |
WORKDIR | 创建并切换工作目录(cd) | 务必写在 COPY 和 RUN 之前,路径不存在会自动创建 |
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 install → COPY . .(没改依赖就复用缓存,秒过)
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 是构建时执行吗? | ❌ 不是!CMD 是 docker 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 时,时刻问自己:“这行命令是造房子时干,还是入住后干?”——区分清楚,你就出师了。
版权声明:除特殊说明,文章均为博主 去吐槽 原创文章,转载请注明原文出处。
原文链接: https://www.qutucao.com/1254.html