Bash: starte für zig Server pro Tag etwas auf nur einzelnen Systemen

Dienstag, 22. November, 2022

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