Files

126 lines
4.8 KiB
Markdown
Raw Permalink Normal View History

2026-05-27 22:18:59 +08:00
---
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 部署模式详解]]