Всем привет! Мониторинг температуры, нагрузки и других показателей системы всегда актуален, специалисты в области ИТ применяют разные подходы для реализации этой задачи, чтобы в конечном итоге визуализировать и наблюдать динамику полученных данных в максимально удобном формате. Существует много готовых решений для построения системы мониторинга, например, Zabbix, где уже из коробки присутствует огромная библиотека шаблонов по сбору данных из различных систем и визуализация в виде графиков, которые можно отобразить без излишних настроек, хотя обычно, этого недостаточно. Также часто бывают ситуации, когда требуется получить специфические показатели, их так же нужно где-то хранить и визуализировать, я же попытаюсь показать полный путь от получения таких метрик до конечной визуализации на примере сбора информации датчиков температуры Windows в минимально возможной конфигурации, чтобы сложилась общая картина данного процесса. Так как работаю администратором Windows систем, и на протяжении нескольких лет ежедневно использую PowerShell в работе, буду применять именного его (вы так же можете найти много полезных заметок по данному языку в моем репозиториями на GitHub). Для начале получим нужные нам метрики, настроим отправку этих метрик в базу данных InfluxDB, создадим службу Windows и отобразим датчики температуры на панели в Grafana.
Метрики используются для описания производительности и стабильность системы, которые можно измерить в процессе работы программного обеспечения или оборудования в определенный момент времени. Например, если был резкий скачек температуры, за ним всегда стоит какой-то исполняемый процесс, и с помощью мониторинга других метрик или анализа логов можно выявить инициатора такого события. Для того, чтобы получить какие-либо метрики, нужно определить источник получения данных и найти подход для их извлечения. Чаще всего таким источником является само программное обеспечение, которое необходимо поставить на мониторинг, а вот для самой операционной системы и физического оборудования без использования стороннего программного обеспечения или специализированного API от производителя получить нестандартные данные, такие как показатели температуры или скорость вращения вентиляторов системы охлаждения (а также их работоспособность) может быть сложнее. Если под вашем управлением серверное оборудование, вероятно на нем уже присутствует интерфейс управления IPMI (iDrac, iLO или подобный). Например, для прямого взаимодействия с Supermicro можно использовать утилиту ipmicfg или ipmitool (есть полезная статья на Habr для настройки мониторинга сенсоров через Prometheus), еще можно обратиться к протоколу SNMP, где доступ к объектам устройств можно получить через штатные шаблоны Zabbix, или используя библиотеку .NET Lextm.SharpSnmpLib, но база mib может не всегда содержать нужные датчики и чаще данный подход применяется для сетевого оборудования и мониторинга Linux систем.
Если у вас десктопный компьютер или предыдущие варианты для вас не дают результата, то в случае с операционной системой Windows можно обратиться к системе WMI (Windows Management Instrumentation) и модели CIM, где достаточно много классов (у меня получилось выявить 4) отвечающих за показатели температуры, которые я перепробовал на разных системах, но все они не поддерживаются, исключением из представленного набора встроенных модулей PowerShell выступает Storage. На ноутбуке и нескольких физических компьютеров в рабочей сети у меня получилось собрать данные со всех дисков. Для удобства, сгруппировал вывод команды в функцию и для некоторых свойств добавил описание в комментариях:
function Get-Smart {
$PhysicalDisk = Get-CimInstance -Namespace root/Microsoft/Windows/Storage -ClassName MSFT_PhysicalDisk
$DiskSensor = $PhysicalDisk | Get-StorageReliabilityCounter
$DiskSensor | Select-Object @{Label="DiskName"; Expression={$PhysicalDisk | Where-Object DeviceId -eq $_.DeviceId | Select-Object -ExpandProperty FriendlyName}},
Temperature,
@{Label="HealthStatus"; Expression={$PhysicalDisk | Where-Object DeviceId -eq $_.DeviceId | Select-Object -ExpandProperty HealthStatus}},
@{Label="OperationalStatus"; Expression={$PhysicalDisk | Where-Object DeviceId -eq $_.DeviceId | Select-Object -ExpandProperty OperationalStatus}},
@{Label="MediaType"; Expression={$PhysicalDisk | Where-Object DeviceId -eq $_.DeviceId | Select-Object -ExpandProperty MediaType}},
@{Label="BusType"; Expression={$PhysicalDisk | Where-Object DeviceId -eq $_.DeviceId | Select-Object -ExpandProperty BusType}},
PowerOnHours, # Количество часов, в течение которых жесткий диск был во включенном состоянии
StartStopCycleCount, # Количество циклов включения и выключения жесткого диска (каждый цикл выключения и последующего включения считается за один раз)
FlushLatencyMax, # Максимальное время задержки (латентность) для операций очистки кэша на диске (сброса кеша на диск).
LoadUnloadCycleCount, # Количество циклов загрузки/выгрузки механизма парковки головок на жёстких дисках с перемещающимися головками (не относится к SSD)
ReadErrorsTotal, # Общее количество ошибок чтения данных с диска
ReadErrorsCorrected, # Количество ошибок чтения, которые были исправлены системой коррекции ошибок
ReadErrorsUncorrected, # Количество ошибок чтения, которые не удалось исправить
ReadLatencyMax, # Максимальная задержка (латентность) при чтении данных с диска
WriteErrorsTotal,
WriteErrorsCorrected,
WriteErrorsUncorrected,
WriteLatencyMax
}
Хотя вывод менее детальный, если сравнивать с CrystalDiskInfo, тем не менее температуру всех дисков собрать получится. Например так:
$(Get-Smart).DiskName
MSI M390 250GB
ST1000DM003-1CH162
WDC WD2005FBYZ-01YCBB2
$(Get-Smart).Temperature
39
35
37
Мы заключаем функцию в скобки и забираем только одно конкретное свойство, которое может представлять массив данных. Первая команда вернет нам массив из имен дисков (в моем случае их три), вторая команда значение текущей температуры в соответствующем порядке. Результат второй команды и есть наши метрики, т.е. измеряемые единицы данных.
Многие знакомы с такими программами, как AIDA64, Everest, HWiNFO и OpenHardwareMonitor, все они в большей степени предоставляют нужную нам информацию. Так как достаточно давно для домашнего применения использую последний инструмент из перечисленных, который к тому же имеет открытый исходный код, мой выбор источника данных пал именно на него. Из очевидных минусов, данный инструмент не поддерживается с 2020 года, в связи с этим, средствами сообщества был создан fork под названием LibreHardwareMonitor, где по мимо поддержки актуального оборудования, расширили функционал, например появилась настройка базовой авторизации для встроенного Web-сервера. Сам же инструмент для локального мониторинга максимально удобен, т.к. есть возможность настроить вывод выбранных датчиков в трей, гаджет или на график.
Если мы используем программу LibreHardwareMonitor или его предшественника, у нас открывается доступ к новому пространству имен системы WMI:
Get-CimInstance -Namespace "root/LibreHardwareMonitor" -ClassName Hardware
или так:
Get-CimInstance -Namespace "root/OpenHardwareMonitor" -ClassName Hardware
Когда у нас запущено программное обеспечение, мы имеем доступ к датчикам, нужно только сгруппировать получаемые данные и пересобрать коллекцию данных. Я же для удобства подготовил модуль (который опубликован на GitHub, работает в версии 5.1, но все равно рекомендую использовать кроссплатформенную версию PowerShell Core), вам достаточно только его установить (предварительно у вас должен быть зарегистрирован менеджер пакетов Nuget):
Register-PSRepository -Name "NuGet" -SourceLocation "https://www.nuget.org/api/v2" -InstallationPolicy Trusted
Install-Module -Name HardwareMonitor -Scope AllUsers
Важно, при установке используется параметр
Scoop
, что бы модуль был доступен всем пользователям в системе, это нужно для того, что бы не указывать путь к модулю для его импорта на моменте создании службы в дальнейшем (попросту меньше действий, хотя не обязательно этого делать).
Теперь можно проверить вывод с помощью одной команды без параметров: Get-Sensor | Format-Table
По итогу мы получаем объект данных без вложений, это особенно наглядно, если использовать вывод в формате таблицы (Format-Table), где можно без труда обратиться к любому сенсору по его имени или отфильтровать по типу. Например с помощью команды Get-Sensor | Where-Object SensorType -match "Temperature" | Format-Table
мы получим данные все сенсоров, который отвечают только за температуру в системе (что бы получить данные из программы OpenHardwareMontior, добавьте к команде Get-Sensor
параметр -Open
).
Если вы обратите внимание на скриншотах выше, в моей консоли, а также на удаленной машине через ssh уже отображаются показатели температуры и загрузки процессора с правом углу экрана, это реализовано с помощью того же инструмента LibreHardwareMonitor. Если вы часто проводите время в консоли, то возможно уже знакомы с инструментом oh-my-posh, он позволяет настроить различные темы для отображения вспомогательной информации, которые обновляются в момент выполнения последней команды. Тему System-Sensors, и пару других вы можете найти на GitHub, для удобства установка темы автоматизирована с помощью модуля:
Set-ExecutionPolicy Bypass -Scope Process -Force; Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://ohmyposh.dev/install.ps1')) # установит сам пакет oh-my-posh (если он еще не установлен)
Install-Module themes-performance -Repository NuGet # установка модуля для настройки темы
Set-PoshTheme -Theme System-Sensors # изменить тему
Есть и другой способ получения информации, так как PowerShell построен на базе .NET, мы можем напрямую импортировать динамическую библиотеку LibreHardwareMonitorLib.dll, которая идёт в составе файлов программы. Данный функционал реализован в модуле с указанием соответствующего параметра Get-Sensor -Library
, но по непонятной мне причине, датчики отображаются только для CPU и некоторых дисках на разных системах, показатели остальных датчиков не загружаются (использовал с повышенными привилегиями, перепробовав все методы сценария), по этому вопросу завел обращение в репозитории GitHub разработчиков, пока что ответа не получил. Нужно учитывать, что в случае с WMI мы получаем данные из работающего экземпляра приложения, а для получения данных из библиотеки понадобится больше времени, у меня в среднем выходило 200мс против 1 секунды (время выполнения команд в консоли удобно фиксировать с помощью упомянутого выше oh-my-posh).
Так как у данного приложения присутствует встроенный веб-сервер, мы можем обращаться к нему с помощью REST-запросов через API. Это нам дает преимущество в том, что при запуске данного приложения, оно может выступать в роле агента сбора метрик, таким образом можем собирать данные на одном компьютере (назовем его сервером-сборщиком) сразу с нескольких хостов. Автоматизировать процесс установки и запуска такого решения на нескольких хостах достаточно просто, вы можете использовать привычную вам систему управления конфигурациями (DSC, Ansible и подобные), или например, если вы являетесь администратором доменной сети и у вас уже настроены групповые политики подключения к компьютерам через WinRM, я бы реализовал данный процесс таким образом:
$ServerList = (
"server-01",
"server-02"
)
foreach ($Server in $ServerList) {
Invoke-Command $Server -ScriptBlock {
# Устанавливаем LibreHardwareMonitor
$path = "$home\Documents\LibreHardwareMonitor"
$zip = "$($path).zip"
$url = "https://api.github.com/repos/LibreHardwareMonitor/LibreHardwareMonitor/releases/latest"
$url_down = $(Invoke-RestMethod $url).assets.browser_download_url
Invoke-RestMethod $url_down -OutFile $zip
Expand-Archive -Path $zip -DestinationPath $path
Remove-Item -Path $zip
# Включаем Web сервер в конфигурации
$Config = Get-Content "$path\LibreHardwareMonitor.config"
$Config = $Config -replace 'key="runWebServerMenuItem" value="false"','key="runWebServerMenuItem" value="true"'
# Меняем порт при необходимости
# $Config = $Config -replace 'key="listenerPort" value="8085"','key="listenerPort" value="8086"'
$Config = $Config -replace 'key="minTrayMenuItem" value="false"','key="minTrayMenuItem" value="true"'
$Config = $Config -replace 'key="minCloseMenuItem" value="false"','key="minCloseMenuItem" value="true"'
$Config | Out-File "$path\LibreHardwareMonitor.config"
# Запускаем LibreHardwareMonitor
Start-Process "$path\LibreHardwareMonitor.exe" -WindowStyle Hidden
}
}
Вначале мы подключаемся поочередно к каждой машине, выгружаем LibreHardwareMonitor последней версии из репозитория GitHub, распаковываем, настраиваем файл конфигурации (включаем web-сервер, меняем порт при необходимости) и запускаем приложение. Для того, чтобы обратиться к удаленной машине и собрать с нее датчики, используйте такой синтаксис:
Get-Sensor -ComputerName 192.168.3.100 | Format-Table
Если настроена авторизация, то заполняем параметры имени пользователя и пароль:
Get-Sensor -ComputerName 192.168.3.100 -User hardware -Password monitor | Format-Table
Такие данные будут идентичны по формату тем, что мы получаем локально, а время ответа еще и в несколько раз быстрее (в пределах одной локальной сети до 50мс).
Теперь необходимо подготовить среду хранения получаемых данных. Для метрик больше всего подходят базы данных временных рядов, так как требуют меньше ресурсов и в перспективе работают сравнительно с реляционными быстрее. Для установки InfluxDB (портативной версии) в Windows достаточно выполнить несколько команд в консоли:
Invoke-RestMethod "https://dl.influxdata.com/influxdb/releases/influxdb-1.8.10_windows_amd64.zip" -OutFile "$home\Downloads\influxdb-1.8.10_windows_amd64.zip" # загружаем архив с программой
Expand-Archive "$home\Downloads\influxdb-1.8.10_windows_amd64.zip" -DestinationPath "$home\Documents\" # распаковываем содержимое
Remove-Item "$home\Downloads\influxdb-1.8.10_windows_amd64.zip" # удаляем архив
& "$home\Downloads\influxdb-1.8.10-1\influxd.exe" # запускаем процесс серверной части
Процесс установки на примере Ubuntu:
wget https://dl.influxdata.com/influxdb/releases/influxdb_1.8.10_amd64.deb
sudo dpkg -i influxdb_1.8.10_amd64.deb
systemctl start influxdb
systemctl status influxdb
Или можете воспользоваться Docker, примеры сборки можно найти на официальном сайте Docker Hub. Теперь нам нужно подключиться к InfluxDB и создать базу данных, в которую мы будем отправлять метрики. Это можно сделать несколькими способами, для версии 1.x проще всего использовать InfluxDB Studio (скачать которую вы можете здесь), по умолчанию вам нужно указать только IP-адрес или имя компьютера, где у вас запущен процесс сервера InfluxDB (порт по умолчанию 8086):
Для этих целей вы так же можете использовать REST API, например, используя PowerShell модуль psinfluxdb:
Install-Module psinfluxdb -Repository NuGet # устанавливаем модуль
Import-Module psinfluxdb
Get-Command -Module psinfluxdb # получить список доступных команд
Get-InfluxDatabases -server 192.168.3.102 # отобразить список текущих баз данных на сервере
Get-InfluxDatabases -server 192.168.3.102 -creat -database powershell # создаем базу данных
Модули psinfluxdb как и HardwareMonitor поддерживают авторизацию InfluxDB в соответствующих параметрах, но для примера намеренно не используется, так как хотелось показать минимум действий в процессе настройки. Создать пользователя можно в InfluxDB Studio, а включить авторизацию в конфигурационном файле
influxdb.conf
(в блоке[http]
, изменить параметрauth-enabled = true
).
Функция Send-SensorToInfluxDB
, отвечающая за отправку данных уже присутствует в составе модуля, нужно определить с тем, каким способом из представленных выше (локально или с удаленных хостов) и какие данные (предварительно отфильтрованные) мы хотим передавать. Для отправки датчиков, которые отвечают только за температуру с локального хоста, можно использовать такой подход:
# Inport-Module "C:\Users\lifailon\Documents\PowerShell\Modules\HardwareMonitor"
while ($True) {
$Data = Get-Sensor | Where-Object {($_.SensorName -match "Temperature") -or ($_.SensorType -match "Temperature")}
Send-SensorToInfluxDB -Data $Data -ServerInflux "192.168.3.102" -Port 8086 -Database "PowerShell" -Table "HardwareMonitor"
Start-Sleep -Seconds 5
}
Первая закомментированная строка отвечает за импорт модуля, ее необходимо использовать, если модуль был установлен только для текущего пользователя (-Scope CurrentUser
, параметр по умолчанию). Проверить путь к установленному модулю в вашей системе вы можете командой: $(Get-Module HardwareMonitor).Path
. В переменную $Data
мы помещаем содержимое из примеров выше (фильтрация на ваше усмотрение, ее можно не использовать и передавать все датчики) и передаем ее содержимое в функцию для отправки, где заполняем параметры адреса сервера InfluxDB и имя базы данных, которое было указано при создании (последний параметр таблицы, или правильнее измерения, создается автоматически, можете задать любое имя). Указываем паузу в 5 секунд (можно сократить до 1-2 секунд), и оборачиваем содержимое в бесконечный цикл while
. Процесс отправки данных с удаленных машин идентичен, только если машин будет несколько, необходимо создать массив из списка хостов (все ip-адреса или имена добавляются с новой строки) и передать его в цикл foreach
:
# Inport-Module "C:\Users\lifailon\Documents\PowerShell\Modules\HardwareMonitor"
while ($True) {
$Server_List = @(
"192.168.3.99"
"192.168.3.100"
)
foreach ($Server in $Server_List) {
$Data = Get-Sensor -ComputerName $Server -Port 8085 | Where-Object {($_.SensorName -match "Temperature") -or ($_.SensorType -match "Temperature")}
Send-SensorToInfluxDB -ComputerName $Server -Data $Data -ServerInflux "192.168.3.102" -Port 8086 -Database "PowerShell" -Table "HardwareMonitor"
Start-Sleep -Seconds 5
}
}
После чего, мы должны убедиться, что данные поступают в базу данных, это можно сделать запросом (New Query
) по умолчанию для таблицы HardwareMonitor
в InfluxDB Studio:
SELECT * FROM "HardwareMonitor" WHERE time > now() - 5m
Мы так же можем отфильтровать вывод по имени нужного сенсора и его значению, например так:
SELECT * FROM "HardwareMonitor" WHERE SensorName =~/.+Package/ and Value > 90 and time > now() - 30m
Информацию по фильтрации, группировки, использование функций и других манипуляций с данными используя SQL-запросы можно найти в официальной документация, так же на эту тему я оставлял заметки с примерами тут. Еще можно воспользоваться модулем psinfluxdb, с помощью которого удобно выгружать данные из базы данных в объектном представлении и работать с ними на прямую, используя привычные командлеты PowerShell:
Get-InfluxTables -server 192.168.3.102 -database PowerShell # смотрим список таблиц в указанной базе данных
$Data = Get-InfluxData -server 192.168.3.102 -database PowerShell -table HardwareMonitor -minutes 30 # забираем данные за последние 30 минут
$Data | Where-Object {$_.SensorName -match "Package" -and $_.Value -gt 90} | ft # фильтруем данные
Когда мы убедились, что данные получены, процесс отправки данных стоит автоматизировать, а для Windows таким процессом проще всего управлять с помощью службы. Сохраните полученный скрипт в файл Write-SensorToInfluxDB.ps1
, например в директории Documents
текущего пользователя и создайте службу, использую следующий скрипт (для создания понадобится запустить консоль с правами администратора):
$script_path = "$home\Documents\Write-SensorToInfluxDB.ps1" # указываем свой путь к скрипту
# Устанавливаем пакет NSSM с официального сайт в директорию Temp (отвечает за созданием и управление службой)
$nssm_path = $(Get-ChildItem $env:TEMP | Where-Object Name -match "nssm")
if ($null -eq $nssm_path) {
Invoke-RestMethod -Uri "https://nssm.cc/release/nssm-2.24.zip" -OutFile "$env:TEMP\nssm.zip"
Expand-Archive -Path "$env:TEMP\nssm.zip" -DestinationPath $env:TEMP
Remove-Item -Path "$env:TEMP\nssm.zip"
}
# Если служба уже существует, останавливаем и удаляем ее (cmdlet из состава PowerShell Core)
if (Get-Service HardwareMonitor -ErrorAction Ignore) {
Get-Service HardwareMonitor | Stop-Service
Remove-Service HardwareMonitor
# Для удаления в PowerShell 5.1:
# (Get-CimInstance win32_service -Filter 'name="HardwareMonitor"').Delete()
# & $nssm_exe_path remove HardwareMonitor
}
# Создаем службу
$service_name = "HardwareMonitor"
$nssm_exe_path = $(Get-ChildItem $env:TEMP -Recurse | Where-Object name -match nssm.exe | Where-Object FullName -Match win64).FullName
$pwsh_path = $(Get-Command pwsh.exe).Source
& $nssm_exe_path install $service_name $pwsh_path "-File $script_path"
& $nssm_exe_path set $Service_Name description "Sending HardwareMonitor sensors to the InfluxDB"
& $nssm_exe_path set $service_name AppExit Default Restart
# Запускаем службу и проверяем статус
Get-Service HardwareMonitor | Start-Service
Get-Service HardwareMonitor
Остался последний шаг, визуализация. Для установки Grafana потребуется VPN. Если вы не используете Docker (для доступа к Docker Hub я обычно использую Windows машину, где запущен бесплатный VPN клиент в режиме split-tunneling и получаю к нему доступ через Proxy-сервер), достаточно включать в браузере плагин Browsec (или любой другой привычный VPN-клиент) и скачать msi-пакет (если используете Windows) или deb-пакет (в примере установки на Ubuntu), после чего передать его на нужную машину (например, через WinSCP), установить одной командой и запустить службу:
dpkg -i grafana-enterprise_10.3.1_amd64.deb
systemctl start grafana-server
systemctl status grafana-server
Входим в Web-интерфейс под стандартным admin:admin, устанавливаем свой пароль и настраиваем подключение к к базе данных InfluxDB:
Теперь остаётся только создать и настроить Dashboard. Указываем Data source: InfluxDB-Database
и заполняем параметры фильтрации вручную, используя выпадающий список (по аналогии из примера с InfluxDB Studio и psinfluxdb) или используя группировку по тэгам. На мой взгляд интерфейс интуитивно понятен, пройдя путь до этого, вы уже будите ориентироваться в параметрах.
В примере, из таблицы HardwareMonitor
фильтруем (WHERE
) содержимое тэга-столбца SensorName
используя ключевое слово Package
, выборка (SELECT
) по умолчанию, т.к. значение с данными Value
у нас только одно. Используя группировку по тэгам Host
и HardwareName
, вместо одного значения, мы получим значения для каждого уникального сочетания наименований в указанных столбцах, тем самым не придется в ручную создавать новые запросы и фильтровать все виды оборудования для каждого хоста отдельно, оперируя только фильтрацией по наименованию сенсоров. Для наглядности отображения в ALIAS
указываем переменные $tag_Host
и $tag_HardwareName
, которые соответствуют уникальным значениям сгруппированных тегов. В интерфейсе справа, можно настроить вспомогательные параметры для отображения данных. Из ключевого, стоит сразу задать тип данных (Unit - Temperature - Celsius
) и добавитьLegend
, с помощью которых силами Grafana будут выводиться максимальные, минимальные, средние и другие значения всех отображаемых показателей за выбранный в текущий момент отрезок времени.
Вот так выглядит мой простой dashboard для двух хостов с отрезком времени за последние 6 часов:
В моей среде данный процесс стабильно работает на протяжении двух месяцев, рекомендую разве что позаботиться о ротации данных с помощью политик хранения, например так:
Get-InfluxPolicy -server 192.168.3.102 -database PowerShell # отобразить список политик
Get-InfluxPolicy -server 192.168.3.102 -database PowerShell -creat -policyName del2d -hours 48 # создаем новую политику хранения на 48 часов
Get-InfluxPolicy -server 192.168.3.102 -database PowerShell -policyName del2d -default # применяем созданную политику к БД по умолчанию
Конечно, данный подход не решен недостатков, было бы интереснее, если данные можно было считывать напрямую из библиотеки .NET, в таком случае не придется держать активным само приложение (возможно, еще получится найти решение), хотя по моим наблюдениям, потребление ресурсов системы LibreHardwareMonitor не превышает Zabbix-агента. Используя представленный модуль, возможно настроить мониторинг через активного Zabbix-агента, за пример можно взять мониторинг количество активных пользовательских сессий на RDSH-фермах через модуль Get-Query (через query.exe), или напишите в комментариях, могу подготовить конфигурацию и шаблон. Похожий процесс сбора метрик можно настроить из IPMItool, CrystalDiskMark (уже есть готовый шаблон для получению актуальных данных через консоль в формате PSObject, который фактически можно использовать как cli-версию) или другого программного обеспечения, главное найти к нему подход для извлечения данных.