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:
parent
5bfb6fea8b
commit
621d3f0a43
35
Makefile
35
Makefile
@ -43,11 +43,23 @@ collectors:
|
||||
ARCH=$$(/usr/bin/uname -m | sed 's/x86_64/amd64/; s/arm64/arm64/'); \
|
||||
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 \
|
||||
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 \
|
||||
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"; \
|
||||
fi
|
||||
@# Убедимся, что скрипты исполняемые
|
||||
@chmod +x ./bin/agent/collectors/*.sh 2>/dev/null || true
|
||||
@ -56,19 +68,32 @@ collectors-darwin:
|
||||
# Кросс-сборка коллекторов для macOS
|
||||
@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 \
|
||||
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:
|
||||
# Кросс-сборка коллекторов для Linux
|
||||
@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 \
|
||||
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:
|
||||
# Кросс-сборка коллекторов для Windows
|
||||
@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 \
|
||||
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"
|
||||
|
||||
|
||||
|
||||
|
@ -11,6 +11,7 @@ SensusAgent — модульный агент сбора метрик. Аген
|
||||
- Сборка и запуск (Make/Docker/Compose): `docs/build_and_run.md`
|
||||
- Деплой (Ansible, systemd): `docs/deploy.md`
|
||||
- **Kafka SSL поддержка**: `docs/kafka_ssl.md` ⭐
|
||||
- **Proxmox кластер**: `docs/collectors/proxcluster.md` ⭐
|
||||
|
||||
Быстрый старт:
|
||||
```bash
|
||||
|
@ -6,7 +6,7 @@ log_level: info
|
||||
|
||||
kafka:
|
||||
enabled: false
|
||||
brokers: ["10.99.0.90:9092"]
|
||||
brokers: ["10.29.91.4:9092"]
|
||||
topic: "sensus.metrics"
|
||||
client_id: "sensusagent"
|
||||
enable_tls: false
|
||||
@ -71,7 +71,7 @@ collectors:
|
||||
exec: "./collectors/sensors"
|
||||
platforms: [linux]
|
||||
docker:
|
||||
enabled: true
|
||||
enabled: false
|
||||
type: exec
|
||||
key: docker
|
||||
interval: "3600s"
|
||||
@ -87,13 +87,21 @@ collectors:
|
||||
exec: "./collectors/gpu"
|
||||
platforms: [linux]
|
||||
kubernetes:
|
||||
enabled: true
|
||||
enabled: false
|
||||
type: exec
|
||||
key: kubernetes
|
||||
interval: "3600s"
|
||||
timeout: "60s"
|
||||
exec: "./collectors/kubernetes"
|
||||
platforms: [linux]
|
||||
proxcluster:
|
||||
enabled: true
|
||||
type: exec
|
||||
key: proxcluster
|
||||
interval: "1800s"
|
||||
timeout: "30s"
|
||||
exec: "./collectors/proxcluster"
|
||||
platforms: [linux]
|
||||
|
||||
|
||||
|
||||
|
@ -46,3 +46,22 @@ make collectors-darwin # darwin/arm64
|
||||
- Ограничивайте потребление ресурсов и время выполнения
|
||||
- На стороне агента используйте `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
256
docs/collectors/gpu.md
Normal 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 в системе
|
445
docs/collectors/proxcluster.md
Normal file
445
docs/collectors/proxcluster.md
Normal 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 и др.)
|
@ -49,7 +49,7 @@
|
||||
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 || 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
|
||||
@ -57,7 +57,7 @@
|
||||
- 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 || true; \
|
||||
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
|
||||
|
@ -43,6 +43,23 @@
|
||||
chmod -R 0755 {{ remote_dir }}/collectors 2>/dev/null || true
|
||||
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
|
||||
ansible.builtin.raw: |
|
||||
cat >/etc/systemd/system/sensusagent.service <<'UNIT'
|
||||
|
@ -1,6 +1,8 @@
|
||||
[targets]
|
||||
# example:
|
||||
# server1 ansible_host=1.2.3.4 ansible_user=root
|
||||
kube_ansible ansible_host=10.14.246.9 ansible_user=devops
|
||||
videotest7 ansible_host=10.13.37.186 ansible_user=devops
|
||||
videotest8 ansible_host=10.13.37.187 ansible_user=devops
|
||||
#kube_ansible ansible_host=10.14.246.9 ansible_user=devops
|
||||
#videotest7 ansible_host=10.13.37.186 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
|
||||
|
@ -17,7 +17,9 @@ import (
|
||||
|
||||
// collectDocker собирает информацию о докере на macOS через docker CLI.
|
||||
func collectDocker(ctx context.Context) (map[string]any, error) {
|
||||
res := map[string]any{}
|
||||
res := map[string]any{
|
||||
"collector_name": "docker",
|
||||
}
|
||||
// краткие версии
|
||||
res["versions"] = versionsDarwin(ctx)
|
||||
|
||||
|
@ -19,7 +19,9 @@ import (
|
||||
)
|
||||
|
||||
func collectDocker(ctx context.Context) (map[string]any, error) {
|
||||
res := map[string]any{}
|
||||
res := map[string]any{
|
||||
"collector_name": "docker",
|
||||
}
|
||||
// краткие версии
|
||||
res["versions"] = versions(ctx)
|
||||
|
||||
|
@ -7,28 +7,149 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"os/exec"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// collectGPU возвращает данные в формате {"gpu": [ {...}, ... ]}
|
||||
// collectGPU возвращает данные в формате {"gpu": [ {...}, ... ], "summary": {...}, "vms": [ {...}, ... ]}
|
||||
func collectGPU(ctx context.Context) (map[string]any, error) {
|
||||
var gpuArray []map[string]any
|
||||
|
||||
// Сначала пробуем NVIDIA
|
||||
if exists("nvidia-smi") {
|
||||
if arr := collectNvidia(ctx); len(arr) > 0 {
|
||||
return map[string]any{"gpu": arr}, nil
|
||||
gpuArray = arr
|
||||
}
|
||||
}
|
||||
// Затем AMD ROCm
|
||||
if exists("rocm-smi") {
|
||||
if exists("rocm-smi") && len(gpuArray) == 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
|
||||
@ -181,4 +302,161 @@ func firstString(m map[string]any, keys ...string) (string, bool) {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -15,7 +15,9 @@ import (
|
||||
|
||||
// collectHBA собирает агрегированный ответ по HBA/RAID контроллерам и массивам.
|
||||
func collectHBA(ctx context.Context) (map[string]any, error) {
|
||||
result := map[string]any{}
|
||||
result := map[string]any{
|
||||
"collector_name": "hba",
|
||||
}
|
||||
|
||||
ctrls := listControllers(ctx)
|
||||
if len(ctrls) > 0 { result["controllers"] = ctrls }
|
||||
|
@ -18,7 +18,9 @@ import (
|
||||
// collectKubernetes собирает сводную информацию по кластеру
|
||||
func collectKubernetes(ctx context.Context) (map[string]any, error) {
|
||||
if _, err := exec.LookPath("kubectl"); err != nil { return nil, nil }
|
||||
res := map[string]any{}
|
||||
res := map[string]any{
|
||||
"collector_name": "kubernetes",
|
||||
}
|
||||
|
||||
// Метрики из metrics.k8s.io (если доступно)
|
||||
nodeUsage := k8sNodeUsage(ctx)
|
||||
|
@ -12,6 +12,10 @@ import (
|
||||
)
|
||||
|
||||
func collectInfo(ctx context.Context) (map[string]any, error) {
|
||||
result := map[string]any{
|
||||
"collector_name": "macos",
|
||||
}
|
||||
|
||||
// CPU
|
||||
cores := toIntSafe(sysctlTrim(ctx, "hw.ncpu"))
|
||||
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
|
||||
gpus, gpuCount := gpuInfo(ctx)
|
||||
|
||||
result := map[string]any{
|
||||
"cpu": map[string]any{
|
||||
result["cpu"] = map[string]any{
|
||||
"cores": cores,
|
||||
"usage_percent": cpuUsage,
|
||||
},
|
||||
"ram": ram,
|
||||
"disks": map[string]any{
|
||||
}
|
||||
result["ram"] = ram
|
||||
result["disks"] = map[string]any{
|
||||
"by_mount": disks,
|
||||
"total_bytes": totalDisk,
|
||||
"used_bytes": usedDisk,
|
||||
"nvme": nvmeCount,
|
||||
"ssd": ssdCount,
|
||||
"hdd": hddCount,
|
||||
},
|
||||
"gpu": map[string]any{
|
||||
}
|
||||
result["gpu"] = map[string]any{
|
||||
"count": gpuCount,
|
||||
"devices": gpus,
|
||||
},
|
||||
"load": map[string]any{
|
||||
}
|
||||
result["load"] = map[string]any{
|
||||
"1m": l1,
|
||||
"5m": l5,
|
||||
"15m": l15,
|
||||
},
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
44
src/collectors/proxcluster/main.go
Normal file
44
src/collectors/proxcluster/main.go
Normal 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
|
||||
}
|
1489
src/collectors/proxcluster/proxcluster_linux.go
Normal file
1489
src/collectors/proxcluster/proxcluster_linux.go
Normal file
File diff suppressed because it is too large
Load Diff
16
src/collectors/proxcluster/proxcluster_unsupported.go
Normal file
16
src/collectors/proxcluster/proxcluster_unsupported.go
Normal 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")
|
||||
}
|
@ -16,7 +16,9 @@ import (
|
||||
|
||||
// collectSensors собирает сводную информацию по температуре/вентиляторам/питанию и статусам chassis.
|
||||
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 ipmi := collectIPMI(ctx); len(ipmi) > 0 { res["ipmi"] = ipmi }
|
||||
if len(res) == 0 { return nil, nil }
|
||||
|
@ -22,7 +22,9 @@ import (
|
||||
// синхронизация времени и обновления) и возвращает их одним JSON-блоком.
|
||||
// Используется как основной вход в коллекторе system для Linux.
|
||||
func collectSystem(ctx context.Context) (map[string]any, error) {
|
||||
result := map[string]any{}
|
||||
result := map[string]any{
|
||||
"collector_name": "system",
|
||||
}
|
||||
|
||||
cpu, err := collectCPU(ctx)
|
||||
if err == nil { result["cpu"] = cpu }
|
||||
|
@ -25,6 +25,7 @@ func main() {
|
||||
return
|
||||
}
|
||||
out := map[string]any{
|
||||
"collector_name": "uptime",
|
||||
"seconds": secs,
|
||||
"human": humanize(time.Duration(secs) * time.Second),
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user