白嫖 Muse 主机搭建博客站
本文以 Skill 形式编写,可直接作为 Muse 的技能使用:把以下元数据保存为 SKILL.md,Muse 在接到搭建博客的需求时即可按本文执行;也可以直接按本文步骤手动执行。默认环境为单台 Muse VM,不要求其它设备。
name: muse-vm-blog-hosting
description: "在 Muse 虚拟机上搭建 Typecho + PostgreSQL 博客,经 Cloudflare Tunnel 对外提供公网访问,并在 VM 被替换后自行恢复。适用于用户要求用 Muse 主机免费搭建博客站的场景。"适用场景
当用户提出以下任一需求时,适用本技能:
- 在 Muse 虚拟机上搭建一个博客站
- 博客程序为 Typecho,数据库为 PostgreSQL
- 博客需经域名在公网访问,域名由 Cloudflare 管理
- VM 被替换后博客应自行恢复,不得每次手动重装
不适用场景:用户要求博客跑在自有服务器、虚拟主机或其它云厂商实例上;用户要求使用 MySQL、WordPress 等其它程序和数据库组合。上述情况应另选对应技能,不得硬套本文。
前置条件:需用户提供的参数
以下参数缺失时,应先停下询问,不得自行编造。
| 参数 | 说明 | 示例(仅为示例,应替换为实际值) |
|---|---|---|
| 博客域名 | 须已在 Cloudflare 拥有对应 zone,或可通过外部 CNAME 接入 | muse.toolcalls.org |
| Cloudflare 登录方式 | 三选一:①用户在云端浏览器登录后交还控制权 ②用户经安全卡片提供 API Token ③用户指定另一台已登录 Cloudflare 的助手创建 Tunnel | 任选其一 |
| Tunnel 名称 | 小写英文加连字符,已被占用时应更换,不得覆盖已有 Tunnel | muse-vm |
| Typecho 站点标题 | 安装向导中填写 | Hello World |
| Typecho 管理员账号和密码 | 安装向导中填写,密码仅经安装页或安全卡片提供,不得写入文章、脚本和记忆 | 用户现场提供 |
Muse 自行生成、无需用户编造的参数:
- 数据库密码:随机生成一次,存放于家目录的
.db-password(600 权限)。安装后该密码还会写入 Typecho 的config.inc.php,后续应复用该文件,不得每次重设。 - Tunnel 运行 token:Cloudflare 创建 Tunnel 后给出,存放于家目录的
.tunnel-token(600 权限),仅供 cloudflared 使用,不得贴入文章、聊天记录和代码仓库。 - 数据库名和用户名:可直接使用
typecho。
步骤 0:核查
在新 VM 上应先核查以下内容,核查结果决定后续路径:
- VM 的持久性边界:确认家目录为持久存储,
/etc中的配置在替换后会丢失;确认 systemd 默认目标为default.target,单元应同时挂载WantedBy=default.target multi-user.target,仅挂载后者时,替换后不会自启。 - 域名的 Cloudflare 归属:确认域名的 NS 为 Cloudflare,并确认待用子域名当前不存在(NXDOMAIN)。已存在的记录不得覆盖,应更换子域名。
- Typecho 的既有状态:若家目录中已有
www/config.inc.php,表明已安装完成,不得重装、不得重置数据库,仅做恢复和域名接入。
步骤 1:安装 Typecho + PostgreSQL(VM 内)
本文示例版本:Typecho v1.3.0、PostgreSQL 16、PHP 8.3、nginx 1.24。版本可替换,但以下规则不应替换:
- 安装软件包:nginx、php-fpm、php-pgsql、php-mbstring、php-xml、php-curl、php-zip、postgresql、unzip、curl。恢复脚本中仅在缺包时安装,不得每次重装。
- PostgreSQL 数据目录不得使用默认的
/var/lib/postgresql,该路径在 VM 替换后会丢失。应存放于家目录,例如~/workspace/typecho/pgdata,属主设为 postgres、权限设为 700。家目录的父目录应赋予 postgres 和 www-data 可执行权限,否则上述用户无法进入家目录。 - 应使用自定义 systemd 单元运行 PostgreSQL,数据目录指向家目录路径,仅监听
127.0.0.1:5432。系统自带的默认集群数据位于/var,不应使用。 - 数据库和用户仅创建一次:数据库名
typecho、用户名typecho、密码读取上述.db-password文件。已存在时不得重建,不得重置已安装 Typecho 的密码。 - Typecho 程序存放于家目录,例如
~/workspace/typecho/www,从官方发布包下载并解压至该路径。nginx 监听 VM 本机的 8080,根目录指向该 www 路径,PHP 经 php-fpm 的 socket 处理,伪静态规则为try_files $uri $uri/ /index.php?$args。 - 安装向导:用户在浏览器中打开安装页,填写数据库主机
127.0.0.1、端口5432、数据库名、用户名、密码,再填写站点标题和管理员账号密码。安装完成后应确认:首页可打开,安装页 302 跳回首页(表明已安装完成,无需再次安装)。
步骤 2:公网入口:Cloudflare Tunnel
判断依据:Typecho 位于 VM 本机 8080,Cloudflare Workers 和 DNS 代理无法直接访问该地址,仅凭二者无法实现公网访问。可行方式为 Cloudflare Tunnel:由 VM 上的 cloudflared 主动向外连接 Cloudflare,Cloudflare 再将域名流量转发回 VM。
- 在 Cloudflare Zero Trust 中新建 Tunnel,名称使用前置条件中用户提供的名称,不得覆盖已有 Tunnel。
- 为 Tunnel 添加 Public Hostname:域名使用前置条件中用户提供的子域名,服务类型为 HTTP,服务地址为
http://localhost:8080。该 localhost 指运行 cloudflared 的 VM 本机。 - 确认域名 zone 中已自动生成该 DNS 记录(CNAME 至
<Tunnel ID>.cfargotunnel.com,proxied)。若 zone 在当前登录账号中不存在,应立即停止并汇报,不得新建 zone、不得修改 NS、不得注册域名。 - 运行 token 存入家目录的
.tunnel-token(600),不得对外公开。 - cloudflared 二进制存放于家目录(VM 替换后仍保留),并以 systemd 单元运行
cloudflared tunnel run --token,Restart=always。单元同样应挂载default.target。 - 仅在公网返回 200 后,方可将 Typecho 的站点地址修改为公网域名。修改方式为数据库中
typecho_options的siteUrl改为https://域名。公网未通之前不得修改该项。
步骤 3:合并进 5 分钟看门狗(VM 替换后复活)
VM 内可另设短周期循环检查,但该检查本身也会在 VM 替换时被清除,因此还需设置一个 5 分钟一轮的兜底检查。
兜底检查每轮应执行:
- 检查 VM 上全部相关服务是否为 active:typecho-postgres、nginx、php-fpm、cloudflared-tunnel;检查 8080、5432 是否在监听;从 VM 侧 curl 一次公网域名,应返回 200。
- 全部通过时安静结束,不向用户发送消息。
按组恢复,不得全量重装:
- Typecho 组:仅在缺包时安装软件包,仅在缺数据时初始化数据目录,仅在缺文件时下载 Typecho,数据库密码复用
.db-password,不得重置已安装配置。 - Cloudflare 组:仅在缺二进制时下载,token 文件缺失时应明确报错,不得编造 token。
- Typecho 组:仅在缺包时安装软件包,仅在缺数据时初始化数据目录,仅在缺文件时下载 Typecho,数据库密码复用
- 恢复后重新全量复查一次,成功则安静结束;仍失败时方可向用户汇报:失败项、已执行的操作、恢复脚本的报错内容。
- 恢复脚本应事先实测:主动停止一个服务后再运行恢复脚本,确认其可在可接受时间内自行恢复。不得等到 VM 替换当天才首次运行恢复脚本。
如用户另有其它服务需纳入检查,应按相同模式追加服务名和监听端口,不得将无关服务混入同一恢复脚本。
校验清单
执行完成后应逐项核对,任一项不通过不得交付:
- [ ] VM 本机
curl http://127.0.0.1:8080/返回 200 - [ ]
systemctl is-active中 typecho-postgres、nginx、php-fpm、cloudflared-tunnel 均为 active - [ ] 从 VM 出口代理
curl https://域名/返回 200,且标题为安装时填写的标题 - [ ] 数据库中
siteUrl已为公网域名 - [ ] 恢复脚本已实测:主动停止一个服务后可自行恢复
- [ ] 5 分钟看门狗已包含上述服务和公网 200 检查
排障表
| 现象 | 应先检查的内容 | 处理方式 |
|---|---|---|
| cloudflared 持续重连,QUIC 失败 | 网络环境是否封禁 UDP | 改用 --protocol http2,经 TCP 连接,不得硬等 QUIC |
| argotunnel 域名解析为 198.18.x 类地址,代理无法 CONNECT | 沙箱 DNS 是否返回 fake-IP | 使用实测的真实边缘 IP 写入 hosts,不得直接修改只读的 /etc/hosts,应以 bind-mount 覆盖 |
| cloudflared 重启后无法启动,报错 metrics 绑定到非本机 IP | /etc/hosts 是否在重启时被截空 | 不得使用 grep /etc/hosts > /run/hosts.cf; mount --bind /run/hosts.cf /etc/hosts 的写法。重启时 /etc/hosts 本身即为该文件,grep 会先截空输入文件,导致 localhost 丢失,metrics 随后绑定到 DNS 返回的假 IP。正确做法为:先卸除已有的 bind-mount,再从真实原始文件或标准模板重建 hosts 文件,最后再 bind-mount 一次。该逻辑应写为独立的 fix-hosts.sh,恢复脚本和单元均调用该脚本 |
| 公网返回 530、隧道未连接 | cloudflared 日志中是否存在 Registered tunnel connection,公网是否为刚建好 | 刚建好时隧道处于重试阶段属正常现象,应待注册成功后再测试;不得在此时修改数据库 |
| 部分客户端解析公网域名失败、返回 000,但 VM 侧和绕 DNS 后均为 200 | 是否为 DNS 负缓存 | 新域名建好前查询过一次 NXDOMAIN 的解析器会将否定结果缓存一段时间(示例中为 SOA 1800 秒)。该现象不属于隧道故障,应待缓存过期,或在客户端刷新 DNS,或临时使用 --resolve 绕过 DNS 验证隧道本身 |
| 恢复脚本首次运行即遇 apt 占锁、索引失败 | 是否与其它 apt 进程冲突 | 重新运行一次恢复脚本,不得直接判定恢复失败 |
交付清单
完成后应向用户提供以下内容,仅说明“已完成”不足以验收:
- 公网地址:
https://域名/,并附实测 http 码和首页标题。 - 服务清单:VM 上各服务是否为 active、各端口是否在监听。
- 恢复方式:恢复脚本各自位于家目录的哪个路径、5 分钟看门狗的名称、VM 替换后预计多久可自行恢复。
- 未变更项:未覆盖已有 Tunnel、未改动已有域名记录、未修改已安装 Typecho 的配置(除上述经用户同意修改的站点地址外)。
- 未存储项:数据库密码和 Tunnel token 仅存放于家目录的 600 文件中,未写入文章、记忆和代码仓库;管理员密码仅在安装页使用一次。
按本技能执行,一台全新的 Muse 在拿到前置条件参数表后,可将一台会被替换的 VM 复现为一个公网博客站。