2182 字
11 分钟
页面浏览--访问次数--
返回首页
Shirone 改造记(四):从本地到线上

前三篇做完了本地的一切。这篇把它推到线上。

先看一遍本地构建#

pnpm build 不是只跑 astro build,它实际上串了一长串:

Terminal window
pnpm build
# 1. 同步内容仓 改的配置在这里变成 src/ 下的产物
# 2. 生成图标 扫 src/,把用到的图标打进本地集合
# 3. 生成说说缩略图
# 4. 子集化字体 只保留文章里真正用到的字形
# 5. astro build 真正的构建
# 6. pagefind 生成全文搜索索引
# 7. 字体校验

这个顺序不是随便排的。第 1 步没跑,后面读到的全是旧产物;第 2 步没跑,config/ 里新写的图标名不会进包(上一篇讲过那条坑)。

懂了这个顺序,你就能解释大部分“本地构建结果和预期不一样”的情况。

字体子集化那一步值得单独看一眼数字。我这站用的中文字体源文件 14.52 MB,子集化之后 360 KB。效果是长文章用的中文显示更好看,首屏还不至于被字体拖死。

三个部署方案#

方案省事程度代价
Vercel / Netlify最省事,仓库连上去就能自动构建国内访问速度随缘,域名绑定要额外折腾
GitHub Actions + 自建服务器中等要自己配服务器、Nginx、证书
服务器上直接跑构建不推荐见下

第三条单独说一下为什么不行。

pnpm build 里最重的是字体子集化、图片处理和全文索引。2 核 2G 的小机器上跑,很容易内存打满或者疯狂用 swap,node_modules 本身还要占一两个 G。放到 GitHub Actions 上跑,那边是十几 G 内存的机器,几分钟完事。

所以结论是:服务器只做静态托管,永远不在上面构建。

我选的是第二条路。想把域名和服务器都握在自己手里,顺便走一遍备案流程。

双仓怎么串起来#

双仓带来的一个额外好处是,CI 的职责也分开了:

仓触发条件干什么
内容仓推 content/ config/ data/ assets/ public/发一个事件给代码仓
代码仓收到事件拉内容仓、构建、把产物同步到服务器

流程是这样的:你在内容仓写完文章推上去,内容仓的 workflow 通过一个令牌通知代码仓,代码仓拉取内容、构建,然后用 rsync 把 dist/ 送到服务器。

如果你不想让两边都配 workflow,用 Vercel 那种平台的话,只要它支持同时拉两个仓库就行,但自建这条路上,双仓 CI 是必须的。

有个细节容易漏:内容仓的触发分支要写对。内容仓的默认分支和代码仓的名字可能不一样,写错了现象是“文章推上去了、线上没动静”,而日志里连报错都没有,因为 workflow 压根没被触发。

服务器:RHEL 系的两道墙#

我用的是阿里云 ECS 上的 Alibaba Cloud Linux,属于 RHEL 系。这点很关键,别照网上的 Ubuntu 教程做:

UbuntuRHEL 系
包管理aptdnf
防火墙ufwfirewalld
Nginx 配置目录sites-available / sites-enabled/etc/nginx/conf.d/
多一层无SELinux

然后是最容易漏的一步:防火墙有两道。

firewalld 是服务器里面那道墙,阿里云安全组是外面那道。两道都要放行 22 / 80 / 443。创建实例时安全组默认只开 22,现象就是“SSH 连得上,但 80 和 443 死活打不开”。补规则的位置在云控制台的实例详情里,协议选自定义 TCP,端口分别填 80 和 443。

还有 SELinux。如果你把站点放在 /www 这种非标准路径下,Nginx 会读不到文件、首页直接 403。要给那个目录打一个“允许网页服务器读”的标记,否则 Nginx 就是没权限。

Nginx:三件容易踩的事#

第一,页面是目录形式的。

主题用了 trailingSlash: always,所以 /about/ 实际对应 /about/index.html。try_files 得按这个写,不然除了首页全是 404。

第二,add_header 不叠加。

这是 Nginx 一个反直觉的行为:某个 location 里一旦自己写了 add_header,从上层继承下来的那几条会被整块丢掉,不是合并。

我把安全响应头单独存成一个文件,然后每个写了 add_header 的 location 里都 include 一次。不这么做的话,现象是“404 页有安全头、首页没有”。

第三,缓存策略要分层。

带内容指纹的静态资源(/assets 下那些文件名带 hash 的)可以放心缓存一年。HTML、搜索索引、sitemap 这些不能缓存,否则改完文章推上去,线上还是旧的。

HTTPS#

RHEL 系上 certbot 在 EPEL 仓库里,所以要先装 epel-release 再装它,这一步和 Ubuntu 不一样。装完跑一次它就会自动改写 Nginx 配置、加上 80 到 443 的跳转,并装好自动续期的定时器。

想连 www 子域一起签,得先把 www 的 DNS 解析配好再跑,不然证书签发会失败。

域名,三处必须一致#

改域名的时候有个地方特别容易漏。站内有三处出现域名,改一处没用:

在哪写什么
config/site.yaml 的 site完整地址,结尾的斜杠不能少
友链申请页那份 md站点链接和头像链接
服务器 Nginx 的 server_name域名加 www 变体

site.yaml 那处最容易出事。少个结尾斜杠、漏了 https、域名拼错一个字母,sitemap、RSS、OG 卡片和 /llms.txt 会全部指错地方,而页面本身看着完全正常。

密钥和令牌#

CI 需要在两个仓库之间穿梭,靠的是几个 Secret:

放在哪名字权限
代码仓DEPLOY_HOST / DEPLOY_USER / DEPLOY_SSH_KEY服务器地址和部署私钥
代码仓CONTENT_REPO_TOKEN只读拉内容仓
内容仓DISPATCH_TOKEN读写,用来通知代码仓

部署用的私钥在服务器上生成,公钥留在服务器自己的 authorized_keys 里,私钥整段贴进 GitHub 的 Secret。这样私钥从头到尾没经过第三方。

回滚#

部署脚本用 releases/ 加一个 current 软链:每次构建产出一个新目录,current 指向当前版本。所以回滚就是让软链指回去:

Terminal window
ls -1t /www/shirone/releases # 新到旧列出来
ln -sfn /www/shirone/releases/<某个目录> current

因为构建在 GitHub 上,磁盘占用不用太担心,每次发布大概几十到一百多 MB,保留五份也就几百 MB。

关于备案#

国内服务器要备案,这是硬门槛。有个容易忽略的官方要求:审核期间网站需要处于可访问状态。

所以顺序上,先把站部署上线跑起来,再去提交备案,两边同时进行。备案号下来之后,把它填到页脚的备案位,别提前放占位文字。

上线检查清单#

  1. curl -I 看首页返回 200,且 cache-control 是不缓存
  2. 打开 sitemap,里面的域名是你自己的
  3. 随便改一篇文章推上去,等两三分钟看页面有没有变。这一步才能证明自动部署真的通了,手动点一次“运行”只能证明手动能跑
  4. 浏览器控制台干净
  5. 手机 4G 打开一次,看看横幅和字体

四篇到这里#

回顾一下这条路径:

  1. 拆成两个仓库,把风险分开
  2. 内容和外观全在配置层搞定,一行源码不动
  3. 真需要动源码的地方,一次做完并登记
  4. 构建交给 CI,服务器只做托管

写这个系列的过程也挺有意思。改的时候觉得每一步都是个坑,回头看,真正难的地方在于搞清楚每个东西该放在哪一层。这件事想明白了,后面基本都是顺的。

如果这篇里某一处的现象跟你遇到的对不上,大概率是主题版本有差异,去翻一眼 src/config/<对应>Config.ts 的默认值,通常能对上。

Shirone 改造记(四):从本地到线上
https://xiaobuhui.xyz/posts/blog-rework-diary/shirone-gaizaoji-4/
作者
xiaobuhui
发布于
2026-10-14
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取