QNAP: munin-lite auf QNAP NAS Geräten
Im Büro steht noch ein altes QNAP-NAS mit ARM-CPU und 4x2 TB Platten. Ich benutze es für Backups. Natürlich soll es, wie alle anderen Server, mit munin
überwacht werden.
Für das NAS steht Munin nicht als Paket zur Verfügung. Stattdessen wird munin-lite
verwendet: http://sourceforge.net/projects/muninlite/
Wichtig ist, dass muninlite
nicht in /root
installiert wird, da dieses Verzeichnis bei jedem Neustart des NAS gelöscht wird. Stattdessen wird /share/MD0_DATA/.qpkg/Optware/local/munin/
genutzt. Außerdem sind einige Anpassungen nötig, damit muninlite auf dem NAS korrekt eingesetzt werden kann.
Damit das muninlite
-Script über einen TCP-Port angesprochen werden kann ist zunächst xinetd
zu installieren und zu konfigurieren:
ipkg install xinetd
Zur Konfiguration muss zunächst /etc/services
angepasst werden:
# /etc/services
# Local services
munin 4949/tcp lrrd # Munin
Anschließend erfolgt die eigentliche xinetd
-Konfiguration:
# /opt/etc/xinetd.conf
munin stream tcp nowait root /opt/local/munin/munin-node
Außerdem muss noch die Datei /opt/etc/xinetd.d/munin
erstellt werden:
# /opt/etc/xinetd.d/munin
service munin
{
socket_type = stream
protocol = tcp
wait = no
user = admin
group = administrators
#only_from = 10.42.42.25
server = /opt/local/munin/munin-node
disable = no
}
Abschließend wird xinetd
neu gestartet:
killall xinetd
/opt/sbin/xinetd
Für Debuggingzwecke kann xinetd
auch mit /opt/sbin/xinetd -d
gestartet werden.
Nun muss muninlite angepasst werden, da das NAS den Parameter -P
für df
nicht kennt. Dieser muss in allen Aufrufen von df
entfernt werden. Außerdem muss der sed
-Befehl in den for
-Schleifen ersetz werden:
for PART in $(df | grep '^/' | awk '{print $6}')
Abschließend bietet es sich noch an einige Überschriften (graph_title
) umzubenennen und einige "plugins" zu deaktiveren, damit munin ein einheitlicheres Erscheinungsbild erhält.
Nachfolgend ein Patchfile um die Änderungen automatisch anwenden zu können. Hierzu sollte der Patch zunächst mit patch -i munin-node.patch --dryrun
getestet werden. Sollte es hierbei keine Fehlermeldungen geben kann der Patch mit patch -i munin-node.patch
angewendet werden.
# munin-node.patch
*** munin-node 2013-01-21 11:03:00.000000000 +0100
--- /opt/local/munin/munin-node 2013-01-21 11:01:26.759985243 +0100
***************
*** 21,27 ****
PLUGINPATTERN=$(dirname $0)"/munin-node-plugin.d/*"
# Remove unwanted plugins from this list
! PLUGINS="df cpu if_ if_err_ load memory processes swap netstat uptime interrupts irqstats ntpdate plugindir_"
# ## LIB FUNCTIONS ##
clean_fieldname() {
echo "$@" | sed -e 's/^[^A-Za-z_]/_/' -e 's/[^A-Za-z0-9_]/_/g'
--- 21,27 ----
PLUGINPATTERN=$(dirname $0)"/munin-node-plugin.d/*"
# Remove unwanted plugins from this list
! PLUGINS="df cpu if_ if_err_ users load memory processes swap uptime ipmi_temp hdd_temp plugindir_"
# ## LIB FUNCTIONS ##
clean_fieldname() {
echo "$@" | sed -e 's/^[^A-Za-z_]/_/' -e 's/[^A-Za-z0-9_]/_/g'
***************
*** 30,43 ****
# ## PLUGINS CODE ##
config_df() {
! echo "graph_title Filesystem usage (in %)
graph_args --upper-limit 100 -l 0
graph_vlabel %
graph_category disk
graph_info This graph shows disk usage on the machine."
! for PART in $(df -P | grep '^/' | sed '/\/[a-z0-9]*$/!d;s/.* \([a-z0-9\/]\{1,\}\)$/\1/g')
do
! PINFO=$(df -P $PART | tail -1);
PNAME=$(echo $PINFO | cut -d\ -f1 | sed 's/\//_/g')
echo "$PNAME.label $PART"
echo "$PNAME.info $PNAME -> $PART"
--- 30,43 ----
# ## PLUGINS CODE ##
config_df() {
! echo "graph_title Disk usage in percent
graph_args --upper-limit 100 -l 0
graph_vlabel %
graph_category disk
graph_info This graph shows disk usage on the machine."
! for PART in $(df | grep '^/' | awk '{print $6}' | sed 1d)
do
! PINFO=$(df $PART | tail -1);
PNAME=$(echo $PINFO | cut -d\ -f1 | sed 's/\//_/g')
echo "$PNAME.label $PART"
echo "$PNAME.info $PNAME -> $PART"
***************
*** 46,54 ****
done
}
fetch_df() {
! for PART in $(df -P | grep '^/' | sed '/\/[a-z0-9]*$/!d;s/.* \([a-z0-9\/]\{1,\}\)$/\1/g')
do
! PINFO=$(df -P $PART | tail -1);
PNAME=$(echo $PINFO | cut -d\ -f1 | sed 's/[\/.-]/_/g')
echo "$PNAME.value" $(echo $PINFO | cut -f5 -d\ | sed -e 's/\%//g')
done
--- 46,54 ----
done
}
fetch_df() {
! for PART in $(df | grep '^/' | awk '{print $6}' | sed 1d)
do
! PINFO=$(df $PART | tail -1);
PNAME=$(echo $PINFO | cut -d\ -f1 | sed 's/[\/.-]/_/g')
echo "$PNAME.value" $(echo $PINFO | cut -f5 -d\ | sed -e 's/\%//g')
done
***************
*** 138,145 ****
fi
}
config_if() {
echo "graph_order down up"
! echo "graph_title $1 traffic"
echo "graph_args --base 1000"
echo "graph_vlabel bits in (-) / out (+) per \${graph_period}"
echo "graph_category network"
--- 138,153 ----
fi
}
config_if() {
+
+ if [ $1 == "eth0" ]; then
+ ethname="eth1"
+ fi
+ if [ $1 == "eth1" ]; then
+ ethname="eth2"
+ fi
+
echo "graph_order down up"
! echo "graph_title $ethname traffic"
echo "graph_args --base 1000"
echo "graph_vlabel bits in (-) / out (+) per \${graph_period}"
echo "graph_category network"
***************
*** 166,173 ****
echo "up.value" $(echo $IINFO | cut -d\ -f9)
}
config_if_err() {
echo "graph_order rcvd trans"
! echo "graph_title $1 errors"
echo "graph_args --base 1000"
echo "graph_vlabel packets in (-) / out (+) per \${graph_period}"
echo "graph_category network"
--- 174,189 ----
echo "up.value" $(echo $IINFO | cut -d\ -f9)
}
config_if_err() {
+
+ if [ $1 == "eth0" ]; then
+ ethname="eth1"
+ fi
+ if [ $1 == "eth1" ]; then
+ ethname="eth2"
+ fi
+
echo "graph_order rcvd trans"
! echo "graph_title $ethname errors"
echo "graph_args --base 1000"
echo "graph_vlabel packets in (-) / out (+) per \${graph_period}"
echo "graph_category network"
***************
*** 387,393 ****
echo "apps.value" $(($APPS * 1024))
}
config_processes() {
! echo "graph_title Number of Processes"
echo "graph_args --base 1000 -l 0 "
echo "graph_vlabel number of processes"
echo "graph_category processes"
--- 403,409 ----
echo "apps.value" $(($APPS * 1024))
}
config_processes() {
! echo "graph_title Processes"
echo "graph_args --base 1000 -l 0 "
echo "graph_vlabel number of processes"
echo "graph_category processes"
***************
*** 469,474 ****
--- 485,491 ----
config_uptime() {
echo "graph_title Uptime"
echo "graph_args --base 1000 -l 0 "
+ echo "graph_category system"
echo "graph_vlabel uptime in days"
echo "uptime.label uptime"
echo "uptime.draw AREA"
***************
*** 559,564 ****
--- 576,644 ----
echo "delay.value $DELAY"
}
+ config_users() {
+ echo "graph_title Logged in users"
+ echo "graph_args --base 1000 -l 0"
+ echo "graph_vlabel Users"
+ echo "graph_scale no"
+ echo "graph_category system"
+ echo "graph_printf %3.0lf"
+ echo "pts.label pts"
+ echo "pts.draw AREASTACK"
+ echo "pts.colour 00FFFF"
+ }
+
+ fetch_users() {
+ pts=$(ps aux | grep pts | wc | awk '{print $1-1}')
+ if [ $pts -lt 0 ]; then
+ pts=0
+ fi
+ echo "pts.value $pts"
+ }
+
+ config_ipmi_temp() {
+ echo "graph_title Machine temperature based on IPMI"
+ echo "graph_vlabel Degrees celcius"
+ echo "graph_category Sensors"
+ echo "SystemTemp.label System Temp"
+ echo "SystemTemp.critical 0:65.000"
+ echo "SystemTemp.warning 0:57.000"
+ }
+
+ fetch_ipmi_temp() {
+ cputemp=$(cat /proc/tsinfo/systemp)
+ echo "SystemTemp.value $cputemp"
+ }
+
+ config_hdd_temp() {
+ echo "graph_title Fan speeds based on IPMI"
+ echo "graph_vlabel Celsius"
+ echo "graph_category Sensors"
+
+ number_of_drives=$(find /sys/block/sd* -name "sd[a-z]" -print | wc -l)
+
+ i=1
+ while [ $i -le $number_of_drives ]
+ do
+ echo "HDD$i.label HDD Temp $i"
+ echo "HDD$i.warning 60"
+ i=`expr $i + 1`
+ done
+ }
+
+ fetch_hdd_temp() {
+
+ number_of_drives=$(find /sys/block/sd* -name "sd[a-z]" -print | wc -l)
+
+ i=1
+ while [ $i -le $number_of_drives ]
+ do
+ hddtemp=$(/sbin/get_hd_temp $i)
+ echo "HDD$i.value $hddtemp"
+ i=`expr $i + 1`
+ done
+ }
+
# ## NODE CODE ##
do_list() {
echo $PLUGINS