Shell-Skripte als Cron-Jobs auf dem NAS

  • Ich bin dabei, alle Dienste und Daten von meinem in die Jahre gekommenen Linux-Heimserver auf ein QNAP TS-228A umzuziehen.

    Dazu gehören auch zwei bash-Skripte, die periodisch per cron ausgeführt werden.

    Das eine läuft alle fünf Minuten und sichert das Ereignisprotokoll meiner Fritzbox fortlaufend in eine Textdatei.

    Es benötigt neben Standardkommandos wie date, echo, test, cat, tail, expr insbesondere die Programme curl, iconv, md5sum und den JSON-Parser jq.Das andere läuft täglich und sichert mein Mailkonto samt Adressbuch und Kalender von meinem Mailprovider in ein lokales git-Archiv.

    Dazu nutzt es die Python-Programme offlineimap und vdirsyncer, es benötigt also eine Python-Umgebung und natürlich git.

    Die gesicherten Daten sollen dann für berechtigte Benutzer in einem Freigabeordner zum Lesen zugänglich sein.


    Was empfiehlt die versammelte QNAP-Expertengemeinde für so einen Anwendungsfall?

    Soll ich dafür die Container Station installieren und einen eigenen Linux-Container mit den nötigen Komponenten anlegen?

    Oder ist das Overkill und die beiden Cronjobs können genausogut direkt ins QTS?

  • Hallo tgsbn!


    Zunächst einmal hat mir ein Blick auf meine TS 251+ folgendes mitgeteilt:

    Was so viel heißen soll wie: du könntest das erste Skript vermutlich auch "nativ" betreiben, vorausgesetzt du hast Entware (opkg) auf deinem NAS installiert.

    Für das zweite Skript: git und python gibt es im offiziellen QNAP Appstore, allerdings sind es meist eher "gut abgehangene" Versionen.

    Daher würde ich persönlich, auch wenn ich mich nicht direkt zu der "Expertengemeinde" zählen würde, auch zu einem Container raten. Dort kannst du einfach die Standard tools verwenden (z.B. apt install python-pip und pip install offlineimap) und musst vermutlich keinerlei weitere Anpassungen an deinen eigentlichen Skripten vornehmen (z.B. hat die QNAP Version von curl immer Probleme mit fehlenden Zertifikaten, was in der Verwendung von Skripten problematisch sein kann).

    Wichtig: um den Overhead so gering wie möglich zu halten, würde ich den Container für deinen Anwendungsfall jedoch nicht dauerhaft laufen lassen sondern so bauen, dass der entsprechende Aufruf einfach via docker run CONTAINER erfolgen kann. Das hat den Vorteil, dass du das QNAP eigene cron verwenden kannst und nicht noch dafür Sorge tragen musst, dass der Container sich nicht automatisch beendet (Docker Container beenden sich im Normalfall nach getaner Arbeit).


    Zu der Thematik "ist das Overkill":

    Meiner Meinung nach nicht. Wenn du einen einfachen Container nimmst, sollte der Festplattenplatz noch vertretbar sein (ubuntu liegt bei ~200MB, Alpine bei 5MB). Wenn du es so wie oben beschrieben konfigurierst, hast du sonst keinerlei weitere Resourcen die verwendet werden, zumindest nicht, wenn das Skript inaktiv ist.


    BTW: alle 5 Minuten das Ereignisprotokoll sichern? Für was das denn, willst du deinem ISP nachweisen, dass was mit der Leitung nicht stimmt? Für das Stromsparverhalten der Festplatten im NAS solltest du nochmal überlegen, ob es wirklich soooo oft sein muss.


    Falls du konkrete Hilfe bei der Erstellung des Dockerfiles brauchst, gibt Bescheid :cup:

  • Hallo tuxflo, danke für die schnelle Antwort.


    Zum Abruf alle fünf Minuten:

    Das stammt aus Zeiten, wo die sich die Fritzbox öfter mal spontan neu gestartet hat und dann natürlich immer genau der Teil des Logs weg war, auf den es angekommen wäre.

    Das ist jetzt länger nicht mehr passiert.

    Auf der Box sind die Ereignisse bis ungefähr drei Tage zurück verfügbar, insofern würde wohl auch ein täglicher Abruf reichen.


    Ob ich Hilfe beim Dockerfile brauche, weiß ich noch nicht.

    Ich schaue mir jetzt mal https://www.qnap.com/de-de/how…ung-der-container-station an und schaue, wie weit ich komme.

  • Ja die Anleitung ist nicht direkt schlecht, ich würde allerdings bei deinem konkreten Anwendungsfall dazu raten sich mehr mit der offiziellen Docker Dokumentation (oder anderen allgemeinen Docker Tutorials) vertraut zu machen und direkt auf die Verwendung der Kommandozeile gehen, denn es ist eher unwahrscheinlich, dass es schon ein fertiges Image für deine Zwecke gibt (und die verlinkte Anleitung ist eher darauf ausgelegt bereits bestehende Images zu verwenden).

  • So, das einfachere der beiden Skripte (Abruf des Fritzbox-Logs) habe ich jetzt erfolgreich in einen Container verpackt.Mein Dockerfile ist recht simpel:

    Docker
    FROM alpine
    ADD fritz-syslog.sh FBgetlog.sh /usr/local/bin/
    RUN apk add --update --no-cache bash curl jq
    ENTRYPOINT ["/usr/local/bin/FBgetlog.sh"]

    Tatsächlich hat das Skript zwei Teile:

    fritz-syslog.sh macht die Kommunikation mit der Fritzbox.

    FBgetlog.sh ruft fritz-syslog.sh auf und schreibt die neuen Meldungen in eine Datei.

    Als Zielordner habe ich /data gewählt, weil ich beim Stöbern auf Docker Hub den Eindruck gewonnen habe, dass das relativ üblich ist.

    Um mein Fritzbox-Passwort aus dem Container rauszuhalten, babe ich FBgetlog.sh beigebracht, eine Datei .fritzlogrc zu sourcen, die ich vor Start des Containers im Zielordner deponiere.


    Gebaut und gestartet habe ich den Container mit:

    Code
    docker build -t fritzlog:0.5 -t fritzlog:latest .
    docker run --rm -it -v /share/homes/Tilman/logs:/data fritzlog

    (0.5, weil es mein fünfter Versuch war.)

    Dann habe ich gemäß dem Rezept von RedDiabolo in Neu angelegtes Script und Eintrag in Cron-Tabelle verschwinden beim Neustart den Crontab-Eintrag

    Code
    17 * * * * /share/CACHEDEV1_DATA/.qpkg/container-station/bin/docker run --rm -v /share/homes/Tilman/logs:/data fritzlog

    gesetzt. (Den Parameter -it musste ich streichen, mit dem läuft docker run nicht als Cronjob.)


    Das funktioniert soweit alles und das Log liegt danach schön auf dem NAS unterhalb meines Home-Verzeichnisses im Subdirectory logs.


    Als nächstes werde ich mich dann mal an dem offlineimap/vdirsyncer-Container versuchen.

    2 Mal editiert, zuletzt von tgsbn () aus folgendem Grund: Problem mit Cron gelöst.

  • Nun gab es doch noch eine kleine Komplikation: wenn der Container ein neues Logfile angelegt hat, gehörte das dem root-Benutzer und hatte Zugriffsrecht 700, d.h. von meinem normalen Benutzer aus konnte ich es nicht lesen.

    Meine Lösung dafür ist ein kleines Wrapper-Skript, das das eigentliche Skript unter der UID meines normalen Benutzers ausführt.:

    Bash
    #!/bin/sh
    # wrapper to set UID/GID
    PATH=/usr/sbin:/usr/bin:/sbin:/bin
    
    WORKUID=${WORKUID:-1000}
    WORKGID=${WORKGID:-100}
    
    # run work script as work user
    echo "workuser:x:${WORKUID}:${WORKGID}::/data:" >> /etc/passwd
    su - workuser -c "/usr/local/bin/FBgetlog.sh"

    Wie immer: Tipps, wie man das eleganter lösen kann, willkommen.

  • offlineimap und vdirsyncer laufen jetzt auch.

    Als Basis bot sich der Container python:alpine an.

    Etwas ärgerlich war, dass vdirsyncer mit Python 3 läuft und offlineimap nur mit Python 2.

    Meine Lösung dafür ist, offlineimap nicht per pip zu installieren, sondern das apk-Paket zu nehmen, das sein eigenes Python mitbringt.

    Jetzt ist der Container halt knapp 200 MB groß und enthält zwei komplette Pythons nebeneinander, aber das kann ich verschmerzen.


    Das Dockerfile ist wieder recht übersichtlich:

    Docker
    FROM python:alpine
    ADD FMbackup.sh run.sh /usr/local/bin/
    RUN pip install vdirsyncer && apk add --update --no-cache ca-certificates offlineimap git && chmod 755 /usr/local/bin/*.sh
    ENTRYPOINT ["/usr/local/bin/run.sh"]

    run.sh ist wieder ein ähnlicher Wrapper wie im vorigen Beitrag, um das eigentliche Skript unter meiner UID statt unter root auszuführen.

    Eine zusätzliche Komplikation war dabei, dass vdirsyncer und offlineimap es sich nicht abgewöhnen lassen, Ausgaben auf der Standardausgabe und der Standardfehlerausgabe zu produzieren. Wenn der Container als Cronjob gestartet wird, führt das sofort zum Abbruch. Deshalb darf das run.sh-Skript dieses Containers sich auch gleich noch darum kümmern, die Standardausgabe umzuleiten:

    Bash
    #!/bin/sh
    # wrapper to set UID/GID
    PATH=/usr/sbin:/usr/bin:/sbin:/bin
    
    WORKUID=${WORKUID:-1000}
    WORKGID=${WORKGID:-100}
    
    # run work script as work user
    echo "workuser:x:${WORKUID}:${WORKGID}::/data:" >> /etc/passwd
    su - workuser -c "/usr/local/bin/FMbackup.sh > /data/FMbackup.log 2>&1"
  • Wie immer: Tipps, wie man das eleganter lösen kann, willkommen.

    Die Idee, das ursprüngliche Skript unter anderer UID auszuführen, sollte auch einfacher gehen, ohne Wrapper, indem Du den cronjob nicht für admin oder root sondern für diese andere UID anlegst. Und die Voreinstellung, mit welchen Rechten neue Dateien versehen werden, lässt sich mit umask unter Linux und UNIX konfigurieren, falls Du die Ergebnisse z.B. nicht nur für Deine UID sondern für eine ganze Gruppe lesbar haben willst. Habe ich jetzt noch nicht unter QTS ausprobiert, insbesondere was noch zu beachten ist, damit eine solche Konfiguration auch ein Reboot der NAS übersteht.

  • Die Idee, das ursprüngliche Skript unter anderer UID auszuführen, sollte auch einfacher gehen, ohne Wrapper, indem Du den cronjob nicht für admin oder root sondern für diese andere UID anlegst.

    Das ist grundsätzlich keine schlechte Idee. Leider funktioniert der Aufruf von docker unter QTS aber nur als root:

    Code
    [Tilman@Nessy ~]$ /share/CACHEDEV1_DATA/.qpkg/container-station/bin/docker run -it --rm -v /share/homes/Tilman/logs:/data fritzlog
    container-station/docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.32/containers/create: dial unix /var/run/docker.sock: connect: permission denied.
    See 'container-station/docker run --help'.
    [Tilman@Nessy ~]$
  • Das Verhalten ist übrigens nicht Container Station spezifisch, sondern ein Problem von Docker im allgemeinen. Das ist einer der Gründe, warum Redhat an einer alternativen Container Engine namens Podman arbeitet. Damit ist es auch möglich "root-less" Container zu betreiben. Ich mach dir aber nicht wirklich viel Hoffnung, dass man das auf QTS zum Laufen bekommt (obwohl es natürlich einen Versuch wert ist).