Bash: starte für zig Server pro Tag etwas auf nur einzelnen Systemen
Ich habe da eine unbekannt lang laufende Aufgabe: ich möchte vom Backup-Tool Restic das Backup-Repository auf Version 2 migrieren. OK, eigentlich ist die Aufgabe ja egal. Alle 100+ Systeme kommen damit nicht in der Nacht durch.
Ich möchte …
- dass pro Nacht nur einige Systeme eine lang laufende Aufgabe wahrnehmen
- nach N Tagen soll sichergestellt sein, dass auch alle Systeme den Job 1x gemacht haben.
Mir kam der Modulus in den Sinn.
Wenn ich etwas täglich starte, dann nehme ich den Unix-Zeitstempel her und nehme dessen Anzahl Tage. Mit dem Modulus der Anzahl Tage, wann die Aktion auf allen Systemen abgeschlossen sein soll, erhalte ich eine Zahl 1..N.
Dann brauche ich etwas systemspezifisches, was auf jedem System anders, aber jeweils konstant ist. Das wäre beispielsweise der Hostname/ FQDN. Um aus einem String eine Zahl zu machen, auf die wiederum der Modulus angewendet wird, braucht es einen Kniff. Es gibt ja den MD5 Hash - dieser liefert eine Hex-Zahl. Diese muss man nur noch eine Dezimal-Zahl umwandeln, und schon ist man am Ziel.
Und wenn es um Skripten geht, dann kippen wir dies in eine Funktion. Wir brauchen md5sum, awk (der ist zumeist vorinstalliert) und bc.
# generate a modulus from a string # param string a text # param integer divider function getModulus(){ local _from="$1" typeset -i local _mod=$2 typeset -i local dec typeset -i local mod hex=$( echo "$_from" | md5sum | awk '{ print $1}') dec=$(echo "ibase=16; ${hex^^}" | bc) mod=$( echo $dec % $_mod | tr -d "-") echo "$mod" }
Ein Beispiel-Rumpf-Skript, das diese Funktion nutzt und die obigen Anforderungen umsetzt, kann so aussehen:
# spread update over all systems within N days typeset -i rollout=30 # modulus of day count since 1970 typeset -i daymod=$(( $(date +%s)/60/60/24 % $rollout )) # modulus of a system specific value typeset -i sysmod=$( getModulus "$( hostname )" $rollout ) echo "DEBUG: modulus of days $daymod vs hostname modulus $sysmod" if [ $daymod -ne $sysmod ]; then echo "SKIP: not today" else echo "starting something ... " fi