概述:设计哲学
单一二进制
所有角色编译进同一个二进制、打进同一个镜像。进程之间只靠配置(
[node].roles)区分职责,不需要不同的构建产物。角色由配置选择
[node].roles 决定本进程对外暴露什么、承载哪些前台职责。默认 ["standalone"],即单进程跑全部职责。状态尽量外置
元数据落 Postgres,数据落对象存储。除 Intake 的 WAL/缓冲外,大多数角色是无状态的,可水平扩。
发现走 Postgres
没有独立的 gossip 或共识组件。每个节点通过心跳写入
cluster_nodes 表,其他节点直接查表发现存活对等节点。- 单机 standalone
- 分角色集群
- 评估、开发、PoC、小流量生产。
- 一个进程暴露 HTTP + gRPC,并在进程内运行全部内部职责(写入、查询、压实、告警评估等)。
- 依赖仍然是外置的 Postgres + 对象存储(或本地文件系统后端)。
角色枚举值在 TOML / 环境变量里使用 snake_case(
standalone、alert_manager),与内部实现的命名约定一致。下文所有配置示例均使用 snake_case。节点角色总表
[node].roles 是一个数组(Vec<Role>),合法取值:standalone、router、intake、querier、compactor、alert_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、告警评估/派发)只有在配置归属角色时才 spawn(standalone 视为全部角色)。少数常驻 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.ROLES、MS_STORE.META.DSN、MS_HTTP.PORT。
- TOML
- 环境变量覆盖
- 一组分角色进程
少数 bootstrap / 密钥类变量是扁平单下划线且不进
Settings,直接从环境读取:MS_CIPHER_KEY、MS_AUTH_JWT_SECRET_OVERRIDE、MS_LICENSE_FILE、MS_SELF_TELEMETRY_CLUSTER_TOKEN,以及仅开发回退使用的 MS_AGENT_<PROVIDER>_*。剩余结构化字段一律走 MS_<SECTION>.<FIELD> 点分形式,例如 MS_NODE.ROLES、MS_STORE.META.DSN 与 MS_CLUSTER.ADVERTISE_ADDR。数据流
写入路径
入口经 Router(限流 + 一致性哈希)落到某个 Intake;Intake 先写 WAL(落盘持久),再写内存缓冲;后台 flush 循环按时间窗或大小阈值把缓冲编码成列式文件 + 检索索引上传对象存储,最后把 FileMeta 落 Postgres,并截断已 flush 的 WAL 段。写入路径
[wal].dir、[wal].segment_size_mb、[wal].flush_strategy(batch/none/every_write)、[wal].sync_level(data/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-After。org_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。
联邦 / 多集群查询
集群成员与发现
1
节点注册
心跳任务周期性 upsert
(node_id, roles, advertise_addr, last_heartbeat_at_micros) 到 Postgres cluster_nodes 表(主键 node_id,ON 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) PK、role VARCHAR(128)(逗号拼接的角色集)、advertise_addr VARCHAR(255)、started_at_micros BIGINT、last_heartbeat_at_micros BIGINT。list_role 扫活跃行后在代码里按成员匹配,故角色查找不依赖该列建索引。
外部依赖与端口
PostgreSQL
元数据库:FileMeta、streams、rules、incidents、users、orgs、audit、quotas、证书、
cluster_nodes、search_jobs、remote_clusters 等。[store.meta]:backend(默认 sqlite,生产请置 postgres)、dsn、max_connections(默认 16)。迁移在编译期内嵌。对象存储
列式文件 + 检索索引侧车。
[store.object].backend:local(默认,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_secretTOML 字段已废弃(仅为兼容旧配置而保留解析)。需要固定密钥时用环境变量MS_AUTH_JWT_SECRET_OVERRIDE。 - API token 形如
ms_<prefix>_<secret>,secret 经 argon2id 哈希存储。 - 启动时
store.meta.dsn、wal.dir、http.port、grpc.port、node.id等被视为不可变字段,运行中变更会告警。
部署拓扑
所有拓扑共用同一镜像(如molesignal:dev),靠 MS_NODE.ROLES 区分。Web 前端是独立的 nginx 镜像(如 molesignal-web:dev)。
- 单机沙箱
- Docker Compose
- Kubernetes
单进程,全部角色合一,最快上手。
本地后端(
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_seconds、wal_append_inflight、wal_fsync_errors_total、file_meta_dump_*;对象存储看 object_store_operations_total、object_store_errors_total、object_store_op_duration_seconds、object_store_probe_*;查询看缓存命中率 cache_*、tantivy_pruned_files_total;告警看 alert_rule_eval_timeout_total。配合 /api/v1/healthz、/api/v1/readyz 与 cluster_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_secs、retention_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 运行期开关。