大肥鱼是我那台 QQ 机器人。
AstrBot 接消息、调模型,NapCat 挂着那个 QQ 号,底下压着一个目录,里面是它的人设、它的记忆、它跟人说过的话。
它原来住在另一台机器上。那台机器是两年前开的,配置放到现在不太够用:两个容器轮流把内存顶到 90%,半夜掉一次线,第二天早上才发现。
所以我把它搬到了腾讯云。
工具怎么装、模型怎么接、二维码怎么扫,这些我另一篇写过:《用 AstrBot + NapCat 把大模型接入 QQ》。这篇只讲服务器这一侧:机器怎么选、系统要准备什么、旧数据怎么一件不少地带过去、端口该开哪几个。
还有最后一件事——换 IP 之后会发生什么。这一件,没法照着命令敲。
零、准备工作
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 月
发表回复