Настраиваем мониторинг на Proxmox с выводом в Talk

Опубликовано jeord -

🚀 Часть 1: Подготовка системы

1.1 Установка необходимых пакетов

apt update && apt install -y lm-sensors smartmontools hddtemp curl

sensors-detect --auto

1.2 Создание бота в Nextcloud Talk

На сервере Nextcloud (через SSH) выполнить:

sudo -u www-data php occ talk:bot:create -s "<СЕКРЕТНЫЙ_КЛЮЧ>" "<ИМЯ_БОТА>"

пример:
sudo -u www-data php occ talk:bot:create -s "7DcTYCuILzgKmqyXJ7JC6FCADAkIdbPDR2K0oCZF1" "ProxmoxMonitor"

Полученные данные сохранить:

  • ID бота (выдаст после создания, например 1)
  • Секретный ключ (который указали с флагом -s)
  • Токен комнаты (из URL чата: https://cloud.example.com/index.php/call/ТОКЕН)

1.3 Добавление бота в комнату

В веб-интерфейсе Nextcloud Talk:

  1. Зайти в нужную комнату
  2. Нажать шестерёнку (настройки)
  3. Выбрать "Боты" → "Выбрать созданного бота" - "Включить"

🐍 Часть 2: Скрипт отправки сообщений

Создать файл /usr/local/bin/talk_send.sh:

#!/bin/bash

# ===== НАСТРОЙКИ (ИЗМЕНИТЬ ПОД СЕБЯ) =====
NC_URL="https://cloud.example.com"
ROOM_TOKEN="токен комнаты "
BOT_SECRET="7DcTYCuILzgKmqyXJ7JC6FCADAkIdbPDR2K0oCZF1"
# =========================================

MESSAGE="$1"

if [ -z "$MESSAGE" ]; then
   echo "Usage: $0 <message>"
   exit 1
fi

RANDOM_HEADER=$(openssl rand -hex 32)
SIGNATURE_DATA="${RANDOM_HEADER}${MESSAGE}"
SIGNATURE=$(echo -n "$SIGNATURE_DATA" | openssl dgst -sha256 -hmac "$BOT_SECRET" | awk '{print $2}')

curl -s -X POST "${NC_URL}/ocs/v2.php/apps/spreed/api/v1/bot/${ROOM_TOKEN}/message" \
 -H "Content-Type: application/json" \
 -H "Accept: application/json" \
 -H "OCS-APIRequest: true" \
 -H "X-Nextcloud-Talk-Bot-Random: ${RANDOM_HEADER}" \
 -H "X-Nextcloud-Talk-Bot-Signature: ${SIGNATURE}" \
 -d "{\"message\":\"${MESSAGE}\"}"

echo ""

Сделать исполняемым:

chmod +x /usr/local/bin/talk_send.sh

Тестирование отправки

/usr/local/bin/talk_send.sh "Test message"

🌡️ Часть 3: Мониторинг температур

Создать файл /usr/local/bin/temp_monitor.sh:

#!/bin/bash

TALK_SEND="/usr/local/bin/talk_send.sh"

CPU_MAX=70
DISK_MAX=50

STATE_FILE="/var/tmp/temp_monitor.state"

send_alert() {
   local alert_text="$1"
   local timestamp=$(date +%s)
   local hash=$(echo "$alert_text" | md5sum | cut -d' ' -f1)
   
   if [ -f "$STATE_FILE" ]; then
       last_sent=$(grep "^${hash}=" "$STATE_FILE" | cut -d= -f2)
       if [ -n "$last_sent" ] && [ $((timestamp - last_sent)) -lt 1800 ]; then
           return 0
       fi
   fi
   
   $TALK_SEND "$alert_text"
   
   grep -v "^${hash}=" "$STATE_FILE" > "${STATE_FILE}.tmp" 2>/dev/null
   echo "${hash}=${timestamp}" >> "${STATE_FILE}.tmp"
   mv "${STATE_FILE}.tmp" "$STATE_FILE"
   logger -t temp_monitor "$alert_text"
}

touch "$STATE_FILE"

# --- CPU ---
if command -v sensors >/dev/null 2>&1; then
   CPU_TEMP=$(sensors 2>/dev/null | grep -i "Package id" | head -1 | awk '{print $4}' | sed 's/[^0-9.]//g' | cut -d. -f1)
   [ -z "$CPU_TEMP" ] && CPU_TEMP=$(sensors 2>/dev/null | grep -i "Core 0" | head -1 | awk '{print $3}' | sed 's/[^0-9.]//g' | cut -d. -f1)
   [ -z "$CPU_TEMP" ] && CPU_TEMP=$(sensors 2>/dev/null | grep -i "CPU" | head -1 | awk '{print $3}' | sed 's/[^0-9.]//g' | cut -d. -f1)
fi

if [ -n "$CPU_TEMP" ] && [ "$CPU_TEMP" -gt "$CPU_MAX" ] 2>/dev/null; then
   send_alert "🔥 CPU = ${CPU_TEMP}°C"
fi

# --- DISKS ---
if command -v smartctl >/dev/null 2>&1; then
   for disk in /dev/sd? /dev/nvme?; do
       [ -e "$disk" ] || continue
       
       MODEL=$(smartctl -i "$disk" 2>/dev/null | grep -i "Device Model\|Model Number" | cut -d: -f2 | sed 's/^[ \t]*//' | head -1)
       SERIAL=$(smartctl -i "$disk" 2>/dev/null | grep -i "Serial Number" | cut -d: -f2 | sed 's/^[ \t]*//' | head -1)
       
       if [[ "$disk" == /dev/nvme* ]]; then
           DISK_TEMP=$(smartctl -A "$disk" 2>/dev/null | grep -i "Temperature:" | awk '{print $2}' | sed 's/[^0-9]//g')
           [ -z "$DISK_TEMP" ] && DISK_TEMP=$(smartctl -A "$disk" 2>/dev/null | grep -i "Composite Temperature" | awk '{print $4}' | sed 's/[^0-9]//g')
       else
           DISK_TEMP=$(smartctl -A "$disk" 2>/dev/null | grep -i "Temperature_Celsius" | awk '{print $10}')
           [ -z "$DISK_TEMP" ] && DISK_TEMP=$(smartctl -A "$disk" 2>/dev/null | grep -i "Airflow_Temperature" | awk '{print $10}')
       fi
       
       [ -z "$MODEL" ] && MODEL=$(basename "$disk")
       
       if [ -n "$DISK_TEMP" ] && [ "$DISK_TEMP" -gt "$DISK_MAX" ] 2>/dev/null; then
           if [ -n "$SERIAL" ]; then
               send_alert "🔥 HDD ${MODEL} (SN: ${SERIAL}) = ${DISK_TEMP}°C"
           else
               send_alert "🔥 HDD ${MODEL} = ${DISK_TEMP}°C"
           fi
       fi
   done
fi

exit 0
 

Сделать исполняемым.

Тестирование (временно снизить порог)

В скрипте временно изменить: CPU_MAX=20, затем выполнить:

/usr/local/bin/temp_monitor.sh

Должно прийти сообщение 🔥 CPU = 37°C (или ваше значение). После теста вернуть порог обратно на 70.

🔄 Часть 4: Мониторинг статуса VM (Start/Stop)

4.1 Создание универсального хука

Создать файл /var/lib/vz/snippets/vm_universal_notify.sh:

#!/bin/bash

TALK_SEND="/usr/local/bin/talk_send.sh"

if [ -z "$1" ]; then
   exit 0
fi

VMID=$1
PHASE=${2:-"manual"}

VMNAME=$(qm config $VMID 2>/dev/null | grep ^name | awk '{print $2}')
[ -z "$VMNAME" ] && VMNAME="unknown"

send_notification() {
   local msg="$1"
   [ -x "$TALK_SEND" ] && $TALK_SEND "$msg"
}

case "$PHASE" in
   post-start)
       send_notification "✅ VM $VMID ($VMNAME) started"
       ;;
   pre-stop)
       send_notification "🛑 VM $VMID ($VMNAME) is stopping"
       ;;
   post-stop)
       send_notification "⏹️ VM $VMID ($VMNAME) stopped"
       ;;
esac

exit 0

Сделать исполняемым:

chmod +x /var/lib/vz/snippets/vm_universal_notify.sh

4.2 Назначение хука для существующих VM

qm set 101 --hookscript local:snippets/vm_universal_notify.sh
qm set 102 --hookscript local:snippets/vm_universal_notify.sh

Заменить 101, 102 на свои VMID (узнать через qm list).

4.3 (Опционально) Автоматическое назначение хука новым VM

Добавить в crontab:

* * * * * for vmid in $(qm list | awk 'NR>1 {print $1}'); do qm config $vmid | grep -q "hookscript: local:snippets/vm_universal_notify.sh" || qm set $vmid --hookscript local:snippets/vm_universal_notify.sh; done

4.4 Тестирование

qm stop 101 && qm start 101

Проверить чат — должны прийти сообщения о остановке и запуске.

💾 Часть 5: Мониторинг ZFS RAID

Создать файл /usr/local/bin/check_zfs.sh:

#!/bin/bash

TALK_SEND="/usr/local/bin/talk_send.sh"

POOL_STATUS=$(zpool status -x)

if [[ "$POOL_STATUS" != *"all pools are healthy"* ]]; then
   $TALK_SEND "⚠️ ZFS Alert: $POOL_STATUS"
fi

Тестирование (имитация ошибки)

# Временно изменить скрипт, добавив в начало: POOL_STATUS="degraded"
/usr/local/bin/check_zfs.sh

⏰ Часть 6: Настройка cron (автоматический запуск)

crontab -e

# Температуры каждые 5 минут
*/5 * * * * /usr/local/bin/temp_monitor.sh

# ZFS каждый час
0 * * * * /usr/local/bin/check_zfs.sh

# (Опционально) Автоматическое назначение хука новым VM
* * * * * for vmid in $(qm list | awk 'NR>1 {print $1}'); do qm config $vmid | grep -q "hookscript: local:snippets/vm_universal_notify.sh" || qm set $vmid --hookscript local:snippets/vm_universal_notify.sh; done

🔧 Часть 7: Возможные проблемы и решения

Проблема Решение
talk_send.sh не отправляет Проверить URL, токен комнаты, секрет. Выполнить вручную curl к API
Температура CPU не определяется Выполнить sensors, скорректировать ключевые слова в скрипте
Хук не срабатывает Проверить права на файл (chmod +x), путь local:snippets/
ZFS не мониторится Проверить наличие пулов (zpool list)
Сообщения дублируются Защита от дублей уже встроена (30 минут)