--- tags: - CI/CD - Docker - Nginx - 部署策略 create time: 2026-05-27 19:30 --- # 部署策略深入 ## 概述 深入分析 Slidev 项目的部署架构,包括 Docker-out-of-Docker 通信模式、Nginx 静态文件服务机制、以及 Pipeline 步骤间的依赖关系。 > [!NOTE] 如需了解 Workflow YAML 逐块配置说明,详见 [[workflow-configuration]]。 ## 正文 ### 为什么需要 docker-cli?DooD vs SID > [!QUESTION] 思考:workflow 已经在 `node:22-alpine` 容器里运行了,为什么还要装 Docker 相关工具?这不是"套娃"吗? **关键理解**:你的命令在 **container A(构建容器)** 中执行,但你要操控的是 **宿主机的 Docker daemon** 来启动 container B(Nginx)。这是经典的 **Docker-out-of-Docker (DooD)** 模式。 ```mermaid flowchart LR HOST["宿主机\nDocker Daemon"] <-->|通信| CLI["container A 中的\ndocker-cli 命令"] CLI --> OP["docker run nginx → 新建 container B"] style HOST fill:#e8f4fd style CLI fill:#fff4e8 style OP fill:#e8ffe8 ``` 这比另一种方案 **SID (Socket-In-Docker)** 更安全:SID 直接把 `/var/run/docker.sock` 挂载进容器,等于给了 root 权限——攻击者一行代码就能逃逸到宿主机。而 DooD 通过独立的 docker-cli 通信,安全边界更清晰。 > [!TIP] 想深入了解两种方案的完整对比?详见 [[Docker-in-Container 部署模式详解]]。 ### Nginx 作为静态文件服务器的运作方式 ```mermaid flowchart LR REQ["浏览器请求 :8080/index.html"] --> NGINX["Nginx 监听端口 80\n(容器内映射到宿主机 8080)"] NGINX --> FS["读取文件系统
/usr/share/nginx/html/"] FS --> FILES["HTML / CSS / JS / 图片
(来自 dist/ 的构建产物)"] FILES --> RESP["返回给浏览器渲染"] style REQ fill:#e8f4fd style RESP fill:#e8ffe8 ``` 核心机制其实非常简单——**Nginx 不做任何处理,只是当个"搬运工"**: | 概念 | 说明 | |------|------| | 为什么选 Nginx | 业界标准、极致轻量(alpine 版仅 ~4MB)、零配置开箱即用 | | 为什么每次重建 | 无状态设计:删掉旧容器 = 彻底清除历史,避免残留配置或缓存问题 | | 为什么用 `docker cp` | `cp dist/. nginx:/usr/share/nginx/html/` 把构建产物直接铺进容器的网页根目录 | | 为什么不重启 Nginx | `cp` 操作不涉及进程热重载,Nginx 自动读最新文件 | 关键参数 `-p 8080:80`:容器内 Nginx 监听 80 端口,将其映射到宿主机的 8080。访客访问 `http://服务器IP:8080` 即可看到 slides。这里没有直接用 80 端口,是为了避免和宿主机上其他可能的 HTTP 服务冲突。 ### Workflow 步骤顺序:谁保证了执行的先后? 答案很直接:**YAML 中 `steps` 的顺序就是执行顺序**。Gitea Actions(与 GitHub Actions 一致)对每个 job 内的 steps 按从上到下 **串行执行**,且某一步失败后后续步骤自动跳过。 ```mermaid flowchart LR S1[Install tools] --> S2[Checkout] S2 --> S3[Install pnpm] S3 --> S4[pnpm install] S4 --> S5[Build] S5 --> S6[Deploy] S1 -.→|.git (代码可用)| S2 S2 -.→|pnpm CLI| S3 S3 -.→|node_modules/| S4 S4 -.→|dist/ 产物| S5 S5 -.→|dist/ 产物| S6 style S1 fill:#fff4e8 style S2 fill:#fff4e8 style S3 fill:#fff4e8 style S4 fill:#fff4e8 style S5 fill:#fff4e8 style S6 fill:#e8ffe8 ``` 每一步的输出自然成为下一步的输入——不需要额外的依赖声明,文件系统天然承载了这一步到那一步的数据传递。这正是 CI/CD pipeline "线性流水线"的设计哲学。 ### 部署命令拆解 ```yaml - name: Deploy to nginx run: | docker rm -f slides-nginx 2>/dev/null || true docker run -d \ --name slides-nginx \ --restart unless-stopped \ -p 8080:80 \ nginx:alpine docker cp dist/. slides-nginx:/usr/share/nginx/html/ ``` 这一阶段做了三件事: 1. **清理旧容器** — `docker rm -f` 强制删除可能存在的同名容器,`2>/dev/null || true` 确保不存在时不报错退出 2. **启动新容器** — 基于 `nginx:alpine` 镜像创建轻量级 web 服务器 3. **复制构建产物** — 将 `dist/` 下的内容拷贝到 Nginx 的默认网页目录 `/usr/share/nginx/html/` ### 关键设计决策 | 决策 | 选择 | 理由 | |------|------|------| | 包管理器 | pnpm | 依赖安装速度快,磁盘占用小 | | 容器基础镜像 | Alpine | 镜像仅几 MB,适合快速构建 | | Web 服务器 | Nginx (每次重建) | 无状态、零配置,构建产物即静态文件 | | 端口映射 | 8080:80 | 避免宿主机占用标准 HTTP 端口 80 | ## 关联笔记 - [[CICD 在 Slidev 中的应用]] - [[workflow-configuration]] - [[Docker-in-Container 部署模式详解]]