把博客搬到自己的服务器后,我用 WorkBuddy 接管了运维
从 Vercel 迁到一台自己的云服务器之后,真正的麻烦才刚开始。这篇文章记录我用 WorkBuddy 处理静默宕机、扫描器骚扰、性能调优和自动发布的完整过程,以及几件我认为必须特别小心的事。
上一篇文章里,这个博客还是靠 Vercel 一键部署的——推代码,等两分钟,站点就更新了。省事到几乎没有存在感。
后来我决定把它搬到一台自己的云服务器上。理由很朴素:手上正好有一台闲置的轻量服务器,而且我确实想亲手摸一遍从系统到 Web 服务器这一整条链路,而不是永远只有一个「Deploy」按钮。
搬家本身不难。难的是搬完之后发生的事。
第一个坑:站点已经挂了将近一个月
搬过去之后我挺自信——服务器管理面板上清清楚楚写着「运行中」。
然后 WorkBuddy 做了件很直接的事:先用 ss -tlnp 看端口在不在听,再用本机 curl 带上 Host 头去请求真实页面。
结果是 502。
顺着这个结果往下查,发现是三个问题叠在一起:
- 面板里配置的启动命令指向的是开发模式,而不是生产模式
- 而我的项目其实是静态导出配置,
next build产出的就是纯静态文件,根本不需要一个常驻的 Node 进程 - 服务器只有 1.8 GB 内存,开发模式跑不动,被系统 OOM 掉了好几次,进程没了也没有任何东西把它拉起来
三个问题互相掩盖,所以从外面看只是一个 502。
这里最值得记住的一点是:面板上的状态和站点的真实可用性,是两件完全不同的事。 面板看的是「进程是否存活」,而我在意的是「用户能不能打开页面」。这两者之间可以差出整整一个月。
修复方式最后反而很朴素:既然构建产物是纯静态的,那就让 Web 服务器直接托管静态目录,把常驻进程整个删掉。少一个进程,就少一个会挂掉的东西。
第二个坑:不知道是谁在敲我的门
站点恢复之后,我去翻了访问日志。
三十多天、七万五千多次请求里,有相当一部分来自我完全不认识的 IP,在成批地试探 /wp-admin、/.env、/phpmyadmin 这类路径——典型的批量扫描器,跟你有没有用 WordPress 毫无关系,它们只是把网段扫一遍。
处理思路是在 Web 服务器层做降噪:
- 空 User-Agent 的直接拒绝
- 已知扫描器特征、常见漏洞路径、脚本后缀,一律拒绝
- 单 IP 请求频率限流,超了返回 429
- 把拦截日志单独切出来,别让它把正常日志淹掉
最后拦掉了大约一半的恶意请求,日志体积从 30 MB 降到 7 MB 出头。
这里有个反直觉的点。 我一开始以为「拦得越多越好」,实际上更重要的是可观测。把拦截日志单独拆出来之后,我才能看出哪条规则真在起作用、哪条只是在误伤。一个你看不见的拦截规则,本质上跟没有规则差不多。
第三件事:先测出真实上限,再谈优化
我的服务器规格很紧:2 核、1.8 GB 内存、40 GB 磁盘,还有一个容易被忽略的硬约束——出网带宽只有 3 Mbps。
在动手调优之前,WorkBuddy 先做了件我觉得非常对的事:先压测,测出真实瓶颈,再决定往哪儿使劲。
测下来,Web 服务器在保持连接的情况下每秒能处理一千多次请求,理论上限接近两千。但真正卡脖子的其实不是 CPU,而是那条带宽。
方向一下就明确了:
- 开启 BBR 拥塞控制
- 限制系统日志占用(从六百多 MB 压到 200 MB 上限)
- 静态资源全部启用 gzip 预压缩,缓存策略拉满
结论是:CPU 和内存都不是瓶颈,带宽才是。 这个判断直接决定了后面所有的取舍——既然卡在传输上,那就把体积压到最小,而不是去优化计算。
第四件事:把发布变成一条命令
前面几件事本质上都是「一次性」的。真正每天要面对的,是怎么发文章。
最早的做法很常规:本地改完推送到 GitHub,服务器上定时拉取、构建、上线。
用了一阵子发现一个问题:GitHub 变成了试验田。 如果我本地写错了代码,推上去之后服务器构建失败——那个坏版本已经躺在远程仓库里了,得手动去收拾。
于是把顺序整个倒过来:
改内容 → 本地提交(先不推)
→ 构建
→ 五项自检(首页 / RSS / 404 页 / 随机一篇文章 / 标签页)
→ 全部通过 → 才推送 GitHub 做备份
→ 任何一步失败 → 回滚上一版本 + 撤销提交,GitHub 完全无感
这套流程跑起来之后,我最满意的其实是失败路径。
我特意塞了一段故意写坏的代码去做测试,结果是:站点照常访问没有挂、GitHub 上依然停在上一版没被污染、那个坏掉的文件还老老实实留在工作区里方便我查——而那个提交已经被自动撤销了。
一条命令,坏结果永远出不了本地。
说几条我实际的感受
过程讲完了,说说好处。
它不迷信面板,只认实测。 如果只信管理面板那句「运行中」,我可能还得继续让站点挂着。它更愿意去查端口、去带 Host 头请求真实页面,把「进程活着」和「页面能打开」当成两个独立的事实验证。
它会先确认自己的能力边界,再动手。 比如发布流水线要跑通,得先确认三件事:服务器能不能连通 GitHub、手上这把密钥是什么类型、有没有写权限。这些都是先做了实测才继续往下走的,而不是先假设没问题。
它会给出可复现的证据,而不是结论。 这点我特别看重。运维里最怕的就是「我觉得它是好的」。它会把手上的命令、原始输出、期望值和实际值一起摆出来。
出问题时会回滚,而不是留一堆烂摊子。 构建失败就恢复上一版本、撤销提交、把失败的版本记下来避免反复重试同一份坏代码。
经验会沉淀。 一件事处理完,这套流程会被写成文档留下来。下次遇到同类问题,不用从零开始摸索。
有几件事我建议你特别注意
下面这些是我踩过的、或者差点踩的坑。
凭据怎么放,是第一个要解决的问题
「让 AI 帮你操作服务器」这件事,本质上是把登录凭据交给它。
我的做法是把凭据放在本地一个配置文件里,用 .gitignore 排除,绝不进版本库。但说实话——更稳妥的做法是用 SSH 密钥登录,并且给自动化用的那把密钥单独限定权限。密码登录确实省事,可它同时也意味着:任何拿到这个密码的人都能直接进服务器。这已经列进我的待办清单了。
仓库是公开还是私有,决定了你能备份什么
我一开始想让 WorkBuddy 顺手把服务器上的运维配置——防护规则、调优参数——也一起备份进仓库。
然后意识到一个问题:这个仓库是公开的。 把防护规则一条条写得清清楚楚公开出去,等于顺手附了一份怎么绕过它的说明书。
所以最终决定:只备份博客代码,运维配置留在服务器上。
如果你确实想让配置也进版本库,那就先把仓库改成私有的。
权限有多大,风险就有多大
要让自动化流程把代码推送到主分支,就意味着服务器上存着一把对这个仓库有写权限的密钥。而同一台服务器上还有部署脚本,能以 root 身份执行命令。
这两件事加在一起,等于「能往主分支推送 = 能在这台服务器上执行任意命令」。
这不是危言耸听,部署流程本身就必须这么设计——它要能拉代码、装依赖、构建、重启服务。所以要记住:这类密钥需要定期轮换,服务器本身的登录权限也要一并收紧。
别让它替你判断「成功」
AI 能跑命令、能读日志,但它一样会误判。
我就遇到过一次:它根据输出判断某段内容没上线,实际上是我俩用的那条文本过滤写法在不同系统上行为不一致,导致它自己的匹配结果为空——是工具误报,不是部署失败。 白折腾了一轮。
教训是:要求它给出可复核的证据。 具体命令、原始输出、期望值、实际值。凡是「结论」,都应该能被第三个人用同样的命令复现出来。
还有一类问题:看起来一切正常
这次搬家还暴露出另一种更难受的情况——系统看起来是好的,但某个角落其实是坏的。
比如有个提交明明早就写好了,却因为流程没接上,在仓库里躺了将近一个月才真正上线;比如构建完全成功、首页正常打开,但某些非英文的页面路径其实是 404——因为构建工具会把这类文件名写成转义形式,而 Web 服务器在处理请求时会先解码再去匹配文件,两边对不上。纯英文的路径完全看不出这个问题。
这类问题没有通用解法,只能靠逐条实测发现。
我现在的发文流程
- 有新文章,我把内容整理好交给 WorkBuddy
- 它写进内容目录,跑一条发布命令
- 服务器上构建、自检、上线
- 全部通过之后,才把代码推到 GitHub 备份
中途任何环节出问题都会自动回滚,最坏的结果不过是「这次没发出去」,站点不会挂,远程仓库也不会被污染。
最后
把博客搬回自己手里的服务器,技术上不算难。难的是意识到——有太多「看起来正常」其实并不正常的状态:进程活着但页面打不开,面板显示运行但站点早已下线,构建成功但某个页面是 404。
这类事靠人肉盯着很累,交给一个会实测、会回滚、会把经验记下来的工具,确实轻松很多。
但前提是:你得清楚哪些东西不能交给它。 比如权限的边界,比如哪些配置不该被公开。
工具能帮你把事做对,而「什么叫做对」,还是得你自己定。
← 返回文章列表