Merge remote-tracking branch 'origin/main'

This commit is contained in:
2026-04-28 22:28:14 +08:00
4 changed files with 1809 additions and 0 deletions
+475
View File
@@ -0,0 +1,475 @@
---
tags:
- docker
- devops
- containerization
create time: 2026-04-28 14:30
---
# 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. 基础镜像
FROM node:20-slim AS builder
# 2. 维护者信息(可选)
LABEL maintainer="yourname@example.com"
# 3. 设置工作目录
WORKDIR /app
# 4. 先复制依赖文件,利用 Docker 缓存
COPY package*.json ./
RUN npm ci --production
# 5. 再复制源代码
COPY . .
# 6. 构建
RUN npm run build
# 7. 暴露端口
EXPOSE 3000
# 8. 启动命令
CMD ["node", "dist/index.js"]
```
---
### 二、常用指令详解
#### 2.1 `FROM` — 基础镜像
> [!question] 为什么镜像大小差距这么大?
> 完整 OS + 工具链 vs 精简版 = 1GB vs 10MB,这就是 `FROM` 选择的影响。
**选镜像原则:有 Alpine 选 Alpine,有 Slim 选 Slim,没有再选完整版。**
```dockerfile
FROM node:20-alpine # 推荐:最小体积 ~150MB
FROM node:20-slim # 推荐:平衡方案 ~200MB
FROM node:20 # 不推荐:完整 OS,镜像臃肿 ~1GB
FROM python:3.12-slim-bookworm
FROM golang:1.22-alpine
FROM ubuntu:24.04 # 一般作为多阶段构建的第一步
```
| 镜像类型 | 大小 | 适用场景 |
|----------|------|----------|
| `alpine` | 5-10MB | 生产环境、追求极致体积 |
| `slim` | 100-200MB | 大多数生产场景,兼容性好 |
| 完整版 | 1GB+ | 开发环境、需要完整工具链 |
#### 2.2 `RUN` — 执行命令
> [!warning] 关键概念:Docker 层不可删
> `RUN` 产生的层会**永久留在镜像中**,即使后续 `rm` 了也没用。所以更新、安装、清理必须在**同一个 RUN** 里。
```dockerfile
# ❌ 不推荐:每行一个 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/*
```
#### 2.3 `COPY` vs `ADD`
> [!abstract] 简单原则
> **99% 的情况用 COPY。** ADD 的额外功能(自动解压、远程下载)行为不可预测。
```dockerfile
# COPY — 简单复制,优先使用
COPY package.json ./
COPY . .
# ADD — 仅在两种场景有用
ADD https://example.com/file.tar.gz /tmp/ # 下载远程文件(但容易出错)
ADD file.tar.gz /app/ # 自动解压(COPY + RUN tar 更清晰)
```
#### 2.4 `WORKDIR` — 工作目录
```dockerfile
WORKDIR /app # 设置工作目录
COPY package.json ./ # 相对于 /app
RUN npm install
COPY . .
# WORKDIR 会被后续指令继承,且会自动创建
WORKDIR /subdir # 现在是 /app/subdir
RUN pwd # 输出 /app/subdir
```
#### 2.5 `EXPOSE` — 声明端口
> [!info] EXPOSE 不自动映射端口
> `EXPOSE` 只是**文档性质**的声明,实际端口映射需要在 `docker run -p` 或 `docker-compose` 中指定。
```dockerfile
EXPOSE 3000
EXPOSE 80 443
```
#### 2.6 `ENV` — 环境变量
```dockerfile
ENV NODE_ENV=production
ENV APP_PORT=3000
# 容器启动时可以覆盖:docker run -e DB_HOST=192.168.1.100 my-app
```
#### 2.7 `CMD` vs `ENTRYPOINT`
| 指令 | 特点 | 可被 docker run 覆盖? |
|------|------|----------------------|
| `CMD` | 默认命令,最灵活 | ✅ 完全覆盖 |
| `ENTRYPOINT` | 入口点,固定执行 | ⚠️ 需用 `--entrypoint` 强制覆盖 |
```dockerfile
# CMD — 大多数情况用这个就够了
CMD ["node", "dist/index.js"]
# ENTRYPOINT + CMD — 入口固定,参数可配
ENTRYPOINT ["node"]
CMD ["dist/index.js"]
```
---
### 三、多阶段构建(核心优化手段)
> [!abstract] 一句话理解多阶段构建
> **先用大镜像构建 → 再把产物复制到最小镜像中运行。** 构建工具不留到最终镜像。
构建流程图:
```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
COPY package*.json ./
RUN npm ci # ① 先装依赖,利用缓存
COPY . . # ② 再复制源码,代码改了才重建
RUN npm run build # ③ 编译
# ========== 第二阶段:运行 ==========
FROM node:20-alpine AS production
RUN apk add --no-cache tini # tini 处理僵尸进程(推荐)
WORKDIR /app
COPY package*.json ./
RUN npm ci --production # 只装 production 依赖
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/public ./public
ENV NODE_ENV=production
EXPOSE 3000
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "dist/index.js"]
```
#### 3.2 Java 多阶段构建
```dockerfile
# 第一阶段:Maven 构建
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /app
COPY pom.xml .
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
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
```
#### 3.3 Go 多阶段构建
```dockerfile
# 第一阶段:编译
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
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
EXPOSE 8080
CMD ["/app/server"]
```
---
### 四、.dockerignore 文件
> [!danger] 和 .gitignore 一样重要!
> 不写 `.dockerignore` 会把不必要的文件塞进镜像,增大体积、暴露密钥。
```dockerignore
# 依赖
node_modules
npm-debug.log
# Git
.git
.gitignore
# 构建产物(别把 build 拷进去,让容器自己 build)
dist
build
*.local
# 编辑器和文档
.vscode
.idea
*.swp
README.md
LICENSE
tests
# Docker 自身文件
docker-compose.yml
Dockerfile
.dockerignore
# 环境变量(千万不要拷进去!)
.env
.env.local
.env.*.local
```
---
### 五、最佳实践清单
#### ✅ 应该做
| 实践 | 说明 |
|------|------|
| 使用具体版本号 | `FROM node:20.11-alpine`,不要用 `latest` |
| 用 `alpine` 或 `slim` | 减小镜像体积 |
| 合并 `RUN` 指令 | 减少镜像层数 |
| 先复制 `package.json` 再 `npm install` | 利用 Docker 层缓存 |
| 用 `.dockerignore` | 排除不必要的文件 |
| 用多阶段构建 | 分离构建和运行环境 |
| 非 root 用户运行 | 安全考虑 |
| 用 `CMD ["exec格式"]` | 避免 shell 包装问题 |
#### ❌ 不应该做
| 实践 | 问题 |
|------|------|
| 用 `latest` 标签 | 构建结果不可复现 |
| 在 Dockerfile 中存密码/密钥 | 暴露敏感信息 |
| 用 `ADD` 替代 `COPY` | 不可预测行为 |
| 安装不必要的应用 | 镜像臃肿 |
| 用 `RUN apt-get install && apt-get remove` | 删除的文件仍在镜像层中 |
| 以 root 用户运行应用 | 安全风险 |
---
### 六、安全加固
#### 6.1 使用非 root 用户
```dockerfile
FROM node:20-alpine
WORKDIR /app
# 利用 --chown 设置文件所有者,避免切换用户后权限问题
COPY --chown=node:node package*.json ./
RUN npm ci
COPY --chown=node:node . .
# 切换到非 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
```
---
### 七、构建缓存优化
> [!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 基础镜像 + 多阶段构建 + .dockerignore
```
#### 8.2 容器启动后立即退出
```bash
# 查看日志
docker logs <容器名>
# 常见原因:
# 1. CMD/ENTRYPOINT 写错了
# 2. 主进程不是 PID 1(用 tini 或 dumb-init)
# 3. 应用启动失败(检查日志)
```
#### 8.3 端口无法访问
```bash
# 1. 检查 docker run -p 是否映射
docker run -p 3000:3000 ...
# 2. 容器内应用监听 0.0.0.0(不是 localhost)
# 3. 检查防火墙
```
---
## 关联笔记
- [[docker-compose]] — 用 Compose 编排多容器应用
- [[Docker常用命令]] — 日常 Docker 操作速查
+433
View File
@@ -0,0 +1,433 @@
---
tags:
- docker
- devops
- containerization
- reference
create time: 2026-04-28 14:40
---
# Docker 常用命令
> 按「容器生命周期」组织的命令速查手册。从拉取镜像到清理收尾,日常 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)
> [!info] 镜像 = 只读模板
> 镜像是容器的"蓝图"。同一个镜像可以运行多个容器实例,它们互不影响。
```bash
# 拉取镜像
docker pull nginx:latest # 拉取最新标签
docker pull redis:7-alpine # 拉取精简版(alpine 体积更小)
docker pull node:20-slim
# 查看所有镜像
docker images # 或 docker image ls
# 搜索镜像
docker search nginx # 搜索官方和社区镜像
# 给镜像打标签
docker tag my-app:latest myrepo/my-app:v1.0
# 删除镜像
docker rmi nginx # 删除单个
docker rmi $(docker images -q) # 删除所有镜像(谨慎!)
# 构建镜像
docker build -t my-app:v1.0 . # 当前目录构建(默认用 Dockerfile)
docker build -t my-app:v1.0 -f Dockerfile.prod . # 指定 Dockerfile
```
> [!tip] 镜像分层缓存
> `docker build` 从上到下逐行执行,每一行都会生成一层缓存。把**不变的部分放前面**(安装依赖),**变化的部分放后面**(复制代码),充分利用缓存加速。
---
### 二、容器管理(Container)
> [!abstract] 容器 = 镜像的运行实例
> 容器是轻量级、隔离的进程。删除容器 = 停止进程 + 释放资源,容器内部写入的数据默认会丢失(除非挂载了 Volume)。
```bash
# 运行容器
docker run -d -p 8080:80 --name web nginx
# -d 后台运行(detached)
# -p 8080:80 宿主机端口:容器端口
# --name 容器名称(方便后续操作)
# 前台运行(查看日志用)
docker run -it --rm nginx bash
# -i 交互式 -t 终端 --rm 退出后自动删除
# 查看所有容器(含已停止的)
docker ps -a # 或 docker container ls -a
# 查看运行中的容器
docker ps
# 启动/停止/重启容器
docker start web
docker stop web # 优雅停止(发 SIGTERM,等 10s 后 SIGKILL)
docker restart web
docker kill web # 强制停止(发 SIGKILL)
# 进入运行中的容器
docker exec -it web bash # 在运行中的容器里执行命令
docker exec -it web sh # 有些精简镜像没有 bash,用 sh
# 删除容器
docker rm web # 删除已停止的容器
docker rm -f web # 强制删除运行中的容器
# 查看容器日志
docker logs web
docker logs -f web # 实时跟踪日志(类似 tail -f)
docker logs --tail 100 web # 只看最后 100 行
```
**`docker run` 常用参数速查:**
| 参数 | 作用 |
|------|------|
| `-d` | 后台运行 |
| `-p host:container` | 端口映射 |
| `-v host:container` | 挂载卷 |
| `-e KEY=VALUE` | 设置环境变量 |
| `--name` | 容器名称 |
| `--network` | 指定网络 |
| `--restart=always` | 容器崩溃/开机自动重启 |
| `--rm` | 退出后自动删除(临时调试用) |
| `-it` | 交互式终端 |
| `--gpus all` | 启用 GPU(NVIDIA) |
---
### 三、网络管理(Network)
> [!question] 为什么容器能互相"看见"?
> 答:Docker 为每个容器分配独立 IP,同一网络下的容器可以通过**容器名**(自动 DNS 解析)互相通信。
```bash
# 查看所有网络
docker network ls
# 创建自定义网络
docker network create my-network
# 容器加入指定网络
docker run -d --name web1 --network my-network nginx
docker run -d --name db1 --network my-network mysql
# 容器间通过名称互相访问(自动 DNS 解析)
docker exec web1 ping db1 # 在同一网络下可直接 ping 容器名
# 删除网络
docker network rm my-network
```
> [!note] 为什么需要自定义网络?
> Docker 默认的 `bridge` 网络不支持容器名 DNS 解析。自定义网络可以让容器通过**名字**互相通信。
---
### 四、数据卷(Volume)
> [!danger] 容器是易失的(ephemeral)
> 删除容器 = 容器内数据消失!必须用 `-v` 把数据持久化到 Volume 或宿主机目录。
```bash
# 查看所有卷
docker volume ls
# 创建卷
docker volume create my-data
# 挂载卷到容器
docker run -d -v my-data:/data --name app nginx
# 挂载宿主机目录到容器(bind mount)
docker run -d -v /host/path:/container/path nginx
# 只读挂载
docker run -d -v my-data:/data:ro nginx
# 查看卷详情
docker volume inspect my-data
```
---
### 五、日志与调试
```bash
# 进入容器
docker exec -it <容器名> bash
# 查看容器资源占用
docker stats
# 查看容器详情(JSON 格式)
docker inspect web
# 查看容器进程
docker top web
# 拷贝文件进出容器
docker cp container.log web:/tmp/
docker cp web:/tmp/remote.log ./local.log
# 查看镜像构建历史
docker history my-app:v1.0
```
---
### 六、清理命令(重要!)
> [!warning] 磁盘占用会持续增长
> 停止的容器、未使用的镜像、悬空卷会悄悄吃掉磁盘空间。定期清理是好习惯。
```bash
# 删除所有已停止的容器
docker container prune
# 删除所有未使用的镜像
docker image prune -a
# 删除所有未使用的卷
docker volume prune
# 一键清理(容器+镜像+卷+网络,谨慎使用)
docker system prune -a
# 查看磁盘占用
docker system df
```
---
### 七、常用完整命令场景
#### 7.1 运行 Nginx
```bash
docker run -d \
-p 80:80 \
-v ./nginx.conf:/etc/nginx/nginx.conf:ro \
-v ./html:/usr/share/nginx/html \
--name my-nginx \
--restart=always \
nginx:alpine
```
#### 7.2 运行 MySQL
```bash
docker run -d \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=root123 \
-e MYSQL_DATABASE=mydb \
-v mysql-data:/var/lib/mysql \
--name my-mysql \
--restart=always \
mysql:8.0
```
#### 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
```
#### 7.4 运行 Node 开发环境
```bash
docker run -it \
-p 3000:3000 \
-v $(pwd):/app \
-w /app \
node:20 \
bash
# -v $(pwd):/app 挂载项目目录,修改代码实时生效
# -w /app 设置工作目录
```
---
### 八、进阶:Docker 网络模式
Docker 提供四种网络模式:
| 模式 | 特点 | 适用场景 |
|------|------|----------|
| `bridge`(默认) | 容器有独立 IP | 大多数场景 |
| `host` | 共享宿主机网络,性能最好 | 高性能需求 |
| `none` | 无网络(完全隔离) | 安全隔离 |
| `container` | 共享另一个容器的网络命名空间 | 特殊协作 |
```bash
# bridge(默认)
docker run -d --network bridge nginx
# host
docker run -d --network host nginx
# none
docker run -d --network none nginx
# container(共享 web 容器的网络)
docker run -d --network container:web nginx
# 推荐:自定义 bridge 网络
docker network create my-net
docker run -d --network my-net --name app nginx
```
---
### 九、进阶:日志驱动
```bash
# 查看容器的日志配置
docker inspect web | grep LogConfig
```
| 驱动类型 | 说明 |
|----------|------|
| `json-file` | 默认,日志写入文件 |
| `local` | 循环写入,控制大小 |
| `syslog` | 发送到 syslog |
| `journald` | 发送到 systemd journal |
| `none` | 不记录日志 |
```yaml
# docker-compose 中限制日志大小
services:
api:
logging:
driver: json-file
options:
max-size: "10m" # 单个日志文件最大 10MB
max-file: "3" # 最多保留 3 个文件
```
---
### 十、进阶:资源限制
> [!tip] 为什么需要限制资源?
> 防止某个容器吃光 CPU/内存导致宿主机崩溃,这是多容器环境的必修课。
```bash
# 限制 CPU 和内存
docker run -d \
--cpus="1.5" \
--memory="512m" \
--memory-swap="1g" \
--pids-limit=100 \
nginx
# --cpus 限制 1.5 个 CPU 核心
# --memory 限制 512MB 内存
# --memory-swap 允许使用的 swap 总量
# --pids-limit 最多 100 个进程(防 fork 炸弹)
```
---
### 十一、进阶:Docker Swarm(简易集群)
> [!info] Swarm vs Kubernetes
> Swarm 适合单节点或小团队快速上手。大规模生产环境建议直接用 Kubernetes。
```bash
# 初始化集群(单节点)
docker swarm init
# 查看节点
docker node ls
# 创建服务(Swarm 管理方式)
docker service create --name web -p 80:80 --replicas 3 nginx
# 扩容
docker service scale web=5
# 查看服务
docker service ls
# 零停机滚动更新
docker service update --image nginx:1.25 web
# 删除服务
docker service rm web
# 退出集群
docker swarm leave
# 多节点集群
docker swarm init --advertise-addr <IP>
docker swarm join-token manager # 获取管理节点令牌
docker swarm join-token worker # 获取工作节点令牌
```
---
## 快速速查表
| 操作 | 命令 |
|------|------|
| 启动 Docker 服务 | `systemctl start docker` |
| 查看版本 | `docker --version` |
| 查看信息 | `docker info` |
| 登录镜像仓库 | `docker login` |
| 推送镜像 | `docker push my-app:v1.0` |
| 导出镜像 | `docker save -o my-app.tar my-app:v1.0` |
| 导入镜像 | `docker load -i my-app.tar` |
| 查看容器日志 | `docker logs -f <容器名>` |
| 进入容器 | `docker exec -it <容器名> bash` |
| 停止所有容器 | `docker stop $(docker ps -q)` |
| 删除所有容器 | `docker rm -f $(docker ps -aq)` |
| 查看磁盘占用 | `docker system df` |
| 全面清理 | `docker system prune -a` |
---
## 关联笔记
- [[Dockerfile编写指南]] — 编写高效的 Dockerfile
- [[docker-compose]] — 用 Compose 编排多容器应用
+71
View File
@@ -0,0 +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% 操作速查 |
| [[Dockerfile编写指南]] | Dockerfile 指令详解、多阶段构建、最佳实践 |
| [[docker-compose]] | 多容器编排、健康检查、多环境配置、完整模板 |
---
## 核心概念速记
```mermaid
flowchart LR
A["镜像 Image — 只读模板"] -->|"实例化"| B["容器 Container — 运行实例"]
B -->|"持久化"| C["Volume — 数据卷"]
B -->|"通信"| D["Network — 网络"]
E["Dockerfile"] -->|"构建"| A
F["docker-compose.yml"] -->|"编排"| B
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)** | 只读模板,类似编程中的「类」 |
| **容器(Container)** | 镜像的运行实例,类似「对象」 |
| **Dockerfile** | 构建镜像的指令文件 |
| **Volume** | 持久化存储,容器删了数据还在 |
| **Network** | 容器间通信网络 |
| **Compose** | 用 YAML 定义和启动多容器应用 |
> [!note] 两条铁律
> 1. Docker 容器是**易失的**,数据必须用 `-v` 持久化
> 2. 镜像构建要善用**多阶段构建**和**层缓存**
---
## 关联笔记
- [[Dockerfile编写指南]]
- [[docker-compose]]
- [[Docker常用命令]]
+830
View File
@@ -0,0 +1,830 @@
---
tags:
- docker
- devops
- containerization
- docker-compose
create time: 2026-04-28 14:35
---
# Docker Compose 使用指南
> 用 **YAML 文件**定义多容器应用,一条 `docker compose up` 启动所有服务。从"一条命令跑起全栈"到多环境编排,本文带你掌握 Compose 从入门到进阶的所有关键能力。
## 概述
Docker Compose 的核心价值:**把分散的 `docker run` 命令变成声明式的 YAML 配置**,让团队用同一份文件即可启动完整应用栈。
> [!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
> ```
> [!question] 什么时候需要 Compose?
> 如果你的应用只跑一个进程(比如单个 Nginx),`docker run` 就够了。但只要涉及**两个及以上**的容器协作(应用 + 数据库、前端 + API + 缓存),Compose 就是标配。
## 正文
### 一、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
```
基本结构示例:
```yaml
# docker-compose.yml
services:
web: # 前端/后端应用
db: # 数据库
redis: # 缓存
nginx: # 反向代理
networks: # 网络定义
volumes: # 数据卷定义
```
> [!note] `version` 字段已弃用
> Compose V2(2020年起)已不需要 `version` 字段。新项目直接用无 version 的新格式,兼容性更好。
---
### 二、完整示例:Node.js + MySQL + Redis 全栈项目
一个生产级别的 Compose 文件应该包含**健康检查、网络隔离、数据持久化**:
```yaml
services:
# ========== 后端应用 ==========
api:
build:
context: ./server
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
depends_on:
db:
condition: service_healthy # 等 db 健康检查通过再启动
redis:
condition: service_started
restart: unless-stopped
networks:
- app-network
volumes:
- api-logs:/app/logs
# ========== 前端应用 ==========
web:
build:
context: ./client
dockerfile: Dockerfile
container_name: my-web
ports:
- "80:3000"
depends_on:
- api
restart: unless-stopped
networks:
- app-network
# ========== 数据库 ==========
db:
image: mysql:8.0
container_name: my-db
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: mydb
MYSQL_USER: admin
MYSQL_PASSWORD: admin123
ports:
- "3306:3306"
volumes:
- db-data:/var/lib/mysql
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
restart: unless-stopped
networks:
- app-network
# ========== 缓存 ==========
redis:
image: redis:7-alpine
container_name: my-redis
ports:
- "6379:6379"
volumes:
- redis-data:/data
command: redis-server --appendonly yes
restart: unless-stopped
networks:
- app-network
# ========== 反向代理 ==========
nginx:
image: nginx:alpine
container_name: my-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- web
- api
restart: unless-stopped
networks:
- app-network
# ========== 网络 ==========
networks:
app-network:
driver: bridge
# ========== 数据卷 ==========
volumes:
db-data:
redis-data:
api-logs:
```
> [!warning] 服务启动顺序
> 理解这个示例的启动链:**db + redis 先启动 → api 启动(依赖它们就绪)→ web 启动(依赖 api 就绪)→ nginx 启动(依赖前端和后端)**。用 `depends_on` + `healthcheck` 确保这个顺序。
---
### 三、常用配置详解
#### 3.1 `build` — 从 Dockerfile 构建
> [!info] build 与 image 的选择
> 开发环境通常用 `build`(代码改动自动重构建),生产环境可以用 `image`(提前打好镜像推送仓库)。
```yaml
services:
# 方式一:当前目录(自动找 Dockerfile)
web:
build: .
# 方式二:指定上下文和 Dockerfile
web:
build:
context: ./frontend
dockerfile: Dockerfile.prod
# 方式三:加构建参数 + 多阶段指定目标
web:
build:
context: .
dockerfile: Dockerfile
args:
NODE_ENV: production
VERSION: 1.0.0
target: production
# 方式四:直接用已有镜像(不构建)
db:
image: mysql:8.0
```
#### 3.2 `depends_on` — 启动顺序
```yaml
services:
api:
depends_on:
db:
condition: service_healthy # 等 db 健康检查通过
redis:
condition: service_started # 等 redis 启动即可
```
> [!note] depends_on 的真相
> `depends_on` 只管**启动顺序**,不管**可用性**。不加 `healthcheck` 时,容器"已启动"不代表服务"已就绪"。数据库可能需要几秒初始化才能接受连接。
#### 3.3 `healthcheck` — 健康检查
> [!tip] 为什么数据库必须配健康检查?
> 容器启动 ≠ 服务就绪。MySQL 启动后可能要跑初始化脚本,`start_period` 就是给这段缓冲时间。
```yaml
services:
db:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
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` |
| Nginx | `curl -f http://localhost/` |
| PostgreSQL | `pg_isready -h localhost` |
#### 3.4 `environment` — 环境变量
```yaml
# 方式一:列表格式(简洁)
environment:
- NODE_ENV=production
- DB_HOST=db
# 方式二:字典格式(推荐,更易读,支持类型)
environment:
NODE_ENV: production
DB_HOST: db
DB_PORT: 3306
# 方式三:从 .env 文件加载(最推荐)
env_file:
- .env.production
# 混合使用
environment:
NODE_ENV: production
DB_HOST: db
env_file:
- .env
```
#### 3.5 `volumes` — 数据持久化
```yaml
volumes:
# 命名卷(推荐,Docker 自动管理)
- db-data:/var/lib/mysql
# 绑定挂载(绑定宿主机路径,开发用)
- ./data:/app/data
# 只读挂载(配置文件)
- ./nginx.conf:/etc/nginx/nginx.conf:ro
# 匿名卷(不需要命名)
- /tmp/data
```
> [!abstract] 命名卷 vs 绑定挂载
> - **命名卷**:Docker 管理存储位置,更安全,推荐生产环境用
> - **绑定挂载**:直接映射宿主机路径,开发方便但可能跨平台不兼容
#### 3.6 `restart` — 重启策略
```yaml
restart: always # 永远重启(包括手动停止后也重启)
restart: on-failure # 仅失败时重启
restart: unless-stopped # 除非手动停止,否则重启(推荐)
restart: no # 不自动重启(默认)
```
#### 3.7 `ports` vs `expose`
```yaml
ports:
- "8080:80" # 宿主端口:容器端口(外部可访问)
- "127.0.0.1:8080:80" # 仅本地可访问(安全)
- "3000" # 随机端口
expose:
- "3000" # 仅容器间通信,外部不可访问
```
> [!danger] 安全警告
> 数据库**不要暴露 `ports` 到宿主机**!用 `expose` 或同网络即可,避免被外部直接访问。
---
### 四、多环境配置
> [!abstract] 多环境策略
> 核心思路:**一份基础配置 + 环境差异覆盖**,而不是为每个环境写完整文件。
#### 4.1 覆盖文件(推荐)
```yaml
# docker-compose.yml(开发环境基础配置)
services:
api:
build: .
environment:
NODE_ENV: development
volumes:
- ./server:/app # 开发热重载
```
```yaml
# docker-compose.prod.yml(生产覆盖)
services:
api:
environment:
NODE_ENV: production
volumes: [] # 生产时不挂载宿主机目录
```
```bash
# 组合使用
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
```
#### 4.2 环境变量控制(简洁)
```yaml
# docker-compose.yml
services:
api:
build: .
environment:
NODE_ENV: ${NODE_ENV:-development}
DB_HOST: ${DB_HOST:-db}
volumes:
- ${MOUNT_VOLUMES:-./server:/app}
```
```bash
# 开发环境(默认值)
docker compose up -d
# 生产环境
NODE_ENV=production MOUNT_VOLUMES="" docker compose up -d
```
---
### 五、常用命令
| 操作 | 命令 |
|------|------|
| 启动全部(后台) | `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 容器间无法互相访问
```yaml
# 确保所有服务在同一网络下
services:
api:
networks: [app-network]
db:
networks: [app-network]
networks:
app-network:
driver: bridge
```
> [!note] 默认网络
> 同一个 Compose 文件下的服务**默认在同一网络**,可以互相通过服务名访问。不同 Compose 文件的容器不能互相访问。
#### 6.2 数据库启动慢,应用连不上
```yaml
services:
api:
depends_on:
db:
condition: service_healthy # 健康检查通过才启动
```
或在应用代码中添加重试逻辑(指数退避)。
#### 6.3 端口冲突
```bash
# 查看端口占用
netstat -ano | findstr :3000 # Windows
lsof -i :3000 # macOS/Linux
```
修改 `docker-compose.yml` 中的端口映射:
```yaml
ports:
- "3001:3000" # 宿主机 3001 → 容器 3000
```
#### 6.4 `.env` 文件
```bash
# 在项目根目录创建 .env 文件
DB_HOST=db
DB_PASSWORD=root123
NODE_ENV=production
COMPOSE_PROJECT_NAME=myapp
```
> [!info] 自动读取
> Docker Compose 会**自动读取**当前目录的 `.env` 文件,`$DB_HOST` 在 `docker-compose.yml` 中会被自动替换。
---
### 七、常用模板速查
#### 7.1 纯前端静态站点(Nginx)
```yaml
services:
web:
build: .
ports: ["80:80"]
restart: unless-stopped
```
#### 7.2 Node.js + PostgreSQL
```yaml
services:
app:
build: .
ports: ["3000:3000"]
environment:
DATABASE_URL: postgresql://user:pass@db:5432/mydb
depends_on:
db:
condition: service_healthy
db:
image: postgres:16-alpine
environment:
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
volumes:
pg-data:
```
#### 7.3 Next.js 全功能模板
```yaml
services:
app:
build:
context: .
dockerfile: Dockerfile
target: production
ports: ["3000:3000"]
environment:
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
volumes: [pg-data:/var/lib/postgresql/data]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
networks: [app-network]
redis:
image: redis:7-alpine
volumes: [redis-data:/data]
networks: [app-network]
networks:
app-network:
volumes:
pg-data:
redis-data:
```
---
### 八、Compose 文件格式演进
> [!tip] 新项目直接用新格式
> Compose V2 起默认使用 Compose Specification 格式,不再需要 `version` 字段。
```yaml
# 新版写法(推荐)
services:
app:
image: node:20-alpine
ports:
- "3000:3000"
environment:
NODE_ENV: production
networks:
default:
driver: bridge
volumes:
data:
```
| 对比项 | V3 及以下(旧) | V4+ / Compose Specification(新) |
|--------|----------------|--------------------------------|
| `version` 字段 | 必须写 | 不需要 |
| 命令 | `docker-compose`(连字符) | `docker compose`(空格) |
| 字段格式 | 点号分隔 | YAML 嵌套格式 |
---
### 九、高级配置
#### 9.1 `deploy` — 资源限制
```yaml
services:
api:
image: my-api:latest
deploy:
replicas: 3 # 副本数(Swarm 模式)
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
resources:
limits:
cpus: '0.5' # 最多 0.5 核 CPU
memory: 512M # 最多 512MB 内存
reservations:
cpus: '0.25'
memory: 128M
```
> [!warning] 注意
> `deploy` 的 `replicas`、`update_config` 在 `docker compose up`(非 Swarm)下**不生效**。但 `resources.limits/reservations` 在普通模式下仍然有效。
#### 9.2 `profiles` — 按需启停服务
```yaml
services:
web:
image: nginx:alpine
profiles: ["app"]
db:
image: postgres:16-alpine
profiles: ["db", "app"]
redis:
image: redis:7-alpine
profiles: ["cache", "app"]
monitoring:
image: grafana/grafana
profiles: ["monitoring"]
```
```bash
# 只启动 app 相关服务(web + db + redis)
docker compose --profile app up -d
# 启动 app + 监控
docker compose --profile app --profile monitoring up -d
# 启动所有服务(包括无 profile 的)
docker compose up -d
```
#### 9.3 `configs` — 配置文件管理
```yaml
services:
nginx:
image: nginx:alpine
configs:
- source: nginx_conf
target: /etc/nginx/conf.d/default.conf
mode: 0444
configs:
nginx_conf:
file: ./configs/nginx.conf
```
#### 9.4 `secrets` — 敏感信息管理
```yaml
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_pass
secrets:
- db_root_pass
secrets:
db_root_pass:
file: ./secrets/db_root_pass.txt
db_config:
string: '{"max_connections": 100}'
```
> [!danger] 安全建议
> 敏感信息不要写死在 `docker-compose.yml` 中,使用 `secrets` 或 `.env` 文件管理。
#### 9.5 其他实用配置
```yaml
services:
app:
image: node:20-alpine
# 指定运行用户
user: "1000:1000"
# 工作目录
working_dir: /app
# 覆盖默认入口点和命令
entrypoint: ["node"]
command: ["server.js"]
# 容器别名
container_name: my-app
# 标签(元数据)
labels:
- "com.example.description=API Server"
# 时区
environment:
- TZ=Asia/Shanghai
# 挂载共享内存(深度学习场景)
shm_size: '2gb'
# 安全选项
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
```
---
### 十、最佳实践
#### 10.1 安全性
```yaml
# ✅ 使用非 root 用户
user: "1000:1000"
# ✅ 最小化权限
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
# ❌ 避免 privileged
# privileged: true
```
#### 10.2 可靠性
```yaml
# ✅ 外部依赖都加 healthcheck
db:
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
start_period: 30s
# ✅ 合理的重启策略
restart: unless-stopped
# ✅ 依赖顺序用 condition
depends_on:
db:
condition: service_healthy
```
#### 10.3 可维护性
```yaml
# ✅ 使用 .env 管理变量
# .env
COMPOSE_PROJECT_NAME=myapp
MYSQL_ROOT_PASSWORD=xxxxx
NODE_ENV=production
# ✅ 命名卷而不是绑定挂载(数据持久化)
volumes:
db-data: # Docker 管理,更安全
# ✅ 用 profiles 组织服务
# 开发只启动 app + db
# docker compose --profile dev up -d
```
#### 10.4 性能优化
```yaml
# ✅ 生产环境使用 Alpine 镜像
image: node:20-alpine # 约 150MB
# vs
image: node:20 # 约 1GB
# ✅ 限制资源使用
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
# ✅ 数据库不要暴露到宿主机
# db:
# ports:
# - "3306:3306" # 删除这行,用 expose 代替
```
---
## 快速参考卡
| 字段 | 用途 | 示例 |
|------|------|------|
| `image` | 使用已有镜像 | `image: nginx:alpine` |
| `build` | 从 Dockerfile 构建 | `build: ./app` |
| `ports` | 端口映射(外部可访问) | `"80:80"` |
| `expose` | 端口暴露(仅容器间) | `"3000"` |
| `volumes` | 数据持久化 | `data:/var/lib/mysql` |
| `environment` | 环境变量 | `NODE_ENV: production` |
| `depends_on` | 启动依赖 | `db: condition: service_healthy` |
| `healthcheck` | 健康检查 | `test: ["CMD", "curl", ...]` |
| `restart` | 重启策略 | `unless-stopped` |
| `networks` | 网络归属 | `app-network` |
| `profiles` | 按需启停 | `["dev", "monitoring"]` |
| `secrets` | 敏感信息管理 | `file: ./secrets/xxx` |
| `deploy.resources` | 资源限制 | `cpus: '0.5', memory: 512M` |
| `container_name` | 固定容器名 | `my-container` |
---
## 关联笔记
- [[Dockerfile编写指南]] — 编写高效的 Dockerfile
- [[Docker常用命令]] — 日常 Docker 操作速查