From 0620da28add7c1f4d017288fa1b698836d550830 Mon Sep 17 00:00:00 2001 From: guoyonghao <1711322114@qq.com> Date: Tue, 28 Apr 2026 21:55:18 +0800 Subject: [PATCH] vault backup: 2026-04-28 21:55:18 --- gyh/docker/Dockerfile编写指南.md | 472 +++++++++++++------------- gyh/docker/Docker常用命令.md | 191 +++++++---- gyh/docker/README.md | 61 +++- gyh/docker/docker-compose使用指南.md | 488 ++++++++++++--------------- 4 files changed, 616 insertions(+), 596 deletions(-) diff --git a/gyh/docker/Dockerfile编写指南.md b/gyh/docker/Dockerfile编写指南.md index dee54c8..1592d45 100644 --- a/gyh/docker/Dockerfile编写指南.md +++ b/gyh/docker/Dockerfile编写指南.md @@ -1,10 +1,44 @@ -# Dockerfile 编写指南 - -> Dockerfile 是**镜像构建的蓝图**,每一行指令都会在镜像中生成一层。写好 Dockerfile 的核心目标:**镜像更小、构建更快、启动更安全**。 - +--- +tags: + - docker + - devops + - containerization +create time: 2026-04-28 14:30 --- -## 一、Dockerfile 基本结构 +# Dockerfile 编写指南 + +> 一份从"能跑"到"跑得好"的 Dockerfile 教程。通过教学式讲解、流程图和实战示例,帮你写出体积小、构建快、启动安全的镜像。 + +## 概述 + +Dockerfile 是镜像构建的蓝图,每一行指令都会在镜像中生成一层。写好 Dockerfile 的核心目标:**镜像更小、构建更快、启动更安全**。 + +> [!tip] 初学者思考题 +> 为什么 `RUN apt-get install nginx && rm -rf /var/lib/apt/lists/*` 不能拆成两个 RUN?拆开后镜像体积会不会变小?(答案见正文 2.2 节) + +## 正文 + +### 一、Dockerfile 基本结构 + +一张图理解 Dockerfile 的构建流程: + +```mermaid +flowchart LR + A["FROM 基础镜像"] --> B["WORKDIR 设工作目录"] + B --> C["COPY 依赖文件 + RUN 安装"] + C --> D["COPY 源代码"] + D --> E["RUN 构建产物"] + E --> F["EXPOSE 声明端口"] + F --> G["CMD 启动命令"] + style A fill:#e1f5fe + style G fill:#c8e6c9 +``` + +> [!note] 构建流程 +> 构建过程是**自上而下逐行执行**的,每一步都基于上一步的结果生成新层。层序排列直接影响缓存命中率和镜像体积。 + +一个标准的 Node.js 项目 Dockerfile: ```dockerfile # 1. 基础镜像 @@ -16,14 +50,14 @@ LABEL maintainer="yourname@example.com" # 3. 设置工作目录 WORKDIR /app -# 4. 复制依赖文件并安装 +# 4. 先复制依赖文件,利用 Docker 缓存 COPY package*.json ./ RUN npm ci --production -# 5. 复制源代码 +# 5. 再复制源代码 COPY . . -# 6. 构建(开发环境需要,生产环境不需要) +# 6. 构建 RUN npm run build # 7. 暴露端口 @@ -35,167 +69,165 @@ CMD ["node", "dist/index.js"] --- -## 二、常用指令详解 +### 二、常用指令详解 -### 2.1 `FROM` — 基础镜像 +#### 2.1 `FROM` — 基础镜像 + +> [!question] 为什么镜像大小差距这么大? +> 完整 OS + 工具链 vs 精简版 = 1GB vs 10MB,这就是 `FROM` 选择的影响。 + +**选镜像原则:有 Alpine 选 Alpine,有 Slim 选 Slim,没有再选完整版。** ```dockerfile -# 选镜像的通用原则: -# 有 Alpine 选 Alpine(体积小) -# 有 Slim 选 Slim(平衡体积和功能) -# 没有再选完整版本 - -FROM node:20-alpine # 推荐:最小体积 -FROM node:20-slim # 推荐:平衡方案 -FROM node:20 # 不推荐:包含完整 OS,镜像臃肿 +FROM node:20-alpine # 推荐:最小体积 ~150MB +FROM node:20-slim # 推荐:平衡方案 ~200MB +FROM node:20 # 不推荐:完整 OS,镜像臃肿 ~1GB FROM python:3.12-slim-bookworm -FROM nginx:alpine FROM golang:1.22-alpine FROM ubuntu:24.04 # 一般作为多阶段构建的第一步 ``` -**常见基础镜像体积对比:** - -| 镜像 | 大小 | 适用场景 | -|------|------|----------| +| 镜像类型 | 大小 | 适用场景 | +|----------|------|----------| | `alpine` | 5-10MB | 生产环境、追求极致体积 | | `slim` | 100-200MB | 大多数生产场景,兼容性好 | | 完整版 | 1GB+ | 开发环境、需要完整工具链 | -### 2.2 `RUN` — 执行命令 +#### 2.2 `RUN` — 执行命令 + +> [!warning] 关键概念:Docker 层不可删 +> `RUN` 产生的层会**永久留在镜像中**,即使后续 `rm` 了也没用。所以更新、安装、清理必须在**同一个 RUN** 里。 ```dockerfile -# 合并 RUN 指令减少镜像层数 -# 不推荐:每行一个 RUN,产生多层 +# ❌ 不推荐:每行一个 RUN,产生多层,rm 无效 RUN apt-get update RUN apt-get install -y nginx RUN rm -rf /var/lib/apt/lists/* -# 推荐:合并为一行,并用 && 连接,用 \ 换行 +# ✅ 推荐:合并为一行,用 && 连接,用 \ 换行 RUN apt-get update && \ apt-get install -y nginx && \ rm -rf /var/lib/apt/lists/* - -# 注意:RUN 产生的层会**永久留在镜像中**,即使后续 rm 了也没用 -# 所以更新和安装、清理要放在同一个 RUN 里 ``` -### 2.3 `COPY` vs `ADD` +#### 2.3 `COPY` vs `ADD` + +> [!abstract] 简单原则 +> **99% 的情况用 COPY。** ADD 的额外功能(自动解压、远程下载)行为不可预测。 ```dockerfile -# COPY — 简单复制,推荐优先使用 +# COPY — 简单复制,优先使用 COPY package.json ./ COPY . . -# ADD — 有额外功能但不可预测,不推荐 -ADD https://example.com/file.tar.gz /tmp/ # 能下载远程文件,但容易出错 -ADD file.tar.gz /app/ # 能自动解压,但 COPY + RUN tar 更清晰 - -# 结论:99% 的情况用 COPY,ADD 只在自动解压 tar 和下载远程文件时有用 +# ADD — 仅在两种场景有用 +ADD https://example.com/file.tar.gz /tmp/ # 下载远程文件(但容易出错) +ADD file.tar.gz /app/ # 自动解压(COPY + RUN tar 更清晰) ``` -### 2.4 `WORKDIR` — 工作目录 +#### 2.4 `WORKDIR` — 工作目录 ```dockerfile WORKDIR /app # 设置工作目录 COPY package.json ./ # 相对于 /app RUN npm install -COPY . . # 复制代码到 /app +COPY . . -# WORKDIR 会被后续指令继承,且会自动创建(不需要提前 mkdir) +# WORKDIR 会被后续指令继承,且会自动创建 WORKDIR /subdir # 现在是 /app/subdir RUN pwd # 输出 /app/subdir ``` -### 2.5 `EXPOSE` — 声明端口 +#### 2.5 `EXPOSE` — 声明端口 + +> [!info] EXPOSE 不自动映射端口 +> `EXPOSE` 只是**文档性质**的声明,实际端口映射需要在 `docker run -p` 或 `docker-compose` 中指定。 ```dockerfile -# 声明容器运行时监听的端口(文档性质,不自动映射) EXPOSE 3000 EXPOSE 80 443 - -# 实际端口映射需要在 docker run -p 或 docker-compose 中指定 ``` -### 2.6 `ENV` — 环境变量 +#### 2.6 `ENV` — 环境变量 ```dockerfile ENV NODE_ENV=production ENV APP_PORT=3000 -ENV DB_HOST=localhost -# 容器启动时可以覆盖: -# docker run -e DB_HOST=192.168.1.100 my-app +# 容器启动时可以覆盖:docker run -e DB_HOST=192.168.1.100 my-app ``` -### 2.7 `CMD` vs `ENTRYPOINT` +#### 2.7 `CMD` vs `ENTRYPOINT` + +| 指令 | 特点 | 可被 docker run 覆盖? | +|------|------|----------------------| +| `CMD` | 默认命令,最灵活 | ✅ 完全覆盖 | +| `ENTRYPOINT` | 入口点,固定执行 | ⚠️ 需用 `--entrypoint` 强制覆盖 | ```dockerfile -# CMD — 容器启动时执行的默认命令(可被覆盖) +# CMD — 大多数情况用这个就够了 CMD ["node", "dist/index.js"] -# 运行 docker run my-app echo "hello" 会覆盖 CMD -# ENTRYPOINT — 入口点,不可被 docker run 覆盖(但可用 --entrypoint 强制覆盖) +# ENTRYPOINT + CMD — 入口固定,参数可配 ENTRYPOINT ["node"] -CMD ["dist/index.js"] # 作为 ENTRYPOINT 的参数 - -# 实际开发中,大多数情况用 CMD 就够了 +CMD ["dist/index.js"] ``` --- -## 三、多阶段构建(关键优化手段) +### 三、多阶段构建(核心优化手段) -### 3.1 为什么需要多阶段构建 +> [!abstract] 一句话理解多阶段构建 +> **先用大镜像构建 → 再把产物复制到最小镜像中运行。** 构建工具不留到最终镜像。 -前端项目用 Next.js/Vite + TypeScript 构建,需要 Node.js 全套环境。但运行只需要一个编译后的静态文件。多阶段构建可以**把构建工具留在第一阶段,最终镜像只放产物**。 +构建流程图: -### 3.2 前端多阶段构建(Node + TypeScript) +```mermaid +flowchart TB + subgraph Phase1["第一阶段:构建(大镜像)"] + A["node:20-alpine"] --> B["安装全套依赖 + devDependencies"] + B --> C["TypeScript / Maven / Go 编译"] + C --> D["生成 dist/ / target/*.jar"] + end + + subgraph Phase2["第二阶段:运行(最小镜像)"] + E["node:20-alpine / JRE Alpine / Alpine"] --> F["只装 production 依赖"] + F --> G["COPY --from=builder 复制产物"] + G --> H["启动应用"] + end + + Phase1 -->|COPY --from=builder| Phase2 + + style Phase1 fill:#fff3e0 + style Phase2 fill:#e8f5e9 +``` + +#### 3.1 前端多阶段构建(Node + TypeScript) ```dockerfile # ========== 第一阶段:构建 ========== FROM node:20-alpine AS builder - WORKDIR /app - -# ① 先复制依赖文件,利用 Docker 缓存 COPY package*.json ./ -RUN npm ci - -# ② 再复制源码(代码改了才重新构建) -COPY . . - -# ③ 构建 -RUN npm run build +RUN npm ci # ① 先装依赖,利用缓存 +COPY . . # ② 再复制源码,代码改了才重建 +RUN npm run build # ③ 编译 # ========== 第二阶段:运行 ========== FROM node:20-alpine AS production - -# 安装 tini(处理僵尸进程,可选但推荐) -RUN apk add --no-cache tini - +RUN apk add --no-cache tini # tini 处理僵尸进程(推荐) WORKDIR /app - -# 仅复制 production 依赖 COPY package*.json ./ -RUN npm ci --production - -# 从构建阶段复制产物 +RUN npm ci --production # 只装 production 依赖 COPY --from=builder /app/dist ./dist COPY --from=builder /app/public ./public - ENV NODE_ENV=production - EXPOSE 3000 - -# 使用 tini 作为 PID 1 ENTRYPOINT ["/sbin/tini", "--"] CMD ["node", "dist/index.js"] ``` -**核心思路:先用大镜像构建 → 再把产物复制到最小镜像中运行。** - -### 3.3 Java 多阶段构建 +#### 3.2 Java 多阶段构建 ```dockerfile # 第一阶段:Maven 构建 @@ -206,7 +238,7 @@ RUN mvn dependency:go-offline -B COPY src ./src RUN mvn package -DskipTests -# 第二阶段:运行 +# 第二阶段:运行(JRE 镜像,无 Maven) FROM eclipse-temurin:21-jre-alpine WORKDIR /app COPY --from=build /app/target/*.jar app.jar @@ -214,7 +246,7 @@ EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"] ``` -### 3.4 Go 多阶段构建 +#### 3.3 Go 多阶段构建 ```dockerfile # 第一阶段:编译 @@ -225,7 +257,7 @@ RUN go mod download COPY . . RUN CGO_ENABLED=0 go build -o /app/server -# 第二阶段:运行 +# 第二阶段:运行(纯 Alpine,仅几 MB) FROM alpine:3.19 RUN apk --no-cache add ca-certificates COPY --from=builder /app/server /app/server @@ -235,9 +267,10 @@ CMD ["/app/server"] --- -## 四、.dockerignore 文件 +### 四、.dockerignore 文件 -> **和 `.gitignore` 一样重要!** 不写 `.dockerignore` 会导致不必要的文件被复制到镜像中,增大体积、暴露敏感信息。 +> [!danger] 和 .gitignore 一样重要! +> 不写 `.dockerignore` 会把不必要的文件塞进镜像,增大体积、暴露密钥。 ```dockerignore # 依赖 @@ -248,26 +281,23 @@ npm-debug.log .git .gitignore -# 构建产物(别把 build 直接拷进去,让容器自己 build) +# 构建产物(别把 build 拷进去,让容器自己 build) dist build *.local -# 编辑器 +# 编辑器和文档 .vscode .idea *.swp - -# Docker 自身 -docker-compose.yml -Dockerfile -.dockerignore - -# 文档和测试 README.md LICENSE tests -__tests__ + +# Docker 自身文件 +docker-compose.yml +Dockerfile +.dockerignore # 环境变量(千万不要拷进去!) .env @@ -277,9 +307,9 @@ __tests__ --- -## 五、最佳实践清单 +### 五、最佳实践清单 -### ✅ 应该做 +#### ✅ 应该做 | 实践 | 说明 | |------|------| @@ -292,7 +322,7 @@ __tests__ | 非 root 用户运行 | 安全考虑 | | 用 `CMD ["exec格式"]` | 避免 shell 包装问题 | -### ❌ 不应该做 +#### ❌ 不应该做 | 实践 | 问题 | |------|------| @@ -305,58 +335,117 @@ __tests__ --- -## 六、非 Root 用户示例 +### 六、安全加固 + +#### 6.1 使用非 root 用户 ```dockerfile FROM node:20-alpine - WORKDIR /app -COPY package*.json ./ +# 利用 --chown 设置文件所有者,避免切换用户后权限问题 +COPY --chown=node:node package*.json ./ RUN npm ci +COPY --chown=node:node . . -COPY . . - -# 创建非 root 用户 -RUN addgroup -S appgroup && \ - adduser -S appuser -G appgroup - -# 切换用户 -USER appuser +# 切换到非 root 用户 +USER node EXPOSE 3000 - CMD ["node", "dist/index.js"] ``` +#### 6.2 不要安装 build 依赖到生产镜像 + +```dockerfile +# ❌ 错误:devDependencies 也被装进镜像 +RUN npm install + +# ✅ 正确:多阶段构建 +FROM node:20-alpine AS builder +RUN npm install # 包含 devDependencies +RUN npm run build + +FROM node:20-alpine +RUN npm ci --production # 只装 production 依赖 +COPY --from=builder /app/dist ./dist +``` + +#### 6.3 密钥管理原则 + +```dockerfile +# ❌ 绝对不要硬编码密码 +RUN echo "password=123456" > /app/config.env + +# ❌ 不要在构建时传递密钥(会留在镜像层中) +# docker build --build-arg DB_PASSWORD=secret . + +# ✅ 运行时传入环境变量 +# docker run -e DB_PASSWORD=secret my-app + +# ✅ 生产环境用 Kubernetes Secrets / AWS Secrets Manager +``` + +#### 6.4 扫描镜像漏洞 + +```bash +# 使用 trivy 扫描镜像 +trivy image my-app:v1.0 + +# 只输出高危及以上 +trivy image --severity HIGH,CRITICAL my-app:v1.0 + +# 直接扫描 Dockerfile(不用构建镜像) +trivy config Dockerfile +``` + --- -## 七、常见问题排查 +### 七、构建缓存优化 -### 7.1 镜像太大 +> [!tip] 缓存命中规则 +> Docker 从上到下逐层检查缓存。**任何一行变更,后续所有层全部失效重构建。** 所以要把经常变的内容放下面。 + +```dockerfile +# ❌ 错误:代码改动 → npm install 也重新执行 +COPY . . +RUN npm install + +# ✅ 正确:先复制不变的依赖文件 +COPY package*.json ./ +RUN npm install +COPY . . +``` + +构建缓存策略流程图: + +```mermaid +flowchart LR + A["package.json 未变"] -->|"缓存命中"| B["跳过 npm install"] + C["package.json 变更"] -->|"缓存失效"| D["重新 npm install"] + E["源码变更"] --> B + B --> F["最终镜像"] + D --> F + + style A fill:#c8e6c9 + style C fill:#fff3e0 + style E fill:#fff3e0 +``` + +--- + +### 八、常见问题排查 + +#### 8.1 镜像太大 ```bash # 分析镜像各层大小 docker history my-app:latest -# 检查是否有未清理的缓存 -# 解决:使用 alpine/slim 基础镜像 + 多阶段构建 + .dockerignore +# 解决:alpine 基础镜像 + 多阶段构建 + .dockerignore ``` -### 7.2 构建缓存失效 - -```dockerfile -# 错误:代码改动导致 npm install 也重新执行 -COPY . . -RUN npm install - -# 正确:依赖文件不变时跳过 npm install -COPY package*.json ./ -RUN npm install -COPY . . -``` - -### 7.3 容器启动后立即退出 +#### 8.2 容器启动后立即退出 ```bash # 查看日志 @@ -364,128 +453,23 @@ docker logs <容器名> # 常见原因: # 1. CMD/ENTRYPOINT 写错了 -# 2. 主进程不是 PID 1(用 tini 或 dumb-init 处理) +# 2. 主进程不是 PID 1(用 tini 或 dumb-init) # 3. 应用启动失败(检查日志) ``` -### 7.4 端口无法访问 +#### 8.3 端口无法访问 ```bash # 1. 检查 docker run -p 是否映射 docker run -p 3000:3000 ... -# 2. 检查容器内应用是否监听 0.0.0.0(不是 localhost) +# 2. 容器内应用监听 0.0.0.0(不是 localhost) # 3. 检查防火墙 - -# 4. 用 docker-compose 时,确认 services 定义正确 ``` --- -## 七、Docker 安全加固 +## 关联笔记 -### 7.1 使用非 root 用户 - -```dockerfile -# 最简方式(Node 官方镜像已内置 node 用户) -FROM node:20-alpine -USER node -CMD ["node", "server.js"] - -# 手动创建用户(通用模式) -FROM node:20-alpine -WORKDIR /app -COPY --chown=node:node package*.json ./ -RUN npm ci -COPY --chown=node:node . . -USER node -CMD ["node", "dist/index.js"] -``` - -> `--chown=user:group` 在 COPY/RUN 时设置文件所有者,**避免 root 拥有文件**后切换到非 root 用户导致权限问题。 - -### 7.2 不要安装 build 依赖到生产镜像 - -```dockerfile -# 错误:所有依赖都装进去了 -FROM node:20-alpine -RUN npm install # devDependencies 也被安装了 - -# 正确:生产环境只装 dependencies -RUN npm ci --only=production - -# 更正确:多阶段构建 -FROM node:20-alpine AS builder -RUN npm install # 包含 devDependencies -RUN npm run build - -FROM node:20-alpine -RUN npm ci --only=production -COPY --from=builder /app/dist ./dist -``` - -### 7.3 扫描镜像漏洞 - -```bash -# 使用 trivy(推荐) -docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ - aquasec/trivy image nginx:latest - -# 扫描指定镜像 -trivy image my-app:v1.0 - -# 只输出高危及以上 -trivy image --severity HIGH,CRITICAL my-app:v1.0 - -# 扫描 Dockerfile 本身(不用构建镜像) -trivy config Dockerfile -``` - -### 7.4 密钥管理原则 - -```dockerfile -# ❌ 绝对不要硬编码密码 -RUN echo "password=123456" > /app/config.env - -# ❌ 不要在构建时传递密钥(会留在镜像层中) -docker build --build-arg DB_PASSWORD=secret . - -# ✅ 运行时传入环境变量 -docker run -e DB_PASSWORD=secret my-app - -# ✅ 或使用 Docker secrets(Swarm 模式) -# ✅ 生产环境用 Kubernetes Secrets / AWS Secrets Manager -``` - ---- - -## 八、实战:Node.js + Express 完整 Dockerfile - -```dockerfile -# Build stage -FROM node:20-alpine AS builder -WORKDIR /app -COPY package*.json ./ -RUN npm ci --only=production -COPY . . - -# Production stage -FROM node:20-alpine -WORKDIR /app -COPY --from=builder /app/node_modules ./node_modules -COPY --from=builder /app/dist ./dist -COPY --from=builder /app/package.json ./package.json - -ENV NODE_ENV=production -ENV PORT=3000 - -RUN addgroup -S app && adduser -S app -G app -USER app - -EXPOSE 3000 -CMD ["node", "dist/server.js"] -``` - ---- - -> 下一步:理解 Dockerfile 后,学习 [[docker-compose]] 来编排多容器应用。 +- [[docker-compose]] — 用 Compose 编排多容器应用 +- [[Docker常用命令]] — 日常 Docker 操作速查 diff --git a/gyh/docker/Docker常用命令.md b/gyh/docker/Docker常用命令.md index 976c468..d71e6df 100644 --- a/gyh/docker/Docker常用命令.md +++ b/gyh/docker/Docker常用命令.md @@ -1,10 +1,48 @@ +--- +tags: + - docker + - devops + - containerization + - reference +create time: 2026-04-28 14:40 +--- + # Docker 常用命令 -> 按「生命周期」组织,从拉取镜像到清理收尾,日常 90% 的操作都在以下命令中。 +> 按「容器生命周期」组织的命令速查手册。从拉取镜像到清理收尾,日常 90% 的操作都在以下命令中。建议收藏,用时直接翻。 + +## 概述 + +Docker 的命令体系看似庞杂,但本质上围绕四个核心对象展开:**镜像(Image)→ 容器(Container)→ 网络(Network)→ 数据卷(Volume)**。 + +> [!tip] 记忆框架 +> 理解这个生命周期,所有命令就不需要死记了: + +```mermaid +flowchart LR + A["拉取镜像"] --> B["构建镜像"] + B --> C["运行容器"] + C --> D["调试/监控"] + D --> E["停止/删除"] + E --> F["清理回收"] + F --> A + + style A fill:#e3f2fd + style B fill:#fff3e0 + style C fill:#e8f5e9 + style D fill:#f3e5f5 + style E fill:#fce4ec + style F fill:#efebe9 +``` --- -## 一、镜像管理(Image) +## 正文 + +### 一、镜像管理(Image) + +> [!info] 镜像 = 只读模板 +> 镜像是容器的"蓝图"。同一个镜像可以运行多个容器实例,它们互不影响。 ```bash # 拉取镜像 @@ -23,18 +61,22 @@ docker tag my-app:latest myrepo/my-app:v1.0 # 删除镜像 docker rmi nginx # 删除单个 -docker rmi $(docker images -q) # 删除所有镜像(谨慎) +docker rmi $(docker images -q) # 删除所有镜像(谨慎!) # 构建镜像 docker build -t my-app:v1.0 . # 当前目录构建(默认用 Dockerfile) docker build -t my-app:v1.0 -f Dockerfile.prod . # 指定 Dockerfile ``` -**镜像分层缓存:** `docker build` 从上到下逐行执行,每一行都会生成一层缓存。把**不变的部分放前面**(安装依赖),**变化的部分放后面**(复制代码),充分利用缓存加速。 +> [!tip] 镜像分层缓存 +> `docker build` 从上到下逐行执行,每一行都会生成一层缓存。把**不变的部分放前面**(安装依赖),**变化的部分放后面**(复制代码),充分利用缓存加速。 --- -## 二、容器管理(Container) +### 二、容器管理(Container) + +> [!abstract] 容器 = 镜像的运行实例 +> 容器是轻量级、隔离的进程。删除容器 = 停止进程 + 释放资源,容器内部写入的数据默认会丢失(除非挂载了 Volume)。 ```bash # 运行容器 @@ -90,7 +132,10 @@ docker logs --tail 100 web # 只看最后 100 行 --- -## 三、网络管理(Network) +### 三、网络管理(Network) + +> [!question] 为什么容器能互相"看见"? +> 答:Docker 为每个容器分配独立 IP,同一网络下的容器可以通过**容器名**(自动 DNS 解析)互相通信。 ```bash # 查看所有网络 @@ -110,11 +155,15 @@ docker exec web1 ping db1 # 在同一网络下可直接 ping 容器名 docker network rm my-network ``` -> **为什么需要自定义网络?** Docker 默认的 `bridge` 网络不支持容器名 DNS 解析。自定义网络(`docker network create`)可以让容器通过**名字**互相通信。 +> [!note] 为什么需要自定义网络? +> Docker 默认的 `bridge` 网络不支持容器名 DNS 解析。自定义网络可以让容器通过**名字**互相通信。 --- -## 四、数据卷(Volume) +### 四、数据卷(Volume) + +> [!danger] 容器是易失的(ephemeral) +> 删除容器 = 容器内数据消失!必须用 `-v` 把数据持久化到 Volume 或宿主机目录。 ```bash # 查看所有卷 @@ -136,11 +185,9 @@ docker run -d -v my-data:/data:ro nginx docker volume inspect my-data ``` -> **数据持久化原则:** 容器是** ephemeral(易失的)**——删除容器,容器内的数据就没了。必须用 `-v` 把数据存到宿主机或 Docker Volume 中。 - --- -## 五、日志与调试 +### 五、日志与调试 ```bash # 进入容器 @@ -149,7 +196,7 @@ docker exec -it <容器名> bash # 查看容器资源占用 docker stats -# 查看容器详情 +# 查看容器详情(JSON 格式) docker inspect web # 查看容器进程 @@ -159,13 +206,16 @@ docker top web docker cp container.log web:/tmp/ docker cp web:/tmp/remote.log ./local.log -# 查看容器创建命令 -docker history my-app:v1.0 # 查看镜像构建历史 +# 查看镜像构建历史 +docker history my-app:v1.0 ``` --- -## 六、清理命令(重要!) +### 六、清理命令(重要!) + +> [!warning] 磁盘占用会持续增长 +> 停止的容器、未使用的镜像、悬空卷会悄悄吃掉磁盘空间。定期清理是好习惯。 ```bash # 删除所有已停止的容器 @@ -186,9 +236,9 @@ docker system df --- -## 七、常用完整命令场景 +### 七、常用完整命令场景 -### 7.1 运行 Nginx +#### 7.1 运行 Nginx ```bash docker run -d \ @@ -200,7 +250,7 @@ docker run -d \ nginx:alpine ``` -### 7.2 运行 MySQL +#### 7.2 运行 MySQL ```bash docker run -d \ @@ -213,18 +263,18 @@ docker run -d \ mysql:8.0 ``` -### 7.3 运行 Redis +#### 7.3 运行 Redis ```bash docker run -d \ -p 6379:6379 \ -v redis-data:/data \ --name my-redis \ + --restart=always \ redis:7-alpine redis-server --appendonly yes - --restart=always ``` -### 7.4 运行 Node 开发环境 +#### 7.4 运行 Node 开发环境 ```bash docker run -it \ @@ -239,41 +289,53 @@ docker run -it \ --- -## 七、进阶:Docker 网络模式 +### 八、进阶:Docker 网络模式 + +Docker 提供四种网络模式: + +| 模式 | 特点 | 适用场景 | +|------|------|----------| +| `bridge`(默认) | 容器有独立 IP | 大多数场景 | +| `host` | 共享宿主机网络,性能最好 | 高性能需求 | +| `none` | 无网络(完全隔离) | 安全隔离 | +| `container` | 共享另一个容器的网络命名空间 | 特殊协作 | ```bash -# Docker 提供四种网络模式 - -# 1. bridge(默认)— 桥接网络,容器有独立 IP +# bridge(默认) docker run -d --network bridge nginx -# 2. host — 共享宿主机网络,性能最好但端口会冲突 +# host docker run -d --network host nginx -# 3. none — 无网络(隔离) +# none docker run -d --network none nginx -# 4. container — 共享另一个容器的网络命名空间 +# container(共享 web 容器的网络) docker run -d --network container:web nginx -# 推荐使用自定义 bridge 网络(前面已讲过) +# 推荐:自定义 bridge 网络 docker network create my-net docker run -d --network my-net --name app nginx ``` -## 八、进阶:Docker 日志驱动 +--- + +### 九、进阶:日志驱动 ```bash # 查看容器的日志配置 docker inspect web | grep LogConfig +``` -# 日志驱动类型 -# json-file — 默认,日志写入文件 -# local — 循环写入,控制大小 -# syslog — 发送到 syslog -# journald — 发送到 systemd journal -# none — 不记录日志 +| 驱动类型 | 说明 | +|----------|------| +| `json-file` | 默认,日志写入文件 | +| `local` | 循环写入,控制大小 | +| `syslog` | 发送到 syslog | +| `journald` | 发送到 systemd journal | +| `none` | 不记录日志 | +```yaml # docker-compose 中限制日志大小 services: api: @@ -284,31 +346,33 @@ services: max-file: "3" # 最多保留 3 个文件 ``` -## 九、进阶:Docker 资源限制 +--- + +### 十、进阶:资源限制 + +> [!tip] 为什么需要限制资源? +> 防止某个容器吃光 CPU/内存导致宿主机崩溃,这是多容器环境的必修课。 ```bash # 限制 CPU 和内存 docker run -d \ - --cpus="1.5" \ # 限制 1.5 个 CPU 核心 - --memory="512m" \ # 限制 512MB 内存 - --memory-swap="1g" \ # 允许使用的 swap 总量 - --pids-limit=100 \ # 最多 100 个进程(防 fork 炸弹) + --cpus="1.5" \ + --memory="512m" \ + --memory-swap="1g" \ + --pids-limit=100 \ nginx - -# docker-compose 中配置 -services: - api: - deploy: - resources: - limits: - cpus: '1.5' - memory: 512M - reservations: - cpus: '0.5' - memory: 128M +# --cpus 限制 1.5 个 CPU 核心 +# --memory 限制 512MB 内存 +# --memory-swap 允许使用的 swap 总量 +# --pids-limit 最多 100 个进程(防 fork 炸弹) ``` -## 十、进阶:Docker Swarm(简易集群) +--- + +### 十一、进阶:Docker Swarm(简易集群) + +> [!info] Swarm vs Kubernetes +> Swarm 适合单节点或小团队快速上手。大规模生产环境建议直接用 Kubernetes。 ```bash # 初始化集群(单节点) @@ -326,7 +390,7 @@ docker service scale web=5 # 查看服务 docker service ls -# 更新服务(零停机滚动更新) +# 零停机滚动更新 docker service update --image nginx:1.25 web # 删除服务 @@ -335,15 +399,15 @@ docker service rm web # 退出集群 docker swarm leave -# 初始化集群 + 管理节点令牌 + 工作节点令牌 +# 多节点集群 docker swarm init --advertise-addr -docker swarm join-token manager # 添加管理节点 -docker swarm join-token worker # 添加工作节点 +docker swarm join-token manager # 获取管理节点令牌 +docker swarm join-token worker # 获取工作节点令牌 ``` -> Swarm 适合轻量级部署。大规模生产环境建议用 Kubernetes。 +--- -## 十一、快速速查表 +## 快速速查表 | 操作 | 命令 | |------|------| @@ -360,3 +424,10 @@ docker swarm join-token worker # 添加工作节点 | 删除所有容器 | `docker rm -f $(docker ps -aq)` | | 查看磁盘占用 | `docker system df` | | 全面清理 | `docker system prune -a` | + +--- + +## 关联笔记 + +- [[Dockerfile编写指南]] — 编写高效的 Dockerfile +- [[docker-compose]] — 用 Compose 编排多容器应用 diff --git a/gyh/docker/README.md b/gyh/docker/README.md index 003a9a0..802ffbf 100644 --- a/gyh/docker/README.md +++ b/gyh/docker/README.md @@ -1,40 +1,71 @@ +--- +tags: + - docker + - devops + - containerization +create time: 2026-04-28 14:45 +--- + # Docker 学习笔记 > Docker 容器化技术全套入门资料,从常用命令到 Dockerfile 编写再到多容器编排。 ---- +## 概述 -## 📂 文档索引 +本文件夹包含三份 Docker 核心文档,按从浅入深的学习路径组织。Docker 是云原生时代的基石技术,掌握它能让你在开发、测试、部署全流程中如鱼得水。 + +> [!tip] 学习路径 +> 建议按以下顺序学习: +> +> 1. **[[Docker常用命令]]** — 先掌握单个容器的基本操作(拉镜像、跑容器、看日志) +> 2. **[[Dockerfile编写指南]]** — 学会构建自己的镜像(从"能用"到"高效安全") +> 3. **[[docker-compose使用指南]]**— 学会用 Compose 编排多容器应用(全栈一键启动) + +## 文档索引 | 文档 | 说明 | |------|------| -| [[Docker常用命令]] | 镜像/容器/网络/卷的常用命令,日常 90% 操作 | +| [[Docker常用命令]] | 镜像/容器/网络/卷的常用命令,日常 90% 操作速查 | | [[Dockerfile编写指南]] | Dockerfile 指令详解、多阶段构建、最佳实践 | -| [[docker-compose使用指南]] | 多容器编排、健康检查、多环境配置、完整模板 | +| [[docker-compose]] | 多容器编排、健康检查、多环境配置、完整模板 | --- -## 🚀 学习路径 +## 核心概念速记 -建议按以下顺序学习: +```mermaid +flowchart LR + A["镜像 Image — 只读模板"] -->|"实例化"| B["容器 Container — 运行实例"] + B -->|"持久化"| C["Volume — 数据卷"] + B -->|"通信"| D["Network — 网络"] + E["Dockerfile"] -->|"构建"| A + F["docker-compose.yml"] -->|"编排"| B -1. **[[Docker常用命令]]** — 先掌握单个容器的基本操作 -2. **[[Dockerfile编写指南]]** — 学会构建自己的镜像 -3. **[[docker-compose使用指南]]** — 学会用 Compose 编排多容器应用 - ---- - -## 🔑 核心概念速记 + style A fill:#e3f2fd + style B fill:#e8f5e9 + style C fill:#fff3e0 + style D fill:#f3e5f5 + style E fill:#fce4ec + style F fill:#e0f2f1 +``` | 概念 | 一句话 | |------|--------| -| **镜像(Image)** | 只读模板,类似「类」 | +| **镜像(Image)** | 只读模板,类似编程中的「类」 | | **容器(Container)** | 镜像的运行实例,类似「对象」 | | **Dockerfile** | 构建镜像的指令文件 | | **Volume** | 持久化存储,容器删了数据还在 | | **Network** | 容器间通信网络 | | **Compose** | 用 YAML 定义和启动多容器应用 | +> [!note] 两条铁律 +> 1. Docker 容器是**易失的**,数据必须用 `-v` 持久化 +> 2. 镜像构建要善用**多阶段构建**和**层缓存** + --- -> Docker 容器是**易失的**,数据必须用 `-v` 持久化;镜像构建要善用**多阶段构建**和**层缓存**。 +## 关联笔记 + +- [[Dockerfile编写指南]] +- [[docker-compose]] +- [[Docker常用命令]] diff --git a/gyh/docker/docker-compose使用指南.md b/gyh/docker/docker-compose使用指南.md index 1cefbbc..8624444 100644 --- a/gyh/docker/docker-compose使用指南.md +++ b/gyh/docker/docker-compose使用指南.md @@ -1,55 +1,76 @@ +--- +tags: + - docker + - devops + - containerization + - docker-compose +create time: 2026-04-28 14:35 +--- + # Docker Compose 使用指南 -> Docker Compose 用 **YAML 文件**定义多容器应用,一条 `docker compose up` 启动所有服务。**开发环境和生产环境都大量使用。** +> 用 **YAML 文件**定义多容器应用,一条 `docker compose up` 启动所有服务。从"一条命令跑起全栈"到多环境编排,本文带你掌握 Compose 从入门到进阶的所有关键能力。 ---- +## 概述 -## 快速开始(TL;DR) +Docker Compose 的核心价值:**把分散的 `docker run` 命令变成声明式的 YAML 配置**,让团队用同一份文件即可启动完整应用栈。 -```bash -# 1. 创建 docker-compose.yml -# 2. 启动所有服务 -docker compose up -d +> [!tip] 快速上手(TL;DR) +> +> ```bash +> # 1. 写好 docker-compose.yml,包含所有服务 +> # 2. 一条命令启动全部 +> docker compose up -d +> # 3. 看日志、看状态 +> docker compose logs -f +> docker compose ps +> # 4. 清理 +> docker compose down +> ``` -# 3. 查看状态 -docker compose ps +> [!question] 什么时候需要 Compose? +> 如果你的应用只跑一个进程(比如单个 Nginx),`docker run` 就够了。但只要涉及**两个及以上**的容器协作(应用 + 数据库、前端 + API + 缓存),Compose 就是标配。 -# 4. 查看日志 -docker compose logs -f +## 正文 -# 5. 停止并清理 -docker compose down +### 一、Compose 文件结构 + +Compose 文件由三个顶级字段构成,理解它们就能掌握 80% 的配置: + +```mermaid +flowchart LR + A["services — 要跑的服务"] --> D["docker compose up"] + B["networks — 服务间的网络"] --> D + C["volumes — 数据持久化"] --> D + style A fill:#e3f2fd + style B fill:#f3e5f5 + style C fill:#e8f5e9 ``` -**目录:** [基本结构](#一基本结构) · [完整示例](#二完整示例) · [常用配置](#三常用配置详解) · [多环境](#四多环境配置) · [常用命令](#五常用命令) · [常见问题](#六常见问题) · [模板速查](#七常用模板速查) · [格式演进](#八compose-文件格式演进) · [高级配置](#九高级配置) · [最佳实践](#十最佳实践) · [速查卡](#十一快速参考卡) - ---- - -## 一、基本结构 +基本结构示例: ```yaml # docker-compose.yml -version: '3.8' - services: - web: # 前端/后端应用 - db: # 数据库 - redis: # 缓存 - nginx: # 反向代理 + web: # 前端/后端应用 + db: # 数据库 + redis: # 缓存 + nginx: # 反向代理 -networks: # 网络定义 -volumes: # 数据卷定义 +networks: # 网络定义 +volumes: # 数据卷定义 ``` +> [!note] `version` 字段已弃用 +> Compose V2(2020年起)已不需要 `version` 字段。新项目直接用无 version 的新格式,兼容性更好。 + --- -## 二、完整示例 +### 二、完整示例:Node.js + MySQL + Redis 全栈项目 -### 2.1 Node.js + MySQL + Redis 全栈项目 +一个生产级别的 Compose 文件应该包含**健康检查、网络隔离、数据持久化**: ```yaml -version: '3.8' - services: # ========== 后端应用 ========== api: @@ -58,24 +79,25 @@ services: dockerfile: Dockerfile args: NODE_ENV: production + target: production container_name: my-api ports: - "3000:3000" environment: - - NODE_ENV=production - - DB_HOST=db - - DB_PORT=3306 - - DB_USER=root - - DB_PASSWORD=root123 - - DB_NAME=mydb - - REDIS_HOST=redis - - REDIS_PORT=6379 + NODE_ENV: production + DB_HOST: db + DB_PORT: 3306 + DB_USER: root + DB_PASSWORD: root123 + DB_NAME: mydb + REDIS_HOST: redis + REDIS_PORT: 6379 depends_on: db: - condition: service_healthy + condition: service_healthy # 等 db 健康检查通过再启动 redis: condition: service_started - restart: always + restart: unless-stopped networks: - app-network volumes: @@ -91,7 +113,7 @@ services: - "80:3000" depends_on: - api - restart: always + restart: unless-stopped networks: - app-network @@ -114,7 +136,8 @@ services: interval: 10s timeout: 5s retries: 5 - restart: always + start_period: 30s + restart: unless-stopped networks: - app-network @@ -127,7 +150,7 @@ services: volumes: - redis-data:/data command: redis-server --appendonly yes - restart: always + restart: unless-stopped networks: - app-network @@ -143,7 +166,7 @@ services: depends_on: - web - api - restart: always + restart: unless-stopped networks: - app-network @@ -159,11 +182,17 @@ volumes: api-logs: ``` +> [!warning] 服务启动顺序 +> 理解这个示例的启动链:**db + redis 先启动 → api 启动(依赖它们就绪)→ web 启动(依赖 api 就绪)→ nginx 启动(依赖前端和后端)**。用 `depends_on` + `healthcheck` 确保这个顺序。 + --- -## 三、常用配置详解 +### 三、常用配置详解 -### 3.1 `build` — 从 Dockerfile 构建 +#### 3.1 `build` — 从 Dockerfile 构建 + +> [!info] build 与 image 的选择 +> 开发环境通常用 `build`(代码改动自动重构建),生产环境可以用 `image`(提前打好镜像推送仓库)。 ```yaml services: @@ -177,7 +206,7 @@ services: context: ./frontend dockerfile: Dockerfile.prod - # 方式三:加构建参数 + # 方式三:加构建参数 + 多阶段指定目标 web: build: context: . @@ -185,14 +214,14 @@ services: args: NODE_ENV: production VERSION: 1.0.0 - target: production # 多阶段构建指定阶段 + target: production # 方式四:直接用已有镜像(不构建) db: image: mysql:8.0 ``` -### 3.2 `depends_on` — 启动顺序 +#### 3.2 `depends_on` — 启动顺序 ```yaml services: @@ -204,9 +233,13 @@ services: condition: service_started # 等 redis 启动即可 ``` -> **注意:** `depends_on` 只管**启动顺序**,不管**可用性**。`condition: service_healthy` + `healthcheck` 才能真正等服务就绪。 +> [!note] depends_on 的真相 +> `depends_on` 只管**启动顺序**,不管**可用性**。不加 `healthcheck` 时,容器"已启动"不代表服务"已就绪"。数据库可能需要几秒初始化才能接受连接。 -### 3.3 `healthcheck` — 健康检查 +#### 3.3 `healthcheck` — 健康检查 + +> [!tip] 为什么数据库必须配健康检查? +> 容器启动 ≠ 服务就绪。MySQL 启动后可能要跑初始化脚本,`start_period` 就是给这段缓冲时间。 ```yaml services: @@ -214,31 +247,31 @@ services: image: mysql:8.0 healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] - interval: 10s # 每 10 秒检查一次 - timeout: 5s # 超时时间 - retries: 5 # 最多重试 5 次 - start_period: 30s # 容器启动后等 30 秒才开始检查(给数据库初始化时间) + interval: 10s # 每 10 秒检查一次 + timeout: 5s # 超时时间 + retries: 5 # 最多重试 5 次 + start_period: 30s # 启动后等 30 秒才开始检查 ``` -**常见健康检查命令:** +**常见健康检查命令速查:** | 服务 | 健康检查命令 | |------|-------------| | MySQL | `mysqladmin ping -h localhost` | | Redis | `redis-cli ping` | -| Node.js | `wget -qO- http://localhost:3000/health || exit 1` | -| Nginx | `curl -f http://localhost/ || exit 1` | +| Node.js | `wget -qO- http://localhost:3000/health` | +| Nginx | `curl -f http://localhost/` | | PostgreSQL | `pg_isready -h localhost` | -### 3.4 `environment` — 环境变量 +#### 3.4 `environment` — 环境变量 ```yaml -# 方式一:列表格式 +# 方式一:列表格式(简洁) environment: - NODE_ENV=production - DB_HOST=db -# 方式二:字典格式(推荐,更易读) +# 方式二:字典格式(推荐,更易读,支持类型) environment: NODE_ENV: production DB_HOST: db @@ -256,66 +289,69 @@ env_file: - .env ``` -### 3.5 `volumes` — 数据持久化 +#### 3.5 `volumes` — 数据持久化 ```yaml volumes: - # 命名卷(推荐,Docker 管理) - volumes: - - db-data:/var/lib/mysql + # 命名卷(推荐,Docker 自动管理) + - db-data:/var/lib/mysql - # 绑定挂载(绑定到宿主机路径) - volumes: - - ./data:/app/data + # 绑定挂载(绑定宿主机路径,开发用) + - ./data:/app/data - # 只读挂载 - volumes: - - ./nginx.conf:/etc/nginx/nginx.conf:ro + # 只读挂载(配置文件) + - ./nginx.conf:/etc/nginx/nginx.conf:ro # 匿名卷(不需要命名) - volumes: - - /tmp/data + - /tmp/data ``` -### 3.6 `restart` — 重启策略 +> [!abstract] 命名卷 vs 绑定挂载 +> - **命名卷**:Docker 管理存储位置,更安全,推荐生产环境用 +> - **绑定挂载**:直接映射宿主机路径,开发方便但可能跨平台不兼容 + +#### 3.6 `restart` — 重启策略 ```yaml -restart: always # 永远重启(包括手动停止后重启) +restart: always # 永远重启(包括手动停止后也重启) restart: on-failure # 仅失败时重启 restart: unless-stopped # 除非手动停止,否则重启(推荐) restart: no # 不自动重启(默认) ``` -### 3.7 `ports` vs `expose` +#### 3.7 `ports` vs `expose` ```yaml ports: - "8080:80" # 宿主端口:容器端口(外部可访问) - - "127.0.0.1:8080:80" # 仅本地可访问 + - "127.0.0.1:8080:80" # 仅本地可访问(安全) - "3000" # 随机端口 expose: - "3000" # 仅容器间通信,外部不可访问 ``` +> [!danger] 安全警告 +> 数据库**不要暴露 `ports` 到宿主机**!用 `expose` 或同网络即可,避免被外部直接访问。 + --- -## 四、多环境配置 +### 四、多环境配置 -### 4.1 使用覆盖文件 +> [!abstract] 多环境策略 +> 核心思路:**一份基础配置 + 环境差异覆盖**,而不是为每个环境写完整文件。 + +#### 4.1 覆盖文件(推荐) ```yaml -# docker-compose.yml(基础配置) +# docker-compose.yml(开发环境基础配置) services: api: build: . environment: - - NODE_ENV=development + NODE_ENV: development volumes: - - ./server:/app # 开发时热重载 - - db: - image: mysql:8.0 + - ./server:/app # 开发热重载 ``` ```yaml @@ -323,17 +359,16 @@ services: services: api: environment: - - NODE_ENV=production - # 覆盖开发时的 volume 挂载 - volumes: [] # 生产时不要挂载宿主机目录 + NODE_ENV: production + volumes: [] # 生产时不挂载宿主机目录 ``` ```bash -# 使用:基础 + 生产覆盖 +# 组合使用 docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d ``` -### 4.2 更简洁的方式(环境变量控制) +#### 4.2 环境变量控制(简洁) ```yaml # docker-compose.yml @@ -341,14 +376,14 @@ services: api: build: . environment: - - NODE_ENV=${NODE_ENV:-development} - - DB_HOST=${DB_HOST:-db} + NODE_ENV: ${NODE_ENV:-development} + DB_HOST: ${DB_HOST:-db} volumes: - ${MOUNT_VOLUMES:-./server:/app} ``` ```bash -# 开发环境(默认) +# 开发环境(默认值) docker compose up -d # 生产环境 @@ -357,67 +392,29 @@ NODE_ENV=production MOUNT_VOLUMES="" docker compose up -d --- -## 五、常用命令 +### 五、常用命令 -```bash -# 启动所有服务(后台) -docker compose up -d - -# 启动所有服务(前台,看日志) -docker compose up - -# 启动单个服务 -docker compose up -d db redis - -# 停止所有服务(不删除容器和数据卷) -docker compose stop - -# 停止并删除所有容器 -docker compose down - -# 停止并删除容器、网络 -docker compose down --rmi all - -# 重建镜像并启动(代码改动后) -docker compose up -d --build - -# 重建并重新初始化数据卷 -docker compose down -v -docker compose up -d --build - -# 查看服务状态 -docker compose ps - -# 查看服务日志 -docker compose logs -docker compose logs -f api # 只看 api 服务的日志 -docker compose logs -f --tail 100 db # 只看 db 的最后 100 行 - -# 进入运行中的容器 -docker compose exec api bash -docker compose exec db mysql -u root -p - -# 执行单次命令 -docker compose run api npm run db:migrate -docker compose run --rm web npm test # --rm 执行完自动删除 - -# 查看资源占用 -docker compose ps - -# 构建并启动 -docker compose build -docker compose up -d - -# 更新镜像 -docker compose pull -docker compose up -d --pull always -``` +| 操作 | 命令 | +|------|------| +| 启动全部(后台) | `docker compose up -d` | +| 启动全部(前台看日志) | `docker compose up` | +| 启动单个服务 | `docker compose up -d db redis` | +| 停止全部 | `docker compose stop` | +| 停止并删除容器 | `docker compose down` | +| 删除容器 + 网络 | `docker compose down --rmi all` | +| 代码改动后重建 | `docker compose up -d --build` | +| 删除容器 + 数据卷 | `docker compose down -v` | +| 查看服务状态 | `docker compose ps` | +| 查看日志 | `docker compose logs -f` | +| 进入容器 | `docker compose exec api bash` | +| 单次执行命令 | `docker compose run --rm web npm test` | +| 拉取新镜像 | `docker compose pull` | --- -## 六、常见问题 +### 六、常见问题 -### 6.1 容器间无法互相访问 +#### 6.1 容器间无法互相访问 ```yaml # 确保所有服务在同一网络下 @@ -432,9 +429,10 @@ networks: driver: bridge ``` -> 默认情况下,Docker Compose 会为同一个 compose 文件创建同一个网络,不同 compose 文件的容器**不能互相访问**。 +> [!note] 默认网络 +> 同一个 Compose 文件下的服务**默认在同一网络**,可以互相通过服务名访问。不同 Compose 文件的容器不能互相访问。 -### 6.2 数据库启动慢,应用连不上 +#### 6.2 数据库启动慢,应用连不上 ```yaml services: @@ -444,9 +442,9 @@ services: condition: service_healthy # 健康检查通过才启动 ``` -或在应用代码中添加重试逻辑。 +或在应用代码中添加重试逻辑(指数退避)。 -### 6.3 端口冲突 +#### 6.3 端口冲突 ```bash # 查看端口占用 @@ -461,7 +459,7 @@ ports: - "3001:3000" # 宿主机 3001 → 容器 3000 ``` -### 6.4 `.env` 文件 +#### 6.4 `.env` 文件 ```bash # 在项目根目录创建 .env 文件 @@ -471,24 +469,24 @@ NODE_ENV=production COMPOSE_PROJECT_NAME=myapp ``` -> Docker Compose 会**自动读取**当前目录的 `.env` 文件,无需额外配置。`$DB_HOST` 在 `docker-compose.yml` 中会被自动替换。 +> [!info] 自动读取 +> Docker Compose 会**自动读取**当前目录的 `.env` 文件,`$DB_HOST` 在 `docker-compose.yml` 中会被自动替换。 --- -## 七、常用模板速查 +### 七、常用模板速查 -### 7.1 纯前端静态站点(Nginx) +#### 7.1 纯前端静态站点(Nginx) ```yaml services: web: build: . - ports: - - "80:80" - restart: always + ports: ["80:80"] + restart: unless-stopped ``` -### 7.2 Node.js + PostgreSQL +#### 7.2 Node.js + PostgreSQL ```yaml services: @@ -496,7 +494,7 @@ services: build: . ports: ["3000:3000"] environment: - - DATABASE_URL=postgresql://user:pass@db:5432/mydb + DATABASE_URL: postgresql://user:pass@db:5432/mydb depends_on: db: condition: service_healthy @@ -504,21 +502,20 @@ services: db: image: postgres:16-alpine environment: - - POSTGRES_USER=user - - POSTGRES_PASSWORD=pass - - POSTGRES_DB=mydb + POSTGRES_USER: user + POSTGRES_PASSWORD: pass + POSTGRES_DB: mydb volumes: - pg-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U user"] interval: 10s -networks: volumes: pg-data: ``` -### 7.3 Next.js 全功能模板 +#### 7.3 Next.js 全功能模板 ```yaml services: @@ -529,18 +526,18 @@ services: target: production ports: ["3000:3000"] environment: - - NODE_ENV=production - - DATABASE_URL=$DATABASE_URL - - NEXTAUTH_SECRET=$NEXTAUTH_SECRET - restart: always + NODE_ENV: production + DATABASE_URL: $DATABASE_URL + NEXTAUTH_SECRET: $NEXTAUTH_SECRET + restart: unless-stopped networks: [app-network] db: image: postgres:16-alpine environment: - - POSTGRES_DB=myapp - - POSTGRES_USER=app - - POSTGRES_PASSWORD=$DB_PASSWORD + POSTGRES_DB: myapp + POSTGRES_USER: app + POSTGRES_PASSWORD: $DB_PASSWORD volumes: [pg-data:/var/lib/postgresql/data] healthcheck: test: ["CMD-SHELL", "pg_isready -U app"] @@ -560,11 +557,10 @@ volumes: --- -## 八、Compose 文件格式演进 +### 八、Compose 文件格式演进 -### 8.1 新版 Compose Specification(推荐) - -**不再需要 `version` 字段**,Docker Compose V2 起默认使用 Compose Specification 格式: +> [!tip] 新项目直接用新格式 +> Compose V2 起默认使用 Compose Specification 格式,不再需要 `version` 字段。 ```yaml # 新版写法(推荐) @@ -584,27 +580,17 @@ volumes: data: ``` -> **关键区别:** -> - V3 及以下:必须写 `version: '3.8'`,字段名用点号分隔(如 `networks.default`) -> - V4 及以上(Compose Specification):**不需要 version**,字段名用嵌套格式(如 `networks:` 下的 `default:`) - -### 8.2 如何判断使用哪种格式 - -```bash -# 查看 Compose 版本 -docker compose version - -# Compose V2+(2020 年起)→ 使用 Compose Specification 格式 -# 使用 `docker compose`(注意是空格,不是连字符) -``` - -> **建议:** 新项目直接使用无 version 的新格式,兼容性更好。 +| 对比项 | V3 及以下(旧) | V4+ / Compose Specification(新) | +|--------|----------------|--------------------------------| +| `version` 字段 | 必须写 | 不需要 | +| 命令 | `docker-compose`(连字符) | `docker compose`(空格) | +| 字段格式 | 点号分隔 | YAML 嵌套格式 | --- -## 九、高级配置 +### 九、高级配置 -### 9.1 `deploy` — 资源限制(适用于 Compose V3+) +#### 9.1 `deploy` — 资源限制 ```yaml services: @@ -617,13 +603,6 @@ services: delay: 5s max_attempts: 3 window: 120s - update_config: - parallelism: 1 # 每次更新 1 个容器 - delay: 10s - order: start-first # 先启动新再停止旧 - rollback_config: - parallelism: 1 - delay: 5s resources: limits: cpus: '0.5' # 最多 0.5 核 CPU @@ -633,9 +612,10 @@ services: memory: 128M ``` -> **注意:** `deploy` 在 `docker compose up`(非 Swarm 模式)下**部分字段不生效**(`replicas`、`update_config` 等)。资源限制 `limits/reservations` 在普通模式下仍然生效。 +> [!warning] 注意 +> `deploy` 的 `replicas`、`update_config` 在 `docker compose up`(非 Swarm)下**不生效**。但 `resources.limits/reservations` 在普通模式下仍然有效。 -### 9.2 `profiles` — 按需启停服务 +#### 9.2 `profiles` — 按需启停服务 ```yaml services: @@ -657,25 +637,17 @@ services: ``` ```bash -# 启动 app 相关服务(web + db + redis) +# 只启动 app 相关服务(web + db + redis) docker compose --profile app up -d # 启动 app + 监控 docker compose --profile app --profile monitoring up -d -# 启动所有带 profile 的服务 -docker compose --profile app --profile db --profile cache --profile monitoring up -d - # 启动所有服务(包括无 profile 的) docker compose up -d - -# 查看当前 profile 配置 -docker compose config --profiles ``` -> **场景:** 开发时只启动 `app`,测试时加上 `db`,生产环境加上 `monitoring`。 - -### 9.3 `configs` — 配置文件管理 +#### 9.3 `configs` — 配置文件管理 ```yaml services: @@ -684,26 +656,14 @@ services: configs: - source: nginx_conf target: /etc/nginx/conf.d/default.conf - mode: 0444 # 文件权限 - - app: - image: my-app:latest - configs: - - source: app_config - target: /etc/app/config.yaml - mode: 0440 + mode: 0444 configs: nginx_conf: file: ./configs/nginx.conf - app_config: - file: ./configs/app.yaml - external: false # 本地文件 ``` -> **适用场景:** 需要分发配置文件但不想硬编码到镜像中。 - -### 9.4 `secrets` — 敏感信息管理 +#### 9.4 `secrets` — 敏感信息管理 ```yaml services: @@ -713,18 +673,18 @@ services: MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_pass secrets: - db_root_pass - - db_config secrets: db_root_pass: - file: ./secrets/db_root_pass.txt # 从文件读取 + file: ./secrets/db_root_pass.txt db_config: - string: '{"max_connections": 100}' # 直接写字符串 + string: '{"max_connections": 100}' ``` -> **安全建议:** 敏感信息不要写死在 `docker-compose.yml` 中,使用 `secrets` 或 `.env` 文件管理。 +> [!danger] 安全建议 +> 敏感信息不要写死在 `docker-compose.yml` 中,使用 `secrets` 或 `.env` 文件管理。 -### 9.5 其他实用配置 +#### 9.5 其他实用配置 ```yaml services: @@ -741,13 +701,12 @@ services: entrypoint: ["node"] command: ["server.js"] - # 容器别名(同一 compose 内通过别名访问) + # 容器别名 container_name: my-app # 标签(元数据) labels: - "com.example.description=API Server" - - "com.example.environment=production" # 时区 environment: @@ -763,20 +722,13 @@ services: - ALL cap_add: - NET_BIND_SERVICE - - # 资源限制 - deploy: - resources: - limits: - cpus: '1.0' - memory: 1G ``` --- -## 十、最佳实践 +### 十、最佳实践 -### 10.1 安全性 +#### 10.1 安全性 ```yaml # ✅ 使用非 root 用户 @@ -788,22 +740,14 @@ cap_drop: security_opt: - no-new-privileges:true -# ✅ 敏感信息用 secrets 或 .env -secrets: - - db_password - -# ❌ 避免使用 privileged +# ❌ 避免 privileged # privileged: true - -# ❌ 避免绑定 0.0.0.0 -# ports: -# - "0.0.0.0:3000:3000" ``` -### 10.2 可靠性 +#### 10.2 可靠性 ```yaml -# ✅ 所有外部依赖都加 healthcheck +# ✅ 外部依赖都加 healthcheck db: healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] @@ -818,7 +762,7 @@ depends_on: condition: service_healthy ``` -### 10.3 可维护性 +#### 10.3 可维护性 ```yaml # ✅ 使用 .env 管理变量 @@ -827,15 +771,6 @@ COMPOSE_PROJECT_NAME=myapp MYSQL_ROOT_PASSWORD=xxxxx NODE_ENV=production -# docker-compose.yml -services: - db: - image: mysql:8.0 - environment: - MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} - volumes: - - db-data:/var/lib/mysql - # ✅ 命名卷而不是绑定挂载(数据持久化) volumes: db-data: # Docker 管理,更安全 @@ -845,7 +780,7 @@ volumes: # docker compose --profile dev up -d ``` -### 10.4 性能优化 +#### 10.4 性能优化 ```yaml # ✅ 生产环境使用 Alpine 镜像 @@ -860,21 +795,15 @@ deploy: cpus: '0.5' memory: 512M -# ✅ 只暴露必要的端口 -# ❌ 数据库不要暴露到宿主机 +# ✅ 数据库不要暴露到宿主机 # db: # ports: -# - "3306:3306" # 删除这行 - -# ✅ 使用 expose 代替 ports(容器间通信) -db: - expose: - - "3306" +# - "3306:3306" # 删除这行,用 expose 代替 ``` --- -## 十一、快速参考卡 +## 快速参考卡 | 字段 | 用途 | 示例 | |------|------|------| @@ -890,7 +819,12 @@ db: | `networks` | 网络归属 | `app-network` | | `profiles` | 按需启停 | `["dev", "monitoring"]` | | `secrets` | 敏感信息管理 | `file: ./secrets/xxx` | -| `configs` | 配置文件管理 | `target: /etc/app/config` | | `deploy.resources` | 资源限制 | `cpus: '0.5', memory: 512M` | | `container_name` | 固定容器名 | `my-container` | -| `user` | 运行用户 | `"1000:1000"` | + +--- + +## 关联笔记 + +- [[Dockerfile编写指南]] — 编写高效的 Dockerfile +- [[Docker常用命令]] — 日常 Docker 操作速查