bash Skript und Multicore Prozessoren

Wenn man viel mit bash-Skripten macht, kommt man oft an das Problem, dass man Multicore-Prozessoren nicht richtig ausreizen kann. Nehmen wir das Beispiel, dass man alle *.tif Bilder in einem Ordner mit Hilfe von imagemagick zu TIFF/JPEG komprimieren moechte. Das Skript:

for i in *.tif ; do convert $i -compress JPEG $i; done

macht dieses, ein Bild nacheinander. Man koennte das ganze nun schneller machen, indem man einfach jeden convert in eine Subshell packt:

for i in *.tif ; do convert $i -compress JPEG $i &; done

kommt dann aber sehr schnell in Schwierigkeiten, wenn dann nen Ordner mit z.B. 500 Bildern hat und dann auch 500 Subshells. Eine elegantere Loesung ist, eine maximale Anzahl von Prozessen anzugeben die nicht ueberschritten wird und dann abzuarbeiten. Dafuer laesst sich xargs gut benutzen. Wenn man z.B. maximal vier Prozesse haben moechte geht das mit:

find *.tif -print0 | xargs -0 -I {} -P 4 convert -compress JPEG {} {}

Dabei bedeuten die Parameter bei find:

  • -print0 -> print the full file name on the standard output, followed by a null character

und bei xargs:

  • -0 -> Input  items  are  terminated  by a null character instead of by whitespace
  • -I {} -> Replace string
  • -P 4 -> Run  up  to max-procs processes at a time

Der String replace wird genutzt um zweimal das gleiche Argument zu uebergeben. Arbeitet man mit -n 2 werden zwei aufeinanderfolgende Argumente uebergeben.

LDAP-Backup erstellen

Ich bin ein Freund vom Plain text. Damit laesst sich IMHO immer noch am besten Arbeiten. Man kann es in alle denkbaren Formate umwandeln, umbiegen, exportieren, was auch immer, und ist deswegen was Backups angeht das Ziel meiner Wahl. Natuerlich habe ich bei dem LDAP-Server das /var/lib/ldap/ im Backuppath mit drin, aber es geht nichts ueber eine Textdatei in der die Werte nochmal als Plain text drinstehen; gleiches gilt uebrigens fuer MySQL-Datenbanken…

Die Backupdatei erstelle ich mit slapcat. Wichtig dabei ist zu beachten, dass die slap* Tools ganz anders arbeiten als die entsprechenden ldap* Gegenstuecke. Beispiel slapadd und ldapadd. Die ldap* Tools sind LDAP-Clients die ueber das LDAP-Protokoll auf die Datenbank zugreifen. Die slap* Befehle aber greifen direkt auf die lokalen Datenbankdateien zu, die Berkley DB = *.bdb Dateien in /var/lib/ldap/. Da die slap* Befehle direkt darauf zugreifen koennen Sie natuerlich nur auf dem Rechner ausgefuehrt werden auf dem der LDAP-Server laeuft, und es ist wichtig, dass der LDAP-Daemon nicht laeuft. Entsprechend ist auch das Backup-Skript was fuer den LDAP laeuft, einmal taeglich per Cron aufgerufen:

#!/bin/bash
 
SLAPCAT=/usr/sbin/slapcat
GZIP=/bin/gzip
BACKUPDIR=/root/ldapbackup/
 
 
# Daemon stoppen
/etc/init.d/slapd stop > /dev/null 2>&1
if [ $? != "0" ]; then
  logger -t "LDAP-Backup:" "Fehler beim Stoppen des slapd Daemons."
  exit 1
fi
 
 
# Backup in Textdatei erzeugen
$SLAPCAT > $BACKUPDIR/$(date +%Y%m%d)-ldapbackup.ldif
if [ $? != "0" ]; then
  logger -t "LDAP-Backup:" "Fehler beim Schreiben der Backupdatei mit slapcat."
  exit 1
fi
 
 
# Textdatei komprimieren
$GZIP $BACKUPDIR/$(date +%Y%m%d)-ldapbackup.ldif
if [ $? != "0" ]; then
  logger -t "LDAP-Backup:" "Fehler beim Komprimieren der erstellten Backupdatei."
  exit 1
fi
 
 
# Daemon starten und wenn es ueberall keine Probleme gab dann Erfolgsmeldung in syslog
/etc/init.d/slapd start > /dev/null 2>&1
if [ $? != "0" ]; then
  logger -t "LDAP-Backup:" "Fehler beim Starten des slapd Daemons."
  exit 1
else
  logger -t "LDAP-Backup:" "Das Backup der LDAP-Datenbank wurde erfolgreich durchgefuehrt."
fi

Skript: Netzwerkinterface neustarten

Ich hatte eine Debian etch Maschine, virtualisiert mit KVM, bei der brach unter Last immer die Netzwerkverbindung zusammen. Reproduzierbar. Das Update auf Lenny brachte nix. Ich habe die Maschine gestern mit Lenny neu aufgesetzt und die Probleme existieren nicht mehr. Hier nun das Skript was ueberprueft ob eine Verbindung nach aussen moeglich ist und ggfs. das Interface neustartet, ich brauche es nicht mehr, aber bevor ich es loesche hier noch einmal dokumentiert:

#!/bin/bash
##
#  check if network is available and if not restart
#  the network interface
##
 
if ping -c 1 -w 1 -q www.google.de &>/dev/null; then
#  echo "Network is up, no further action required"
  echo ""&>/dev/null
else
  echo "Network is down, restarting the interface..."
  ifdown eth0 && ifup eth0
 fi

Wann is noch der naechste routinemaessige fsck?

Einige von euch kennen sicherlich das Problem: Ein wichtiges Kernelupdate zwingt zum Reboot der Serverinfrastruktur. Per SSH wird das Update eingespielt und ein Reboot abgesetzt, aber nicht alle Server koennen ueber DRAC-Karten oder Web-KVM ueberwacht werden. Von Zeit zu Zeit bleibt einem das Herz dann stehen wenn ein Server nicht wiederkommt, die Pings kommen zurueck mit einem „Destiniation or Host unreachable“ oder vergleichbar…
Waerend man seine Jacke anzieht, Schuhe zubindet und parallel dazu fieberhaft ueberlegt was das Problem sein koennte und was da schiefgelaufen ist, schweift auf dem Weg zur Wohnungstuer noch ein letzter Blick zum Monitor und genau in dem Moment geht dann doch gerade der so herbeigesehnte erste Ping durch. Man rennt an den Rechner zurueck, loggt sich per SSH ein, ueberprueft das alles laeuft und waerend der Adrenalinspiegel sinkt kommt einem in den Sinn, dass das nur ein routinemaessiger Dateisystemcheck gewesen sein wird…

Wann der naechste check ausgefuehrt wird, kann man leicht mit dumpe2fs herausfinden. Bin ich aber ehrlich gesagt zu faul zu und vergesse ich auch jedes mal. Darum habe ich mir (nach einem entsprechenden Herzstillstand) ein kleines Skript geschrieben mit dem Namen nextfscheck, was ich unter /usr/local/sbin/ abgelegt habe und das ich vor einem Reboot aufrufen kann um zu wissen ob es einen fsck geben wird oder nicht. Das Skript ist simpel, das Skript ist billig. Als Parameter wird ein gueltiges gemountetes Device angegeben und dann bekomme ich die Info wieviele remounts noch zum naechsten fsck sind, bzw. an welchem Datum der naechste routinemaessige Dateisystemcheck durchgefuehrt wird. Fuer alle die die ebenfalls Interesse an dem Skript haben, hier:

#!/bin/bash
#
# Give information about the next filesystem check 
#
# 2009-11-08:	initial Version   / Jan Toenjes <mail@jan-toenjes.de>
# 2009-11-09:    changed dumpe2fs to "tune2fs -l" as suggested by kero / Jan Toenjes <mail@jan-toenjes.de>
 
 
# check if skript is run as root for tune2fs 
if [ `id -u` != "0" ] ; then 
  echo ""
  echo "     Usage: $0 /dev/disk"
  echo ""
  echo "     Error: You need to be root to run this skript"
  echo ""
  exit 3
fi
 
 
# check if needed programs are installed
which bc 1>/dev/null 
if [ $? -ne 0 ]; then
  echo "    ERROR: Cant find bc"
  exit 3        
fi        
 
 
# check if we got a parameter 
if [ "$1" == "" ] ; then
  echo ""
  echo "     Usage: $0 /dev/disk"
  echo ""
  echo "     Error: You need to give a valid mounted device as parameter i.e. /dev/sda1" 
  echo ""
  exit 3
fi
 
 
 
 
# check if the device given as a parameter exists
DEVEXIST=`mount | grep -ir "$1" | grep "/dev/" | wc | awk {'print $1'}`
if [ "$DEVEXIST" == "0" ] ; then
  echo ""
  echo "     Usage: $0 /dev/disk/"
  echo ""
  echo "     Error: The given parameter  \"$1\"  is not a valid mounted device."
  echo ""
  exit 3
fi
 
 
 
# get remaining mounts
MOUNTCOUNT=`tune2fs -l $1 2>&1 | grep "Mount count:" | awk {'print $3'}`
MAXIMUMMOUNTCOUNT=` tune2fs -l $1 2>&1 | grep "Maximum mount count:" | awk {'print $4'}`
REMAININGMOUNTS=`echo $MAXIMUMMOUNTCOUNT-$MOUNTCOUNT|bc`
 
 
 
# check if we have a fsck after a specific time value and give wanted information
TIMECHECK=`tune2fs -l $1 2>&1 | grep "Check interval:" | awk {'print $3'}`
if [ "$TIMECHECK" == "0" ] ; then
  echo "The next filesystem check of $1 will be after another $REMAININGMOUNTS mounts."
 
else
  NEXTCHECKAFTER=`tune2fs -l $1 2>&1 | grep "Next check after:" | sed 's/Next check after:[ \t]*$//g'`
  echo "The next filesystem check of $1 will be on $NEXTCHECKAFTER or after another $REMAININGMOUNTS mounts."
 
fi