Anubis 1.28 常用命令与运维实战(核心篇)
anubis 1.28 命令与运维核心手册:server 启动参数分组速查、systemd 实例运维与资源实测、策略前台校验与错误字典、bbolt 持久化与锁、Prometheus 指标族、JSON 日志分析、升级回滚演练与五则排障实录,Ubuntu 24.04 原生部署实测。
Anubis 1.28 常用命令与运维实战(核心篇)
Anubis(TecharoHQ 项目)是部署在反向代理位置、以 Proof-of-Work 质询拦截 AI 爬虫的防护网关。本篇是 Anubis 专栏的命令与运维核心手册:以原生 systemd 部署形态为基准,覆盖
anubis二进制的全部启动参数、systemd 实例化运维、策略前台校验与错误字典、Prometheus 指标、JSON 结构化日志分析,以及一次完整的版本升级与回滚演练。前置阅读:《Anubis 介绍与选型指南》、《Anubis v1.28.2 配置参数详解》;容器化部署形态见 《Anubis 1.28 Docker Compose 部署实战》。文中所有命令输入与输出均来自 2026-10-28 的实测,回显逐行照抄(涉及密钥、时间戳的字段按样例保留)。策略与配置语法细节不在本篇重复展开,请配合配置参数详解篇使用。
一、环境信息
| 项 | 值 |
|---|---|
| 主机系统 | Ubuntu 24.04 LTS(原生 .deb 安装,systemd 托管) |
| Anubis 版本 | v1.28.2(官方 GitHub Releases 的 anubis_1.28.2_amd64.deb) |
| 运行实例 | anubis@gitea:保护本机 http://localhost:3000 上的演示上游站点 |
| 监听 | 服务端口 127.0.0.1:8239,指标端口 127.0.0.1:8240 |
| 配置 | /etc/anubis/gitea.env(环境变量)+ /etc/anubis/gitea.botPolicies.yaml(策略) |
| 状态存储 | bbolt 单文件 /var/lib/anubis/gitea/cache.db(systemd 实际映射至 /var/lib/private/anubis/gitea/) |
| 发布渠道 | https://github.com/TecharoHQ/anubis/releases |
二、命令速查总览
2.1 顶层命令与附属工具
| 命令 | 用途 |
|---|---|
anubis | 主进程(无子命令设计,server 即默认行为),读取环境变量或等效 flag 启动反向代理 |
anubis --version | 打印版本号 |
anubis --healthcheck | 对本实例指标端口执行健康探测,退出码 0/1 |
anubis --extract-resources <目录> | 导出内置静态资源与出厂规则片段 |
anubis-robots2policy | 附属工具:把 robots.txt 规则转换为 Anubis 策略片段 |
实测回显:
$ anubis --version
Anubis v1.28.2
$ mkdir -p /root/res && anubis --extract-resources /root/res
Extracted embedded static files to /root/res
$ find /root/res -maxdepth 1
/root/res
/root/res/data
/root/res/static
导出的 data/ 下就是内置的出厂策略(botPolicies.yaml)与规则片段库(bots/、meta/),static/ 是质询页面静态资源。运维价值:不确定某个 (data)/... 片段名是否存在时,先导出盘点再写策略,详见排障实录第 2 条。
2.2 anubis 启动参数分组速查
anubis --help 列出全部 flag(v1.28.2 共 50 余项)。与同名大写环境变量一一等价(如 -bind ↔ BIND、-policy-fname ↔ POLICY_FNAME),systemd 形态优先用环境变量文件,容器/一次性调试用 flag。核心项按用途分组如下(默认值照抄 --help):
监听与上游
| flag(默认值) | 说明 |
|---|---|
-bind(":8923") | 服务监听地址 |
-bind-network("tcp") | 监听网络族:tcp 或 unix |
-target("http://localhost:3923") | 反向代理目标;置空则只做 auth request 应答不代理 |
-target-host | 覆写转发到上游的 Host 头 |
-public-url | 对外可达 URL,用于 auth request 场景的重定向构造 |
-base-prefix("") | 应用挂载的子路径前缀,如 /myapp |
-strip-base-prefix | 转发上游前剥离子路径前缀 |
-custom-real-ip-header | 用指定头替代 X-Real-IP 读取客户端 IP |
-use-remote-address | 直接从网络连接读客户端 IP(裸金属/调试用) |
策略与质询
| flag(默认值) | 说明 |
|---|---|
-policy-fname("") | 策略文件路径;缺省用编译内置的出厂策略 |
-difficulty(4) | 质询难度(前导 0 的十六进制位数) |
-difficulty-in-jwt | 在 JWT claims 中写入难度字段 |
-ed25519-private-key-hex / -ed25519-private-key-hex-file | JWT 签名私钥(hex / 文件二选一) |
-hs512-secret | 改用 HS512 对称密钥签名 JWT |
-jwt-restriction-header("X-Real-IP") | JWT 仅在该头取值与签发时一致时有效 |
Cookie
| flag(默认值) | 说明 |
|---|---|
-cookie-prefix("techaro.lol-anubis") | Cookie 名前缀 |
-cookie-domain | 限定 Cookie 顶级域(多域名站需要) |
-cookie-expiration-time(168h) | 通过质询后的通行有效期(7 天) |
-cookie-secure(true) | Secure 标记 |
-cookie-same-site("None") | 同站策略;cookie-secure=false 时自动降级为 Lax |
-cookie-partitioned(true) | 启用 CHIPS 分区 Cookie |
-cookie-http-only | HttpOnly 标记 |
-dynamic-cookie-suffix(true) | 按站点设置动态生成 Cookie 名后缀 |
指标、日志与杂项
| flag(默认值) | 说明 |
|---|---|
-metrics-bind(":9090") | 指标/健康检查端口 |
-metrics-bind-network("tcp") | 指标端口网络族 |
-slog-level("INFO") | 结构化日志级别 |
-serve-robots-txt | 直接对外提供"全站禁止抓取"的 robots.txt |
-webmaster-email | 拒绝页展示的站长邮箱(误杀申诉入口) |
-healthcheck | 执行健康探测后立即退出(见 §5.3) |
-extract-resources("") | 导出内置资源(见 §2.1) |
-debug-benchmark-js | 每个请求都返回质询页用于算力基准测试(见 §4.4) |
另有 -thoth-* 三个 flag 已标记 deprecated(Thoth GeoIP 能力已并入策略文件的 geoip 配置),新配置不应再使用。
三、systemd 实例运维
3.1 模板单元解剖
官方 .deb 自带模板单元 anubis@.service,%i 为实例名:
[Unit]
Description="Anubis HTTP defense proxy (instance %i)"
[Service]
ExecStart=/usr/bin/anubis
Restart=always
RestartSec=30s
EnvironmentFile=/etc/anubis/%i.env
LimitNOFILE=infinity
DynamicUser=yes
CacheDirectory=anubis/%i
CacheDirectoryMode=0755
StateDirectory=anubis/%i
StateDirectoryMode=0755
RuntimeDirectory=anubis/%i
RuntimeDirectoryMode=0755
ReadWritePaths=/run
[Install]
WantedBy=multi-user.target
要点:
- 实例模型:
systemctl enable --now anubis@<名字>即拉起一个独立实例,各自持有/etc/anubis/<名字>.env与专属状态目录。一台机器可并行防护多个上游。 - DynamicUser=yes:进程以临时动态 UID 运行,
StateDirectory声明的目录实际落在/var/lib/private/anubis/<实例>/(透过符号链接/var/lib/anubis/<实例>访问),直接ls /var/lib/anubis看不到文件不是故障。 - Restart=always + RestartSec=30s:进程崩溃后 30 秒重启,策略解析失败这类确定性错误会表现为 30 秒一次的 crash loop,
systemctl status能直接看到节奏。
3.2 实例启停与巡检
$ systemctl enable --now anubis@gitea
$ systemctl is-active anubis@gitea
active
$ systemctl status anubis@gitea --no-pager -l | head -6
● anubis@gitea.service - "Anubis HTTP defense proxy (instance gitea)"
Loaded: loaded (/usr/lib/systemd/system/anubis@.service; enabled; preset: enabled)
Active: active (running) since Wed 2026-10-28 02:12:37 UTC; 11min ago
Main PID: 1075 (anubis)
Tasks: 11 (limit: 3172)
Memory: 12.0M (peak: 19.3M)
日常巡检一条龙(活跃性、健康检查、指标端点、版本四连验,全部通过无输出差错):
$ systemctl is-active anubis@gitea && METRICS_BIND=:8240 anubis --healthcheck && curl -s -o /dev/null -w "healthz: HTTP %{http_code}\n" http://127.0.0.1:8240/healthz && anubis --version
active
2026/10/28 02:26:47 running healthcheck
healthz: HTTP 200
Anubis v1.28.2
启动成功的日志锚点(JSON 结构化):
$ journalctl -u anubis@gitea -o cat --no-pager | grep '"msg":"listening"'
{"time":"2026-10-28T02:11:52.812839753Z","level":"INFO","msg":"listening","subsystem":"anubis","bind":"127.0.0.1:8239","bind_network":"tcp","version":"v1.28.2","use_remote_address":false,"difficulty":4,"serve_robots_txt":false}
3.3 资源占用实测
空载常驻内存约 12 MiB,质询求解期间峰值约 19–20 MiB:
$ systemctl show anubis@gitea -p MainPID,MemoryCurrent
MainPID=3211
MemoryCurrent=20770816
$ ps -o rss=,comm= -p 3211
20772 anubis
(上述取值为质询活跃时刻的峰值;§3.2 的 status 输出则显示常态 Memory: 12.0M (peak: 19.3M)。)Go 单二进制的典型水平。生产容量规划可按单实例 20–40 MiB 估列,内存不是该组件的瓶颈项。
3.4 状态目录与持久化
bbolt 存储由策略文件 store 段指定路径(v1.28.2 的正确写法是 backend: bbolt + parameters.path,写错会被静默回落为进程内缓存,详见排障实录第 4 条)。落盘验证:
$ grep -A3 '^store:' /etc/anubis/gitea.botPolicies.yaml
store:
backend: bbolt
parameters:
path: /var/lib/anubis/gitea/cache.db
$ ls -l /var/lib/private/anubis/gitea/cache.db
-rw-r--r-- 1 root root 16384 Oct 28 10:05 /var/lib/private/anubis/gitea/cache.db
bbolt 是单文件嵌入式存储,持久化内容为已通过质询的通行凭证(签名 JWT 状态),重启实例后已放行用户的 Cookie 依然有效(验证方法见 §7.2)。
四、策略校验与调试
4.1 标准校验法:前台一次性启动
v1.28.2 没有独立的 anubis validate 子命令,官方校验手段就是前台把进程拉起来看能否走到 listening。标准三步:
$ timeout 6 anubis -policy-fname /root/policy-validate.yaml -bind 127.0.0.1:18923 -metrics-bind 127.0.0.1:19090 -target http://127.0.0.1:3000 -slog-level INFO | grep -oE '"msg":"[^"]{0,40}' | tail -4
"msg":"generating random key, Anubis will have
"msg":"REDIRECT_DOMAINS is not set, Anubis will
"msg":"USE_SIMPLIFIED_EXPLANATION is deprecated
"msg":"listening
命令 6 秒后被 timeout 杀掉属预期(退出码 124),成功锚点就是末尾的 listening;消息链从 starting up Anubis、loading policy file 起。注意:待校验的策略若带 store 段且生产实例正占用同一个 bbolt 文件,校验进程会挂在文件锁上(见 §4.3),校验副本应临时去掉 store 段。
4.2 校验错误字典(实测回显)
四类典型错误,回显逐行照抄:
① YAML 语法错误
$ printf 'bots:\n - name: broken\n user_agent_regex: [Mozilla\n' > /root/policy-broken.yaml
$ timeout 6 anubis -policy-fname /root/policy-broken.yaml -bind 127.0.0.1:18923 -metrics-bind 127.0.0.1:19090 -target http://127.0.0.1:3000
2026/10/28 02:21:47 can't parse policy file: can't parse policy file /root/policy-broken.yaml: can't parse policy config YAML /root/policy-broken.yaml: error converting YAML to JSON: yaml: line 3: did not find expected ',' or ']'
(解析失败时进程打印该行后立即退出,无 listening。)
② 动作字段拼错(action: DENYD,非法值)
$ timeout 6 anubis -policy-fname /root/p-action.yaml -bind 127.0.0.1:18923 -metrics-bind 127.0.0.1:19090 -target http://127.0.0.1:3000
2026/10/28 02:21:36 can't parse policy file: can't parse policy file /root/p-action.yaml: config is not valid:
bot 0: config: bot entry for "badaction" is not valid:
config.Bot: unknown action: "DENYD"
③ 正则编译失败(user_agent_regex: "[Mozilla")
$ timeout 6 anubis -policy-fname /root/p-regex.yaml -bind 127.0.0.1:18923 -metrics-bind 127.0.0.1:19090 -target http://127.0.0.1:3000
2026/10/28 02:21:36 can't parse policy file: can't parse policy file /root/p-regex.yaml: config is not valid:
bot 0: config: bot entry for "badregex" is not valid:
config.Bot: invalid user agent regex
error parsing regexp: missing closing ]: `[Mozilla`
④ import 规则混入其他键
$ # - name: ai-block-aggressive 与 import: 写进同一条规则
2026/10/28 01:41:39 failed to import policy file "/etc/anubis/gitea.botPolicies.yaml": error running policy file validation: yaml: unmarshal errors:
line 12: import rules may not contain other keys
import 规则的正确写法是该条规则只有
import:一个键:- import: (data)/meta/ai-block-aggressive.yaml。(data)/前缀指向内置片段库,可先anubis --extract-resources导出盘点(§2.1)。
| 症状 | 一行修复 |
|---|---|
did not find expected node content | YAML 缩进/引号错误,用 python3 -c "import yaml,sys;yaml.safe_load(open(sys.argv[1]))" 文件名 定位行号 |
invalid action | action 只能是 ALLOW / CHALLENGE / DENY / WEIGH |
missing closing ] | 正则先过一遍本地编译再入库 |
import rules may not contain other keys | import 规则去掉 name: 等其他键,只留 import: |
4.3 隐性故障:bbolt 独占锁
对生产实例正在使用的策略文件(含同一路径的 store 段)执行前台校验,进程会静默挂起直到 timeout 杀掉。实测:输出停在 loading policy file 后再无下文,且不打印任何错误:
$ timeout 10 anubis -policy-fname /etc/anubis/gitea.botPolicies.yaml -bind 127.0.0.1:18923 -metrics-bind 127.0.0.1:19090 -target http://127.0.0.1:3000 > /tmp/hang.log 2>&1
$ cat /tmp/hang.log
{"time":"2026-10-28T02:24:00.659278291Z","level":"INFO","source":{"function":"main.run","file":"github.com/TecharoHQ/anubis/cmd/anubis/main.go","line":286},"msg":"starting up Anubis"}
{"time":"2026-10-28T02:24:00.659394965Z","level":"INFO","source":{"function":"main.run","file":"github.com/TecharoHQ/anubis/cmd/anubis/main.go","line":308},"msg":"loading policy file","fname":"/etc/anubis/gitea.botPolicies.yaml"}
根因:bbolt 打开文件时取独占锁,store 段路径与生产实例相同即挂死在锁等待上(卡点就在装载策略、打开存储这一步)。ps 视角下两个 anubis 进程都在等锁(Go 运行时等待的表象相同),判定依据是新进程迟迟打不出 listening:
$ ps -o pid,stat,wchan:26,comm -C anubis
PID STAT WCHAN COMMAND
1075 Ssl futex_do_wait anubis
1375 Sl futex_do_wait anubis
规避:校验副本临时删掉 store 段(§4.1),或停生产实例后再校验。
4.4 -debug-benchmark-js:质询算力基准模式
$ anubis -debug-benchmark-js -policy-fname /root/policy-validate.yaml -bind 127.0.0.1:18923 -metrics-bind 127.0.0.1:19090 -target http://127.0.0.1:3000 &
$ curl -s -A 'Mozilla/5.0' http://127.0.0.1:18923/ | grep -oE '<title>[^<]*</title>'
<title>Making sure you're not a bot</title>
$ curl -s -A 'Mozilla/5.0' http://127.0.0.1:18923/ | grep -oE 'Getting average benchmark time[^<]*'
Getting average benchmark time
该模式下每个请求都返回基准质询页,浏览器端反复求解并上报算力,用于评估不同 -difficulty 对客户端的耗时影响。调难度参数前建议先在自己目标用户的主流设备上跑一遍基准,再定策略档位。
4.5 anubis-robots2policy:robots.txt 转策略
$ printf 'User-agent: GPTBot\nDisallow: /\n\nUser-agent: Googlebot\nCrawl-delay: 2\nDisallow: /private/\n' > /root/robots.txt
$ anubis-robots2policy -input /root/robots.txt
policy:
bots:
- name: gptbot
user_agent_regex: \bGPTBot\b
action: DENY
- name: googlebot
user_agent_regex: \bGooglebot\b
path_regex: ^/private/.*$
action: DENY
输出可直接粘进 bots: 列表。注意它把 Disallow 全部译成 DENY:若只想给合规爬虫设质询门槛而非硬拒,把 action 手工改为 CHALLENGE 即可。
4.6 内置策略盘点
出厂默认策略(未配置 POLICY_FNAME 时生效的那份)同样可以导出审阅:
$ grep -n 'import:' /root/res/data/botPolicies.yaml | head -8
- import: (data)/bots/_deny-pathological.yaml
- import: (data)/bots/_denied-networks.yaml
- import: (data)/crawlers/_all-bots.yaml
- import: (data)/meta/ai-block-aggressive.yaml
...
两个实用结论:其一,内置片段名带 _ 前缀(如 _deny-pathological.yaml),凭直觉写 default-deny.yaml 会得到 unknown snippet 错误(排障实录第 2 条);其二,meta/ai-block-aggressive.yaml 展开后是一组 AI 爬虫 UA 聚合规则(如 bot/ai-catchall),是生产策略里"AI 爬虫一刀切"模块的实体。
五、Prometheus 指标与健康检查
5.1 指标端点与指标族
$ curl -s http://127.0.0.1:8240/metrics | grep '^# HELP' | grep anubis
# HELP anubis_policy_results The results of each policy rule
# HELP anubis_proxied_requests_total Number of requests proxied through Anubis to upstream targets
v1.28.2 的 Anubis 业务指标只有两个指标族(端点同时暴露 Prometheus 客户端库自带的标准指标族,此处只看 anubis_ 前缀):
anubis_policy_results:按命中规则与动作分桶的计数器。一个请求命中策略即计数一次,是"谁被拦、为什么"的核心观测面。anubis_proxied_requests_total:按上游 host 分桶的成功代理计数器,即"放行后真实到达上游的流量"。
实测样本(与 §6 日志、§4 场景对应):
$ curl -s http://127.0.0.1:8240/metrics | grep '^anubis_'
anubis_policy_results{action="CHALLENGE",asn="",asn_description="",rule="bot/generic-browser"} 2
anubis_policy_results{action="DENY",asn="",asn_description="",rule="bot/ai-catchall"} 1
anubis_proxied_requests_total{asn="",asn_description="",host="127.0.0.1:8239"} 2
判读:两条 CHALLENGE 记录来自普通浏览器 UA(规则 bot/generic-browser,v1.28 的 policy_results 维度里 DENY 动作即硬拒绝,CHALLENGE 动作含"已通过"与"待通过"两种状态);DENY 记录来自 Amazonbot UA,命中 AI 爬虫聚合规则 bot/ai-catchall。注意:该版本没有独立的"质询通过数"业务指标,"通过量"用 anubis_proxied_requests_total 近似,"发放质询数"从日志统计(§6.3)。
5.2 --healthcheck:监控探针
anubis --healthcheck 探测的是本进程指标端口的 /healthz,适合直接做 systemd ExecStartPost 或外部拨测。三种取值的实测对照(这一坑必须记牢):
$ METRICS_BIND=":8240" anubis --healthcheck
2026/10/28 02:17:21 running healthcheck
exit=0
$ METRICS_BIND="127.0.0.1:8240" anubis --healthcheck
2026/10/28 02:17:21 running healthcheck
2026/10/28 02:17:21 failed to fetch metrics: Get "http://localhost127.0.0.1:8240/healthz": dial tcp: lookup localhost127.0.0.1 on 127.0.0.11:53: no such host
exit=1
$ METRICS_BIND="localhost:8240" anubis --healthcheck
2026/10/28 02:17:21 running healthcheck
2026/10/28 02:17:21 failed to fetch metrics: Get "http://localhostlocalhost:8240/healthz": dial tcp: lookup localhostlocalhost on 127.0.0.11:53: server misbehaving
exit=1
实现把探测 URL 拼成 http://localhost + METRICS_BIND,只有 :端口 形式取值能拼出合法地址。健康检查命令里写 METRICS_BIND=:8240(与实例 env 文件中的值保持同形式),错误端口则报 connection refused 并以退出码 1 结束。
六、日志分析与审计
6.1 JSON 日志字典
日志为 JSON 行,journalctl -o cat 去前缀后即原文。实测出现的消息类型:
| msg | 含义 | 关键字段 |
|---|---|---|
listening | 实例就绪 | bind、version、difficulty |
loading policy file / configuration imported | 策略装载过程 | fname |
explicit deny | 命中 DENY 规则拒绝请求 | check_result.name(规则名)、user_agent、x_real_ip |
new challenge issued | 向请求方发放质询 | challenge(质询 ID)、weight |
challenge accepted | 质询求解通过、发通行凭证 | challenge |
两条完整实测样本(升级演练期间,字段照抄):
{"time":"2026-10-28T02:16:09.53144794Z","level":"INFO","source":{"function":"github.com/TecharoHQ/anubis/lib.(*Server).issueChallenge","file":"github.com/TecharoHQ/anubis/lib/anubis.go","line":226},"msg":"new challenge issued","subsystem":"anubis","host":"127.0.0.1:8239","method":"GET","path":"/","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0","accept_language":"","priority":"","x-forwarded-for":"","x-real-ip":"203.0.113.10","challenge":"01a1804b-203a-7e4c-a8d5-3ac271b73352","weight":0}
{"time":"2026-10-28T02:15:22.551808285Z","level":"INFO","source":{"function":"github.com/TecharoHQ/anubis/lib.(*Server).checkRules","file":"github.com/TecharoHQ/anubis/lib/anubis.go","line":464},"msg":"explicit deny","subsystem":"anubis","host":"127.0.0.1:8239","method":"GET","path":"/","user_agent":"Amazonbot/0.1 (+https://developer.amazon.com/support/amazonbot)","accept_language":"","priority":"","x-forwarded-for":"","x-real-ip":"203.0.113.77","check_result":{"name":"bot/ai-catchall","rule":"DENY","weight":0}}
6.2 实用分析管道
# 被拒 UA 排行(直接定位该拉黑/该降档的爬虫)
$ journalctl -u anubis@gitea -o cat --no-pager | grep 'explicit deny' \
| grep -o '"user_agent":"[^"]*"' | sort | uniq -c | sort -rn | head -3
1 "user_agent":"Amazonbot/1.0"
1 "user_agent":"Amazonbot/0.1 (+https://developer.amazon.com/support/amazonbot)"
# 观测期内发放的质询总数
$ journalctl -u anubis@gitea -o cat --no-pager | grep -c 'new challenge issued'
6
# 按来源 IP 汇总被拒请求
$ journalctl -u anubis@gitea -o cat --no-pager | grep 'explicit deny' \
| grep -o '"x-real-ip":"[^"]*"' | sort | uniq -c | sort -rn | head
采集侧注意:x-forwarded-for 在接入层不透传该头时为空串,客户端 IP 一律取 x_real_ip 字段做归并,避免把反代自身 IP 当成攻击源。
七、版本升级与回滚
7.1 升级演练:1.28.1 → 1.28.2
$ anubis --version
Anubis v1.28.1
$ dpkg -i /root/anubis_1.28.2_amd64.deb
(正在读取数据库 ... 系统当前共安装有 15244 个文件和目录。)
准备解包 .../anubis_1.28.2_amd64.deb ...
正在解包 anubis (1.28.2) 并覆盖 (1.28.1) ...
正在设置 anubis (1.28.2) ...
$ anubis --version
Anubis v1.28.2
**关键发现:.deb 升级只换文件,不会重启正在运行的实例。**升级后立刻查日志,进程仍是旧版本:
$ journalctl -u anubis@gitea -o cat --no-pager --since '1 minute ago' | grep -o '"version":"[^"]*"' | tail -1
"version":"v1.28.1"
必须显式滚动重启:
$ systemctl restart anubis@gitea
$ sleep 2 && systemctl is-active anubis@gitea
active
$ journalctl -u anubis@gitea -o cat --no-pager --since '30 seconds ago' | grep -o '"version":"[^"]*"' | tail -1
"version":"v1.28.2"
多实例机器上升级后的标准动作是逐实例 systemctl restart anubis@<名字>,全部重启完成后再进入验证。
7.2 升级后状态连续性验证
三件事验证:配置未被动过、持久化文件延续、旧用户凭证依然有效(用已通过质询的浏览器 Cookie 重放请求):
$ md5sum /etc/anubis/gitea.env /etc/anubis/gitea.botPolicies.yaml
5df8b403be0b4b7a6f9d28b22a44d3f0 /etc/anubis/gitea.env
7699b9550746c6b665f92c78a8e0e475 /etc/anubis/gitea.botPolicies.yaml
$ ls -l /var/lib/private/anubis/gitea/cache.db
-rw-r--r-- 1 root root 16384 Oct 28 10:05 /var/lib/private/anubis/gitea/cache.db
$ # 用升级前留下的通行 Cookie 直接回访
$ python3 /root/pow_client.py replay
step4 revisit -> 200 | upstream: demo upstream for anubis ops
Cookie 跨升级版本有效:JWT 签名密钥来自 env 文件(未变)、通行状态在 bbolt(未变),两把钥匙都在,票据自然有效——用户无感知,这就是把签名私钥持久化进配置的价值。
7.3 回滚演练
apt 体系下用 dpkg -i 装低版本 .deb 即完成回滚(同款包管理路径,无需卸载):
$ dpkg -i /root/anubis_1.28.1_amd64.deb
正在解包 anubis (1.28.1) 并覆盖 (1.28.2) ...
$ anubis --version
Anubis v1.28.1
$ systemctl restart anubis@gitea && journalctl -u anubis@gitea -o cat --no-pager --since '30 seconds ago' | grep -o '"version":"[^"]*"' | tail -1
"version":"v1.28.1"
$ python3 /root/pow_client.py replay
step4 revisit -> 200 | upstream: demo upstream for anubis ops
回滚后旧 Cookie 依然有效(策略文件与签名密钥未动)。演练后按 §7.1 流程升回 1.28.2 收尾。运维纪律:升级前把上一个大版本的 .deb 留在本地包目录,回滚窗口就在手边。
容器化部署形态的升级与回滚操作见 《Anubis 1.28 Docker Compose 部署实战》的对应章节。
八、排障实录
以下五则均为本次实测中真实发生、逐行留痕的故障,按"现象 → 定位 → 处置"沉淀。
8.1 启动失败:import rules may not contain other keys
- 现象:
systemctl start anubis@gitea后服务 crash loop,systemctl status显示activating (auto-restart),30 秒一轮。 - 日志:
journalctl -u anubis@gitea -o cat | tail -3给出error running policy file validation: yaml: unmarshal errors: line 12: import rules may not contain other keys。 - 定位:策略里把 import 写成了
- name: ai-block-aggressive+import: ...两键同条。import 是"整段引入"语义,不允许再挂其他键。 - 处置:改成单键规则
- import: (data)/meta/ai-block-aggressive.yaml;systemctl restart后is-active转绿。 - 预防:import 一律独立成条;上线前按 §4.1 前台校验。
8.2 启动失败:unknown snippet
- 现象:换用
- import: (data)/bots/default-deny.yaml后依然 crash loop,报unknown snippet: (data)/bots/default-deny.yaml。 - 定位:凭记忆写片段名。内置片段库实际名为
_deny-pathological.yaml等,带_前缀,且并非所有直觉命名都存在。 - 处置:
anubis --extract-resources /root/res导出,ls /root/res/data/bots/ /root/res/data/meta/盘点真实名字后引用(data)/bots/_deny-pathological.yaml。 - 预防:引用内置片段前必盘点(§2.1),不要手写印象中的片段名。
8.3 策略校验命令静默挂起
- 现象:对生产策略文件做前台校验(§4.1),进程 30 秒无任何输出,被
timeout杀掉,既没有报错也没有listening。 - 定位:
ps -o pid,stat,comm -C anubis见新进程D+状态——卡在 bbolt 文件锁;生产实例持锁,校验副本store段指向同一cache.db,无限等待。 - 处置:校验副本临时去掉
store段(或停生产实例再校验);校验秒过。 - 预防:校验用"去 store 副本"是标准姿势;把这条写进团队 checklist,否则每次校验都要白等一个 timeout 周期。
8.4 配置了 bbolt 持久化,但 cache.db 不生成
- 现象:策略写了
store:段,服务正常跑,/var/lib/anubis/gitea/下始终没有cache.db,重启后通行凭证全丢。 - 定位:写法错误——
store:下直接嵌bbolt:键。v1.28 的正确 schema 是backend: bbolt+parameters.path: ...。错误 schema 不报错,静默回落为进程内缓存,这正是"配置看似生效、实则没生效"的典型。 - 处置:改为正确 schema(§3.4),重启后
cache.db落盘(16384 字节)。 - 预防:store 改动后除了
is-active,必须加"文件是否落盘"这步验证——服务活着不代表配置生效。
8.5 升级后版本没变
- 现象:
dpkg -i装完 1.28.2,anubis --version已是新版本,日志里的运行实例仍是"version":"v1.28.1"。 - 定位:.deb 升级只替换文件,不触碰运行中进程(单元未声明
ExecReload,也没有升级钩子)。 - 处置:逐实例
systemctl restart anubis@<名字>,日志确认新版本号(§7.1)。 - 预防:把"升级 ≠ 生效,重启 + 日志验版本"固化为升级 SOP 的固定两步。
九、例行维护清单
| 周期 | 动作 | 判据 |
|---|---|---|
| 每日 | systemctl is-active anubis@<实例> + METRICS_BIND=:8240 anubis --healthcheck | 双 active/退出码 0 |
| 每日 | curl -s http://127.0.0.1:8240/metrics | grep '^anubis_' 存档 | 样本量连续、无异常清零 |
| 每周 | §6.2 拒绝 UA 排行复盘 | 新增高频爬虫 UA → 评估策略条目 |
| 每月 | bbolt 文件与 /etc/anubis/ 配置备份 | 备份前 systemctl stop 实例,拷贝后拉起 |
| 每季 | 对比 GitHub Releases 最新 tag,走 §7 升级演练 | 日志验证新版本号 + Cookie 连续性 |
| 事件驱动 | 策略变更 | §4.1 前台校验 + 变更后指标/日志双确认 |