Skip to main content
MoleSignal 是一个单一二进制(single binary):同一份可执行文件和镜像服务所有角色,进程配置决定运行角色。同一个二进制既能作为一条命令启动的沙箱,也能横向扩展成多角色集群。 本篇面向运维 / SRE,系统说明角色划分、后台 worker 归属、数据流、集群发现、外部依赖与端口,以及单机 / Docker Compose / Kubernetes 三种部署拓扑。

概述:设计哲学

单一二进制

所有角色编译进同一个二进制、打进同一个镜像。进程之间只靠配置([node].roles)区分职责,不需要不同的构建产物。

角色由配置选择

[node].roles 决定本进程对外暴露什么、承载哪些前台职责。默认 ["standalone"],即单进程跑全部职责。

状态尽量外置

元数据落 Postgres,数据落对象存储。除 Intake 的 WAL/缓冲外,大多数角色是无状态的,可水平扩。

发现走 Postgres

没有独立的 gossip 或共识组件。每个节点通过心跳写入 cluster_nodes 表,其他节点直接查表发现存活对等节点。
何时单机、何时分角色:
  • 评估、开发、PoC、小流量生产。
  • 一个进程暴露 HTTP + gRPC,并在进程内运行全部内部职责(写入、查询、压实、告警评估等)。
  • 依赖仍然是外置的 Postgres + 对象存储(或本地文件系统后端)。
角色枚举值在 TOML / 环境变量里使用 snake_casestandalonealert_manager),与内部实现的命名约定一致。下文所有配置示例均使用 snake_case。

节点角色总表

[node].roles 是一个数组(Vec<Role>),合法取值:standalonerouterintakequeriercompactoralert_manager。默认 ["standalone"]
多角色可在一个进程内组合。 形如 [node].roles = ["intake", "querier"] 会启动各角色所需的去重后前台 server(此例为同时承载 intake + 扫描的 gRPC server),按角色 gate 对应后台循环,并把节点的全部角色登记进 cluster_nodes。对等节点可以按任一已登记角色完成发现。standalone 是“全部角色合一进程”的简写。
独立 querier(或任何含 querier / intake 的角色集)会启动承载 Arrow Flight 扫描服务的 gRPC server。协调端经 list_role(querier) 发现各 querier 并向对等节点散播分片。单进程 standalone 仍如常工作;分布式散播需 ≥2 个 querier 对等节点。

后台 worker 由哪个角色承载

角色相关的循环(intake flush、compaction + file_meta_dumper、告警评估/派发)只有在配置归属角色时才 spawnstandalone 视为全部角色)。少数常驻 worker(心跳、sweeper、对象存储探针、MMDB 刷新、search_jobs、scheduled_reports)在每个节点都运行。下表给出归属角色与周期。
ACME 签发 / 续期已实现:签发循环扫 pending 域名、续期循环对进入 30 天窗口的 active 证书续签,二者都走单次签发路径并带每域名冷却。runner 由承担 TLS 终结的 HTTP server 在 [http.tls].enabled = true 时拉起。TLS/ACME 编入所有构建(无 feature 门),纯由 [http.tls].enabled 运行期开关。

如何选择角色

角色通过 [node].roles 配置;可用环境变量覆盖。环境变量统一前缀 MS_section 与 field 之间用 .(点)分隔(去掉 MS_ 前缀后,剩余部分按 . 切分)。例如 MS_NODE.ROLESMS_STORE.META.DSNMS_HTTP.PORT
少数 bootstrap / 密钥类变量是扁平单下划线且不进 Settings,直接从环境读取:MS_CIPHER_KEYMS_AUTH_JWT_SECRET_OVERRIDEMS_LICENSE_FILEMS_SELF_TELEMETRY_CLUSTER_TOKEN,以及仅开发回退使用的 MS_AGENT_<PROVIDER>_*。剩余结构化字段一律走 MS_<SECTION>.<FIELD> 点分形式,例如 MS_NODE.ROLESMS_STORE.META.DSNMS_CLUSTER.ADVERTISE_ADDR

数据流

写入路径

入口经 Router(限流 + 一致性哈希)落到某个 Intake;Intake 先写 WAL(落盘持久),再写内存缓冲;后台 flush 循环按时间窗或大小阈值把缓冲编码成列式文件 + 检索索引上传对象存储,最后把 FileMeta 落 Postgres,并截断已 flush 的 WAL 段。
写入路径

写入路径

关键配置:[wal].dir[wal].segment_size_mb[wal].flush_strategybatch/none/every_write)、[wal].sync_leveldata/all);[intake].buffer_max_mb(默认 256)、flush_interval_secs(默认 30)、flush_parallelism(默认 4);[router.rate_limit].intake_qps(默认每 org 1000,0=不限)。
Router 限流粒度为 (org_id, route_class),超限返回 429 并带 Retry-Afterorg_id 来自请求头 X-Org-Id,缺省为 default

查询路径

查询入口在暴露 HTTP 的节点上。引擎按集群规模逐层包装:本地查询引擎 →(≥2 个 querier 对等节点时)分布式引擎 →(指定了远程集群时)联邦引擎。
查询路径

查询路径

分布式查询的分片对 object_key 取哈希散到对等节点;分片 SQL 仅做扫描(SELECT * FROM <stream>),完整聚合在协调端执行以避免部分/最终聚合不一致。仅当集群中有 ≥2 个 querier 对等节点时才走分布式;否则回退到本地引擎,无网络跳。

异步搜索作业管线

异步搜索作业管线

异步搜索作业管线

配置:[search_jobs].workers(默认 2)、idle_poll_secs(默认 2)、cleanup_interval_secs(默认 3600);自动异步阈值 [querier].auto_async_threshold_rows(默认 5000 万行)。
FOR UPDATE SKIP LOCKED 的领取语义对多 worker / 多节点安全:多个承载 search_jobs 的进程可以共享同一张 search_jobs 表并发领取,不会重复执行同一作业。

联邦 / 多集群查询

通过 ?clusters=local,sf,nyc 指定目标集群。协调端本地扫描后,对每个启用的远程集群发起一次内部扫描 RPC(带 Bearer token),把各集群回传的批次与本地 UNION ALL 后执行完整 SQL。
联邦 / 多集群查询

联邦 / 多集群查询

联邦查询受许可证门控: 只要 clusters 含非 local 目标且当前许可证不含 federated_search 功能,HTTP 层直接返回 403。OpenSource Edition 保持单集群。远程集群定义存于 Postgres remote_clusters 表(advertise_addrtoken_secret_reftls_verifyenabled),enabled=false 的集群在 Fan-out 时跳过。当前远程身份验证仅支持 Bearer Token;tls_verify=false 映射为 http://,而不是“HTTPS 跳过验证”。

集群成员与发现

1

节点注册

心跳任务周期性 upsert (node_id, roles, advertise_addr, last_heartbeat_at_micros) 到 Postgres cluster_nodes 表(主键 node_idON CONFLICT DO UPDATE)。节点的完整角色集以逗号拼接存一行,因此多角色节点只占一行,并可按任一已登记角色被发现。首次心跳立即发出,之后按间隔执行。
2

心跳间隔

[cluster].heartbeat_interval_secs,默认 5s。advertise_addr 默认 127.0.0.1:5082,即对等节点用来互联的 gRPC 地址(host:port)。
3

存活窗口与 stale 清理

存活窗口由 [cluster].peer_timeout_secs 控制,默认 15s:注册表只返回 last_heartbeat_at >= now - peer_timeout 的节点。另有 sweeper 每 60s 删除超过 5 分钟未心跳的 cluster_nodes 行。
4

发现对等节点

没有 gossip / 共识;分布式模式下各角色直接查 cluster_nodes 表,按角色成员筛选存活对等节点(节点的角色集包含所求角色即命中)。standalone 模式跳过整套发现,只返回本地节点。
5

选址算法

Router 选 Intake:对 org_id|stream_name 做一致性哈希后取模,确定性落到某个 Intake。Router 选 Querier:朴素轮询(now_ns % peer_count),不是完整一致性哈希。分布式查询分片:对 object_key 取哈希取模散到 querier 对等节点。
6

分布式扫描 RPC

协调端把扫描请求(含 org/stream/sql/file_metas/time_range)编码成 ticket,经内部扫描 RPC 发到对等节点;对等节点读列式文件、注册内存表、跑分片 SQL、回传结果流。该 RPC 与 gRPC 节点服务、数据接入服务共用同一端口(默认 5082)。
集群内部的本地扫描 RPC 调用不带鉴权;只有联邦 / 远程集群调用使用可选的 Bearer token。
cluster_nodes 表结构:node_id VARCHAR(64) PKrole VARCHAR(128)(逗号拼接的角色集)、advertise_addr VARCHAR(255)started_at_micros BIGINTlast_heartbeat_at_micros BIGINTlist_role 扫活跃行后在代码里按成员匹配,故角色查找不依赖该列建索引。

外部依赖与端口

PostgreSQL

元数据库:FileMeta、streams、rules、incidents、users、orgs、audit、quotas、证书、cluster_nodessearch_jobsremote_clusters 等。[store.meta]backend(默认 sqlite,生产请置 postgres)、dsnmax_connections(默认 16)。迁移在编译期内嵌。

对象存储

列式文件 + 检索索引侧车。[store.object].backendlocal(默认,root=./data/objects)/ s3(含 MinIO、R2、阿里云 OSS,用 endpoint 覆盖)/ azure / gcs。凭据优先级:环境变量 > 凭据文件 > 内联 TOML。

无外部缓存 / 共识

仅进程内 LRU+TTL 缓存、限流器、异步运行时。无 Redis / Memcached,无外部共识组件。

渲染依赖(可选)

定时报告的 PNG/PDF 渲染需要 Headless Chromium、启用的 Renderer 与可访问的 Web base_url。渲染不可用时,请求会返回明确错误,不会生成占位文件。

监听端口

/metrics(GET,Prometheus 文本 0.0.4)始终装配。请在反向代理或网络边界限制访问。该端点会暴露固定基数的缓存、对象存储、WAL、查询、告警与平台可观测性指标。

健康 / 就绪探针语义

1

启动时对象存储探测(阻塞)

startup_ping() 同步对 _health/{uuid}.probe 做 PUT→GET→DELETE(128 字节)。失败则进程不启动。
2

后台周期探针

[store.object].health_probe_interval_secs(默认 30s)做一次同样的往返;连续 3 次失败才置 object_store_degraded=true,成功则计数归零。
3

Intake WAL 重放

Intake 启动时扫描 [wal].dir 下的段文件,按 (org, stream_type, stream) 重放进内存缓冲,全部载入后强制 flush 一次,再置 replay_done=true。重放未完成前 /api/v1/readyz 返回 503。

认证与配置覆盖

  • JWT 密钥在首次启动时由数据库自动 bootstrap;旧的 jwt_secret TOML 字段已废弃(仅为兼容旧配置而保留解析)。需要固定密钥时用环境变量 MS_AUTH_JWT_SECRET_OVERRIDE
  • API token 形如 ms_<prefix>_<secret>,secret 经 argon2id 哈希存储。
  • 启动时 store.meta.dsnwal.dirhttp.portgrpc.portnode.id 等被视为不可变字段,运行中变更会告警。

部署拓扑

所有拓扑共用同一镜像(如 molesignal:dev),靠 MS_NODE.ROLES 区分。Web 前端是独立的 nginx 镜像(如 molesignal-web:dev)。
单进程,全部角色合一,最快上手。
本地后端(store.object.backend=local)适合开发;生产请用对象存储后端。

扩缩与高可用

Router — 可水平扩

无状态,副本数随入口流量扩。限流器是进程内、临时的:多副本下每副本各自计数,实际 org QPS 上限约为 配置值 × 副本数,需要时把限流前置到统一网关或下调单副本阈值。

Querier — 可水平扩

无状态。querier 进程经 gRPC 提供扫描 RPC;≥2 个 querier 对等节点才触发分布式扫描,否则协调端回退到进程内引擎、无网络跳。瓶颈在列式文件读取 + 查询引擎内存。

Intake — 有状态

用 StatefulSet + 每副本 PVC 持久 WAL。Router 按 org|stream 一致性哈希落点,扩缩会改变取模结果导致再平衡;缩容前应确保 WAL 已 flush。就绪探针留足重放窗口(清单设 10s 初始延迟)。

Compactor / AlertManager — 单实例

两者均建议单副本:Compactor 多实例会产生合并冲突(lease 表加锁为未来计划),AlertManager 评估 / 事件状态为进程内。可靠性靠快速重启而非多副本。
监控建议指标:写路径看 wal_append_lock_wait_secondswal_append_inflightwal_fsync_errors_totalfile_meta_dump_*;对象存储看 object_store_operations_totalobject_store_errors_totalobject_store_op_duration_secondsobject_store_probe_*;查询看缓存命中率 cache_*tantivy_pruned_files_total;告警看 alert_rule_eval_timeout_total。配合 /api/v1/healthz/api/v1/readyzcluster_nodes 表观察成员存活。

最小生产清单 / 校验清单

1

外部依赖就绪

Postgres 可达、[store.meta].backend = "postgres"dsn 正确;对象存储后端选 s3/azure/gcs(非 local),凭据按”环境变量 > 凭据文件 > 内联”优先级注入。
2

角色与互联地址

每个进程 MS_NODE.ROLES 明确;MS_CLUSTER.ADVERTISE_ADDR 设为对等节点可达的 host:5082(K8s 用 $(POD_IP):5082)。一个进程可同时承担多个角色(如 ["intake","querier"]),会起去重后的 server 集并以全部角色登记。
3

Intake 持久化

Intake 用 StatefulSet + PVC 挂 WAL 目录;[wal].flush_strategy / sync_level 按持久性要求设置;就绪探针留足重放延迟。
4

单实例角色

Compactor、AlertManager 各保持 1 副本。确认 [compactor].interval_secsretention_days[storage.file_meta_dump].enabled 符合预期。
5

入口与限流

Router 前置 LB;按 org 设 [router.rate_limit].intake_qps / query_qps;注意多副本下限流为近似值。Ingress 对 /api/v1/query/stream 关闭缓冲。
6

可观测与探针

/metrics 接入 Prometheus;K8s 探针指向 /api/v1/healthz(liveness)与 /api/v1/readyz(readiness)。确认对象存储启动探测能通过(否则进程不启动)。
7

安全与 license

需要固定 JWT 密钥时设 MS_AUTH_JWT_SECRET_OVERRIDE,否则由 DB 自动 bootstrap。注入 cipher key(生产勿用 dev 全零值)。联邦查询需 federated_search license 特性。TLS + 证书自动签发/续期编入所有构建(无 feature 门),由 [http.tls].enabled 运行期开关。
落地前请明确当前边界:分布式共识 WAL Term Source 仍是静态值(多节点共识未实现);联邦查询与 OIDC/SAML SSO 需要相应许可证功能;更改 [node].roles 需要重启进程。
最后修改于 2026年8月9日