Files
cs-note/hzh/CICD/CICD 在 Slidev 中的应用/deployment-strategy.md
T

126 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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["读取文件系统<br/>/usr/share/nginx/html/"]
FS --> FILES["HTML / CSS / JS / 图片<br/>(来自 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 部署模式详解]]