diff --git a/gyh/docker/Dockerfile编写指南.md b/gyh/docker/Dockerfile编写指南.md new file mode 100644 index 0000000..1592d45 --- /dev/null +++ b/gyh/docker/Dockerfile编写指南.md @@ -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 操作速查 diff --git a/gyh/docker/Docker常用命令.md b/gyh/docker/Docker常用命令.md new file mode 100644 index 0000000..d71e6df --- /dev/null +++ b/gyh/docker/Docker常用命令.md @@ -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 +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 编排多容器应用 diff --git a/gyh/docker/README.md b/gyh/docker/README.md new file mode 100644 index 0000000..802ffbf --- /dev/null +++ b/gyh/docker/README.md @@ -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常用命令]] diff --git a/gyh/docker/docker-compose使用指南.md b/gyh/docker/docker-compose使用指南.md new file mode 100644 index 0000000..8624444 --- /dev/null +++ b/gyh/docker/docker-compose使用指南.md @@ -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 操作速查