Autostart von supervisor will nicht

fumdock

New member
Hallo zusammen,

ich komme leider einfach nicht weiter und habe mich nun extra hier angemeldet. Ich sehe den Wald vor lauter Bäumen nicht mehr.

Ausgangspunkt ist dieses Docker Image von Github: https://github.com/accetto/ubuntu-vnc-xfce-g3
Es stellt Ubuntu mit einer GUI zur Verfügung und ist über Webbrowser erreichbar.

Im Dockerfile lasse ich zusätzlich java, supervisor und mein testprogramm ins Image installieren. Gestartet wird der Docker dann mit dem user headless und der Entrypoint startup.sh stellt noVNC zur Verfügung. Der Prozess läuft dauerhaft. Killt man ihn verliert man die noVNC Verbindung.

Zahlreiche versuche supervisor automatisiert starten zu lassen schlugen fehl. Egal was ich in [CMD] im Dockerfile eintrage, funktioniert nicht. Mit "sudo /usr/bin/supervisord" im Terminal des Dockers läuft alles einwandfrei.
Was übersehe ich? Wie kann ich dafür sorgen dass supervisor unter Root automatisch mit dem Docker gestartet wird?

Hier mein config file für supervisor:

Rich (BBCode):
[supervisord]
logfile=/var/log/supervisord.log
logfile_maxbytes=10MB
logfile_backups=3
loglevel=error
user=root
pidfile=/var/run/supervisord.pid
nodaemon=true
minfds=1024
minprocs=200
childlogdir=/var/log/

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock

[program:test]
autostart=true
autorestart=true
redirect_stderr=true
user=headless
stdout_events_enabled=true
stderr_events_enabled=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
command=/usr/bin/java -jar /home/headless/test.jar
 
Ein Container ist für Anwendungs- bzw. Prozess-Virtualisierung da, und ist am Ende keine VM. Schon mehrere Prozesse in einem Container betreiben zu wollen, ist nicht "Dockeresque" und wirkt eigentlich eher so, als wenn man eigentlich eine VM verwenden sollte. Nur weil man etwas im Container machen kann (und andere es machen), muss es noch lange keine gute Idee sein :D

Wie sehen denn dein ENTRYPOINT und CMD im Dockerfile aus?
 
Zuletzt bearbeitet:
wie gesagt läuft alles einwanfrei, wenn ich supervisor im Docker manuell starte ... dann sollte das auch irgendwie automatisiert gehen. aktuell läuft das auf einem rasperry mit Desktop Umgebung. Da ich einen neuen Linux Server aufsetzen will, soll es in einen Docker wandern. Der Entrypoint ist orginal aus dem Image. Und mit CMD hab ich einiges experimentiert. Vor dem User change und danach. Auch bei der Syntax vieles probier:

Code:
ENTRYPOINT [ "/usr/bin/tini", "--", "/dockerstartup/startup.sh" ]
USER "${HEADLESS_USER_ID}"
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf", "-n"]
 
Zuletzt bearbeitet:
Bei ENTRYPOINT und CMD gibt es immer wieder Verwirrungen:
  • Wenn nur ENTRYPOINT deklariert ist, dann ist das der Befehl, der beim Start des Containers verwendet wird.
  • Wenn nur CMD deklariert ist, dann ist das der Befehl, der beim Start des Containers verwendet wird.
  • Wenn ENTRYPOINT und CMD deklariert werden, dann wird CMD als Argument vom ENTRYPOINT verwendet.

In deinem Fall wird sowohl ENTRYPOINT und CMD deklariert, so dass der Container mit diesem Befehl startet:
Code:
/usr/bin/tini -- /dockerstartup/startup.sh /usr/bin/supervisord -c /etc/supervisor/supervisord.conf -n

Das wiederum kann nur funktionieren, wenn das Entrypoint-Skript das auch unterstützt. Dafür haben viele Entrypoint-Skripte in der letzten Zeile den Befehl exec $@ stehen.

Ist es bei dem Entrypoint Skript so? Wenn nicht, dann kann die Lösung so nicht aussehen...

Was denkbar wäre:
- supervisord als ENTRYPOINT verwenden
- Dein Zeug darüber starten
- /dockerstartup/startup.sh darüber starten.

Keine Ahnung wie das aussehen muss. In den letzten 10 Jahren mit Docker habe ich nie supervisord benötigt. Bei mir sind die Images so geschnitten, dass alles was über das Netzwerk miteinander kommunizieren kann in eigenen Images paketiert ist und als eigenständiger Container betrieben wird.
 
ja der Entrypoint hat ein main$@
startup.sh

Standardmäßig wird in diesem "fertigen" noVNC docker nur Entrypoint verwendet. Aber da es damit nicht lief, hab ich auch mit CMD experimentiert

mit CMD für supervisor hatte ich immer irgendwelche Rechte Probleme ... mal nicht als root gestartet, mal fehlten Berechtigungen logs zu schreiben, mal meckerte er wegen einem HTTP Server usw. etc. pp... aber über terminal läuft es eben immer unter sudo
 
Dir ist schon klar, dass ich deine Kopfwissen über das, was du wie getan hast, nicht habe, oder? Ich bin mir sicher, dass das was Du schreibst, für jemanden mit deinem Kopfwissen klar verständlich ist und sofort Sinn ergibt. Mir sagt es zumindest nichts außer "ich hab was probiert und ich laufe immer in Probleme".

Die angegebenen Informationen reicht nicht aus, um zu verstehen, was das Problem verursacht und wie man es beheben könnte.

Ich sehe hier drei Möglichkeiten:
1. Du probierst weiter wild rum -> könnte zum Ziel führen, oder auch nicht
2. Du setzt Dich mit dem Entrypoint Skript auseinander, und baust Verständnis auf, unter welcher Bedingung es was tut, und ob es wirklich den Befehl aus CMD via exec $@ ausführen würde. Du musst main $@ schon folgen um zu verstehen was es tut. Nur weil dort ein $@ vorkommt, macht es noch lange nicht das was Du brauchst.
3. Wie oben schon geschrieben: mach supervisord zum Entrypoint. Anscheinend definiert man je Anwendung einen [program:xx]-Block, Was spricht dagegen einen hinzuzufügen, der dann das ursprüngliche Skript aus dem bisher Entrypoint startet?

Variante 3 erscheint mir am zielführendsten zu sein.
 
ja. das ist mir schon klar, dass man mir nicht in den Kopf schauen kann ... darum habe ich ja den Github Link oben eingestellt. dort sind alle Infos drin, wie es dort realisiert wird. und da meine "Lösung" über manuellen Start im Docker funktioniert, kann es nicht viel sein, was Probleme bereitet.

mir ging es auch eher allgemein darum, wie man sowas am besten realisiert. Ich bin halt nur Laie und habe mich nun 2 Wochen intensiv damit beschäftigt und weiß gerade nicht mehr weiter. Es ist immer schwer, sich alles erstmal beibringen zu müssen und zu verstehen. aber aus Fehlern lernt man und es geht immer einen Schritt weiter

Aber dein Tipp hilft mir schon mal, was ich noch probieren kann.
Ich werde versuchen den ursprünglichen Entrypoint zu ändern, damit startup.sh irgendwie mit dem supervisor gestartet wird
 
danke @Confluencer für den Tipp. Es läuft nun alles wie es soll ... für mich war das mühselig, 2 Wochen rumprobieren, aber ich habe wieder einiges dazu gelernt. Vielleicht hilft diese Lösung ja dem ein oder anderen

Im Dockerfile gibt es nur noch einen Entrypoint, kein CMD mehr.
Dockerfile :
Code:
ENTRYPOINT ["/dockerstartup/startup.sh"]

supervisord wird in startup.sh nun unter dem non-root User headless gestartet. Das Script ist hier aufs wesentliche gekürzt. Vollständig siehe Link oben.
startup.sh :
Code:
#!/bin/bash

#set -e     ### do not use this

declare _mydir=$(dirname $0)

source "${_mydir}"/help.rc
source "${_mydir}"/parser.rc
source "${_mydir}"/user_generator.rc
source "${_mydir}"/vnc_startup.rc

cleanup () {
    ...
}

execute_command() {
    ...
}

envv_override() {
    ...
}

start_supervisor() {
    if [[ -n "${_verbose}" ]]; then
        echo "Starting Supervisor..."
    fi

    supervisord -c "/etc/supervisor/supervisord.conf" -u headless &
    _supervisor_pid=$!
}

main() {

 ...

    ### default backround execution mode
    ### be sure to end all previous branches by calling 'cleanup'
    ### option '--wait' is purely because of the parser

    ### this handles also '--skip-vnc' and '--skip-novnc' options
    start_vnc

    ### start Supervisor
    start_supervisor

    ### Warten auf die PIDs von VNC und Supervisor, damit das Skript nicht beendet wird
    if [[ -n "${_wait_pid}" && -n "${_supervisor_pid}" ]] ; then
        wait ${_wait_pid} ${_supervisor_pid}
    elif [[ -n "${_wait_pid}" ]] ; then
        wait ${_wait_pid}
    elif [[ -n "${_supervisor_pid}" ]] ; then
        wait ${_supervisor_pid}
    else
        ### Wenn weder VNC noch Supervisor gestartet wurden, gehen wir dauerhaft in den Schlafmodus
        sleep infinity
    fi
}


### MAIN ENTRY POINT

if [[ -z "${DEBUGGER}" ]] ; then

    trap cleanup SIGINT SIGTERM ERR
    : ${HOME?} ${STARTUPDIR?}
fi

declare _novnc_log="${STARTUPDIR}"/novnc.log
declare _verbose=""
declare _vnc_log="${STARTUPDIR}"/vnc.log
declare _wait_pid=""

### option '--skip-startup'
if [[ "${_arg_skip_startup}" == "on" ]] ; then

    ### command array expands to all elements quoted as a whole
    execute_command "${_arg_command[*]}"

else

    main $@

fi

cleanup

Da der User headless in den Standard Pfaden der LOG und PID Datei keine Schreibrechte hat, mussten diese verschoben werden. Dazu dient nun das Home Verzeichnis von user headless.
supervisord.conf :
Code:
[supervisord]
logfile=/home/headless/supervisord.log
logfile_maxbytes=10MB
logfile_backups=3
loglevel=error
pidfile=/home/headless/supervisord.pid
nodaemon=true
minfds=1024
minprocs=200
childlogdir=/var/log/

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock

[program:javaprogramm]
autostart=true
autorestart=true
redirect_stderr=true
user=headless
stdout_events_enabled=true
stderr_events_enabled=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
command=/usr/bin/java -jar /home/headless/java.jar
 
Danke, dass Du Deine Lösung geteilt hast! So können auch andere von der Lösung profitieren.

Ich würde Deine Implementierung trotzdem nochmal challengend wollen:
Was passiert, wenn das Base-Image Änderung an der startup.sh erfährt? Bei Deiner Lösung müsstest Du Deine aktuelle Fassung und die neue Fassung diffen und deine Ergänzungen hereinbringen. So wie es jetzt implementiert ist, müsstest der Container vom pid1 Zombie Reaping Problem betroffen sein. Der Container wird lange beim stoppen brauche und nie Gracefull nach einem SIGTERM sauber beendet werden, sondern nach der Graceperiod hart mit SIGKILL gekillt werden. Das kann zu Datenkorruption führen.

Das Probem hättest du nicht, wenn Du supervisord zum Entrypoint gemacht hättest, und einen zweiten Programm Block für das jetzige startup.sh ergänzt hättest. Bei der Lösung müsstest Du nie Hand anlegen an der setups.sh.

Docker ist nun Mal prozess virtualisierung, und eigentlich sollte es in einem Container nur pid1 geben... Wenn ein Prozessmanager wie Supervisord (oder tini, s6-overlay und wie sie alle heissen) pid1 hat, dann kümmert es sich um seine Kind-Prozesse sauber, und da pid1 Zombie Reaping Problem kann nicht entstehen.
 
:eek: nun hast du mich total abgehängt. die Bergriffe die du erwähnst hab ich noch nie gehört :ROFLMAO: ich versteh nur Bahnhof. Ich hab das nicht gelernt oder studiert. das ist nur Hobby von mir. learning by doing .. solang rummachen bis es eben funktioniert ... ich bin froh, dass das nun läuft wie ich es wollte. ich werd es nun eine Woche testen und dann "scharf schalten"

ich hab noch viele andere Ideen die ich umsetzen möchte. wenn sich mal Zeit findet, kann ich das gern nochmal probieren mit dem Entrypoint von supervisor und damit dann startup.sh laufen lassen
 
Im wesentlich sind es zwei Punkte der aktuellen Lösung die zu hinterfragen sind:

- Will man wirklich immer, wenn im Basis-Image das Skript ersetzt wurde, auf diese Änderungen verzichten, oder die eigenen Änderungen nachziehen? Ein Image das nicht gepflegt wird, ist schnell veraltet, sodass mit der Zeit immer mehr Schwachstellen bekannt werden.

- Will man wirklich, dass der Container beim Beenden jedes Mal hart gekillt wird, weil er nicht mehr normal beendet werden kann (siehe pid1 Zombie Reaping Problem Link).

Du wirst schon dahin kommen, dass Du greifen kannst was die Probleme für Dich bedeuten, und dann einschätzen kannst. ob Du dafür bereit bist etwas zu tun.
 

Zurzeit aktive Besucher

Letzte Anleitungen

Statistik des Forums

Themen
4.676
Beiträge
47.748
Mitglieder
4.325
Neuestes Mitglied
Tim Schroth
Zurück
Oben