Server Statistik Skript als Debian Paket erstellen/installieren
Mit dem Skript serverstats.sh
haben wir uns ein Skript geschrieben, was als SystemV Service läuft und Statistiken des Host in einem Logfile schreibt. Das Skript verwendet /etc/default/serverstats
als Konfiguration. Dabei werden vom Skript definierte Default Werte überschrieben. Außerdem ist für das Logfile eine Logfile Rotation unter /etc/logrotate.d/serverstats
eingerichtet. Das Skript selbst ist unter /usr/local/share/serverstats/serverstats.sh
installiert.
Aus diesen Skript und Konfigurationen werden wir uns nun ein Debian Paket erstellen, und es als root installieren.
Struktur erstellen
Die Debian Pakete werden als Benutzer gebaut, nur das installieren wird mir root ausgeführt. Wir erstellen uns als Benutzer in unseren Homeverzeichnis das Verzeichnis ~/build
. Darin erstellen wird ein Verzeichnis, wie später das Debian Package heißen soll. In unserem Fall ist das serverstats
.
tux@earth:~$ mkdir -p ~/build/serverstats && cd ~/build/serverstats
Dann gehen wir in das erstellte Verzeichnis und erstellen uns die Verzeichnis-Struktur, wie sie später im Dateisystem vorhanden sein soll.
tux@earth:~/build/serverstats$ mkdir -p DEBIAN
tux@earth:~/build/serverstats$ mkdir -p etc/{default,logrotate.d}
tux@earth:~/build/serverstats$ mkdir -p lib/systemd/system
tux@earth:~/build/serverstats$ mkdir -p usr/local/share/serverstats
Steuerungsdatei erstellen
Nun erstellen wir uns eine Steuerungsdatei (control) im DEBIAN
Verzeichnis. In dieser Datei werden wir die wichtigsten Informationen zu unserem Paket angeben.
tux@earth:~/build/serverstats$ vi DEBIAN/control
Package: serverstats
Version: 0.0.1-0
Section: misc
Priority: optional
Architecture: all
Depends: awk,perl,grep,iproute2,bash,hostname,sed
Installed-Size: 9.7
Maintainer: Marko Schulz <info@tuxnet24.de>
Homepage: https://deb.tuxnet24.de/
Description: A shell script that runs as a SystemV
service and collects statistics from the current host
and stores them in a defined log file.
.
The default configuration can be overwritten in the file
/etc/default/serverstats.
Wie wir sehen besteht die Datei aus einer Auflistung von Feldnamen gefolgt von einem Wert oder Text dafür. In der folgenden Tabelle schauen wir uns einmal die verwendeten Felder etwas genauer an:
Feldname | Beschreibung |
Package | Der Name den das Paket haben soll. Er sollte möglichst noch nicht in Verwendung sein, da man über ihn auch das Paket im Paketmanager installieren kann. Weiters darf er auch keine Whitespaces (Leerzeichen etc.) oder Sonderzeichen enthalten. |
Version | Die Version des Pakets. Über die Version kann der Paketmanager auch erkennen ob eine eventuell schon installierte Version des Programmes neuer ist als die im Paket. |
Section | Gibt die Sektion an, zu der das Paket gehört. Zuerst geben wir die Kategorie 'main', 'contrib' oder 'non-free' gefolgt von einem Slash ('/') an. Wenn das Paket in die Kategorie 'main', dh. mit einer Open Source Lizenz lizenziert ist, dann können wir die Kategorie auch weglassen, da dann automatisch 'main' angenommen wird. Anschließen müssen wir noch die Sektion angeben, bei der es sehr viele Möglichkeiten gibt. Einige Beispiele sind 'admin', 'devel' (development), 'doc', 'editors', 'games', 'graphics', 'math', 'misc', 'science' oder 'utils'. Es gibt noch ein paar weitere, die wir auf der Debian Homepage finden. |
Priority | Hier geben wir an wie wichtig unser Paket für ein System bzw. seinen Anwender ist. Normalerweise werden wir hier 'extra' angeben. |
Architecture | Gibt die Prozessorarchitektur an, auf denen dieses Programm/Skript lauffähig ist. Mögliche Werte sind 'i386' für die meisten Heimcomputer die mit 32 Bit laufen, 'amd64' für 64-Bit Heimcomputer und 'all' für alle Computer (zb. für viele Skripte). Es gibt zwar auch noch andere Möglichkeiten, die aber nicht so relevant sind. |
Depends | Abhängigkeiten, dh. Programme bzw. eigentlich Pakete die unser Programm/Skript zur Ausführung unbedingt braucht können wir hier angeben. Wir brauchen nur die Paketnamen getrennt durch Komma und Leerzeichen angeben, also zum Beispiel: Depends: cmake, libsdl1.2, libsdl-image1.2 |
Installed-Size | Wie der Name schon sagt geben wir hier die Größe unseres installierten Paketes an. Die Angabe erfolgt in KB. |
Maintainer | Hier geben wir den bzw. die Namen der Programmierer/Ersteller des Pakets/Programms an. Meistens wird hier auch eine E-Mail-Adresse angegeben. |
Homepage | Dieser Punkt sollte wohl selbsterklärend sein. Am Besten sollte man unter dieser Adresse auch den Quellcode des Pakets erhalten. |
Description | In der gleichen Zeile wie "Description" eine kurze Beschreibung des Pakets und anschließend in der nächsten Zeile eingerückt die ausführliche Beschreibung. |
Benötigten Dateien/Skripte und Konfigurationen kopieren
Als erstes erstellen wir die Konfiguration für Logrotate.
tux@earth:~/build/serverstats$ vi etc/logrotat.d/serverstats
/var/log/serverstats.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 640 root adm
}
Dann kommt die Konfigurationsdatei.
tux@earth:~/build/serverstats$ vi etc/default/serverstats
#
# This is the configuration file for the Serverstats service
# (/lib/systemd/system/serverstats.service)
#
# Interval in seconds in which the statistics are written/retrieved.
# You can also use 1m => 60 seconds or 1h => 3600 seconds or 1d => 86400 seconds.
INTERVAL="5m"
# Path to the output logfile of the statistics.
# Important!!! If you change the path here, you have to change
# the path also in /etc/logrotate.d/serverstats.
OUTPUTFILE="/var/log/serverstats.log"
# Here you can define the logformat. Each %<name> is also a stat_<name> function.
# You can add parameters to some functions, like %<function>_<arg1>_<arg2>.
LOGFORMAT="%timestamp %hostname %loadavg %memoryusage %memorycached %swapusage %membuffer %cpuusage %diskspace %traffic"
# Unit for the interface traffic. You can define mb (megabytes) or kb (kilobytes).
# Default is bytes per seconds bytes/s.
UNIT="kb"
# vim: syntax=sh ts=4 sw=4 sts=4 sr noet
Es folgt die Service Unit Datei.
tux@earth:~/build/serverstats$ vi lib/systemd/system/serverstats.service
[Unit]
Description=collect memory/cpu statistics of the current server maschine
[Service]
Type=oneshot
ExecStart=/bin/sh -c "/usr/local/share/serverstats/serverstats.sh &"
ExecStop=/bin/sh -c "kill $(ps -fade | grep serverstats.sh | grep -v grep | awk '{print $2}')"
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Als letztes erstellen wir noch unser Skript.
tux@earth:~/build/serverstats$ vi usr/local/share/serverstats/serverstats.sh
#!/bin/bash
# **********************************************************************
# $File: serverstats.sh $
# $Author: mschulz $ - $Date: 2023-03-09 12:26:09 +0100 (Mi, 09 März 2023) $
# $Description: Script to collect memory/cpu statistics of the current server maschine. $
# **********************************************************************
# Define the idle time for this service. You can define 1d => 24h or 86400 seconds.
interval="1m"
# The path to the output logfile, where the statistics metrics will be storend
outfile="/var/log/serverstats.log"
# The default logformat. Each %name is a stat_<name> function.
# You can add parameters to some functions, like %<function>_<arg1>_<arg2>.
logformat="%timestamp %hostname %loadavg %memoryusage %memorycached %swapusage %membuffer %cpuusage %diskspace %traffic"
# Unit for the interface traffic. You can define mb or kb. default is bytes.
inet_unit="kb"
# Load configuration, when exists
if [ -f /etc/default/serverstats ]; then
. /etc/default/serverstats
[ -n "${INTERVAL}" ] && interval=${INTERVAL}
[ -n "${OUTPUTFILE}" ] && outfile=${OUTPUTFILE}
[ -n "${LOGFORMAT}" ] && logformat=${LOGFORMAT}
[ -n "${UNIT}" ] && inet_unit=${UNIT}
fi
# Get the default interface with ip
inet_iface=$(ip -4 route ls | grep default | grep -Po '(?<=dev )(\S+)' | head -n1)
# **********************************************************************
# This function converts time designations such as
# 1d or 12h into seconds. Supported are Xd, Xh, Xm, Xs.
#
# @param $1 - The time designations
# @return init
#
str2seconds () {
local item=$1
local digit=$(echo $item | perl -n -e 'print $1 if ($_ =~ m/^(\d+)((?:d|h|m|s))$/g);')
local unit=$(echo $item | perl -n -e 'print $2 if ($_ =~ m/^(\d+)((?:d|h|m|s))$/g);')
local sec
case $unit in
d) sec=$((${digit}*86400)) ;;
h) sec=$((${digit}*3600)) ;;
m) sec=$((${digit}*60)) ;;
s) sec=${digit} ;;
esac
echo $sec
}
# **********************************************************************
# This function print out the timestamp for the record.
#
# @return string
#
stat_timestamp () {
# Print out the date in format like [09/Mar/2023:00:56:04 +0100]
echo -n "[$(date +%d/%m/%Y\ %H:%M:%S\ %z)] "
}
# **********************************************************************
# This function print out the hostname for the record.
#
# @return string
#
stat_hostname () {
# Print out the hostname
echo -n "\"$(hostname)\" "
}
# **********************************************************************
# This function get the network traffic statistics for an defined interface.
#
# @param $1 - The interface name
# @param $2 - The unit of the traffic (mb,kb,bytes)
# @param $3 - Verbose on|off
# @return string
#
stat_traffic () {
local iface=$1
local unit=${2:-"b"}
local verbose=${3:-"on"}
local label="Bytes/s"
local prefix="traffic,"
# Get received and transmited bytes of the current interface
local rxold=$(cat /sys/class/net/${iface}/statistics/rx_bytes 2>/dev/null)
local txold=$(cat /sys/class/net/${iface}/statistics/tx_bytes 2>/dev/null)
# We wait one second
sleep 1
# Get received and transmited bytes of the current interface
local rxnew=$(cat /sys/class/net/${iface}/statistics/rx_bytes 2>/dev/null)
local txnew=$(cat /sys/class/net/${iface}/statistics/tx_bytes 2>/dev/null)
# Calculate received and transmited Byte/KB/MB/ per second of the current interface
if [ $unit = "KB" -o $unit = "kb" ]; then
tx=$(echo "$txnew $txold" | perl -a -n -e 'printf("%.2f", (($F[0]-$F[1])/1024));')
rx=$(echo "$rxnew $rxold" | perl -a -n -e 'printf("%.2f", (($F[0]-$F[1])/1024));')
label="KB/s"
elif [ $unit = "MB" -o $unit = "mb" ]; then
tx=$(echo "$txnew $txold" | perl -a -n -e 'printf("%.2f", (($F[0]-$F[1])/131072));')
rx=$(echo "$rxnew $rxold" | perl -a -n -e 'printf("%.2f", (($F[0]-$F[1])/131072));')
label="MB/s"
else
tx=$(echo "$txnew $txold" | perl -a -n -e 'printf("%.2f", ($F[0]-$F[1]));')
rx=$(echo "$rxnew $rxold" | perl -a -n -e 'printf("%.2f", ($F[0]-$F[1]));')
fi
if [ $verbose = "on" ]; then
local suffix="RX $iface: $rx ${label}, TX $iface: $tx ${label}"
else
local suffix="net_rx=${rx},net_tx=${tx}"
fi
echo -n "\"${prefix}${suffix}\" "
}
# **********************************************************************
# This function get the Load Average (1,5,15) statistics.
#
# @return string
#
stat_loadavg () {
local prefix="loadavg,"
local suffix=$(awk '{print "loadavg1="$1",loadavg5="$2",loadavg15="$3}' /proc/loadavg)
echo -n "\"${prefix}${suffix}\" "
}
# **********************************************************************
# This function get the Physical Memory Usage (total, free) statistics.
#
# @return string
#
stat_memoryusage () {
local memory=$(awk '$0 ~ /^(MemFree|MemTotal)/{ printf "%%s=%s,", $2 }' /proc/meminfo | sed 's/,$//g')
local prefix="memory,"
local suffix=$(printf "${memory}" "mem_total" "mem_free")
echo -n "\"${prefix}${suffix}\" "
}
# **********************************************************************
# This function get the Cached Memory Usage (free, buffers, cache) statistics.
#
# @return string
#
stat_memorycached () {
local memcache=$(awk '$0 ~ /^(MemFree|Buffers|Cached)/{ printf "%%s=%s,", $2 }' /proc/meminfo | sed 's/,$//g')
local prefix="memcache,"
local suffix=$(printf "${memcache}" "mem_free" "mem_buffers" "mem_cache")
echo -n "\"${prefix}${suffix}\" "
}
# **********************************************************************
# This function get the Swap Usage (total, free) statistics.
#
# @return string
#
stat_swapusage () {
local swapusage=$(awk '$0 ~ /^(SwapFree|SwapTotal)/{ printf "%%s=%s,", $2 }' /proc/meminfo | sed 's/,$//g')
local prefix="swapusage,"
local suffix=$(printf "${swapusage}" "swap_total" "swap_free")
echo -n "\"${prefix}${suffix}\" "
}
# **********************************************************************
# This function get the Memory Buffers (total, buffers) statistics.
#
# @return string
#
stat_membuffer () {
local membuffer=$(awk '$0 ~ /^(Buffers|MemTotal)/{ printf "%%s=%s,", $2 }' /proc/meminfo | sed 's/,$//g')
local prefix="membuffer,"
local suffix=$(printf "${membuffer}" "mem_total" "mem_buffers")
echo -n "\"${prefix}${suffix}\" "
}
# **********************************************************************
# This function get the Used Space / (total, used) statistics.
#
# @param $1 - The mountpoint, default is /
# @return string
#
stat_diskspace () {
local mount=${1:-"/"}
local label=${mount}
# Mask shlashes
mount=$( echo ${mount} | sed 's!\/!\\\/!g' )
local suffix=$(df | awk '$6 ~ /^'${mount}'$/ {print "space_total="$2",space_used="$3}')
local prefix="usedspace_${label},"
echo -n "\"${prefix}${suffix}\" "
}
# **********************************************************************
# This function get the CPU Usage (user,nice,system,idle,iowait,irq) statistics.
#
# @return string
#
stat_cpuusage () {
local prefix="cpuusage,"
local cpu=$(top -n1 -b | grep '%Cpu(s)' | sed 's/\,/\./g')
local system=$(echo $cpu | awk '{printf "%.2f", $4}')
local user=$(echo $cpu | awk '{printf "%.2f", $2}')
local nice=$(echo $cpu | awk '{printf "%.2f", $6}')
local total=$(awk '{u=$2+$4; t=$2+$4+$5; if (NR==1){u1=u; t1=t;} else printf "%.2f\n", ($2+$4-u1) * 100 / (t-t1); }' <(grep 'cpu ' /proc/stat) <(sleep 1;grep 'cpu ' /proc/stat))
local suffix="system=${system},user=${user},nice=${nice},total=${total}"
echo -n "\"${prefix}${suffix}\" "
}
# **********************************************************************
# MAIN
# Get the interval as seconds
interval=$(str2seconds "${interval}")
# Check, if any interface was defined for traffic statistics (1=yes|0=no)
default=$( echo ${logformat} | perl -ne 'print ($_ =~ m/\%traffic_\w+/g && "1" || "0")' )
# Set default parameter for network traffic
if [ ${default} -eq 0 ]; then
logformat=$( echo ${logformat} | perl -p -n -e 's#\%traffic#\%traffic_'${inet_iface}'_'${inet_unit}'#g')
fi
# Start endless loop
while true; do
# Collect metrics and write it to output file
(
for item in ${logformat}; do
eval "$(echo ${item} | perl -F_ -a -n -e '$func = shift @F; $func =~ s/\%//g; print "stat_",$func, " @F";')"
done
echo
) >>${outfile}
# Sleep for X seconds
sleep ${interval}
done
# vim: syntax=sh ts=4 sw=4 sts=4 sr noet
# EOF
Ich erstelle mit folgendem Befehl noch eine md5sum Datei, in der alle Dateien als md5 Hash festgehalten sind.
tux@earth:~/build/serverstats$ find {etc,lib,usr}/ -type f -exec md5sum '{}' \; >md5sums
Bevor wir nun zum bauen kommen, setzten wir noch die Berechtigungen, wie diese später im System vorhanden sein sollen.
tux@earth:~/build/serverstats$ find ./ -type d -exec chmod 755 '{}' \;
tux@earth:~/build/serverstats$ find ./ -type f -exec chmod 644 '{}' \;
tux@earth:~/build/serverstats$ find ./ -name '*.sh' -exec chmod 755 '{}' \;
tux@earth:~/build/serverstats$ sudo chown -R root.root *
Ein Debian Paket bauen
Das bauen des Debian Paket ist eigentlich der geringste Aufwand. Dazu geben wir einfach folgenden Befehl ein. Zuvor gehen wir im Verzeichnis noch eine Ebene höher in das ~/build
Verzeichnis.
tux@earth:~/build/serverstats$ cd ../
tux@earth:~/build$ dpkg -b ./serverstats serverstats.deb
dpkg-deb: Paket »serverstats« wird in »serverstats.deb« gebaut.
Informationen des Paketes anzeigen lassen
Mit folgendem Befehl können wir uns Informationen zum neu erstellen Paket anzeigen lassen.
tux@earth:~/build$ dpkg -I serverstats.deb
neues Debian-Paket, Version 2.0.
Größe 4192 Byte: control-Archiv= 556 Byte.
506 Byte, 15 Zeilen control
Package: serverstats
Version: 0.0.1-0
Section: misc
Priority: optional
Architecture: all
Depends: awk,perl,grep,iproute2,bash,hostname,sed
Installed-Size: 9.7
Maintainer: Marko Schulz <info@tuxnet24.de>
Homepage: https://deb.tuxnet24.de/
Description: A shell script that runs as a SystemV
service and collects statistics from the current host
and stores them in a defined log file.
.
The default configuration can be overwritten in the file
/etc/default/serverstats.
Debian Paket installieren
Zum installieren verwenden wir für dpkg -i
(install). Dabei werden die Abhängigkeiten geprüft und das Paket im System installiert.
tux@earth:~/build$ sudo dpkg -i serverstats.deb
(Lese Datenbank ... 77020 Dateien und Verzeichnisse sind derzeit installiert.)
Vorbereitung zum Entpacken von serverstats.deb ...
Entpacken von serverstats (0.0.1-0) über (0.0.1-0) ...
serverstats (0.0.1-0) wird eingerichtet ...
Das das Paketerfolgreich im System installiert wurde, können wir uns ebenfalls mit dpkg -l
(list) anzeigen lassen.
tux@earth:~/build$ dpkg -l | grep serverstats
ii serverstats 0.0.1-0 all A shell script that runs as a SystemV
Serverstats aktivieren und starten
Im Grunde ist schon alles vorkonfiguriert und wir können nun das Skript als Service aktivieren und starten.
Den Service aktivieren wir wie folgt.
root:~# systemctl enable serverstats
Created symlink /etc/systemd/system/multi-user.target.wants/serverstats.service → /lib/systemd/system/serverstats.service.
Mit der Option start
, starten wir den Service.
root:~# systemctl start serverstats
Den aktuellen Status können wir uns mit der Option status
anzeigen lassen.
root:~# systemctl status serverstats
● serverstats.service - collect memory/cpu statistics of the current server maschine
Loaded: loaded (/lib/systemd/system/serverstats.service; enabled; vendor preset: enabled)
Active: active (exited) since Fri 2023-03-10 11:52:20 CET; 8s ago
Process: 2918618 ExecStart=/bin/sh -c /usr/local/share/serverstats/serverstats.sh & (code=exited, status=0/SUCCESS)
Main PID: 2918618 (code=exited, status=0/SUCCESS)
Tasks: 2 (limit: 4074)
Memory: 1.0M
CPU: 136ms
CGroup: /system.slice/serverstats.service
├─2918619 /bin/bash /usr/local/share/serverstats/serverstats.sh
└─2918734 sleep 300
Mär 10 11:52:20 sudebrp2378 systemd[1]: Starting collect memory/cpu statistics of the current server maschine...
Mär 10 11:52:20 sudebrp2378 systemd[1]: Finished collect memory/cpu statistics of the current server maschine.
Wenn man sich in der Zwischenzeit eine Tasse Kaffee gönnt, wird man nach ca. 20 Minuten sehen, dass schon fleißig Statistiken geschrieben wurden.
root:~# cat /var/log/serverstats.log
[10/03/2023 11:52:20 +0100] "earth" "loadavg,loadavg1=0.16,loadavg5=0.12,loadavg15=0.04" "memory,mem_total=3510052,mem_free=946948" "memcache,mem_free=946948,mem_buffers=220320,mem_cache=1992608" "swapusage,swap_total=0,swap_free=0" "membuffer,mem_total=3510052,mem_buffers=220320" "cpuusage,system=0,00,user=0,00,nice=0,00,total=0,00" "usedspace_/,space_total=30786468,space_used=17202404" "traffic,RX eth0: 0.00 KB/s, TX eth0: 0.00 KB/s"
[10/03/2023 11:57:22 +0100] "earth" "loadavg,loadavg1=0.05,loadavg5=0.08,loadavg15=0.03" "memory,mem_total=3510052,mem_free=918464" "memcache,mem_free=918464,mem_buffers=221228,mem_cache=2019584" "swapusage,swap_total=0,swap_free=0" "membuffer,mem_total=3510052,mem_buffers=221228" "cpuusage,system=0,00,user=0,00,nice=0,00,total=0,99" "usedspace_/,space_total=30786468,space_used=17202652" "traffic,RX eth0: 460.44 KB/s, TX eth0: 20.59 KB/s"
[10/03/2023 12:02:25 +0100] "earth" "loadavg,loadavg1=0.03,loadavg5=0.11,loadavg15=0.07" "memory,mem_total=3510052,mem_free=922976" "memcache,mem_free=922976,mem_buffers=221292,mem_cache=2020364" "swapusage,swap_total=0,swap_free=0" "membuffer,mem_total=3510052,mem_buffers=221292" "cpuusage,system=0,00,user=0,00,nice=0,00,total=4,08" "usedspace_/,space_total=30786468,space_used=17202944" "traffic,RX eth0: 0.00 KB/s, TX eth0: 0.00 KB/s"
[10/03/2023 12:07:27 +0100] "earth" "loadavg,loadavg1=0.07,loadavg5=0.10,loadavg15=0.08" "memory,mem_total=3510052,mem_free=918188" "memcache,mem_free=918188,mem_buffers=221308,mem_cache=2020968" "swapusage,swap_total=0,swap_free=0" "membuffer,mem_total=3510052,mem_buffers=221308" "cpuusage,system=0,00,user=0,00,nice=0,00,total=8,00" "usedspace_/,space_total=30786468,space_used=17203080" "traffic,RX eth0: 0.18 KB/s, TX eth0: 0.09 KB/s"
Das Debian Paket wieder entfernen
Das Paket können wir mit apt-get
und der Option remove
wieder aus dem System entfernen.
root:~# sudo apt-get remove serverstats
Debian-Paket herunterladen
Hier könnt ihr das fertige Debian-Paket herunterladen.
Datei: serverstats.deb
Datum: März 12, 2023
Benutzer: Marko Schulz