From 67740efe9c32ece864e0bdc6ebe7198e030b2634 Mon Sep 17 00:00:00 2001 From: BigCoke233 Date: Tue, 27 Jan 2026 23:58:50 +0800 Subject: docs: new draft Former-commit-id: 95078896fadf5c393a5a5a1717a336752cb4ab5d --- ...235\231\346\200\201\347\275\221\347\253\231.md" | 122 +++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 "content/essays/\345\246\202\344\275\225\344\274\230\351\233\205\345\234\260\351\203\250\347\275\262\344\270\200\344\270\252\351\235\231\346\200\201\347\275\221\347\253\231.md" diff --git "a/content/essays/\345\246\202\344\275\225\344\274\230\351\233\205\345\234\260\351\203\250\347\275\262\344\270\200\344\270\252\351\235\231\346\200\201\347\275\221\347\253\231.md" "b/content/essays/\345\246\202\344\275\225\344\274\230\351\233\205\345\234\260\351\203\250\347\275\262\344\270\200\344\270\252\351\235\231\346\200\201\347\275\221\347\253\231.md" new file mode 100644 index 00000000..01a76a24 --- /dev/null +++ "b/content/essays/\345\246\202\344\275\225\344\274\230\351\233\205\345\234\260\351\203\250\347\275\262\344\270\200\344\270\252\351\235\231\346\200\201\347\275\221\347\253\231.md" @@ -0,0 +1,122 @@ ++++ +title = '如何优雅地部署一个静态网站?' +slug = 'deploying-static-sites' +date = 2026-01-27 22:40:11 +draft = true ++++ + +PaaS 软件用了太久,导致自己对部署网站这件事产生了很多不切实际的想象,总感觉不使用 Vercel、Netlify 和 Cloudflare Pages 就不够现代。实际上,静态网站就是一系列 HTML 文件和必要的静态资源而已,即便是用 Hugo 和 Hexo 等静态网站生成器,得到的也是相同的文件。这些文件如果被用户下载到本地,用浏览器直接打开,也能够正常浏览,毕竟不涉及任何服务端的计算,那为什么还要把事情弄得这么复杂呢? + + + +## 使用 PaaS + +PaaS 的意思是平台即服务(Platform as a Service),可以把平台理解为开发和运行应用的基础设施,PaaS 提供了这些设施,降低了开发者进行运维工作和开发过程中部分工作的复杂度。比如,你编写了一个 Web 应用,通常情况下你需要购置一台服务器,然后配置应用的运行环境、配置 Web 服务器和反向代理、申请 SSL 证书并管理续期、设置防火墙和其他必要的安全措施、维护数据库、维护依赖等等,当应用的代码更新之后,你还需要重新部署。 + +PaaS 能让开发者做到,直接提交代码库给平台,应用就能跑起来并且运转良好,并且往往具有持续集成和持续部署(CI/CD)的能力,使得新的代码在推送到仓库主分支过后就能自动构建并部署应用。 + +与之相似的架构是 Serverless,也就是无服务器架构。这两种架构其实都极大地弱化了后端的存在,不过 Serverless 更偏向通过事件触发的云函数,而非随时在线的常规 Web 应用。这里略过不谈。 + +使用 Vercel、Netlify 和 Cloudflare Workers/Pages 这样的服务构建和部署静态网站是可行的,并且非常简单,一般来说步骤是这样的: + +1. 将你的网站源代码推送到远程 Git 仓库。 +2. 在 PaaS 平台创建一个新的应用,设置 Git 仓库并选择软件架构(如果是 Node.js 等常见技术栈,成熟的 PaaS 平台往往会自动识别) +3. PaaS 克隆 Git 仓库,运行构建脚本,然后将构建产物部署到他们提供的网络上。 +4. 此后每次有新的推送,PaaS 都会重新克隆仓库、构建和部署。 + +简单来说,你只需要把代码准备好就行,几乎不需要做任何运维操作。 + +问题在于,部署一个静态网站并不复杂,运行脚本构建好网站之后,将构建产物上传到一个可以分发文件(静态资源)的服务器上就好了。这个服务器甚至不需要有计算能力,可以是对象储存桶。印象中 Vercel 会把应用部署到他们的边缘网络(edge network)上,所谓边缘指的是靠近端系统(end system)也就是用户的网络,网络的边缘部分离用户最近,所以响应速度往往很快。对于静态网站而言,这跟 CDN(内容分发网络)差不多。 + +所以,如果不用 PaaS,能把这个过程自动化吗? + +## 使用自动化工作流部署 + +由于我只使用过 GitHub Actions 和 Forgejo Actions,所以这里只谈这两个。Forgejo 的 Actions 几乎是和 GitHub 完全兼容的,语法也相似,所以在下文统称 Actions。 + +Actions 是自动化的工作流,有多种触发条件,最常见的是 `push`,也就是在有代码推送到远程仓库后触发;也可以限定分支,比如只有代码被合并到主分支之后才触发工作流。工作流可以执行命令,自然也可以执行用于构建静态网站的脚本,比如 `npm run build` 和 `hugo --minify` 等等。 + +需要说明的是,Actions 往往跑在与其他运行环境隔离的容器里,不能直接在宿主服务器上运行代码,并且工作流被执行完毕之后,往往会恢复现场,运行的结果不会保留在容器里,所以除了执行构建步骤,还要执行部署操作。如果要部署到一个对象储存桶,可以使用 `rclone`;如果要同步到远程服务器上,可以使用 `rsync`;如果要部署到 Cloudflare Workers 上,可以使用官方提供的 Wrangler 工具(不过你为什么不直接用 Cloudflare Workers 构建呢?)。 + +可以把工作流理解为「剧本」,程序员是编剧,把剧本交给 Actions 之后,它就会在规定的场合下按照剧本表演,结束后,会有人收拾舞台,一切都会恢复成原来的样子。如果要把舞台上的表演保留下来,就需要在表演进行时拍照和录像。 + +同样地,不同的剧目所需要的道具不同,就像不同的软件和脚本运行所需要的依赖项不同,戏班子上场的时候要把道具带上,编剧也要把道具在剧本里写清楚。Actions 一般跑在一个最小的、容器化、只有一个 CPU 核心的 Linux 系统上,以下称作 Runner。Runner 比较轻量,可以在 Docker 里运行,甚至给我一种 WSL(Windows Sub-system Linux)的感觉,上面预装的软件包很少,所以工作流里还要写安装依赖的步骤。 + +比如,构建一个 Hugo 网站,部署远程服务器上,工作流可能需要这样写: + +1. 安装 Node.js(如果有 Node.js 依赖) +2. `npm install` +3. `npm run build`(运行 Node.js 工具,比如构建 UnoCSS) +4. 安装 Go 语言依赖 +5. 安装 Hugo +6. `hugo --minify`(构建网站) +7. 安装 Rsync +8. `rsync ./public user@host.ip:/path/to/site`(部署网站到服务器)\ + +由于在远程服务器上写入文件往往需要认证,最安全的方法是生成一对 SSH 密钥,在 `rsync` 之前先配置 SSH 私钥,保证目标服务器上有储存对应的公钥,这样才能正常访问目标服务器。此外,还需要在目标服务器上配置目录权限,保证工作流有权限写入。 + +具体的 `workflow.yml` 可能是这样的: + +```yaml +name: Build and Deploy Hugo + +on: + push: + branches: + - master + +jobs: + deploy: + runs-on: docker + steps: + - uses: actions/checkout@v4 + with: + submodules: true + fetch-depth: 0 + - name: setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.25.1" + - name: setup Hugo + uses: https://github.com/peaceiris/actions-hugo@v3 + with: + hugo-version: "latest" + extended: false + - name: build site + run: hugo --minify + - name: Install rsync + run: | + apt-get update && apt-get install -y rsync + - name: Setup SSH + run: | + mkdir -p ~/.ssh + echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts + - name: Deploy via rsync + run: | + rsync -avz --delete public/ \ + ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:/home/eltrac/homepage/ +``` + +之后在服务器上配置好 Web 服务器,将通过自动流程部署的文件公开就好了。以下是一个 `Caddyfile` 示例: + +```Caddyfile +eltr.ac { + root * /home/eltrac/homepage/ + file_server +} +``` + +这个方案的缺点非常明显,我用这个方法搭了一个网站之后就再也不想搭第二个了。 + +1. 你需要管理一对 SSH 密钥,并且你允许了外部程序向你的服务器直接写入文件,这有安全风险。 +2. Actions 没法在本地测试,必须推送到远程仓库后,Forgejo/GitHub 才会运行工作流,这使得调试工作流变得**非常**痛苦。 +3. 每次运行工作流都需要下载并安装依赖项,对于 Hugo 这种构建速度非常快,甚至不会超过一秒的静态网站生成器,大部分的构建时间都花费在「准备运行环境」上了;而且,这个运行环境在跑完一次之后就会被恢复。如果你使用自己管理的 Forgejo Runner,可以在 Runner 里预安装自定义的依赖项,但依然很麻烦。 +4. *用 YAML 写工作流十分反人类。* + +既然如此,我能不能直接在已经安装好运行环境的宿主服务器上直接运行构建脚本呢?构建产物直接在宿主服务器上生成。这样就可以省去与服务器通过 SSH 密钥认证并建立连接的过程,也不需要把大量的时间浪费在准备运行环境上了。 + +## 使用 Webhook 触发远程服务器部署 + +## 本地构建并同步到文件服务器或储存桶 -- cgit v1.2.3