文

把大肥鱼搬上腾讯云

把大肥鱼搬上腾讯云


大肥鱼是我那台 QQ 机器人。

AstrBot 接消息、调模型,NapCat 挂着那个 QQ 号,底下压着一个目录,里面是它的人设、它的记忆、它跟人说过的话。

它原来住在另一台机器上。那台机器是两年前开的,配置放到现在不太够用:两个容器轮流把内存顶到 90%,半夜掉一次线,第二天早上才发现。

所以我把它搬到了腾讯云。

工具怎么装、模型怎么接、二维码怎么扫,这些我另一篇写过:《用 AstrBot + NapCat 把大模型接入 QQ》。这篇只讲服务器这一侧:机器怎么选、系统要准备什么、旧数据怎么一件不少地带过去、端口该开哪几个。

还有最后一件事——换 IP 之后会发生什么。这一件,没法照着命令敲。

这篇只管服务器:装 Docker、搬数据、开端口、换 IP 之后的事。AstrBot 和 NapCat 本身的配置不在这里,那些在上面的链接里。

零、准备工作

1. 一台腾讯云服务器

先说配置。这套东西吃内存:AstrBot 是一个常驻的 Python 进程,NapCat 是 Node 加一个无头 QQ 客户端。两个都跑起来,光是空转就要吃掉一 G 上下;真正开始跑群聊和模型调用,2G 会经常贴着顶,然后 OOM killer 挑一个杀掉——表现是「它突然不回话了」。

所以 2 核 2G 是底线,不是推荐值。我现在用的是 2 核 4G。

腾讯云有两条产品线:轻量应用服务器(Lighthouse)和云服务器 CVM。我选轻量,Ubuntu 24.04 LTS,系统镜像。

理由:单机单服务,CVM 那套 VPC、安全组、弹性 IP 我用不上。

轻量的短板得先讲清楚:

  • 配置只能整档升,不能单独加内存或带宽
  • 公网 IP 跟着实例走,不能解绑挂到别的机器
  • 防火墙规则只到端口级,做不了按网段、按协议组合的精细控制
  • 套餐带月流量包,跑超了限速

2. 旧机器上的东西

这是搬家的行李单。先对着目录翻一遍,看看有没有放在别处的:

  • astrbot/data —— 人设、模型配置、会话数据库、插件
  • napcat/config —— OneBot 的连接信息
  • napcat/qq —— QQ 的登录态。这个不带走,新机器必须重新扫码
  • 如果改过人格文件、写过自定义插件、存过语音模型,确认它们都在 data 里面,而不是散落在 ~/ 底下

3. 一个停服窗口

选凌晨。搬家期间它是不在线的,群里会以为它死了——这件事没法避免,只能选一个人少的时候。

4. 手机,能登那个 QQ 号

最后一步要用。


一、先把新机器弄成能装东西的样子

1.1 你不是 root

腾讯云的 Ubuntu 镜像给的不是 root 账号,是 ubuntu。登进去 whoami,打出来是 ubuntu。

装东西要 sudo。可以先 sudo -i 切过去,省得每条命令都加。

1.2 装 Docker

别用 apt install docker.io。Ubuntu 源里那个版本旧,跑新镜像是给自己找麻烦。用官方脚本:

curl -fsSL https://get.docker.com | sudo sh
sudo systemctl enable --now docker
docker -v

打出 Docker version 2x.x 就成了。

顺带一句:如果你习惯加 sudo,可以把当前用户塞进 docker 组,之后不用每条命令都提权。但那样等于给自己一个免密 root,我不做。多敲四个字母的事。

1.3 加 swap

4G 内存跑这两个容器是够的,但「够」和「有余量」是两件事。模型调用、上下文拼装、群消息刷屏的时候内存会蹿一下,蹿到顶就是 OOM。

加 2G swap 当缓冲:

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -h

Swap 那行出现 2.0Gi 就行。它是缓冲,不是内存,别指望靠它扛负载。

1.4 目录对齐

旧机器上的路径是 /www/wwwroot/astrbot 和 /www/wwwroot/napcat。新机器上沿用同一个路径。

这不是强迫症。路径一致,docker run 的挂载参数可以从旧机器原样抄过来,容器里那些写死了绝对路径的配置也不用改。搬家时少一个变量,就少一处查半天的问题。

sudo mkdir -p /www/wwwroot

1.5 不装面板

旧机器上有宝塔。新机器我没装。

理由很实在:面板本身要占两百来兆内存,而我用它基本只干一件事——看日志。这件事 docker logs 就能干。

少一层东西,就少一层半夜出问题时你要怀疑的对象。


二、打包,传,拆包

2.1 先停容器,再打包

顺序不能反。

AstrBot 的会话记录是 SQLite。SQLite 在写入时会生成 -wal 和 -shm 两个伴生文件,主库和这俩不同步的时候你直接 cp,拷出来的是一份「半句话」的数据库——启动能起来,但历史消息可能缺一截,或者干脆报 database is locked。

所以:

docker stop astrbot napcat

等到 docker ps 里两个都不在了,再动文件。

补一句让人安心的:docker stop 之后 SQLite 会自己把 wal 合并进主库,这时候 -wal 文件是空的,或者只剩几个字节,tar 打走的是一份完整的库。前提是你真的停了——没停就打包,等于赌运气。

2.2 打包,留个校验值

cd /www/wwwroot
sudo tar czf /tmp/whale-$(date +%m%d).tar.gz astrbot/data napcat/config napcat/qq
sha256sum /tmp/whale-*.tar.gz

把 sha256 那串记下来。传完在新机器上再算一次,两个一样才说明路上没坏。这一步三十秒,能省掉「为什么配置缺了一半」这种查一晚上的问题。

2.3 传

scp /tmp/whale-1031.tar.gz ubuntu@新服务器IP:/tmp/

包大或者网络抖,用 rsync,断了能续:

rsync -avP /tmp/whale-1031.tar.gz ubuntu@新服务器IP:/tmp/

2.4 拆包,把属主改回来

cd /www/wwwroot
sudo tar xzf /tmp/whale-1031.tar.gz
sudo chown -R root:root /www/wwwroot/astrbot /www/wwwroot/napcat
sha256sum /tmp/whale-1031.tar.gz   # 和旧机器上那个数对一下

属主这条别省。容器的 NAPCAT_GID=0 是按 root 跑的,文件属主不对,NapCat 会读不到登录态,然后表现成「每次都让我重新扫码」——你会以为是风控,其实是权限。


三、拉起来

3.1 端口清单

先把这几个数字对齐,后面全用得上:

端口 谁在用 给谁看
6185 AstrBot WebUI 你,改人设看日志
6199 AstrBot 的 OneBot 反向 WS 只有 NapCat 连
6099 NapCat WebUI 你,配连接和扫码
22 SSH 只有你

6199 是容器之间对话用的,永远不该出现在公网上。6185 和 6099 是给你自己看的,也不该。

3.2 起容器

命令基本照抄旧机器,加三个参数:

docker run -d --name astrbot \
  --network host \
  --restart unless-stopped \
  -e TZ=Asia/Shanghai \
  --log-opt max-size=10m --log-opt max-file=3 \
  -v /www/wwwroot/astrbot/data:/AstrBot/data \
  soulter/astrbot:latest
docker run -d --name napcat \
  --network host \
  --restart unless-stopped \
  -e TZ=Asia/Shanghai \
  -e NAPCAT_GID=0 \
  --log-opt max-size=10m --log-opt max-file=3 \
  -v /www/wwwroot/napcat/config:/app/napcat/config \
  -v /www/wwwroot/napcat/qq:/app/.config/QQ \
  mlikiowa/napcat-docker:latest

新增的三样,每样都对应一个会咬人的场景:

--restart unless-stopped —— 这条最要紧。没有它,服务器重启(腾讯云偶尔维护、你手滑 reboot)之后容器不会自己起来,你在群里看到的是一条永远不回的机器人。

-e TZ=Asia/Shanghai —— 容器默认 UTC。不加这个,日志时间和聊天记录时间全部差八小时,出了问题对不上时间线。

--log-opt max-size=10m --log-opt max-file=3 —— Docker 的 json 日志默认不限大小。一个刷屏的群,一个月能把磁盘写满。写满之后先是写不进日志,然后是数据库写不进去,然后是你凌晨三点被叫起来。

搬到新机器之后你多半要改配置。记住一件事:改 cmd_config.json 要先 docker stop,不能改完直接 docker restart。AstrBot 启动时会把内存里的旧配置写回文件,你的改动会被覆盖,而且不报错。这条在另一篇的 5.6 里写细了。

3.3 腾讯云的端口入口

这里有个别扭:两条产品线的入口不在一块儿。

你买的 入口 路径
轻量应用服务器 防火墙 实例详情 → 防火墙 → 添加规则
云服务器 CVM 安全组 实例 → 安全组 → 编辑规则 → 入站规则

轻量没有「安全组」这个页面,CVM 没有「防火墙」这个页面。网上教程混着写,这是最容易卡住的一处。

那篇里我写的是「起来之后浏览器打开 http://服务器IP:6185」——能跑。但那是「先让它活过来」的写法。

搬到新机器上,这一条我改了主意:一个都别开。

除了 22(SSH),6185 和 6099 都不用对公网放行。要看得走隧道:

ssh -N -L 16185:127.0.0.1:6185 -L 16099:127.0.0.1:6099 ubuntu@服务器IP

然后浏览器开 127.0.0.1:16185 看 AstrBot,127.0.0.1:16099 看 NapCat。

理由很直白:AstrBot 的 WebUI 能改人设、能看全部聊天记录、能配 API Key。6099 能配 OneBot 连接。这两个东西挂在公网上、还是默认弱口令,等于把家门钥匙插在门上。扫描器是全天候的,6185 这种端口不出一天就会被摸到。

3.4 验证:容器起来了 ≠ 服务通了

docker ps --format 'table {{.Names}}\t{{.Status}}'

两个都是 Up,只说明进程活着。

再确认它们真的在监听:

ss -lntp | grep -E '6185|6199|6099'

三个端口都在,且绑的是 0.0.0.0(--network host 就是这样)。

到这里为止,只有「进程在跑」这一条被证明了。消息能不能收发,得等下一节。

3.5 想更稳就换 SnowLuma

NapCat 有个毛病:跑久了会被踢下线。但不是崩——容器还在,日志显示在线,消息就是发不出去。

这个毛病如果在你身上反复出现,换 SnowLuma。它也是 OneBot v11 协议端,AstrBot 那头一行不用改。社区里唯一一条「比 NapCat 更稳」的一手反馈说的就是这个:换过去之后再没掉过。

替换三步:

docker stop napcat
docker rm napcat
docker run -d --name snowluma \
  --network host \
  --restart unless-stopped \
  -e TZ=Asia/Shanghai \
  -v /www/wwwroot/snowluma/data:/app/data \
  <镜像名>

然后在它的 WebUI(5099,初始账号 admin,密码在 docker logs snowluma 里)配一条反向 WS:ws://127.0.0.1:6199/ws。扫码不在 WebUI 里,在 noVNC(6081)——那是个网页版远程桌面,看到的是真 QQ 的登录窗口。

它凭什么更稳,一句话:两个都在跑真 QQ,但 NapCat 跑的是拆掉显示层的 QQ,SnowLuma 跑的是原样的 QQ。NapCat 省掉渲染和显示,只留引擎和消息通道,换来 50~100MB 的内存;SnowLuma 把整个客户端连 Xvfb 显示一起跑起来,登录是在真 QQ 的界面里扫的。

风控看的不是「你发了什么」,是「这个设备像不像正常人在用」。这是推断,不是官方结论——但在我这台机器上,换过去之后确实没再出现过「日志在线、消息不发」。

代价也在同一句话里:它真的在跑一个完整的 QQ。渲染进程、Xvfb、VNC 一个不少,这就是下面第二条说的内存。

三件事得先知道:

  • 登录态不通用。上面那份 napcat/qq 拷过去没用,得重新扫。换的那天手机要在手边。
  • 端口编号变了。3.1 那张表是 NapCat 的:WebUI 从 6099 变 5099,它自己还开一个正向 WS 在 3001;6199 不变,那是 AstrBot 开的。SSH 隧道那条命令里的 16099 跟着换成 15099。
  • 它更吃内存。单账号 300MB 到 1.2G。这就是零节里我说 2G 只是底线、自己上 4G 的原因之一。

四、换 IP 之后

前面三节都是照着敲。这一节不是。

QQ 那套风控看的不是你的密码,是环境的一致性:登录的设备、IP、常用地、行为节奏。搬家一次性改掉了 IP 和机器,在它眼里约等于「这个号今天出现在另一个城市的另一台设备上」。

4.1 登录态带过去了,也可能要重扫

napcat/qq 里的登录态是有效的,但 IP 变了之后,服务端可能直接判它失效,让你重新验证。

所以先别急着庆祝:

docker logs napcat 2>&1 | tail -40

要么看到正常的连接日志,要么看到二维码。看到二维码就扫——这一步和第一次装的时候一样,另一篇第三节写过流程。

4.2 触发了验证怎么办

手机 QQ 上会弹「异地登录」或者「设备异常」,要短信、要好友辅助。

按提示走。别在这个环节反复试——连续多次失败会被判成盗号,直接冻结,那就不是扫码能解决的了。

如果旧机器还在,还有一个更稳的走法:先让旧机器下线,隔一段时间再登新机器,而不是两个同时在线。同一个号在两个 IP 上同时活跃,是最容易被盯上的形态。

4.3 搬家期间别做的几件事

  • 别反复重启容器。每重启一次就是一次重新登录,一次新的设备握手。频繁握手本身就是风控的信号。
  • 别在同一台手机上反复切号。手机是设备指纹的一部分,切来切去等于自己制造异常。
  • 别一上线就狂发消息测试。刚换 IP 的号,先让它安静收几条消息,再开口。

4.4 过渡:让旧的先活着

最稳的顺序是:

  • 新机器起容器,扫上码,确认能收消息
  • 让它在群里待半天,只看不说
  • 确认稳定了,再停旧机器
  • 旧机器再留 48 小时

反过来「先停旧再起新」看着干净,实际是把退路先砍了——新机器万一登不上,你连个能用的都没有。


五、退之前

旧机器不要当天退。4.4 说过为什么——新机器万一登不上,你得有地方退回去。这里说怎么退。

1. 备份落在本地,不是另一台服务器上

scp /tmp/whale-1031.tar.gz ~/backup/

「另一台服务器」和「这台服务器」是同一个云、同一个账号、大概率同一个可用区。它要是出事,备份跟着一起出事。硬盘比服务器便宜,存本地。

2. 确认三样东西是活的

群里发一条,它回。去 AstrBot 的 WebUI,能看到刚才那轮会话。

第三个是重启策略,单独验:

docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' astrbot
docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' napcat

两条都打出 unless-stopped 才算数。容器现在跑着,不代表服务器重启之后它还跑——这两件事在 docker ps 里长得一模一样。

3. 最后才是退租

轻量是整台退,没有「单独退带宽」这回事。点退订之前,先确认本地那份包能解开:

tar tzf ~/backup/whale-1031.tar.gz | head

能列出一串路径,再点。

退完把 IP 记在笔记里。以后翻旧日志,得靠它认得出哪条是哪条。


碎碎念

搬家这件事,装的部分只花了四十分钟。

剩下两天全耗在后面:登录验证弹了两次,消息发不出去一次,群里有人问「它是不是坏了」。

所以如果有人问我这套东西难在哪,我不会说配置。配置是死的,照着抄就行。

难的是——它的身份不在配置文件里,在那个 QQ 号上。机器可以换,号只有一个;容器可以重建,登录态重建不了。

我现在这台机器上跑着两个容器,常驻内存一 G 多,swap 基本没动过。它不掉线了,群里也没人再问。

搬完那天我在 ~/.ssh/config 里加了一行:

Host whale
    HostName 新服务器IP
    User ubuntu
    LocalForward 16185 127.0.0.1:6185
    LocalForward 16099 127.0.0.1:6099

以后要改人设,ssh -N whale,开 127.0.0.1:16185。

比以前开面板少点两下。

写于 2026 年 10 月

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注