diff --git a/gyh/docker/Dockerfile编写指南.md b/gyh/docker/Dockerfile编写指南.md new file mode 100644 index 0000000..dee54c8 --- /dev/null +++ b/gyh/docker/Dockerfile编写指南.md @@ -0,0 +1,491 @@ +# Dockerfile 编写指南 + +> Dockerfile 是**镜像构建的蓝图**,每一行指令都会在镜像中生成一层。写好 Dockerfile 的核心目标:**镜像更小、构建更快、启动更安全**。 + +--- + +## 一、Dockerfile 基本结构 + +```dockerfile +# 1. 基础镜像 +FROM node:20-slim AS builder + +# 2. 维护者信息(可选) +LABEL maintainer="yourname@example.com" + +# 3. 设置工作目录 +WORKDIR /app + +# 4. 复制依赖文件并安装 +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` — 基础镜像 + +```dockerfile +# 选镜像的通用原则: +# 有 Alpine 选 Alpine(体积小) +# 有 Slim 选 Slim(平衡体积和功能) +# 没有再选完整版本 + +FROM node:20-alpine # 推荐:最小体积 +FROM node:20-slim # 推荐:平衡方案 +FROM node:20 # 不推荐:包含完整 OS,镜像臃肿 +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` — 执行命令 + +```dockerfile +# 合并 RUN 指令减少镜像层数 +# 不推荐:每行一个 RUN,产生多层 +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` + +```dockerfile +# 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 和下载远程文件时有用 +``` + +### 2.4 `WORKDIR` — 工作目录 + +```dockerfile +WORKDIR /app # 设置工作目录 +COPY package.json ./ # 相对于 /app +RUN npm install +COPY . . # 复制代码到 /app + +# WORKDIR 会被后续指令继承,且会自动创建(不需要提前 mkdir) +WORKDIR /subdir # 现在是 /app/subdir +RUN pwd # 输出 /app/subdir +``` + +### 2.5 `EXPOSE` — 声明端口 + +```dockerfile +# 声明容器运行时监听的端口(文档性质,不自动映射) +EXPOSE 3000 +EXPOSE 80 443 + +# 实际端口映射需要在 docker run -p 或 docker-compose 中指定 +``` + +### 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 +``` + +### 2.7 `CMD` vs `ENTRYPOINT` + +```dockerfile +# CMD — 容器启动时执行的默认命令(可被覆盖) +CMD ["node", "dist/index.js"] +# 运行 docker run my-app echo "hello" 会覆盖 CMD + +# ENTRYPOINT — 入口点,不可被 docker run 覆盖(但可用 --entrypoint 强制覆盖) +ENTRYPOINT ["node"] +CMD ["dist/index.js"] # 作为 ENTRYPOINT 的参数 + +# 实际开发中,大多数情况用 CMD 就够了 +``` + +--- + +## 三、多阶段构建(关键优化手段) + +### 3.1 为什么需要多阶段构建 + +前端项目用 Next.js/Vite + TypeScript 构建,需要 Node.js 全套环境。但运行只需要一个编译后的静态文件。多阶段构建可以**把构建工具留在第一阶段,最终镜像只放产物**。 + +### 3.2 前端多阶段构建(Node + TypeScript) + +```dockerfile +# ========== 第一阶段:构建 ========== +FROM node:20-alpine AS builder + +WORKDIR /app + +# ① 先复制依赖文件,利用 Docker 缓存 +COPY package*.json ./ +RUN npm ci + +# ② 再复制源码(代码改了才重新构建) +COPY . . + +# ③ 构建 +RUN npm run build + +# ========== 第二阶段:运行 ========== +FROM node:20-alpine AS production + +# 安装 tini(处理僵尸进程,可选但推荐) +RUN apk add --no-cache tini + +WORKDIR /app + +# 仅复制 production 依赖 +COPY package*.json ./ +RUN npm ci --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 多阶段构建 + +```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 + +# 第二阶段:运行 +FROM eclipse-temurin:21-jre-alpine +WORKDIR /app +COPY --from=build /app/target/*.jar app.jar +EXPOSE 8080 +ENTRYPOINT ["java", "-jar", "app.jar"] +``` + +### 3.4 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 + +# 第二阶段:运行 +FROM alpine:3.19 +RUN apk --no-cache add ca-certificates +COPY --from=builder /app/server /app/server +EXPOSE 8080 +CMD ["/app/server"] +``` + +--- + +## 四、.dockerignore 文件 + +> **和 `.gitignore` 一样重要!** 不写 `.dockerignore` 会导致不必要的文件被复制到镜像中,增大体积、暴露敏感信息。 + +```dockerignore +# 依赖 +node_modules +npm-debug.log + +# Git +.git +.gitignore + +# 构建产物(别把 build 直接拷进去,让容器自己 build) +dist +build +*.local + +# 编辑器 +.vscode +.idea +*.swp + +# Docker 自身 +docker-compose.yml +Dockerfile +.dockerignore + +# 文档和测试 +README.md +LICENSE +tests +__tests__ + +# 环境变量(千万不要拷进去!) +.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 用户运行应用 | 安全风险 | + +--- + +## 六、非 Root 用户示例 + +```dockerfile +FROM node:20-alpine + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY . . + +# 创建非 root 用户 +RUN addgroup -S appgroup && \ + adduser -S appuser -G appgroup + +# 切换用户 +USER appuser + +EXPOSE 3000 + +CMD ["node", "dist/index.js"] +``` + +--- + +## 七、常见问题排查 + +### 7.1 镜像太大 + +```bash +# 分析镜像各层大小 +docker history my-app:latest + +# 检查是否有未清理的缓存 +# 解决:使用 alpine/slim 基础镜像 + 多阶段构建 + .dockerignore +``` + +### 7.2 构建缓存失效 + +```dockerfile +# 错误:代码改动导致 npm install 也重新执行 +COPY . . +RUN npm install + +# 正确:依赖文件不变时跳过 npm install +COPY package*.json ./ +RUN npm install +COPY . . +``` + +### 7.3 容器启动后立即退出 + +```bash +# 查看日志 +docker logs <容器名> + +# 常见原因: +# 1. CMD/ENTRYPOINT 写错了 +# 2. 主进程不是 PID 1(用 tini 或 dumb-init 处理) +# 3. 应用启动失败(检查日志) +``` + +### 7.4 端口无法访问 + +```bash +# 1. 检查 docker run -p 是否映射 +docker run -p 3000:3000 ... + +# 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]] 来编排多容器应用。 diff --git a/gyh/docker/Docker常用命令.md b/gyh/docker/Docker常用命令.md new file mode 100644 index 0000000..976c468 --- /dev/null +++ b/gyh/docker/Docker常用命令.md @@ -0,0 +1,362 @@ +# Docker 常用命令 + +> 按「生命周期」组织,从拉取镜像到清理收尾,日常 90% 的操作都在以下命令中。 + +--- + +## 一、镜像管理(Image) + +```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 +``` + +**镜像分层缓存:** `docker build` 从上到下逐行执行,每一行都会生成一层缓存。把**不变的部分放前面**(安装依赖),**变化的部分放后面**(复制代码),充分利用缓存加速。 + +--- + +## 二、容器管理(Container) + +```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) + +```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 +``` + +> **为什么需要自定义网络?** Docker 默认的 `bridge` 网络不支持容器名 DNS 解析。自定义网络(`docker network create`)可以让容器通过**名字**互相通信。 + +--- + +## 四、数据卷(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 +``` + +> **数据持久化原则:** 容器是** ephemeral(易失的)**——删除容器,容器内的数据就没了。必须用 `-v` 把数据存到宿主机或 Docker Volume 中。 + +--- + +## 五、日志与调试 + +```bash +# 进入容器 +docker exec -it <容器名> bash + +# 查看容器资源占用 +docker stats + +# 查看容器详情 +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 # 查看镜像构建历史 +``` + +--- + +## 六、清理命令(重要!) + +```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 \ + redis:7-alpine redis-server --appendonly yes + --restart=always +``` + +### 7.4 运行 Node 开发环境 + +```bash +docker run -it \ + -p 3000:3000 \ + -v $(pwd):/app \ + -w /app \ + node:20 \ + bash +# -v $(pwd):/app 挂载项目目录,修改代码实时生效 +# -w /app 设置工作目录 +``` + +--- + +## 七、进阶:Docker 网络模式 + +```bash +# Docker 提供四种网络模式 + +# 1. bridge(默认)— 桥接网络,容器有独立 IP +docker run -d --network bridge nginx + +# 2. host — 共享宿主机网络,性能最好但端口会冲突 +docker run -d --network host nginx + +# 3. none — 无网络(隔离) +docker run -d --network none nginx + +# 4. container — 共享另一个容器的网络命名空间 +docker run -d --network container:web nginx + +# 推荐使用自定义 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 — 不记录日志 + +# docker-compose 中限制日志大小 +services: + api: + logging: + driver: json-file + options: + max-size: "10m" # 单个日志文件最大 10MB + max-file: "3" # 最多保留 3 个文件 +``` + +## 九、进阶:Docker 资源限制 + +```bash +# 限制 CPU 和内存 +docker run -d \ + --cpus="1.5" \ # 限制 1.5 个 CPU 核心 + --memory="512m" \ # 限制 512MB 内存 + --memory-swap="1g" \ # 允许使用的 swap 总量 + --pids-limit=100 \ # 最多 100 个进程(防 fork 炸弹) + nginx + +# docker-compose 中配置 +services: + api: + deploy: + resources: + limits: + cpus: '1.5' + memory: 512M + reservations: + cpus: '0.5' + memory: 128M +``` + +## 十、进阶:Docker Swarm(简易集群) + +```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 +docker swarm join-token manager # 添加管理节点 +docker swarm join-token worker # 添加工作节点 +``` + +> Swarm 适合轻量级部署。大规模生产环境建议用 Kubernetes。 + +## 十一、快速速查表 + +| 操作 | 命令 | +|------|------| +| 启动 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` | diff --git a/gyh/docker/README.md b/gyh/docker/README.md new file mode 100644 index 0000000..003a9a0 --- /dev/null +++ b/gyh/docker/README.md @@ -0,0 +1,40 @@ +# Docker 学习笔记 + +> Docker 容器化技术全套入门资料,从常用命令到 Dockerfile 编写再到多容器编排。 + +--- + +## 📂 文档索引 + +| 文档 | 说明 | +|------|------| +| [[Docker常用命令]] | 镜像/容器/网络/卷的常用命令,日常 90% 操作 | +| [[Dockerfile编写指南]] | Dockerfile 指令详解、多阶段构建、最佳实践 | +| [[docker-compose使用指南]] | 多容器编排、健康检查、多环境配置、完整模板 | + +--- + +## 🚀 学习路径 + +建议按以下顺序学习: + +1. **[[Docker常用命令]]** — 先掌握单个容器的基本操作 +2. **[[Dockerfile编写指南]]** — 学会构建自己的镜像 +3. **[[docker-compose使用指南]]** — 学会用 Compose 编排多容器应用 + +--- + +## 🔑 核心概念速记 + +| 概念 | 一句话 | +|------|--------| +| **镜像(Image)** | 只读模板,类似「类」 | +| **容器(Container)** | 镜像的运行实例,类似「对象」 | +| **Dockerfile** | 构建镜像的指令文件 | +| **Volume** | 持久化存储,容器删了数据还在 | +| **Network** | 容器间通信网络 | +| **Compose** | 用 YAML 定义和启动多容器应用 | + +--- + +> Docker 容器是**易失的**,数据必须用 `-v` 持久化;镜像构建要善用**多阶段构建**和**层缓存**。 diff --git a/gyh/docker/docker-compose使用指南.md b/gyh/docker/docker-compose使用指南.md new file mode 100644 index 0000000..1cefbbc --- /dev/null +++ b/gyh/docker/docker-compose使用指南.md @@ -0,0 +1,896 @@ +# Docker Compose 使用指南 + +> Docker Compose 用 **YAML 文件**定义多容器应用,一条 `docker compose up` 启动所有服务。**开发环境和生产环境都大量使用。** + +--- + +## 快速开始(TL;DR) + +```bash +# 1. 创建 docker-compose.yml +# 2. 启动所有服务 +docker compose up -d + +# 3. 查看状态 +docker compose ps + +# 4. 查看日志 +docker compose logs -f + +# 5. 停止并清理 +docker compose down +``` + +**目录:** [基本结构](#一基本结构) · [完整示例](#二完整示例) · [常用配置](#三常用配置详解) · [多环境](#四多环境配置) · [常用命令](#五常用命令) · [常见问题](#六常见问题) · [模板速查](#七常用模板速查) · [格式演进](#八compose-文件格式演进) · [高级配置](#九高级配置) · [最佳实践](#十最佳实践) · [速查卡](#十一快速参考卡) + +--- + +## 一、基本结构 + +```yaml +# docker-compose.yml +version: '3.8' + +services: + web: # 前端/后端应用 + db: # 数据库 + redis: # 缓存 + nginx: # 反向代理 + +networks: # 网络定义 +volumes: # 数据卷定义 +``` + +--- + +## 二、完整示例 + +### 2.1 Node.js + MySQL + Redis 全栈项目 + +```yaml +version: '3.8' + +services: + # ========== 后端应用 ========== + api: + build: + context: ./server + dockerfile: Dockerfile + args: + NODE_ENV: 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 + redis: + condition: service_started + restart: always + 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: always + 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 + restart: always + 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: always + 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: always + networks: + - app-network + +# ========== 网络 ========== +networks: + app-network: + driver: bridge + +# ========== 数据卷 ========== +volumes: + db-data: + redis-data: + api-logs: +``` + +--- + +## 三、常用配置详解 + +### 3.1 `build` — 从 Dockerfile 构建 + +```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 启动即可 +``` + +> **注意:** `depends_on` 只管**启动顺序**,不管**可用性**。`condition: service_healthy` + `healthcheck` 才能真正等服务就绪。 + +### 3.3 `healthcheck` — 健康检查 + +```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 || exit 1` | +| Nginx | `curl -f http://localhost/ || exit 1` | +| 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 管理) + volumes: + - db-data:/var/lib/mysql + + # 绑定挂载(绑定到宿主机路径) + volumes: + - ./data:/app/data + + # 只读挂载 + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + + # 匿名卷(不需要命名) + volumes: + - /tmp/data +``` + +### 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" # 仅容器间通信,外部不可访问 +``` + +--- + +## 四、多环境配置 + +### 4.1 使用覆盖文件 + +```yaml +# docker-compose.yml(基础配置) +services: + api: + build: . + environment: + - NODE_ENV=development + volumes: + - ./server:/app # 开发时热重载 + + db: + image: mysql:8.0 +``` + +```yaml +# docker-compose.prod.yml(生产覆盖) +services: + api: + environment: + - NODE_ENV=production + # 覆盖开发时的 volume 挂载 + 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 +``` + +--- + +## 五、常用命令 + +```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 +``` + +--- + +## 六、常见问题 + +### 6.1 容器间无法互相访问 + +```yaml +# 确保所有服务在同一网络下 +services: + api: + networks: [app-network] + db: + networks: [app-network] + +networks: + app-network: + driver: bridge +``` + +> 默认情况下,Docker Compose 会为同一个 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 +``` + +> Docker Compose 会**自动读取**当前目录的 `.env` 文件,无需额外配置。`$DB_HOST` 在 `docker-compose.yml` 中会被自动替换。 + +--- + +## 七、常用模板速查 + +### 7.1 纯前端静态站点(Nginx) + +```yaml +services: + web: + build: . + ports: + - "80:80" + restart: always +``` + +### 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 + +networks: +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: always + 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 文件格式演进 + +### 8.1 新版 Compose Specification(推荐) + +**不再需要 `version` 字段**,Docker Compose V2 起默认使用 Compose Specification 格式: + +```yaml +# 新版写法(推荐) +services: + app: + image: node:20-alpine + ports: + - "3000:3000" + environment: + NODE_ENV: production + +networks: + default: + driver: bridge + +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 的新格式,兼容性更好。 + +--- + +## 九、高级配置 + +### 9.1 `deploy` — 资源限制(适用于 Compose V3+) + +```yaml +services: + api: + image: my-api:latest + deploy: + replicas: 3 # 副本数(Swarm 模式) + restart_policy: + condition: on-failure + 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 + memory: 512M # 最多 512MB 内存 + reservations: + cpus: '0.25' + memory: 128M +``` + +> **注意:** `deploy` 在 `docker compose up`(非 Swarm 模式)下**部分字段不生效**(`replicas`、`update_config` 等)。资源限制 `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 --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` — 配置文件管理 + +```yaml +services: + nginx: + image: nginx:alpine + 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 + +configs: + nginx_conf: + file: ./configs/nginx.conf + app_config: + file: ./configs/app.yaml + external: false # 本地文件 +``` + +> **适用场景:** 需要分发配置文件但不想硬编码到镜像中。 + +### 9.4 `secrets` — 敏感信息管理 + +```yaml +services: + db: + image: mysql:8.0 + environment: + 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 # 从文件读取 + db_config: + string: '{"max_connections": 100}' # 直接写字符串 +``` + +> **安全建议:** 敏感信息不要写死在 `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"] + + # 容器别名(同一 compose 内通过别名访问) + container_name: my-app + + # 标签(元数据) + labels: + - "com.example.description=API Server" + - "com.example.environment=production" + + # 时区 + environment: + - TZ=Asia/Shanghai + + # 挂载共享内存(深度学习场景) + shm_size: '2gb' + + # 安全选项 + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + cap_add: + - NET_BIND_SERVICE + + # 资源限制 + deploy: + resources: + limits: + cpus: '1.0' + memory: 1G +``` + +--- + +## 十、最佳实践 + +### 10.1 安全性 + +```yaml +# ✅ 使用非 root 用户 +user: "1000:1000" + +# ✅ 最小化权限 +cap_drop: + - ALL +security_opt: + - no-new-privileges:true + +# ✅ 敏感信息用 secrets 或 .env +secrets: + - db_password + +# ❌ 避免使用 privileged +# privileged: true + +# ❌ 避免绑定 0.0.0.0 +# ports: +# - "0.0.0.0:3000:3000" +``` + +### 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 + +# 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 管理,更安全 + +# ✅ 用 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 代替 ports(容器间通信) +db: + expose: + - "3306" +``` + +--- + +## 十一、快速参考卡 + +| 字段 | 用途 | 示例 | +|------|------|------| +| `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` | +| `configs` | 配置文件管理 | `target: /etc/app/config` | +| `deploy.resources` | 资源限制 | `cpus: '0.5', memory: 512M` | +| `container_name` | 固定容器名 | `my-container` | +| `user` | 运行用户 | `"1000:1000"` |