feat: добавлена информация о VM в коллектор GPU

- Добавлен сбор информации о виртуальных машинах и контейнерах Proxmox
- Генерация уникального vm_id на основе cluster_uuid + vmid (16 символов SHA256)
- Убрана информация о VM из коллектора proxcluster
- Обновлена документация по коллектору GPU
- Исправлен возврат пустого массива вместо null для vms

Автор: Сергей Антропов, сайт: https://devops.org.ru
This commit is contained in:
Sergey Antropoff 2025-09-11 16:14:42 +03:00
parent 5bfb6fea8b
commit 621d3f0a43
21 changed files with 2662 additions and 47 deletions

View File

@ -43,11 +43,23 @@ collectors:
ARCH=$$(/usr/bin/uname -m | sed 's/x86_64/amd64/; s/arm64/arm64/'); \ ARCH=$$(/usr/bin/uname -m | sed 's/x86_64/amd64/; s/arm64/arm64/'); \
docker run --rm -v $$PWD:/workspace -w /workspace \ docker run --rm -v $$PWD:/workspace -w /workspace \
-e GOOS=darwin -e GOARCH=$$ARCH -e GOCACHE=/workspace/.cache/go-build -e GOMODCACHE=/workspace/.cache/go-mod golang:1.22 \ -e GOOS=darwin -e GOARCH=$$ARCH -e GOCACHE=/workspace/.cache/go-build -e GOMODCACHE=/workspace/.cache/go-mod golang:1.22 \
sh -c "go mod tidy >/dev/null 2>&1 && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/uptime ./src/collectors/uptime && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/macos ./src/collectors/macos && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/docker ./src/collectors/docker"; \ sh -c "go mod tidy >/dev/null 2>&1 && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/uptime ./src/collectors/uptime && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/macos ./src/collectors/macos && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/docker ./src/collectors/docker"; \
else \ else \
docker run --rm -v $$PWD:/workspace -w /workspace \ docker run --rm -v $$PWD:/workspace -w /workspace \
-e GOOS=linux -e GOARCH=amd64 -e GOCACHE=/workspace/.cache/go-build -e GOMODCACHE=/workspace/.cache/go-mod golang:1.22 \ -e GOOS=linux -e GOARCH=amd64 -e GOCACHE=/workspace/.cache/go-build -e GOMODCACHE=/workspace/.cache/go-mod golang:1.22 \
sh -c "go mod tidy >/dev/null 2>&1 && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/uptime ./src/collectors/uptime && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/macos ./src/collectors/macos && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/system ./src/collectors/system && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/hba ./src/collectors/hba && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/sensors ./src/collectors/sensors && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/docker ./src/collectors/docker && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/gpu ./src/collectors/gpu && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/kubernetes ./src/collectors/kubernetes"; \ sh -c "go mod tidy >/dev/null 2>&1 && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/uptime ./src/collectors/uptime && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/macos ./src/collectors/macos && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/system ./src/collectors/system && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/hba ./src/collectors/hba && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/sensors ./src/collectors/sensors && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/docker ./src/collectors/docker && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/gpu ./src/collectors/gpu && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/kubernetes ./src/collectors/kubernetes && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/proxcluster ./src/collectors/proxcluster"; \
fi fi
@# Убедимся, что скрипты исполняемые @# Убедимся, что скрипты исполняемые
@chmod +x ./bin/agent/collectors/*.sh 2>/dev/null || true @chmod +x ./bin/agent/collectors/*.sh 2>/dev/null || true
@ -56,19 +68,32 @@ collectors-darwin:
# Кросс-сборка коллекторов для macOS # Кросс-сборка коллекторов для macOS
@mkdir -p ./bin/agent/collectors .cache/go-build .cache/go-mod; \ @mkdir -p ./bin/agent/collectors .cache/go-build .cache/go-mod; \
docker run --rm -v $$PWD:/workspace -w /workspace -e GOOS=darwin -e GOARCH=arm64 -e GOCACHE=/workspace/.cache/go-build -e GOMODCACHE=/workspace/.cache/go-mod golang:1.22 \ docker run --rm -v $$PWD:/workspace -w /workspace -e GOOS=darwin -e GOARCH=arm64 -e GOCACHE=/workspace/.cache/go-build -e GOMODCACHE=/workspace/.cache/go-mod golang:1.22 \
sh -c "go mod tidy >/dev/null 2>&1 && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/uptime-darwin ./src/collectors/uptime && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/macos-darwin ./src/collectors/macos" sh -c "go mod tidy >/dev/null 2>&1 && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/uptime-darwin ./src/collectors/uptime && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/macos-darwin ./src/collectors/macos"
collectors-linux: collectors-linux:
# Кросс-сборка коллекторов для Linux # Кросс-сборка коллекторов для Linux
@mkdir -p ./bin/agent/collectors .cache/go-build .cache/go-mod; \ @mkdir -p ./bin/agent/collectors .cache/go-build .cache/go-mod; \
docker run --rm -v $$PWD:/workspace -w /workspace -e GOOS=linux -e GOARCH=amd64 -e GOCACHE=/workspace/.cache/go-build -e GOMODCACHE=/workspace/.cache/go-mod golang:1.22 \ docker run --rm -v $$PWD:/workspace -w /workspace -e GOOS=linux -e GOARCH=amd64 -e GOCACHE=/workspace/.cache/go-build -e GOMODCACHE=/workspace/.cache/go-mod golang:1.22 \
sh -c "go mod tidy >/dev/null 2>&1 && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/uptime ./src/collectors/uptime && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/macos ./src/collectors/macos && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/system ./src/collectors/system && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/hba ./src/collectors/hba && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/sensors ./src/collectors/sensors && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/docker ./src/collectors/docker && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/gpu ./src/collectors/gpu && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/kubernetes ./src/collectors/kubernetes" sh -c "go mod tidy >/dev/null 2>&1 && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/uptime ./src/collectors/uptime && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/macos ./src/collectors/macos && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/system ./src/collectors/system && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/hba ./src/collectors/hba && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/sensors ./src/collectors/sensors && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/docker ./src/collectors/docker && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/gpu ./src/collectors/gpu && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/kubernetes ./src/collectors/kubernetes && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/proxcluster ./src/collectors/proxcluster"
collectors-windows: collectors-windows:
# Кросс-сборка коллекторов для Windows # Кросс-сборка коллекторов для Windows
@mkdir -p ./bin/agent/collectors .cache/go-build .cache/go-mod; \ @mkdir -p ./bin/agent/collectors .cache/go-build .cache/go-mod; \
docker run --rm -v $$PWD:/workspace -w /workspace -e GOOS=windows -e GOARCH=amd64 -e GOCACHE=/workspace/.cache/go-build -e GOMODCACHE=/workspace/.cache/go-mod golang:1.22 \ docker run --rm -v $$PWD:/workspace -w /workspace -e GOOS=windows -e GOARCH=amd64 -e GOCACHE=/workspace/.cache/go-build -e GOMODCACHE=/workspace/.cache/go-mod golang:1.22 \
sh -c "go mod tidy >/dev/null 2>&1 && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/uptime-windows.exe ./src/collectors/uptime && CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/macos-windows.exe ./src/collectors/macos" sh -c "go mod tidy >/dev/null 2>&1 && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/uptime-windows.exe ./src/collectors/uptime && \
CGO_ENABLED=0 go build -trimpath -o ./bin/agent/collectors/macos-windows.exe ./src/collectors/macos"

View File

@ -11,6 +11,7 @@ SensusAgent — модульный агент сбора метрик. Аген
- Сборка и запуск (Make/Docker/Compose): `docs/build_and_run.md` - Сборка и запуск (Make/Docker/Compose): `docs/build_and_run.md`
- Деплой (Ansible, systemd): `docs/deploy.md` - Деплой (Ansible, systemd): `docs/deploy.md`
- **Kafka SSL поддержка**: `docs/kafka_ssl.md` - **Kafka SSL поддержка**: `docs/kafka_ssl.md`
- **Proxmox кластер**: `docs/collectors/proxcluster.md`
Быстрый старт: Быстрый старт:
```bash ```bash

View File

@ -6,7 +6,7 @@ log_level: info
kafka: kafka:
enabled: false enabled: false
brokers: ["10.99.0.90:9092"] brokers: ["10.29.91.4:9092"]
topic: "sensus.metrics" topic: "sensus.metrics"
client_id: "sensusagent" client_id: "sensusagent"
enable_tls: false enable_tls: false
@ -71,7 +71,7 @@ collectors:
exec: "./collectors/sensors" exec: "./collectors/sensors"
platforms: [linux] platforms: [linux]
docker: docker:
enabled: true enabled: false
type: exec type: exec
key: docker key: docker
interval: "3600s" interval: "3600s"
@ -87,13 +87,21 @@ collectors:
exec: "./collectors/gpu" exec: "./collectors/gpu"
platforms: [linux] platforms: [linux]
kubernetes: kubernetes:
enabled: true enabled: false
type: exec type: exec
key: kubernetes key: kubernetes
interval: "3600s" interval: "3600s"
timeout: "60s" timeout: "60s"
exec: "./collectors/kubernetes" exec: "./collectors/kubernetes"
platforms: [linux] platforms: [linux]
proxcluster:
enabled: true
type: exec
key: proxcluster
interval: "1800s"
timeout: "30s"
exec: "./collectors/proxcluster"
platforms: [linux]

View File

@ -46,3 +46,22 @@ make collectors-darwin # darwin/arm64
- Ограничивайте потребление ресурсов и время выполнения - Ограничивайте потребление ресурсов и время выполнения
- На стороне агента используйте `interval` ≥ 10s для тяжелых коллекторов - На стороне агента используйте `interval` ≥ 10s для тяжелых коллекторов
## Доступные коллекторы
### Встроенные коллекторы
- **system** - системные метрики (CPU, RAM, сеть, диски, обновления)
- **uptime** - время работы системы
- **macos** - специфичные для macOS метрики
- **hba** - информация о HBA адаптерах
- **sensors** - данные датчиков температуры/напряжения
- **docker** - информация о Docker контейнерах
- **gpu** - информация о GPU устройствах
- **kubernetes** - метрики Kubernetes кластера
- **proxcluster** - информация о Proxmox кластере ⭐
### Документация коллекторов
- [proxcluster](collectors/proxcluster.md) - сбор информации о Proxmox кластере
- [gpu](collectors/gpu.md) - сбор информации о GPU устройствах с агрегированной статистикой

256
docs/collectors/gpu.md Normal file
View File

@ -0,0 +1,256 @@
# GPU коллектор
**Автор:** Сергей Антропов
**Сайт:** https://devops.org.ru
## Описание
Коллектор GPU собирает информацию о графических процессорах (GPU) в системе и виртуальных машинах Proxmox. Поддерживает NVIDIA GPU через `nvidia-smi` и AMD GPU через `rocm-smi`. Возвращает детальную информацию о каждом GPU, агрегированную статистику по всем устройствам, а также информацию о виртуальных машинах и контейнерах в Proxmox кластере.
## Собираемые данные
### Детальная информация о GPU
Для каждого GPU собирается:
- `id` - индекс GPU
- `name` - модель GPU
- `driver_version` - версия драйвера
- `mem_total_mb` - общий объем видеопамяти в МБ
- `mem_used_mb` - используемая видеопамять в МБ
- `gpu_util_pct` - утилизация GPU в процентах
- `mem_util_pct` - утилизация видеопамяти в процентах
- `temperature_c` - температура в градусах Цельсия
- `power_watt` - потребляемая мощность в ваттах
### Агрегированная статистика
В секции `summary`:
- `total_count` - общее количество GPU
- `models` - количество GPU по моделям (map[string]int)
- `total_mem_mb` - суммарный объем видеопамяти всех GPU
- `total_used_mem_mb` - суммарная используемая видеопамять
- `total_watt` - суммарное потребление всех GPU
- `avg_utilization_pct` - средняя утилизация всех GPU
- `temperature_c` - статистика по температуре:
- `min` - минимальная температура среди всех GPU
- `max` - максимальная температура среди всех GPU
- `avg` - средняя температура всех GPU
### Информация о виртуальных машинах
В секции `vms` (только для Proxmox):
- `vmid` - ID виртуальной машины/контейнера
- `vm_id` - уникальный ID (16 символов SHA256 от cluster_uuid + vmid)
- `name` - имя виртуальной машины/контейнера
- `status` - статус (running, stopped, suspended)
- `node` - нода, на которой запущена VM
- `cpu` - текущее использование CPU
- `maxcpu` - максимальное количество CPU
- `mem` - текущее использование памяти в МБ
- `maxmem` - максимальная память в МБ
- `disk` - текущее использование диска в ГБ
- `maxdisk` - максимальный размер диска в ГБ
- `uptime` - время работы в секундах
- `template` - является ли шаблоном
- `pid` - PID процесса
- `netin` - входящий сетевой трафик в байтах
- `netout` - исходящий сетевой трафик в байтах
- `diskread` - прочитано с диска в байтах
- `diskwrite` - записано на диск в байтах
- `guest_agent` - статус guest agent (только для VM)
- `type` - тип: "qemu" для виртуальных машин, "lxc" для контейнеров
## Зависимости
### NVIDIA GPU
- `nvidia-smi` - утилита для мониторинга NVIDIA GPU
### AMD GPU
- `rocm-smi` - утилита для мониторинга AMD GPU
### Виртуальные машины Proxmox
- `pvesh` - утилита командной строки Proxmox VE
- Доступ к `/etc/corosync/corosync.conf` или `/etc/pve/corosync.conf` для получения cluster_uuid
## Примеры вывода
### NVIDIA GPU
```json
{
"collector_name": "gpu",
"gpu": [
{
"id": 0,
"name": "NVIDIA GeForce RTX 3080",
"driver_version": "525.60.13",
"mem_total_mb": 10240,
"mem_used_mb": 5120,
"gpu_util_pct": 75,
"mem_util_pct": 50,
"temperature_c": 65,
"power_watt": 250.5
},
{
"id": 1,
"name": "NVIDIA GeForce RTX 4090",
"driver_version": "525.60.13",
"mem_total_mb": 24576,
"mem_used_mb": 12288,
"gpu_util_pct": 85,
"mem_util_pct": 50,
"temperature_c": 70,
"power_watt": 450.0
}
],
"summary": {
"total_count": 2,
"models": {
"NVIDIA GeForce RTX 3080": 1,
"NVIDIA GeForce RTX 4090": 1
},
"total_mem_mb": 34816,
"total_used_mem_mb": 17408,
"total_watt": 700.5,
"avg_utilization_pct": 80.0,
"temperature_c": {
"min": 65,
"max": 70,
"avg": 67.5
}
}
}
```
### AMD GPU
```json
{
"collector_name": "gpu",
"gpu": [
{
"id": 0,
"name": "AMD Radeon RX 6800 XT",
"driver_version": "22.40.0",
"mem_total_mb": 16384,
"mem_used_mb": 8192,
"gpu_util_pct": 60,
"mem_util_pct": 50,
"temperature_c": 55,
"power_watt": 200.0
}
],
"summary": {
"total_count": 1,
"models": {
"AMD Radeon RX 6800 XT": 1
},
"total_mem_mb": 16384,
"total_used_mem_mb": 8192,
"total_watt": 200.0,
"avg_utilization_pct": 60.0,
"temperature_c": {
"min": 55,
"max": 55,
"avg": 55
}
}
}
```
### Отсутствие GPU
```json
{
"collector_name": "gpu",
"gpu": null
}
```
## Примеры использования
### Получить общую информацию о GPU
```bash
./collectors/gpu | jq '.summary'
```
### Получить список всех моделей GPU
```bash
./collectors/gpu | jq '.summary.models'
```
### Получить суммарное потребление GPU
```bash
./collectors/gpu | jq '.summary.total_watt'
```
### Получить среднюю утилизацию
```bash
./collectors/gpu | jq '.summary.avg_utilization_pct'
```
### Получить статистику по температуре
```bash
./collectors/gpu | jq '.summary.temperature_c'
```
### Получить максимальную температуру
```bash
./collectors/gpu | jq '.summary.temperature_c.max'
```
### Получить минимальную температуру
```bash
./collectors/gpu | jq '.summary.temperature_c.min'
```
### Получить среднюю температуру
```bash
./collectors/gpu | jq '.summary.temperature_c.avg'
```
### Получить информацию о конкретном GPU
```bash
./collectors/gpu | jq '.gpu[0]'
```
### Получить GPU с высокой утилизацией (>80%)
```bash
./collectors/gpu | jq '.gpu[] | select(.gpu_util_pct > 80)'
```
### Получить GPU с высокой температурой (>70°C)
```bash
./collectors/gpu | jq '.gpu[] | select(.temperature_c > 70)'
```
## Конфигурация
Пример конфигурации в `config.yaml`:
```yaml
collectors:
gpu:
enabled: true
type: exec
key: gpu
interval: "30s"
timeout: "10s"
exec: "./collectors/gpu"
platforms: [linux]
```
## Особенности
- **Приоритет NVIDIA**: Если доступен `nvidia-smi`, используется он, иначе `rocm-smi`
- **Fallback**: Если ни один из драйверов недоступен, возвращается пустой результат
- **Агрегация**: Статистика вычисляется только при наличии GPU
- **Совместимость**: Поддерживает различные версии `nvidia-smi` и `rocm-smi`
- **Производительность**: Минимальное время выполнения за счет оптимизированных запросов
## Мониторинг
Рекомендуемые метрики для мониторинга:
- `summary.total_watt` - общее потребление GPU
- `summary.avg_utilization_pct` - средняя утилизация
- `summary.temperature_c.max` - максимальная температура GPU
- `summary.temperature_c.avg` - средняя температура GPU
- `gpu[].temperature_c` - температура каждого GPU
- `gpu[].mem_util_pct` - утилизация видеопамяти
- `summary.total_count` - количество GPU в системе

View File

@ -0,0 +1,445 @@
# Коллектор proxcluster
## Автор: Сергей Антропов, сайт: https://devops.org.ru
## Описание
Коллектор `proxcluster` собирает подробную информацию о Proxmox кластере, включая основную информацию о кластере, кворуме, хранилищах, нодах и данных corosync.
## Поддерживаемые платформы
- **Linux**: Полная поддержка
- **macOS/Windows**: Не поддерживается (возвращает пустой JSON)
## Собираемые данные
### Основная информация о кластере
```json
{
"cluster_id": "a1b2c3d4e5f67890", // Уникальный ID на основе cluster_name + cluster_uuid (SHA256, 16 символов)
"name": "production-cluster", // Имя кластера из corosync.conf
"cluster_uuid": "12345678-1234-1234-1234-123456789abc", // UUID кластера
"version": "8.1.4" // Версия Proxmox VE
}
```
### Информация о кворуме
```json
{
"quorum": {
"quorate": true, // Есть ли кворум
"members": 3, // Количество участников
"total_votes": 3, // Общее количество голосов
"expected_votes": 3 // Ожидаемое количество голосов
}
}
```
### Хранилища кластера
```json
{
"storages": [
{
"storage_id": "local-lvm", // Идентификатор хранилища
"type": "lvmthin", // Тип хранилища
"content": ["images", "rootdir"], // Типы контента
"total_gb": 500, // Общий размер в ГБ
"used_gb": 320, // Использовано в ГБ
"avail_gb": 180, // Доступно в ГБ
"shared": false // Является ли хранилище общим
},
{
"storage_id": "nfs-storage",
"type": "nfs",
"content": ["images", "backup"],
"total_gb": 2000,
"used_gb": 1500,
"avail_gb": 500,
"shared": true
}
]
}
```
### Подробная информация о нодах
```json
{
"nodes": [
{
"node_uid": "d8b7a93c2f0e4f16", // Уникальный ID ноды (SHA256 от cluster_uuid + node_id, 16 символов)
"node_id": "1", // ID ноды в кластере
"name": "pve1", // Имя ноды
"ip": "192.168.1.10", // IP адрес ноды
"online": true, // Статус ноды
"cluster_id": "a1b2c3d4e5f67890", // ID кластера (SHA256 от cluster_name + cluster_uuid, 16 символов)
"cluster_uuid": "12345678-1234-1234-1234-123456789abc", // UUID кластера
"machine_id": "4b1d2c3a4e5f6789aabbccddeeff0011", // Machine ID системы
"product_uuid": "C9E4DDB2-1F27-4F3B-B3A9-ACD2C66F73B0", // UUID продукта
"os": { // Информация об ОС
"kernel": "Linux 5.15.108-1-pve",
"pve_version": "7.4-3",
"uptime_sec": 523423
},
"hardware": { // Информация о железе
"cpu_model": "Intel(R) Xeon(R) Gold 6226R",
"cpu_cores": 32,
"sockets": 2,
"threads": 64,
"memory_total_mb": 262144
},
"resources": { // Использование ресурсов
"cpu_usage_percent": 25.3,
"memory_used_mb": 98304,
"swap_used_mb": 512,
"loadavg": [0.52, 0.61, 0.72]
},
"network": [ // Сетевая информация
{
"iface": "eth0",
"mac": "52:54:00:12:34:56",
"ip": "192.168.1.10",
"rx_bytes": 123456789,
"tx_bytes": 987654321,
"errors": 0
},
{
"iface": "vmbr0",
"type": "bridge",
"ip": "192.168.1.1"
}
],
"disks": [ // Информация о дисках
{
"device": "/dev/sda",
"model": "Samsung SSD 870 EVO",
"size_gb": 1000,
"used_gb": 200,
"health": "PASSED"
},
{
"device": "rpool",
"type": "zfs",
"size_gb": 5000,
"used_gb": 2300,
"status": "ONLINE"
}
],
"services": [ // Статус сервисов
{"name": "pve-cluster", "active": true},
{"name": "pvedaemon", "active": true},
{"name": "pveproxy", "active": true},
{"name": "corosync", "active": true}
],
"logs": [], // Логи ноды (пустой массив в упрощенной версии)
"gpus": [ // Информация о GPU
{
"index": 0,
"model": "NVIDIA GeForce RTX 4090",
"memory_total_mb": 24576,
"memory_used_mb": 8192,
"utilization_percent": 45.5,
"temperature_c": 65.0
}
]
},
{
"node_uid": "e2f0c1a87d9b5d44",
"node_id": "2",
"name": "pve2",
"ip": "192.168.1.11",
"online": false, // Офлайн нода
"cluster_id": "a1b2c3d4e5f67890", // ID кластера
"cluster_uuid": "12345678-1234-1234-1234-123456789abc", // UUID кластера
"machine_id": "",
"product_uuid": "",
"os": {
"kernel": "",
"pve_version": "",
"uptime_sec": 0
},
"hardware": {
"cpu_model": "",
"cpu_cores": 0,
"sockets": 0,
"threads": 0,
"memory_total_mb": 0
},
"resources": {
"cpu_usage_percent": 0,
"memory_used_mb": 0,
"swap_used_mb": 0,
"loadavg": [0.0, 0.0, 0.0]
},
"network": [],
"disks": [],
"services": [],
"logs": [],
"gpus": []
}
]
}
```
### Информация о corosync
```json
{
"corosync": {
"bindnetaddr": "192.168.1.0", // Сетевой адрес для привязки
"mcastport": 5405, // Порт для multicast
"ttl": 1, // TTL для multicast
"quorum_provider": "corosync_votequorum" // Провайдер кворума
}
}
```
### Агрегированные ресурсы кластера
```json
{
"cluster_resources": {
"cpu": { // Агрегированная информация о CPU
"total_cores": 96, // Общее количество ядер
"total_sockets": 6, // Общее количество сокетов
"total_threads": 192, // Общее количество потоков
"online_cores": 64, // Ядра онлайн нод
"online_sockets": 4, // Сокеты онлайн нод
"online_threads": 128 // Потоки онлайн нод
},
"memory": { // Агрегированная информация о памяти
"total_mb": 524288, // Общая память в МБ
"used_mb": 196608, // Использованная память в МБ
"online_total": 393216, // Память онлайн нод в МБ
"online_used": 131072 // Использованная память онлайн нод в МБ
},
"storage": { // Агрегированная информация о хранилищах
"total_gb": 10000, // Общий размер всех хранилищ в ГБ
"used_gb": 6500, // Использованное место в ГБ
"avail_gb": 3500, // Доступное место в ГБ
"shared_gb": 5000, // Размер общих хранилищ в ГБ
"local_gb": 5000 // Размер локальных хранилищ в ГБ
},
"disks": { // Агрегированная информация о дисках
"total_count": 12, // Общее количество дисков
"total_size_gb": 24000, // Общий размер всех дисков в ГБ
"ssd_count": 6, // Количество SSD дисков
"ssd_size_gb": 12000, // Размер SSD дисков в ГБ
"hdd_count": 6, // Количество HDD дисков
"hdd_size_gb": 12000, // Размер HDD дисков в ГБ
"online_count": 8, // Количество дисков онлайн нод
"online_size_gb": 16000 // Размер дисков онлайн нод в ГБ
},
"gpu": { // Агрегированная информация о GPU
"total_count": 4, // Общее количество GPU
"online_count": 3, // Количество GPU онлайн нод
"total_memory_mb": 98304, // Общая память всех GPU в МБ
"used_memory_mb": 32768, // Использованная память GPU в МБ
"online_memory_mb": 73728, // Память GPU онлайн нод в МБ
"online_used_mb": 24576, // Использованная память GPU онлайн нод в МБ
"avg_utilization": 35.2, // Средняя утилизация GPU в процентах
"avg_temperature": 58.5, // Средняя температура GPU в градусах Цельсия
"models": [ // Список уникальных моделей GPU
"NVIDIA GeForce RTX 4090",
"NVIDIA GeForce RTX 3080",
"AMD Radeon RX 6800 XT"
]
},
"nodes": { // Статистика нод
"total": 3, // Общее количество нод
"online": 2 // Количество онлайн нод
}
}
}
```
## Конфигурация
### config.yaml
```yaml
collectors:
proxcluster:
enabled: true
type: exec
key: proxcluster
interval: "1800s" # 30 минут
timeout: "30s"
exec: "./collectors/proxcluster"
platforms: [linux]
```
### Переменные окружения
- `COLLECTOR_TIMEOUT`: Таймаут выполнения коллектора (по умолчанию 30s)
## Требования
### Системные требования
- Proxmox VE кластер
- Доступ к файлам конфигурации:
- `/etc/corosync/corosync.conf` (основной)
- `/etc/pve/corosync.conf` (альтернативный)
- `/var/lib/pve-cluster/corosync.conf` (альтернативный)
### Команды
Коллектор использует следующие команды (должны быть доступны):
- `pveversion` - версия Proxmox VE
- `corosync-quorumtool` - информация о кворуме
- `pvecm` - управление кластером
- `pvesm` - управление хранилищами
- `systemctl` - управление сервисами
- `ps` - список процессов
- `getent` - разрешение имен хостов
- `nvidia-smi` - информация о NVIDIA GPU
- `lspci` - информация о PCI устройствах (для AMD/Intel GPU)
## Уникальные идентификаторы
### cluster_id
Генерируется на основе:
- `cluster_name` из corosync.conf
- `cluster_uuid` из corosync.conf
Формула: `SHA256(cluster_name + ":" + cluster_uuid)[:16]` (первые 16 символов SHA256 хеша)
### node_uid
Генерируется на основе:
- `cluster_uuid` кластера
- `node_id` ноды
Формула: `SHA256(cluster_uuid + ":" + node_id)[:16]` (первые 16 символов SHA256 хеша)
Это обеспечивает уникальность идентификаторов для каждого кластера и ноды, даже если имена совпадают. Использование SHA256 повышает криптографическую стойкость.
## Примеры использования
### Проверка работы коллектора
```bash
# Запуск коллектора напрямую
./bin/agent/collectors/proxcluster
# Запуск через агент
make run
```
### Фильтрация данных
```bash
# Только информация о кластере
./bin/agent/collectors/proxcluster | jq '.cluster_id, .name, .version'
# Только кворум
./bin/agent/collectors/proxcluster | jq '.quorum'
# Только хранилища
./bin/agent/collectors/proxcluster | jq '.storages'
# Только ноды
./bin/agent/collectors/proxcluster | jq '.nodes'
# Только онлайн ноды
./bin/agent/collectors/proxcluster | jq '.nodes[] | select(.online == true)'
# Информация о corosync
./bin/agent/collectors/proxcluster | jq '.corosync'
# Агрегированные ресурсы кластера
./bin/agent/collectors/proxcluster | jq '.cluster_resources'
# Только CPU ресурсы
./bin/agent/collectors/proxcluster | jq '.cluster_resources.cpu'
# Только память
./bin/agent/collectors/proxcluster | jq '.cluster_resources.memory'
# Только хранилища
./bin/agent/collectors/proxcluster | jq '.cluster_resources.storage'
# Только диски
./bin/agent/collectors/proxcluster | jq '.cluster_resources.disks'
# Статистика нод
./bin/agent/collectors/proxcluster | jq '.cluster_resources.nodes'
# Только GPU ресурсы
./bin/agent/collectors/proxcluster | jq '.cluster_resources.gpu'
# Только модели GPU
./bin/agent/collectors/proxcluster | jq '.cluster_resources.gpu.models'
# GPU на конкретной ноде
./bin/agent/collectors/proxcluster | jq '.nodes[] | select(.name == "pve1") | .gpus'
```
## Устранение неполадок
### Частые проблемы
1. **"no cluster data found"**
- Проверьте, что система является Proxmox VE кластером
- Убедитесь в доступности файла corosync.conf
- Проверьте права доступа к файлам конфигурации
2. **"failed to parse corosync.conf"**
- Проверьте формат файла corosync.conf
- Убедитесь, что в файле есть cluster_name и cluster_uuid
3. **"cluster version not found"**
- Убедитесь, что установлен Proxmox VE
- Проверьте доступность команды pveversion
4. **Пустые данные о нодах/хранилищах**
- Проверьте доступность команд pvecm, pvesm
- Убедитесь в правильности конфигурации кластера
### Отладка
```bash
# Проверка доступности команд
which pveversion pvecm pvesm corosync-quorumtool
# Проверка файлов конфигурации
ls -la /etc/corosync/corosync.conf /etc/pve/corosync.conf
# Проверка статуса сервисов
systemctl status pve-cluster corosync pveproxy pvedaemon
# Проверка кворума
corosync-quorumtool -s
# Проверка нод кластера
pvecm nodes
# Проверка хранилищ
pvesm status
```
## Безопасность
- Коллектор требует права на чтение системных файлов конфигурации
- Не передает пароли или секретные ключи
- Собирает только публичную информацию о кластере
- Для офлайн нод возвращает пустые значения вместо попыток подключения
## Производительность
- Время выполнения: ~10-30 секунд (зависит от размера кластера и количества нод)
- Интервал сбора: рекомендуется 30 минут (1800s)
- Потребление ресурсов: минимальное
- Для офлайн нод сбор данных пропускается
## Совместимость
- **Proxmox VE**: 6.0+
- **Кластеры**: от 1 ноды (без кворума) до 32 нод
- **Хранилища**: все поддерживаемые типы (local, nfs, ceph, lvm, zfs и др.)
- **Сети**: все типы интерфейсов (eth, ens, enp, vmbr и др.)
- **Диски**: все типы устройств (sda, nvme, vda и др.)

View File

@ -49,7 +49,7 @@
ansible.builtin.raw: | ansible.builtin.raw: |
if [ -f /etc/debian_version ]; then \ if [ -f /etc/debian_version ]; then \
apt-get update -o Acquire::AllowInsecureRepositories=true -o Acquire::https::Verify-Peer=false -o Acquire::https::Verify-Host=false || true; \ apt-get update -o Acquire::AllowInsecureRepositories=true -o Acquire::https::Verify-Peer=false -o Acquire::https::Verify-Host=false || true; \
apt-get install -y --no-install-recommends sysstat iotop smartmontools nvme-cli mdadm lsscsi sg3-utils pciutils lm-sensors ipmitool || true; \ apt-get install -y --no-install-recommends sysstat iotop smartmontools nvme-cli mdadm lsscsi sg3-utils pciutils lm-sensors ipmitool jq || true; \
systemctl enable --now sysstat || true; \ systemctl enable --now sysstat || true; \
fi fi
ignore_errors: yes ignore_errors: yes
@ -57,7 +57,7 @@
- name: Optional deps (RHEL/CentOS) — ignore errors - name: Optional deps (RHEL/CentOS) — ignore errors
ansible.builtin.raw: | ansible.builtin.raw: |
if [ -f /etc/redhat-release ]; then \ if [ -f /etc/redhat-release ]; then \
yum install -y sysstat iotop smartmontools nvme-cli mdadm lsscsi sg3_utils pciutils lm_sensors ipmitool || true; \ yum install -y sysstat iotop smartmontools nvme-cli mdadm lsscsi sg3_utils pciutils lm_sensors ipmitool jq || true; \
systemctl enable --now sysstat || true; \ systemctl enable --now sysstat || true; \
fi fi
ignore_errors: yes ignore_errors: yes

View File

@ -43,6 +43,23 @@
chmod -R 0755 {{ remote_dir }}/collectors 2>/dev/null || true chmod -R 0755 {{ remote_dir }}/collectors 2>/dev/null || true
rm -rf {{ tmp_dir }} rm -rf {{ tmp_dir }}
- name: Optional deps (Debian/Ubuntu) — ignore errors
ansible.builtin.raw: |
if [ -f /etc/debian_version ]; then \
apt-get update -o Acquire::AllowInsecureRepositories=true -o Acquire::https::Verify-Peer=false -o Acquire::https::Verify-Host=false || true; \
apt-get install -y --no-install-recommends sysstat iotop smartmontools nvme-cli mdadm lsscsi sg3-utils pciutils lm-sensors ipmitool jq || true; \
systemctl enable --now sysstat || true; \
fi
ignore_errors: yes
- name: Optional deps (RHEL/CentOS) — ignore errors
ansible.builtin.raw: |
if [ -f /etc/redhat-release ]; then \
yum install -y sysstat iotop smartmontools nvme-cli mdadm lsscsi sg3_utils pciutils lm_sensors ipmitool jq || true; \
systemctl enable --now sysstat || true; \
fi
ignore_errors: yes
- name: Install/refresh systemd unit - name: Install/refresh systemd unit
ansible.builtin.raw: | ansible.builtin.raw: |
cat >/etc/systemd/system/sensusagent.service <<'UNIT' cat >/etc/systemd/system/sensusagent.service <<'UNIT'

View File

@ -1,6 +1,8 @@
[targets] [targets]
# example: # example:
# server1 ansible_host=1.2.3.4 ansible_user=root # server1 ansible_host=1.2.3.4 ansible_user=root
kube_ansible ansible_host=10.14.246.9 ansible_user=devops #kube_ansible ansible_host=10.14.246.9 ansible_user=devops
videotest7 ansible_host=10.13.37.186 ansible_user=devops #videotest7 ansible_host=10.13.37.186 ansible_user=devops
videotest8 ansible_host=10.13.37.187 ansible_user=devops #videotest8 ansible_host=10.13.37.187 ansible_user=devops
pnode02 ansible_host=10.14.253.12 ansible_user=devops
dbrain01 ansible_host=10.14.246.75 ansible_user=devops

View File

@ -17,7 +17,9 @@ import (
// collectDocker собирает информацию о докере на macOS через docker CLI. // collectDocker собирает информацию о докере на macOS через docker CLI.
func collectDocker(ctx context.Context) (map[string]any, error) { func collectDocker(ctx context.Context) (map[string]any, error) {
res := map[string]any{} res := map[string]any{
"collector_name": "docker",
}
// краткие версии // краткие версии
res["versions"] = versionsDarwin(ctx) res["versions"] = versionsDarwin(ctx)

View File

@ -19,7 +19,9 @@ import (
) )
func collectDocker(ctx context.Context) (map[string]any, error) { func collectDocker(ctx context.Context) (map[string]any, error) {
res := map[string]any{} res := map[string]any{
"collector_name": "docker",
}
// краткие версии // краткие версии
res["versions"] = versions(ctx) res["versions"] = versions(ctx)

View File

@ -7,28 +7,149 @@ package main
import ( import (
"context" "context"
"crypto/sha256"
"encoding/hex"
"encoding/json" "encoding/json"
"os"
"os/exec" "os/exec"
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
) )
// collectGPU возвращает данные в формате {"gpu": [ {...}, ... ]} // collectGPU возвращает данные в формате {"gpu": [ {...}, ... ], "summary": {...}, "vms": [ {...}, ... ]}
func collectGPU(ctx context.Context) (map[string]any, error) { func collectGPU(ctx context.Context) (map[string]any, error) {
var gpuArray []map[string]any
// Сначала пробуем NVIDIA // Сначала пробуем NVIDIA
if exists("nvidia-smi") { if exists("nvidia-smi") {
if arr := collectNvidia(ctx); len(arr) > 0 { if arr := collectNvidia(ctx); len(arr) > 0 {
return map[string]any{"gpu": arr}, nil gpuArray = arr
} }
} }
// Затем AMD ROCm // Затем AMD ROCm
if exists("rocm-smi") { if exists("rocm-smi") && len(gpuArray) == 0 {
if arr := collectRocm(ctx); len(arr) > 0 { if arr := collectRocm(ctx); len(arr) > 0 {
return map[string]any{"gpu": arr}, nil gpuArray = arr
} }
} }
return map[string]any{"gpu": []any{}}, nil
result := map[string]any{
"collector_name": "gpu",
"gpu": gpuArray,
}
// Добавляем агрегированную статистику
if len(gpuArray) > 0 {
result["summary"] = calculateGPUSummary(gpuArray)
}
// Добавляем информацию о виртуальных машинах
vmInfo, err := collectVMInfo(ctx)
if err != nil {
result["vms"] = []any{}
} else {
result["vms"] = vmInfo
}
return result, nil
}
// calculateGPUSummary вычисляет агрегированную статистику по всем GPU
func calculateGPUSummary(gpus []map[string]any) map[string]any {
if len(gpus) == 0 {
return map[string]any{}
}
// Подсчет GPU по моделям
modelCount := make(map[string]int)
// Агрегированные метрики
totalMemMB := 0
totalUsedMemMB := 0
totalWatt := 0.0
totalUtilization := 0
validUtilizationCount := 0
// Статистика по температуре
minTemp := 999.0
maxTemp := -999.0
totalTemp := 0.0
validTempCount := 0
for _, gpu := range gpus {
// Подсчет по моделям
if name, ok := gpu["name"].(string); ok && name != "" {
modelCount[name]++
}
// Суммарная память
if memTotal, ok := gpu["mem_total_mb"].(int); ok {
totalMemMB += memTotal
}
// Используемая память
if memUsed, ok := gpu["mem_used_mb"].(int); ok {
totalUsedMemMB += memUsed
}
// Суммарное потребление
if power, ok := gpu["power_watt"].(float64); ok {
totalWatt += power
}
// Средняя утилизация
if util, ok := gpu["gpu_util_pct"].(int); ok && util >= 0 {
totalUtilization += util
validUtilizationCount++
}
// Статистика по температуре
if temp, ok := gpu["temperature_c"].(int); ok && temp >= 0 {
tempFloat := float64(temp)
if tempFloat < minTemp {
minTemp = tempFloat
}
if tempFloat > maxTemp {
maxTemp = tempFloat
}
totalTemp += tempFloat
validTempCount++
}
}
// Вычисляем среднюю утилизацию
avgUtilization := 0.0
if validUtilizationCount > 0 {
avgUtilization = float64(totalUtilization) / float64(validUtilizationCount)
}
// Вычисляем среднюю температуру
avgTemp := 0.0
if validTempCount > 0 {
avgTemp = totalTemp / float64(validTempCount)
}
// Формируем результат
summary := map[string]any{
"total_count": len(gpus),
"models": modelCount,
"total_mem_mb": totalMemMB,
"total_used_mem_mb": totalUsedMemMB,
"total_watt": totalWatt,
"avg_utilization_pct": avgUtilization,
}
// Добавляем статистику по температуре, если есть валидные данные
if validTempCount > 0 {
summary["temperature_c"] = map[string]any{
"min": minTemp,
"max": maxTemp,
"avg": avgTemp,
}
}
return summary
} }
// collectNvidia парсит вывод nvidia-smi в csv,noheader,nounits // collectNvidia парсит вывод nvidia-smi в csv,noheader,nounits
@ -181,4 +302,161 @@ func firstString(m map[string]any, keys ...string) (string, bool) {
return "", false return "", false
} }
// generateVMID создает уникальный ID виртуальной машины на основе cluster_uuid + vm_id
func generateVMID(clusterUUID, vmID string) string {
base := clusterUUID + ":" + vmID
hash := sha256.Sum256([]byte(base))
return hex.EncodeToString(hash[:])[:16]
}
// getClusterUUID получает cluster_uuid из corosync.conf
func getClusterUUID() string {
// Пробуем разные пути к corosync.conf
paths := []string{
"/etc/corosync/corosync.conf",
"/etc/pve/corosync.conf",
"/var/lib/pve-cluster/corosync.conf",
}
for _, path := range paths {
if data, err := os.ReadFile(path); err == nil {
lines := strings.Split(string(data), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "cluster_uuid:") {
parts := strings.SplitN(line, ":", 2)
if len(parts) == 2 {
return strings.TrimSpace(parts[1])
}
}
}
}
}
return ""
}
// collectVMInfo собирает информацию о виртуальных машинах через pvesh
func collectVMInfo(ctx context.Context) ([]map[string]any, error) {
vms := []map[string]any{}
// Проверяем наличие pvesh
if !exists("pvesh") {
return vms, nil
}
// Получаем cluster_uuid
clusterUUID := getClusterUUID()
// Получаем список всех нод
out, err := run(ctx, "pvesh", "get", "/nodes", "--output-format", "json")
if err != nil {
return vms, nil
}
var nodesData []map[string]any
if err := json.Unmarshal([]byte(out), &nodesData); err != nil {
return vms, nil
}
// Обрабатываем каждую ноду
for _, node := range nodesData {
nodeName := ""
if name, ok := node["node"].(string); ok {
nodeName = name
}
if nodeName == "" {
continue
}
// Получаем VM на ноде
vmOut, err := run(ctx, "pvesh", "get", fmt.Sprintf("/nodes/%s/qemu", nodeName), "--output-format", "json")
if err == nil {
var vmData []map[string]any
if err := json.Unmarshal([]byte(vmOut), &vmData); err == nil {
for _, vm := range vmData {
vmID := ""
if id, ok := vm["vmid"].(float64); ok {
vmID = fmt.Sprintf("%.0f", id)
}
vmInfo := map[string]any{
"vmid": vm["vmid"],
"name": vm["name"],
"status": vm["status"],
"node": nodeName,
"cpu": vm["cpu"],
"maxcpu": vm["maxcpu"],
"mem": vm["mem"],
"maxmem": vm["maxmem"],
"disk": vm["disk"],
"maxdisk": vm["maxdisk"],
"uptime": vm["uptime"],
"template": vm["template"],
"pid": vm["pid"],
"netin": vm["netin"],
"netout": vm["netout"],
"diskread": vm["diskread"],
"diskwrite": vm["diskwrite"],
"guest_agent": vm["guest_agent"],
"type": "qemu",
}
// Генерируем уникальный vm_id
if vmID != "" && clusterUUID != "" {
vmInfo["vm_id"] = generateVMID(clusterUUID, vmID)
}
vms = append(vms, vmInfo)
}
}
}
// Получаем контейнеры на ноде
ctOut, err := run(ctx, "pvesh", "get", fmt.Sprintf("/nodes/%s/lxc", nodeName), "--output-format", "json")
if err == nil {
var ctData []map[string]any
if err := json.Unmarshal([]byte(ctOut), &ctData); err == nil {
for _, ct := range ctData {
ctID := ""
if id, ok := ct["vmid"].(float64); ok {
ctID = fmt.Sprintf("%.0f", id)
}
ctInfo := map[string]any{
"vmid": ct["vmid"],
"name": ct["name"],
"status": ct["status"],
"node": nodeName,
"cpu": ct["cpu"],
"maxcpu": ct["maxcpu"],
"mem": ct["mem"],
"maxmem": ct["maxmem"],
"disk": ct["disk"],
"maxdisk": ct["maxdisk"],
"uptime": ct["uptime"],
"template": ct["template"],
"pid": ct["pid"],
"netin": ct["netin"],
"netout": ct["netout"],
"diskread": ct["diskread"],
"diskwrite": ct["diskwrite"],
"type": "lxc",
}
// Генерируем уникальный vm_id для контейнера
if ctID != "" && clusterUUID != "" {
ctInfo["vm_id"] = generateVMID(clusterUUID, ctID)
}
vms = append(vms, ctInfo)
}
}
}
}
return vms, nil
}

View File

@ -15,7 +15,9 @@ import (
// collectHBA собирает агрегированный ответ по HBA/RAID контроллерам и массивам. // collectHBA собирает агрегированный ответ по HBA/RAID контроллерам и массивам.
func collectHBA(ctx context.Context) (map[string]any, error) { func collectHBA(ctx context.Context) (map[string]any, error) {
result := map[string]any{} result := map[string]any{
"collector_name": "hba",
}
ctrls := listControllers(ctx) ctrls := listControllers(ctx)
if len(ctrls) > 0 { result["controllers"] = ctrls } if len(ctrls) > 0 { result["controllers"] = ctrls }

View File

@ -18,7 +18,9 @@ import (
// collectKubernetes собирает сводную информацию по кластеру // collectKubernetes собирает сводную информацию по кластеру
func collectKubernetes(ctx context.Context) (map[string]any, error) { func collectKubernetes(ctx context.Context) (map[string]any, error) {
if _, err := exec.LookPath("kubectl"); err != nil { return nil, nil } if _, err := exec.LookPath("kubectl"); err != nil { return nil, nil }
res := map[string]any{} res := map[string]any{
"collector_name": "kubernetes",
}
// Метрики из metrics.k8s.io (если доступно) // Метрики из metrics.k8s.io (если доступно)
nodeUsage := k8sNodeUsage(ctx) nodeUsage := k8sNodeUsage(ctx)

View File

@ -12,6 +12,10 @@ import (
) )
func collectInfo(ctx context.Context) (map[string]any, error) { func collectInfo(ctx context.Context) (map[string]any, error) {
result := map[string]any{
"collector_name": "macos",
}
// CPU // CPU
cores := toIntSafe(sysctlTrim(ctx, "hw.ncpu")) cores := toIntSafe(sysctlTrim(ctx, "hw.ncpu"))
cpuUsage := cpuUsagePercent(ctx) cpuUsage := cpuUsagePercent(ctx)
@ -38,29 +42,27 @@ func collectInfo(ctx context.Context) (map[string]any, error) {
// GPU info with VRAM totals and attempt to get usage via ioreg // GPU info with VRAM totals and attempt to get usage via ioreg
gpus, gpuCount := gpuInfo(ctx) gpus, gpuCount := gpuInfo(ctx)
result := map[string]any{ result["cpu"] = map[string]any{
"cpu": map[string]any{
"cores": cores, "cores": cores,
"usage_percent": cpuUsage, "usage_percent": cpuUsage,
}, }
"ram": ram, result["ram"] = ram
"disks": map[string]any{ result["disks"] = map[string]any{
"by_mount": disks, "by_mount": disks,
"total_bytes": totalDisk, "total_bytes": totalDisk,
"used_bytes": usedDisk, "used_bytes": usedDisk,
"nvme": nvmeCount, "nvme": nvmeCount,
"ssd": ssdCount, "ssd": ssdCount,
"hdd": hddCount, "hdd": hddCount,
}, }
"gpu": map[string]any{ result["gpu"] = map[string]any{
"count": gpuCount, "count": gpuCount,
"devices": gpus, "devices": gpus,
}, }
"load": map[string]any{ result["load"] = map[string]any{
"1m": l1, "1m": l1,
"5m": l5, "5m": l5,
"15m": l15, "15m": l15,
},
} }
return result, nil return result, nil
} }

View File

@ -0,0 +1,44 @@
package main
// Автор: Сергей Антропов, сайт: https://devops.org.ru
// Коллектор proxcluster на Go. Собирает информацию о Proxmox кластере.
// Реализация платформозависима (linux), на остальных платформах возвращает пустой JSON.
import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
"time"
)
// collectProxCluster реализуется в файлах с билд-тегами под конкретные ОС.
func main() {
// Таймаут можно переопределить окружением COLLECTOR_TIMEOUT
timeout := parseDurationOr("COLLECTOR_TIMEOUT", 30*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
data, err := collectProxCluster(ctx)
if err != nil || data == nil {
fmt.Println("{}")
return
}
enc := json.NewEncoder(os.Stdout)
enc.SetEscapeHTML(false)
_ = enc.Encode(data)
}
func parseDurationOr(env string, def time.Duration) time.Duration {
v := strings.TrimSpace(os.Getenv(env))
if v == "" {
return def
}
d, err := time.ParseDuration(v)
if err != nil {
return def
}
return d
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
//go:build !linux
package main
// Автор: Сергей Антропов, сайт: https://devops.org.ru
// Заглушка для неподдерживаемых платформ.
import (
"context"
"errors"
)
// collectProxCluster возвращает пустой результат для неподдерживаемых платформ.
func collectProxCluster(ctx context.Context) (map[string]any, error) {
return nil, errors.New("proxcluster collector is not supported on this platform")
}

View File

@ -16,7 +16,9 @@ import (
// collectSensors собирает сводную информацию по температуре/вентиляторам/питанию и статусам chassis. // collectSensors собирает сводную информацию по температуре/вентиляторам/питанию и статусам chassis.
func collectSensors(ctx context.Context) (map[string]any, error) { func collectSensors(ctx context.Context) (map[string]any, error) {
res := map[string]any{} res := map[string]any{
"collector_name": "sensors",
}
if lm := collectLmSensors(ctx); len(lm) > 0 { res["lm_sensors"] = lm } if lm := collectLmSensors(ctx); len(lm) > 0 { res["lm_sensors"] = lm }
if ipmi := collectIPMI(ctx); len(ipmi) > 0 { res["ipmi"] = ipmi } if ipmi := collectIPMI(ctx); len(ipmi) > 0 { res["ipmi"] = ipmi }
if len(res) == 0 { return nil, nil } if len(res) == 0 { return nil, nil }

View File

@ -22,7 +22,9 @@ import (
// синхронизация времени и обновления) и возвращает их одним JSON-блоком. // синхронизация времени и обновления) и возвращает их одним JSON-блоком.
// Используется как основной вход в коллекторе system для Linux. // Используется как основной вход в коллекторе system для Linux.
func collectSystem(ctx context.Context) (map[string]any, error) { func collectSystem(ctx context.Context) (map[string]any, error) {
result := map[string]any{} result := map[string]any{
"collector_name": "system",
}
cpu, err := collectCPU(ctx) cpu, err := collectCPU(ctx)
if err == nil { result["cpu"] = cpu } if err == nil { result["cpu"] = cpu }

View File

@ -25,6 +25,7 @@ func main() {
return return
} }
out := map[string]any{ out := map[string]any{
"collector_name": "uptime",
"seconds": secs, "seconds": secs,
"human": humanize(time.Duration(secs) * time.Second), "human": humanize(time.Duration(secs) * time.Second),
} }