feat(instance): метрики всей машины вторым рядом дашборда

Дашборд показывал только контейнер (лимиты профиля), поэтому не было видно,
сколько ресурсов у хоста всего.

- `sysinfo.NewHostSampler` и `ReadHostMemory` всегда читают `/proc/stat` и
  `/proc/meminfo`, игнорируя cgroup;
- в ответе метрик появились `host_cpu` и `host_memory` (`source: host`);
- недоступные источники попадают в проверку `metrics`, а не ломают ручку.
This commit is contained in:
2026-09-20 22:10:09 +03:00
parent 3cfab89965
commit 5668d779df
5 changed files with 118 additions and 6 deletions
+21
View File
@@ -67,6 +67,8 @@ type metricsCheck struct {
type metricsPayload struct {
CPU metricsCPU `json:"cpu"`
Memory metricsMemory `json:"memory"`
HostCPU metricsCPU `json:"host_cpu"`
HostMemory metricsMemory `json:"host_memory"`
Disk metricsDisk `json:"disk"`
Database metricsDatabase `json:"database"`
Checks []metricsCheck `json:"checks"`
@@ -145,6 +147,25 @@ func (s *Server) collectMetrics(ctx context.Context) metricsPayload {
}
}
// Второй ряд дашборда — вся машина: видно, сколько ресурсов у хоста всего.
if s.hostCPUSampler == nil {
problems = append(problems, "host cpu: sampler is not configured")
} else if percent, cores, source, err := s.hostCPUSampler.CPUPercent(); err != nil {
problems = append(problems, "host cpu: "+err.Error())
} else {
payload.HostCPU = metricsCPU{Percent: round1(percent), Cores: cores, Source: source}
}
if memory, err := sysinfo.ReadHostMemory(s.sysinfoPaths); err != nil {
problems = append(problems, "host memory: "+err.Error())
} else {
payload.HostMemory = metricsMemory{
UsedBytes: memory.UsedBytes,
TotalBytes: memory.TotalBytes,
Percent: round1(memory.Percent()),
Source: memory.Source,
}
}
if disk, err := sysinfo.ReadDisk(s.cfg.DataDir); err != nil {
problems = append(problems, "disk: "+err.Error())
} else {