跳转到内容

部署与升级

本页带你把一个能通过 --test 的 katana 二进制文件,变成可以长期运行、升级时不出意外的服务。内容包括:安装哪种 release 构建、文件放在哪里、每个配置文件运行一个 katana 进程的 systemd 模板单元、如何阅读日志、先重启一个实例再重启其他实例的升级流程、如何回滚、重启后要测试什么,以及内存和文件描述符如何随负载增长。

本页面向在装有 systemd 的 x86_64 Linux 上运行 katana v3.0.1 的运维人员。命令中使用两个示例实例 xboard 和 sspanel,每个面板一个。如果你还没有运行过 katana,请先阅读快速上手;katana 与 etemenanki-app 共有的服务行为(退出码、信号、日志过滤器)见生产环境运行。

flowchart LR
  SD["systemd: katana@xboard"] --> P1["katana 进程"]
  SD2["systemd: katana@sspanel"] --> P2["katana 进程"]
  P1 --> C1["/etc/katana/config-xboard.toml"]
  P2 --> C2["/etc/katana/config-sspanel.toml"]
  P1 -- "stdout" --> J["journald"]
  P2 -- "stdout" --> J
  P1 -- "轮询并上报" --> PA["面板 A"]
  P2 -- "轮询并上报" --> PB["面板 B"]
  • 一个二进制文件 /usr/local/bin/katana,所有实例共用。
  • 每个实例一个配置文件。每个文件可以包含多个 [[node]] 条目。
  • 一个 systemd 模板单元 katana@.service。实例名决定使用哪个文件。
  • 日志写到标准输出,由 journald 保存。
  • katana 不写任何文件。它需要读取自己的配置、证书、geodata、规则列表,以及设置了的 [dns] ca_file,除此之外不需要访问磁盘上的其他内容。

每个 katana release tag 都会发布两个 x86_64 Linux 可执行文件,各附一个 SHA-256 校验和文件。VERSION 是 tag,例如 v3.0.1。

katana-VERSION-linux-x86_64-gnu katana-VERSION-linux-x86_64-musl
链接方式 动态链接主机的 glibc。OpenSSL 编译在内。 完全静态:没有程序解释器,也没有共享库。
主机要求 glibc 版本不低于构建主机,即 Ubuntu 22.04(glibc 2.35)。 任意 x86_64 Linux,包括 Alpine。
[dns] backend = "system" glibc 的 getaddrinfo,遵循 /etc/nsswitch.conf 及其 NSS 模块。 musl 的解析器,直接读取 /etc/hosts 和 /etc/resolv.conf。
内存分配器 glibc 的 malloc musl 的 malloc

release workflow 会检查它承诺的这两项特性:如果 -gnu 二进制文件动态加载 libssl 或 libcrypto,workflow 失败;如果 -musl 二进制文件有任何动态依赖,workflow 也失败。

如何选择:

主机 构建
Ubuntu 22.04 及以上,Debian 12 及以上 -gnu;如果希望所有主机用同一个二进制文件,也可以用 -musl
Debian 11、Ubuntu 20.04、RHEL 9 及其重构发行版(glibc 2.34)、Alpine -musl
域名解析依赖 NSS 模块(例如 nss-resolve、LDAP 或自定义的 hosts: 行) -gnu
不确定 -musl

katana 不自带内存分配器,因此两种构建在分配每个缓冲区和用户表时使用的分配器也不同。一种构建的内存数据无法推断另一种构建的表现。选定一种构建后,升级时保持不变,这样前后对比衡量的是新版本,而不是新的分配器。

要确认主机能否运行 -gnu 构建,执行该二进制文件的 --version。glibc 太旧时会在这一步直接失败,报 version `GLIBC_2.xx' not found,与配置无关。

从 release 页面或用 GitHub CLI,把二进制文件和对应的 .sha256 文件下载到同一目录。OWNER 是托管 katana 仓库的账号,你需要有该仓库的读取权限。.sha256 文件按资产的原始文件名引用它,因此请在下载目录中、重命名或移动任何文件之前执行校验:

终端窗口
VERSION=v3.0.1
BUILD=musl # 或 gnu
gh release download "$VERSION" --repo OWNER/katana \
--pattern "katana-$VERSION-linux-x86_64-$BUILD*"
sha256sum -c "katana-$VERSION-linux-x86_64-$BUILD.sha256"
katana-v3.0.1-linux-x86_64-musl: OK

输出不是 OK 就说明下载不完整或已损坏:请重新下载,不要安装。校验和与二进制文件来自同一个 CI job,因此它能发现文件损坏,但无法发现文件被替换。这些资产是普通可执行文件,不是压缩包。首次安装时的同一下载流程见安装。

katana 没有内置的文件位置。它从 -c 读取配置,其他所有文件都从配置中写明的路径读取。下面的布局把所有实例的文件都放在 /etc/katana 下:

  • 文件夹usr/local/bin/
    • katana 所有实例运行的二进制文件
    • katana.prev 上一个版本,升级后保留,用于回滚
  • 文件夹etc/
    • 文件夹katana/ 属主 root,属组 katana,权限 0750
      • config-xboard.toml 实例 katana@xboard,权限 0640
      • config-sspanel.toml 实例 katana@sspanel,权限 0640
      • 文件夹certs/
        • fullchain.pem TLS 和 Hysteria 2 节点使用的证书链
        • privkey.pem 私钥,权限 0640,属组 katana
      • geoip.dat 仅当路由规则使用 geoip 时需要
      • geosite.dat 仅当路由规则使用 geosite 时需要
      • rules.txt 本地审计规则,仅当设置了 rule_list_path 时需要
    • 文件夹systemd/

配置中的每个路径都写成绝对路径。像 geoip = "geoip.dat" 这样的相对路径,katana 会相对于进程的工作目录打开,而不是相对于配置文件,并且报错时不会指出是哪个文件:

configuration error: No such file or directory (os error 2)

按上述布局配置的节点如下:

/etc/katana/config-xboard.toml
[log]
level = "info"
[[node]]
panel_type = "NewV2board"
[node.api]
host = "https://panel.example.com"
node_id = 1
key = "replace-with-the-panel-key"
node_type = "V2ray"
rule_list_path = "/etc/katana/rules.txt"
[node.controller]
listen_ip = "0.0.0.0"
update_periodic = 60
[node.controller.cert]
mode = "file"
cert_file = "/etc/katana/certs/fullchain.pem"
key_file = "/etc/katana/certs/privkey.pem"
[node.route]
default = "direct"
geoip = "/etc/katana/geoip.dat"
geosite = "/etc/katana/geosite.dat"
[[node.route.rule]]
outbound = "block"
geoip = ["private"]

创建服务用户,并一次性设置好属主和权限:

终端窗口
sudo useradd --system --no-create-home --shell /usr/sbin/nologin katana
sudo install -d -o root -g katana -m 0750 /etc/katana /etc/katana/certs
sudo chown root:katana /etc/katana/*.toml /etc/katana/certs/*.pem
sudo chmod 0640 /etc/katana/*.toml /etc/katana/certs/*.pem

配置文件中保存着面板密钥,因此不要让其他用户能读取它们。katana 只读取这些文件,所以目录和文件都不需要对 katana 可写。

将下面的内容保存为 /etc/systemd/system/katana@.service。实例名中 @ 之后的部分决定配置文件:katana@xboard 运行 /etc/katana/config-xboard.toml。

/etc/systemd/system/katana@.service
[Unit]
Description=katana node agent (%i)
After=network-online.target
Wants=network-online.target
[Service]
Type=exec
User=katana
Group=katana
WorkingDirectory=/etc/katana
Environment=NO_COLOR=1
# 仅当 [dns] backend 为 "tls" 或 "https" 且未设置 ca_file 时需要:
# Environment=SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
ExecStartPre=/usr/local/bin/katana --test -c /etc/katana/config-%i.toml
ExecStart=/usr/local/bin/katana -c /etc/katana/config-%i.toml
Restart=on-failure
RestartSec=5s
LimitNOFILE=1048576
# 仅当面板为某个节点分配了 1024 以下的端口时需要。
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK
RestrictNamespaces=yes
LockPersonality=yes
SystemCallArchitectures=native
[Install]
WantedBy=multi-user.target

然后启动实例并跟踪它们的日志:

终端窗口
sudo systemctl daemon-reload
sudo systemctl enable --now katana@xboard katana@sspanel
journalctl -u 'katana@*' -f
设置 原因
After= 和 Wants=network-online.target 每个节点在启动后会立即向面板请求自己的设置。首次尝试失败的节点会自行重试:第一次在 1 s 后,之后等待时间逐次翻倍,最长 60 s;如果 update_periodic 更短,则以它为上限。在网络和 DNS 就绪之后再启动,可以避免 journal 中出现一批首次尝试的错误,也能避免最长约一分钟的延迟:等到网络就绪时,节点距下一次尝试的等待时间可能已经增长到这么长。见重启与节点故障。
ExecStartPre=… --test 拒绝启动有以下问题的文件:无法解析、含未知配置项、指定了未知的 panel_type、node_type、出站或 geodata 分类、指向无法读取的 geodata 文件、Hysteria 2 设置无效,或者没有任何 [[node]]。直接启动时,katana 会跳过无法构建的节点并运行其余节点;这项检查让有问题的节点导致整个启动失败,你才能注意到。--test 会在 journal 中打印 Configuration OK。
Restart=on-failure katana 以状态 1 退出,或被 SIGTERM、SIGINT、SIGHUP、SIGPIPE 以外的信号杀死(例如 OOM killer 发出的 SIGKILL)后,重新启动它。systemctl stop 或 SIGHUP 之后不会重启。ExecStartPre= 失败也算作失败:在 RestartSec=5s 下,永远达不到 systemd 默认的启动次数上限(10 秒内 5 次),因此有问题的配置会每 5 秒重试一次,每次记录相同的错误,修好文件后实例会自动启动。
LimitNOFILE=1048576 每个客户端连接占用一个描述符,通常出站还要再占一个。systemd 默认的软限制 1024 很快就会耗尽。见文件描述符。
AmbientCapabilities=CAP_NET_BIND_SERVICE 让非特权的 katana 用户可以绑定 1024 以下的端口。见1024 以下的端口。
Environment=NO_COLOR=1 即使标准输出不是终端,katana 也会输出 ANSI 颜色码。NO_COLOR 可以关闭颜色,journal 中显示的就是纯文本,而不是 [2m 之类的序列。
WorkingDirectory=/etc/katana 为配置中的相对路径提供兜底。使用绝对路径时不需要它。
ProtectSystem=strict 及其他加固选项 katana 只读取文件和打开网络 socket,因此文件系统对它可以是只读的,系统的其余部分也可以对它隐藏。如果你的环境需要某一行所禁止的功能,删掉那一行即可。

TimeoutStopSec= 保持默认的 90 秒。收到 SIGTERM 后,每个节点会关闭监听器和连接,然后发送最后一次流量上报,在 SSPanel 上还会再发送一次审计上报。每次上报最多耗时 api.timeout 秒(未设置时为 5 秒),各节点并行执行。如果 systemd 在此之前就不得不发送 SIGKILL,最后一个轮询周期的流量就会丢失。

每个节点的端口由面板决定,而不是配置文件。唯一的例外是设置了 [node.hysteria].port 的 Hysteria 2 节点,它直接监听该端口,不询问面板。被分配端口 443 的节点需要 CAP_NET_BIND_SERVICE。没有该能力时,节点会记录下面的错误并不断重试,而进程和其他所有节点照常运行:

ERROR katana::manager::node: node 1: initial start failed: Permission denied (os error 13); retrying in 1s

这些重试不会自行成功。进程的 capability 是在启动时获得的,因此授予该权限后,需要重启实例。

授予该权限有三种方式:

方式 做法 说明
单元 capability AmbientCapabilities= 和 CapabilityBoundingSet=,如上面的单元所示 推荐。它属于服务本身,因此升级二进制文件后依然有效。
文件 capability sudo setcap cap_net_bind_service=+ep /usr/local/bin/katana 保存在文件上。下文的升级流程会安装一个新文件,新文件没有该 capability,因此下次重启后所有低端口节点都会失败。避免使用。
全系统设置 sysctl net.ipv4.ip_unprivileged_port_start=443 允许主机上的所有用户绑定这些端口。

如果所有节点都使用 1024 及以上的端口,删除 AmbientCapabilities= 这一行,并把 CapabilityBoundingSet= 留空,这样进程就不持有任何 capability。

Restart=on-failure 监视的是进程,而不是进程内的节点。只有在无法加载配置、无法构建出站池、没有任何 [[node]],或者一个节点都无法创建时,katana 才会以状态 1 退出。无法启动的节点则会自行重试,原因可能是启动时无法连上面板、面板返回端口 0 或没有返回用户列表,或者端口已被占用。每次尝试失败它都会记录一条错误,而 systemd 仍然显示该单元为 active (running):

ERROR katana::manager::node: node 2: node_info failed: …; retrying in 1s
ERROR katana::manager::node: node 3: initial start failed: Address already in use (os error 98); retrying in 4s

第一次失败后 1 s 进行第一次重试。之后每次等待时间翻倍,最长 60 s;如果 update_periodic 更短,则以它为上限。每次尝试都会重新向面板请求并重新构建监听器,因此原因排除后,节点会在下一次尝试时启动:面板有响应、端口空闲、证书可读。保存对该节点 [[node]] 条目的修改,会立即触发下一次尝试。只有进程在启动时才读取的修改,例如单元设置或 capability,才需要重启实例。

每次启动后,都要按冒烟测试中的说明,确认每个节点都记录了 listening on 行。

一个 katana 进程可以服务多个节点:在一个文件中重复写 [[node]] 即可。在以下情况下,可以把节点拆分到多个实例:

  • 希望重启一组节点时不断开其他节点的连接,例如每个面板一个实例;
  • 希望先升级一组节点,其余节点保留旧二进制文件,直到新版本经过验证;
  • 希望日志分开,即 journalctl -u katana@xboard 和 journalctl -u katana@sspanel。

实例之间不会相互协调。请遵守以下规则:

规则 原因
每个面板节点只由一个实例服务。 面板只给节点分配一个端口,因此后启动的实例无法绑定它。该实例会为这个节点记录 initial start failed: Address already in use (os error 98); retrying in …s 并不断重试,因此另一个实例释放该端口后(例如那个实例停止时),它会在下一次尝试时占用该端口。
listen_ip 有重叠的实例之间,端口不能重复。默认值 0.0.0.0 与所有 IPv4 地址都重叠。 面板按节点分配端口,而 katana 看不到其他实例的端口。第二次绑定会以 Address already in use (os error 98) 失败,该节点会不断重试。Hysteria 2 节点监听 UDP,因此可以与 TCP 节点使用同一个端口号。
每个实例使用自己的 WireGuard 密钥,或者把所有 WireGuard 出站放在同一个实例中。 每个进程各自建立隧道。使用同一私钥连接同一对端的两条隧道会互相抢占对端的会话。
内存和 DNS 流量会略有增加。 每个进程都会构建自己的出站池、解析器缓存和用户表。

每个实例都会监视 /etc/katana,因为这是它配置文件所在的目录。保存某个实例的文件,会让所有实例重新读取各自的文件。文件没有变化的实例不会应用任何修改,也不会记录日志。

对于只有某一个实例需要的设置,使用 drop-in,而不是修改模板:

终端窗口
sudo systemctl edit katana@xboard
/etc/systemd/system/katana@xboard.service.d/override.conf
[Service]
Environment=RUST_LOG=info,katana=debug

重启该实例使其生效:sudo systemctl restart katana@xboard。

katana 每个事件向标准输出写一行,journald 以单元名保存。每行包含 UTC 时间戳、级别、模块和消息:

2026-09-24T08:00:01.512304Z INFO katana::manager::node: node 1: listening on 0.0.0.0:443
2026-09-24T08:00:01.733918Z WARN katana::manager::node: node 2: report traffic: …
2026-09-24T09:14:52.004816Z INFO katana::runtime: shutting down
终端窗口
journalctl -u katana@xboard -f # 跟踪单个实例
journalctl -u 'katana@*' --since '10 min ago' # 所有实例
journalctl -u 'katana@*' -b -g 'WARN|ERROR' # 本次开机以来的问题
journalctl -u katana@xboard -g 'listening on' -n 50 # 哪些节点已启动

日志过滤器取以下各项中第一个已设置的:

  1. 环境变量 RUST_LOG,前提是它能被解析;
  2. 配置文件中的 [log].level;
  3. info。

如果只想在某次运行中使用某个级别而不改配置,按运行多个实例中的方法,在 drop-in 中设置 RUST_LOG。如果希望长期生效,修改配置中的 [log].level:katana 会立即应用新的 [log].level,无需重启,也不会断开连接,并且此后它会取代 RUST_LOG 过滤器。常用的值:

值 输出内容
info 启动、监听器、重载和关闭相关的日志行,以及警告和错误。这是默认值,也是生产环境的合适级别。
warn 只有警告和错误。
info,katana=debug katana 自身的模块使用 debug 级别,例如因超出限制而被拒绝的连接。
debug 所有模块都使用 debug 级别,包括内核的逐连接错误。在繁忙的节点上输出量很大。

在 debug 级别下,繁忙的节点可能超出 journald 的速率限制,journald 随后会丢弃日志行,并记录 Suppressed N messages from katana@xboard.service。调试期间,可以在该实例的 drop-in 中设置 LogRateLimitIntervalSec=0 来放宽限制,调试结束后再删掉。过滤器语法的完整说明见生产环境运行。

日志行 级别 含义
node N: listening on IP:PORT INFO 节点正在提供服务。面板没有为该节点列出任何用户时,不会出现这一行:节点有用户之前,katana 不绑定任何端口。
node N: node_info failed: …、node N: panel returned no node info、node N: panel returned port 0、node N: user_list failed: …、node N: panel returned no user list、node N: initial start failed: …,均以 ; retrying in Ns 结尾 ERROR 节点尚未启动,将在 N 秒后重试。等待时间从 1 s 开始翻倍,最长 60 s;如果 update_periodic 更短,则以它为上限。
node N: node_info: …、node N: user_list: … WARN 某次轮询无法连上面板。节点保留上一次的设置和用户。
node N: report traffic: … WARN 一次流量上报失败。katana 保留这些字节数,在下一次上报时一并发送。
node N: rebuild failed: … ERROR 运行中的节点失去了监听器,例如因为证书缺失。它会在每次轮询时重试。
accept error, backing off 100ms: Too many open files (os error 24) WARN 达到了描述符上限。请提高 LimitNOFILE=。
node TAG: N inbound handshake failures in the last 1s (possible handshake scan/DoS or misconfigured clients) WARN 某个节点在一秒内有超过 10 次握手失败。TAG 的形式为 V2ray_0.0.0.0_443。
cannot read rule_list_path PATH: … WARN 本地审计规则列表不存在,或 katana 无法读取。在之后某次读取成功之前,节点不使用本地规则运行。在 SSPanel 上,只有面板下发了规则列表的那次轮询才会读取。
config reload failed, keeping current: …、reload: bad outbounds, keeping current config: …、reload: node …: …; keeping current config ERROR 对配置文件的修改被整体拒绝,例如因为某个节点指定了未知的 node_type。修改的任何部分都不会应用,每个节点都按原样继续运行。
node N: config edit refused, keeping the running one: … ERROR 该节点修改后的条目无法构建,例如因为某条路由规则引用了未知的出站 tag。该节点继续以之前的设置运行。
shutting down INFO katana 收到了 SIGINT 或 SIGTERM。

重启实例会断开其上的所有连接,并且在旧进程发送最后几次上报、新进程从面板获取节点期间,该实例的节点会有几秒钟无法访问。客户端会自行重连。下面的流程确保在停止任何服务之前,新二进制文件能接受所有配置;保留旧二进制文件以便回滚;并且在新版本通过冒烟测试之前,只让一个实例运行新版本。

flowchart TB
  D["下载并校验校验和"] --> T{"所有配置都通过 --test?"}
  T -- 否 --> X["到此为止,旧二进制文件继续运行"]
  T -- 是 --> K["把旧二进制文件保留为 katana.prev"]
  K --> I["通过重命名安装新二进制文件"]
  I --> C["重启一个实例"]
  C --> W{"节点在监听、客户端可用、流量已上报?"}
  W -- 否 --> R["回滚"]
  W -- 是 --> A["重启其他实例"]
  A --> M["观察内存一天"]
  1. 记录基线。 记下每个实例的内存,并针对旧版本执行一遍冒烟测试,以便有可以对比的数据:

    终端窗口
    systemctl show -p MemoryCurrent katana@xboard katana@sspanel
  2. 下载并校验新版本,方法同下载并校验。使用与当前运行版本相同的构建,-gnu 或 -musl。

  3. 把新二进制文件以临时名称放在旧文件旁边,并确认它能在这台主机上运行:

    终端窗口
    sudo install -m 0755 "katana-$VERSION-linux-x86_64-$BUILD" /usr/local/bin/katana.new
    /usr/local/bin/katana.new --version
  4. 以服务用户身份,用新二进制文件测试每个配置。新版本可能拒绝旧版本接受的配置项,而 katana 会拒绝未知配置项:

    终端窗口
    sudo -u katana sh -c '
    for f in /etc/katana/config-*.toml; do
    printf "%s: " "$f"
    /usr/local/bin/katana.new --test -c "$f" || echo FAILED
    done'

    整个循环以 katana 身份运行,因此文件列表来自只有 root 和 katana 组能读取的目录。每个文件都必须打印 Configuration OK。如果有文件失败,就此停下,先修好配置;正在运行的实例不受影响。以 katana 身份运行还能发现服务用户无法读取的文件,例如 geodata 或 Hysteria 2 节点的私钥。--test 不会读取其他节点类型的证书,也不读取规则列表;这些由冒烟测试覆盖。

  5. 保留当前二进制文件,以便回滚:

    终端窗口
    sudo cp -p /usr/local/bin/katana /usr/local/bin/katana.prev
  6. 通过重命名安装新二进制文件,覆盖旧文件:

    终端窗口
    sudo mv -f /usr/local/bin/katana.new /usr/local/bin/katana

    不要用 cp 覆盖 /usr/local/bin/katana。Linux 不允许以写方式打开正在运行的可执行文件,因此复制会以 Text file busy 失败。重命名只替换目录项:正在运行的进程继续使用旧文件,下次启动时使用新文件。

  7. 重启一个实例,观察它启动:

    终端窗口
    sudo systemctl restart katana@xboard
    journalctl -u katana@xboard -f

    预期先看到启动检查输出的 Configuration OK,然后每个有用户的节点各输出一行 node N: listening on …,并且没有 ERROR 行。

  8. 对该实例做冒烟测试,至少持续一个轮询周期,即 update_periodic 秒(默认 60),确保第一次流量上报已经发出。按冒烟测试操作,并与基线对比。

  9. 重启其余实例,可以逐个重启,也可以一起重启:

    终端窗口
    sudo systemctl restart katana@sspanel
  10. 持续观察接下来一天的内存和 journal。预期情况见内存。

要回到上一个版本,先确认它仍然接受这些配置,再把它重命名回原位,然后重启运行新版本的实例:

终端窗口
sudo -u katana sh -c '
for f in /etc/katana/config-*.toml; do
printf "%s: " "$f"
/usr/local/bin/katana.prev --test -c "$f" || echo FAILED
done'
sudo mv -f /usr/local/bin/katana.prev /usr/local/bin/katana
sudo systemctl restart katana@xboard

如果你在升级过程中添加了只有新版本才认识的配置项,--test 这一步就很重要。旧版本会以 unknown field 拒绝该文件,在上面的单元下会拒绝启动;请先删掉该配置项。与任何重启一样,回滚会再次断开该实例的连接。

重命名会用掉 katana.prev。如果想保留它,先复制一份(sudo cp -p /usr/local/bin/katana.prev /usr/local/bin/katana.prev2)。

--test 通过只说明文件有效。它不会联系面板、不绑定端口、不读取规则列表,也不读取除 Hysteria 2 节点以外任何节点的证书。只有真正启动,才能说明节点可以工作。

要只查看本次运行的日志行,而不包括重启前那个进程的日志,可以按本次运行的 systemd invocation ID 过滤:

终端窗口
id=$(systemctl show -p InvocationID --value katana@xboard)
journalctl _SYSTEMD_INVOCATION_ID="$id" -g 'listening on|WARN|ERROR'

按顺序完成以下检查,只需几分钟:

检查项 方法 预期结果
每个节点都已启动 按上面的方法查看本次运行的 journal 每个有用户的节点各有一行 listening on,端口为面板分配的端口,并且没有 ERROR 行
端口已绑定 sudo ss -ltnp | grep katana(TCP)和 sudo ss -lunp | grep katana(Hysteria 2) 每个节点的端口都归 katana 所有
客户端能连接 使用真实客户端和面板中某个测试用户的订阅,通过它打开一个 HTTPS 网站 页面正常加载。对于 TLS 节点,客户端接受证书
路由正常 通过客户端访问某条规则会发往 block 或某个具名出站的目标 被拦截,或从预期的出口出去
UDP 正常 如果节点中继 UDP,通过客户端执行一次 DNS 查询或运行其他 UDP 应用 收到响应
流量已上报 通过客户端下载一个已知大小的文件,然后等待 update_periodic 秒 面板中测试用户的用量增加约等于该大小乘以节点的流量倍率
没有新的警告 journalctl -u katana@xboard --since '15 min ago' -g 'WARN|ERROR' 与基线相比没有新增内容

关于这些检查的几点说明:

  • 流量上报成功时 katana 不记录任何日志,只有失败时才记录。需要到面板确认上报结果。
  • 有些面板在后台队列中处理上报,因此面板上的数字可能比上报稍晚一些。例如 Xboard 会把每次上报交给一个队列任务处理,这需要它的 queue worker 正在运行。
  • 升级前,针对旧版本运行同样的客户端测试。比较两次运行,比单独评判一次运行更快发现差异。
  • 如果某个 TLS 节点没有 listening on 行,检查它的证书路径和权限。对于 Hysteria 2 以外的所有节点类型,构建监听器时 katana 才第一次读取这些文件,失败会表现为 initial start failed: …; retrying in …s,而不会出现在 --test 中。节点每次重试都会重新读取这些文件,因此文件就位且可读后,节点就会启动。

katana 在构建节点的监听器时读取该节点的证书和私钥,因此续期后的证书要等监听器重建后才会生效。续期时配置文件没有任何变化,所以不会自动触发重建。请在 ACME 客户端的 deploy hook 中,在新文件就位且 katana 组可读之后,重启使用该证书的实例:

deploy hook
install -m 0640 -o root -g katana fullchain.pem /etc/katana/certs/fullchain.pem
install -m 0640 -o root -g katana privkey.pem /etc/katana/certs/privkey.pem
systemctl restart katana@xboard katana@sspanel

与任何重启一样,这次重启会断开这些实例的连接。由于升级一节中的警告,不要让证书续期在升级过程中执行。

按节点、以及每个节点上的用户规划内存:

  • 用户。 每个节点都会为自己的用户构建一张表,包含每个用户的密钥材料和流量计数器。内存随用户数乘以服务这些用户的节点数而增长。一个可以使用十个节点的用户会被保存十份。
  • 连接。 每个打开的连接都占用自己的缓冲区;对于流式节点,还持有一个指向其连接时所用用户表的引用。面板的用户列表变化时,节点会为新连接构建一张新表,而旧表会一直留在内存中,直到最后一个在旧表下建立的连接关闭。在用户多、连接持续时间长的节点上,可能同时存在好几代用户表。
  • 实例。 每个实例都持有自己的用户表、解析器缓存和出站。

因此,启动后随着客户端连接和用户列表变化,内存会先上涨,然后才趋于平稳。评判新版本时,要看它在数小时内稳定下来的水平,而不是最初几分钟,并与同一构建的基线对比。

终端窗口
systemctl status katana@xboard | grep Memory
systemctl show -p MemoryCurrent katana@xboard

如果你在 drop-in 中用 MemoryMax= 设置了硬限制,请在观察到的水平之上留出充足余量。内核在达到限制时杀死 katana,实例上的所有连接都会断开,而且由于 katana 把计数器保存在内存中,自上次上报以来统计的流量会丢失。之后 Restart=on-failure 会再次启动它。

每个已接受的 TCP 连接占用一个描述符,每个到达出站的流通常还要再占一个。一个 gRPC 连接可以承载多个 stream,每个 stream 都有自己的出站。一个直连的 UDP 流可能为每个地址族各占一个 socket。Hysteria 2 节点的所有客户端共用一个 UDP socket,但它的 stream 与其他节点一样会打开出站 socket。

katana 不会自行提高描述符上限,因此 systemd 给它的上限就是它能用的上限。用尽时,节点会停止接受连接:它记录 accept error, backing off 100ms: Too many open files (os error 24),并每隔 100 ms 重试一次,直到有描述符被释放。LimitNOFILE=1048576 足以覆盖多个节点都达到连接上限的情况。查看运行中实例的情况:

终端窗口
pid=$(systemctl show -p MainPID --value katana@xboard)
sudo ls /proc/$pid/fd | wc -l
grep 'open files' /proc/$pid/limits

以下限制是内置的,无法配置。它们保护的是节点而不是主机,因此请按实际负载来设定 LimitNOFILE= 和内存,而不是按这些数字。

限制 值 达到时
每节点的活动连接数(流式节点) 65,536 个 socket 新 socket 被立即关闭,并计为一次握手失败。
每节点处于 TLS、WebSocket 或 HTTP/2 握手中的 socket 数 2,048 节点停止接受连接,直到有一个握手完成,或占位满 10 s。
每节点处于协议握手中的 stream 数 512 新 stream 被丢弃,并计为一次握手失败。
完成协议握手的时限(流式节点) 10 s 连接被关闭,并计为一次握手失败。
双向都没有流量的连接(流式节点) 360 s 连接被关闭。通常协议自身 300 s 的空闲超时会先关闭空闲的流。
每节点的 Hysteria 2 QUIC 连接数 4,096 新连接被拒绝。
每节点所有连接合计的 Hysteria 2 中继 stream 和 UDP 关联数 65,536 新 stream 被拒绝;新 UDP 关联的数据包被丢弃。
症状 原因 解决方法
单元为 active (running),但某个节点没有 listening on 行 节点的面板请求失败、面板返回了端口 0 或没有返回用户列表,或者绑定失败。也可能是节点还没有用户。 查看该节点最新的 ERROR 行,它以 ; retrying in Ns 结尾。排除原因后,节点会在下一次重试时启动;只有修改了单元设置或 capability 后才需要重启实例。如果是没有用户,节点会在出现用户后的第一次轮询时绑定端口。
initial start failed: Permission denied (os error 13); retrying in …s 使用 1024 以下的端口却没有 CAP_NET_BIND_SERVICE,常见于升级后 setcap 设置的 capability 丢失。 使用单元中的 AmbientCapabilities=,然后重启实例。
initial start failed: Address already in use (os error 98); retrying in …s 另一个实例或程序占用了该端口。--test 无法检测到这种情况。 保证各实例之间端口不重复。其他占用者释放该端口后,节点会在下一次重试时占用它。
单元在 ExecStartPre 失败,报 configuration error: No such file or directory (os error 2) 配置文件不存在,或者它引用的文件(geodata 或 [dns] ca_file)不存在,通常是因为使用了相对路径。 核对实例名与文件名,并使用绝对路径。
启动检查报 configuration error: Permission denied (os error 13),或者 Hysteria 2 节点的证书或私钥报 configuration error: node N: Permission denied (os error 13) katana 用户无法读取配置或其中引用的文件。 属组设为 katana,权限设为 0640。
version `GLIBC_2.xx' not found 在 glibc 较旧的主机上使用了 -gnu 构建。 改用 -musl 构建。
cp: cannot create regular file '/usr/local/bin/katana': Text file busy 试图复制覆盖正在运行的二进制文件。 按升级步骤中的方法,通过重命名安装。
journal 中出现 [2m 之类的序列 颜色码。 Environment=NO_COLOR=1。
journalctl -p warning 什么也不显示 journald 把 katana 的日志行保存为 info。 用 -g 'WARN|ERROR' 过滤。
服务停止了,systemd 没有重启它 它收到了 SIGHUP,或者是被有意停止的。 删除任何发送 SIGHUP 的 ExecReload=。
accept error, backing off 100ms: Too many open files (os error 24) 描述符上限太低。 提高 LimitNOFILE=。

更多节点级错误以及面板响应的含义,见故障排查。