vault backup: 2026-04-28 21:55:18

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