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

4.8 KiB
Raw Blame History

tags, create time
tags create time
CI/CD
Docker
Nginx
部署策略
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) 模式。

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 作为静态文件服务器的运作方式

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 按从上到下 串行执行,且某一步失败后后续步骤自动跳过。

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 "线性流水线"的设计哲学。

部署命令拆解

- 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 部署模式详解