跳转到主要内容
MoleSignal 是单个二进制文件。[node].roles 配置决定进程承担哪些职责。默认值 standalone 在一个进程内运行全部角色。拆分角色后,接入、查询和后台任务可以独立扩缩容和独立故障。 router 角色是分角色集群的入口:它提供完整的 HTTP API 与网页界面,把写入转发给 intake 节点,把查询交给 querier 节点。其余角色各自提供一个内部 HTTP 监听,用于健康检查与指标。

角色

一个进程可以承担多个角色,例如 ["intake", "querier"]。

监听端口

不提供完整 API 的节点,其健康、就绪与指标监听只响应三个不需要认证的路径,不响应其他任何路径: 请把这个监听放在内部网络中。

选择角色

在配置文件中或通过环境变量设置角色。环境变量使用 MS_ 前缀,并用单个下划线连接配置路径的每一层:[node].roles 对应 MS_NODE_ROLES,[cluster].advertise_addr 对应 MS_CLUSTER_ADVERTISE_ADDR。带点号的名称不会被识别。
集群中的所有节点必须使用同一个 PostgreSQL 数据库和同一个对象存储,并共用以下设置:MS_CIPHER_KEY、MS_AUTH_JWT_SECRET_OVERRIDE(需要固定签名密钥时)与 MS_SELF_TELEMETRY_CLUSTER_TOKEN。为每个节点设置唯一的 [node].id,以及对端能通过内部 gRPC 端口访问到的 advertise_addr。 两个进程以同一个 [node].id 发布同一个数据集时会互相栅栏:其中一个发布过之后,另一个的刷写会被拒绝。被栅栏的进程会丢弃仍在内存中的行,WAL 保留在磁盘上,并且对该数据集的写入返回 503,直到它换用唯一的 id 重启。

数据流向

接入路径

发往 router 接入端点(/api/v1/intake/* 或 OTLP gRPC)的请求,先在 router 上完成认证与解码。随后 router 经内部 gRPC 端口把批次转发给一个 intake 节点:按组织与数据流,在存活 intake 节点的 ID 上做 rendezvous 哈希选出。各个 router 把同一条数据流的记录发给同一个节点;该节点不可达时,各个 router 也按同样的顺序改投下一个,最多尝试三个节点。节点加入或离开时,只迁移它自己名下的数据流。一条数据流的写入全部进同一个节点,数据量大的来源可以拆成几条数据流来分摊。没有存活的 intake 节点时,router 返回 503。 intake 节点执行流水线、脱敏与 schema 演化,先把每个批次追加到 WAL,再写入内存缓冲,并向 router 返回与 standalone 节点相同的结果。后台刷写把缓冲编码为 Parquet 文件和检索索引,上传到对象存储,把它们登记到 PostgreSQL 中的文件目录,并截断已刷写的 WAL 分段。大小达到 [object_store].multipart_threshold_size 的对象分片上传。 采集器也可以把 OTLP gRPC 直接发给 intake 节点。其他角色自身产生的数据(例如派生数据集)在各自节点上用同样的 WAL 与刷写写入。
接入路径接入路径

接入路径

WAL 目录、分段大小、刷写策略、缓冲大小与刷写间隔由配置中的 [wal] 与 [intake] 控制。

查询路径

SQL 或 PromQL 请求到达 router 后,router 检查授权、准入与结果缓存,再经内部 gRPC 端口把请求交给轮流选出的 querier 节点。querier 用自己的引擎完整执行查询并返回结果。选中的 querier 不可达时,router 会尝试其余 querier;没有存活的 querier 时返回 503 与 no querier node is available。查询指定了远端集群时,router 把它们的结果合并进来。
查询路径查询路径

查询路径

查询读取文件目录列出的文件。在分角色集群中,intake 节点仍缓冲着的记录要等下一次刷写之后才对查询可见,时间在 [intake].flush_interval_secs(默认 30 秒)之内,缓冲达到大小上限时更早。

异步搜索任务

估算规模超过 [querier].auto_async_threshold_rows 的查询会成为持久化的搜索任务。router 把任务存入 PostgreSQL,querier 节点上的 worker 领取任务,同一个任务不会被重复执行。参见配置中的 [search_jobs]。
异步搜索任务流水线异步搜索任务流水线

异步搜索任务流水线

联邦查询

在查询中添加 ?clusters=local,<cluster-name> 即可包含远端集群。接收请求的节点先扫描自己的部分,再携带 Bearer Token 向每个已启用的远端集群请求扫描,合并各批数据后执行完整 SQL。无法访问的远端集群会在联邦元数据中标记为降级,而不会悄悄变成本地数据。远端集群在 设置 中管理。
联邦查询联邦查询

联邦查询

集群成员

没有 gossip 或共识组件。每个节点每隔 [cluster].heartbeat_interval_secs(默认 5 秒)向 PostgreSQL 写入一行心跳,包含节点 id、角色和 advertise_addr。最后一次心跳早于 [cluster].peer_timeout_secs(默认 15 秒)的对端会被忽略,静默超过五分钟的行会被清理。节点通过读取这张表并按角色过滤来发现对端,结果最多缓存 2 秒。standalone 节点在本进程内处理写入与查询,不做转发。 收到 SIGTERM 或 SIGINT 的节点先删除自己的行,再处理完手上的工作,因此对端在 2 秒内就不再使用它,不必等待超时。通过 POST /api/v1/node/drain 排空的节点在下一次心跳时删除自己的行。 内部 gRPC 端口 5082 在节点之间传输写入转发、查询转发与扫描流量,不做认证,请把它放在可信网络中。

Compactor 节点之间的分工

可以同时运行多个 compactor 节点。每个数据流和每个组织在存活的 compactor 节点中只有一个负责节点,由它执行该数据流或组织的工作。standalone 节点也算作 compactor 节点之一。可以重复执行的删除在每个节点上运行。 负责节点由对存活 compactor 节点的 id 做 rendezvous 哈希得出。看到相同成员的节点会算出同一个结果,不需要协调者或锁表。节点加入或离开时,只有该节点名下的数据流与组织会迁移。
  • 节点故障。 停止心跳的节点在 [cluster].peer_timeout_secs 之后退出成员列表。节点发现结果最多缓存 2 秒,因此默认配置下,其余节点在最后一次心跳之后约 17 秒接管它的数据流与组织,并在各项任务的下一轮中处理。
  • 节点退出。 收到 SIGTERM 或 SIGINT 的节点先退出成员列表,再处理完手上的工作。其余节点约 2 秒后接管它的数据流与组织,并在各项任务的下一轮中处理。
  • 新节点。 节点在成员列表中出现满 [cluster].peer_timeout_secs 之后才开始承担工作。其余节点一看到它就让出它名下的部分,因此这部分工作会等待一个超时窗口,而不会被处理两次。
  • 重叠。 各节点读取成员列表的时刻略有先后,成员变化期间两个节点可能短暂处理同一个数据流。每个结果都通过一次原子操作发布到文件目录,落败的节点丢弃自己的产物。重叠只多耗一些 I/O,不会造成数据重复或丢失。
为每个节点设置唯一的 [node].id。id 相同的节点会被算作同一个成员,各自处理该成员名下的全部工作。 每个 compactor 节点各自应用 [compactor].max_concurrent_groups,因此总的合并并发度随节点数增长。 /metrics 中的 work_ownership_members、work_ownership_active 与 work_ownership_owned_units 分别表示节点看到的成员数、节点当前是否承担工作,以及它在最近一轮中负责的数据流或组织数量。work_ownership_owned_units 的 work 标签表示任务类型:compaction 与 partition_manifest 统计数据流,index_rebuild 与 storage_reconciler 统计组织。成员稳定时,所有 compactor 节点的数值之和等于数据流或组织的总数。

Alert 节点之间的分工

可以同时运行多个 alert 节点。节点按条目分工,其中一个节点是主节点,负责只能运行一份的任务。standalone 节点也算作 alert 节点之一。 条目的负责节点由对存活 alert 节点的 id 做 rendezvous 哈希得出,方式与 compactor 节点相同。主节点是一个固定 key 的负责节点。成员变化时沿用上文 compactor 的规则:节点故障或退出后,其余节点按相同的时间接管它的条目;新节点在成员列表中出现满 [cluster].peer_timeout_secs 之后才开始承担工作。主节点故障或退出后由另一个节点接任主节点,原来由主节点运行的任务在新主节点的下一轮中继续。新加入的节点也可能接任主节点,在它完成预热之前这些任务会暂停。
  • 规则。 节点在每一轮([alert].eval_interval_secs)评估自己负责的规则。成员变化期间,两个节点可能短暂地同时负责同一条规则。距上一次评估不足 [alert].eval_interval_secs 一半的规则会被跳过,因此同一个周期只计一次数,for_periods 的去抖不会被提前耗尽。
  • 报告、流水线与连接器。 节点先以租约认领条目再运行,运行结束后释放租约。运行期间崩溃的节点会让租约保留最多 10 分钟,之后由另一个节点重新运行该条目。运行超过 10 分钟的条目可能被另一个节点重复运行。连接器从保存的游标继续拉取,不会丢失数据,但被中断的那个窗口里的事件可能被写入两次。
请保持各节点的时钟同步,因为节点之间会比较各自写入的时间戳。为每个节点设置唯一的 [node].id。 /metrics 中,work_ownership_owned_units 的 work 标签取 rule_evaluation 与 escalation_dispatch 时,分别表示节点在最近一轮中负责的规则数与事件数;取 primary 时,主节点为 1,其余节点为 0,该值每隔 [cluster].heartbeat_interval_secs 更新一次。work_overlap_skipped_total 统计节点因为另一个节点已在同一周期运行过而跳过的条目数,成员稳定期间该计数保持不变。

速率限制

router 角色用 [router.rate_limit] 按组织和路由类别限流:接入类别覆盖 /api/v1/intake/*,查询类别覆盖 /api/v1/query 及其下的路径。限流按已认证组织的请求计数,超限时返回 429 并附带 Retry-After 响应头。每个 router 副本各自计数,因此实际上限随副本数增长。standalone 节点不应用这个限制。

扩缩容与高可用

  • Router。 在负载均衡器之后增加副本。
  • Querier。 增加副本。每个查询在一个 querier 上运行,因此 querier 越多,能同时运行的查询越多。
  • Intake。 每个节点使用自己的持久化 WAL 卷(Kubernetes 中使用 StatefulSet)。节点数变化时,加入或离开的节点名下的数据流会迁移,因此缩容前要确认 WAL 已刷写,并在把流量路由给重启的节点之前留出 WAL 重放时间。
  • Compactor。 增加副本。节点之间按数据流与组织分工,某个节点故障后由其余节点接管,可以滚动更新,参见 Compactor 节点之间的分工。
  • Alert。 增加副本。节点之间按规则、报告、流水线与连接器分工,由一个主节点运行只能运行一份的任务,某个节点故障后由其余节点接管,可以滚动更新,参见 Alert 节点之间的分工。
  • 多节点共识。 intake 节点之间不做 WAL 复制。丢失某个 intake 节点的卷,会丢失尚未刷写的记录。修改 [node].roles 需要重启。
安装与端口参见部署 MoleSignal,上线前参见生产检查清单。
最后修改于 2026年10月26日