通过 HTTP 轮询 VM 内部的 Vector 进程(Prometheus 格式 metrics),具体链路:
VM 内部 Vector (host_metrics 源, 每秒采集)
→暴露 /metrics 端点 (默认 9100 端口)
→autoscaler-agent 每 5 秒 HTTP GET 拉取
→用 prometheus expfmt 解析
四个维度的扩缩容指标
| 维度 | 信号 | 目标阈值 |
|---|---|---|
| CPU | host_load1 / host_load5 (Linux load average) | load ≤ 90% 可用 CPU |
| 内存使用 | host_memory_total - host_memory_available | 使用率 ≤ 75% |
| 内存总量 (含 page cache) | 同上 + host_memory_cached_bytes | 总量 ≤ 90% |
| LFC 本地缓存 | lfc_hits/misses + working set size 多窗口斜率估算 | 预分配缓存空间,防误降 |
核心算法是分别计算三个维度的 goal CU(Compute Unit),取最大值作为目标规格。
扩缩容流程
-
扩容:agent →scheduler plugin 检查节点资源预留 →NeonVM CRD patch →通知 vm-monitor
-
缩容:agent →vm-monitor 确认安全 →NeonVM CRD patch →通知 scheduler plugin
配置化程度很高,每个 VM 还可通过 annotation 覆盖参数。
NeonVM 是一个基于 Kubernetes 的轻量级虚拟机管理器(类似 KubeVirt,但更轻)。它的核心思想是把每台 VM 建模成一个 CRD 资源对象(Custom Resource
Definition),叫做 VirtualMachine。
什么是 CRD Patch
在 Kubernetes 里,CRD 扩展了 API 的原生资源类型。NeonVM 定义了自己的 VirtualMachine 类型,比如:
在 Kubernetes 里,CRD 扩展了 API 的原生资源类型。NeonVM 定义了自己的 VirtualMachine 类型,比如:
yaml
apiVersion: vm.neon.tech/v1beta1
kind: VirtualMachine
metadata:
name: my-vm
spec:
guest:
cpus:
use: 4 # 当前分配 4 vCPU
memorySlots:
use: 8 # 当前分配 8 Gi 内存
Patch 就是指 autoscaler-agent 调用 Kubernetes API,对已有的 VM 资源对象做增量修改(JSON Patch),而不是删除重建。比如扩容时发一个 PATCH 请求:
json
[
{
"op": "replace",
"path": "/spec/guest/cpus/use",
"value": 8
},
{
"op": "replace",
"path": "/spec/guest/memorySlots/use",
"value": 16
}
]
为什么这么做
-
不重启 VM ---相比删掉旧 VM 再创建新的,PATCH 让 NeonVM 控制器热调整资源
-
声明式 ---最终一致模型,Kubernetes 保证状态收敛
-
与 k8s 生态集成 ---可以用 kubectl 查看/调试,有事件日志
简单说:CRD Patch = 通过修改 Kubernetes 自定义资源的字段来动态调整 VM 配置。
- CPU 指标由 autoscaler-agent 直接采集
在 runner.go:273-287,autoscaler-agent 启动后台进程 getMetricsLoop 来获取系统指标:
在 runner.go:273-287,autoscaler-agent 启动后台进程 getMetricsLoop 来获取系统指标:
go
r.spawnBackgroundWorker(ctx, logger, "get system metrics", func(ctx2 context.Context, logger2 *zap.Logger) {
getMetricsLoop(
r, ctx2, logger2,
r.global.config.Metrics.System, // ←配置指向 system metrics 端点
metricsMgr[*core.SystemMetrics]{...},
)
})
在 pkg/agent/core/metrics.go:82-116,这些指标是从 vector.dev 暴露的 Prometheus 端点(端口 9100)直接爬取的:
go
func (m *SystemMetrics) fromPrometheus(mfs map[string]*promtypes.MetricFamily) error {
// ...
load1 := getFloat("host_load1") // ←CPU 负载指标
load5 := getFloat("host_load5") // ←CPU 负载指标
memTotal := getFloat("host_memory_total_bytes")
memAvailable := getFloat("host_memory_available_bytes")
memCached := getFloat("host_memory_cached_bytes")
// ...
}
- vm-monitor 不负责 CPU 监控
vm-monitor 运行在每个 VM 内部,它主要处理:
-
内存使用量(从 PostgreSQL 进程读取)
-
downscale 请求(拒绝缩容如果正在忙)
-
upscale 请求(通知 autoscaler-agent 它需要更多资源)
CPU 负载(load average)是宿主机的指标,在 VM 内部看不到整个宿主机的 load。所以 vm-monitor 不采集也不上报 CPU 指标。
- CPU 扩容完全由 autoscaler-agent 决策
在 pkg/agent/core/goalcu.go:84-94,autoscaler-agent 用自采集的 LoadAverage1Min/LoadAverage5Min 计算目标 CPU:
go
blendedLoadAverage := load1Weight*systemMetrics.LoadAverage1Min +
(1-load1Weight)*systemMetrics.LoadAverage5Min
然后在 core/state.go 的 NextActions() 状态机中,通过 calculatePluginAction 向 scheduler plugin 请求增加 CPU,通过 calculateNeonVMAction 更新 VirtualMachine
的 VCPU 规格。
- 完整的扩容流程
CPU 指标采集 →vector (端口9100) →autoscaler-agent 解析 host_load1/host_load5
↓
计算 desired CU
↓
┌────────────┴────────────┐
↓ ↓
scheduler plugin (permit) NeonVM API (调整 VCpu)
↑ ↑
最终执行靠 vm-monitor vm-monitor 配合调整
响应 downscale/upscale 内存,但不参与 CPU 决策
总结:autoscaler-agent 自采 CPU 指标,vm-monitor 不收集和上报 load average。
vector 不登录数据库,它从主机操作系统层面采集指标。
vector 的工作原理
看配置 vm-builder/files/vector.yaml:
看配置 vm-builder/files/vector.yaml:
yaml
sources:
host_metrics:
type: host_metrics # ←内置的宿主机指标采集源
scrape_interval_secs: 1 # 每秒采样一次
sinks:
prom_exporter:
type: prometheus_exporter # ←暴露为 Prometheus 文本格式
inputs:
- host_metrics
address: "0.0.0.0:9100" # ←监听在 9100 端口
type: host_metrics 是 vector 自带的 source,底层读的是 Linux /proc 和 /sys 文件系统,不是连数据库:
Vector 指标来源对照表
| 指标名 | 来源 |
|---|---|
| host_load1, host_load5 | /proc/loadavg |
| host_memory_total_bytes | /proc/meminfo |
| host_memory_available_bytes | /proc/meminfo |
| host_memory_cached_bytes | /proc/meminfo |
| host_cpu_seconds_total | /proc/stat |
| ... | ... |
验证方法
在一个开启了 autoscaling 的 VM 上执行:
bash
# curl vector 的端点
curl http://localhost:9100/metrics | grep 'host_load\|host_memory'
应该能看到类似输出:
text
host_load1 0.45
host_load5 0.38
host_memory_total_bytes 4294967296
host_memory_available_bytes 1073741824
host_memory_cached_bytes 524288000
整体数据流
text
/proc/loadavg ──┐
/proc/meminfo ──┼── vector (host_metrics source) ──→ Prometheus 文本格式 (:9100)
/proc/stat ─────┘
↓
autoscaler-agent
每2秒 curl localhost:9100/metrics
ParseMetrics() 解析
总结: vector 只是一个"搬运工",把宿主机的系统指标从 /proc 转成 Prometheus 格式,autoscaler-agent 去拉取、解析,然后做扩容决策。跟数据库没有任何关系。
两个 sql_exporter 实例: 监控LFC指标
sql_exporter 是一个独立的可执行二进制文件(burningalchemist/sql_exporter (https://github.com/burningalchemist/sql_exporter),v0.17.3),编译进 compute 的
Docker 镜像中,在容器里以 daemon 形式运行。
工作链路是:
*.jsonnet (源码)
→Makefile jsonnet 编译
→生成 *.yml (构建产物,见 .gitignore)
→sql_exporter 二进制读取 yml
→连接本地 PostgreSQL,定期执行其中的 SQL
→暴露 Prometheus 格式的 /metrics 端点
→Prometheus 拉取指标
Makefile 里明确定义了 connection_string:
postgresql://cloud_admin@127.0.0.1:5432/postgres?sslmode=disable
也就是说 sql_exporter 跑在同一台 compute 上,连的是本地的 Neon Postgres。
自动扩缩容用这些指标吗?
分两个 collector:
- sql_exporter.yml / neon_collector.yml ---常规监控
Neon Collector 包含 ~50 个指标(neon_collector.jsonnet 里 imported),包括 checkpoint、replication delay、connections、WAL、buffer cache、pageserver
请求等。这些主要用于 Prometheus 监控告警,不是用来触发扩缩容的。
- sql_exporter_autoscaling.yml / neon_collector_autoscaling.yml ---专门给 HSCC 用的
neon_collector_autoscaling.jsonnet 只引入 5 个指标,全部跟 LFC(Local File Cache) 相关:
HSCC 自动扩缩容专用指标
| 指标 | 用途 |
|---|---|
| lfc_approximate_working_set_size_windows | 过去 1-60 分钟的 working set 大小(核心指标) |
| lfc_cache_size_limit | LFC 缓存上限 |
| lfc_hits / lfc_misses | 缓存命中率 |
| lfc_used / lfc_writes | 缓存使用量 |
这组才是 HSCC(Hyper Automatic Autoscaler)用来判断该扩还是缩的依据。 尤其是 lfc_approximate_working_set_size_windows 看历史 working set 趋势。
补充说明
注意到有个 .17 后缀的文件(如 checkpoints_req.17.sql),对应 PG 17 有单独的查询,因为 PG 16→17内部某些 catalog 结构变了(比如 checkpoint 相关的系统表)。
Neon 的 compute VM 里跑了两个 sql_exporter 进程,各自监听不同端口:
sql_exporter 实例配置
| 实例 | 配置 | 端口 | 用户 |
|---|---|---|---|
| sql-exporter | /etc/sql_exporter.yml | :9399 | nobody |
| sql-exporter-autoscaling | /etc/sql_exporter_autoscaling.yml | :9499 | nobody |
启动命令就是:
bash
/bin/sql_exporter -config.file=/etc/sql_exporter.yml -web.listen-address=:9399
/bin/sql_exporter -config.file=/etc/sql_exporter_autoscaling.yml -web.listen-address=:9499
它们是通过 init system(sysvinit / runit 之类,由 vm-image-spec 管理)拉起并自动 respawn 的。
访问 /metrics 端点
在 compute VM 内部:
bash
# 常规监控指标
curl http://localhost:9399/metrics
自动扩缩容专用指标
curl http://localhost:9499/metrics
或者直接用 ps -ef 能看到:
bash
ps -ef | grep sql_exporter
会看到两个进程,分别带不同的 -config.file 和 -web.listen-address 参数。
外部怎么访问
这些端口只在 VM 内部绑定。外部 Prometheus 采集时,通过 Neon 的 metrics
collector(apps/base/compute-metrics/scrape-compute-sql-exporter.yaml)把流量转发到对应的 VM 端口,或者通过内部的 service mesh / proxy 访问。