feat: container process supervision with sueprvisord

Rafael Uzarowski committed Mar 20, 2025 at 23:22 UTC fbcb579a91937d436f18123dfa22bdfb156bef84
8 files changed +156 -48
docker/run/Dockerfile
+4 -2
@@ -28,5 +28,7 @@ RUN bash /ins/post_install.sh $BRANCH
28 # Expose ports
29 EXPOSE 22 80
30
31 -# initialize runtime
32 -CMD ["/bin/bash", "-c", "/bin/bash /exe/initialize.sh $BRANCH"]
\ No newline at end of file
31 +RUN chmod +x /exe/initialize.sh /exe/run_A0.sh /exe/run_searxng.sh
32 +
33 +# initialize runtime and switch to supervisord
34 +CMD ["/exe/initialize.sh", "$BRANCH"]
docker/run/DockerfileKali
+4 -2
@@ -29,5 +29,7 @@ RUN bash /ins/post_install.sh $BRANCH
29 # Expose ports
30 EXPOSE 22 80
31
32 -# initialize runtime
33 -CMD ["/bin/bash", "-c", "/bin/bash /exe/initialize.sh $BRANCH"]
\ No newline at end of file
32 +RUN chmod +x /exe/initialize.sh /exe/run_A0.sh /exe/run_searxng.sh
33 +
34 +# initialize runtime and switch to supervisord
35 +CMD ["/exe/initialize.sh", "$BRANCH"]
docker/run/fs/etc/supervisor/conf.d/supervisord.conf new
+77
@@ -0,0 +1,77 @@
1 +[supervisord]
2 +nodaemon=true
3 +user=root
4 +logfile=/dev/stdout
5 +logfile_maxbytes=0
6 +pidfile=/var/run/supervisord.pid
7 +exitcodes=0,2
8 +directory=/
9 +
10 +[rpcinterface:supervisor]
11 +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
12 +
13 +[supervisorctl]
14 +serverurl=unix:///var/run/supervisor.sock
15 +
16 +[program:run_sshd]
17 +command=/usr/sbin/sshd -D
18 +environment=LC_ALL=en_US.UTF-8
19 +stopwaitsecs=1
20 +stdout_logfile=/dev/stdout
21 +stdout_logfile_maxbytes=0
22 +stderr_logfile=/dev/stderr
23 +stderr_logfile_maxbytes=0
24 +autorestart=true
25 +startretries=3
26 +stopasgroup=true
27 +killasgroup=true
28 +
29 +[program:run_cron]
30 +command=/usr/sbin/cron -f
31 +environment=
32 +stopwaitsecs=1
33 +stdout_logfile=/dev/stdout
34 +stdout_logfile_maxbytes=0
35 +stderr_logfile=/dev/stderr
36 +stderr_logfile_maxbytes=0
37 +autorestart=true
38 +startretries=3
39 +stopasgroup=true
40 +killasgroup=true
41 +
42 +[program:run_searxng]
43 +command=/exe/run_searxng.sh
44 +environment=SEARXNG_SETTINGS_PATH=/etc/searxng/settings.yml
45 +user=searxng
46 +directory=/usr/local/searxng/searxng-src
47 +stopwaitsecs=1
48 +stdout_logfile=/dev/stdout
49 +stdout_logfile_maxbytes=0
50 +stderr_logfile=/dev/stderr
51 +stderr_logfile_maxbytes=0
52 +autorestart=true
53 +startretries=3
54 +stopasgroup=true
55 +killasgroup=true
56 +
57 +[program:run_ui]
58 +command=/exe/run_A0.sh
59 +environment=
60 +user=root
61 +stopwaitsecs=60
62 +stdout_logfile=/dev/stdout
63 +stdout_logfile_maxbytes=0
64 +stderr_logfile=/dev/stderr
65 +stderr_logfile_maxbytes=0
66 +autorestart=true
67 +startretries=3
68 +stopasgroup=true
69 +killasgroup=true
70 +
71 +[eventlistener:the_listener]
72 +command=python3 /exe/supervisor_event_listener.py
73 +events=PROCESS_STATE_FATAL
74 +stdout_logfile=/dev/stdout
75 +stdout_logfile_maxbytes=0
76 +stderr_logfile=/dev/stderr
77 +stderr_logfile_maxbytes=0
docker/run/fs/exe/initialize.sh
+1 -12
@@ -17,15 +17,4 @@ chmod 444 /root/.profile
17 # update package list to save time later
18 apt-get update > /dev/null 2>&1 &
19
20 -# Start SSH service in background
21 -/usr/sbin/sshd -D &
22 -
23 -# Start searxng server in background
24 -su - searxng -c "bash /exe/run_searxng.sh \"$@\"" &
25 -
26 -# Start A0 and restart on exit
27 -bash /exe/run_A0.sh "$@"
28 -if [ $? -ne 0 ]; then
29 - echo "A0 script exited with an error. Restarting container..."
30 - exit 1
31 -fi
\ No newline at end of file
20 +exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
docker/run/fs/exe/run_A0.sh
+15 -29
@@ -1,33 +1,19 @@
1 #!/bin/bash
2
3 -# Loop to restart the Python script when it finishes
4 -while true; do
3 +. "/ins/setup_venv.sh" "$@"
4 +. "/ins/copy_A0.sh" "$@"
5
6 - . "/ins/setup_venv.sh" "$@"
7 - . "/ins/copy_A0.sh" "$@"
6 +python /a0/prepare.py --dockerized=true
7 +python /a0/preload.py --dockerized=true
8
9 - python /a0/prepare.py --dockerized=true
10 - python /a0/preload.py --dockerized=true
11 -
12 - echo "Starting A0..."
13 - python /a0/run_ui.py \
14 - --dockerized=true \
15 - --port=80 \
16 - --host="0.0.0.0" \
17 - --code_exec_docker_enabled=false \
18 - --code_exec_ssh_enabled=true \
19 - # --code_exec_ssh_addr="localhost" \
20 - # --code_exec_ssh_port=22 \
21 - # --code_exec_ssh_user="root" \
22 - # --code_exec_ssh_pass="toor"
23 -
24 - # Check the exit status
25 - if [ $? -ne 0 ]; then
26 - echo "A0 script exited with an error. Restarting..."
27 - else
28 - echo "A0 script finished. Restarting..."
29 - fi
30 -
31 - # Optional: Add a small delay if needed to avoid rapid restarts
32 - sleep 1
33 -done
9 +echo "Starting A0..."
10 +exec python /a0/run_ui.py \
11 + --dockerized=true \
12 + --port=80 \
13 + --host="0.0.0.0" \
14 + --code_exec_docker_enabled=false \
15 + --code_exec_ssh_enabled=true \
16 + # --code_exec_ssh_addr="localhost" \
17 + # --code_exec_ssh_port=22 \
18 + # --code_exec_ssh_user="root" \
19 + # --code_exec_ssh_pass="toor"
docker/run/fs/exe/run_searxng.sh
+5 -2
@@ -1,7 +1,10 @@
1 #!/bin/bash
2
3 # start webapp
4 -sudo -H -u searxng -i
4 cd /usr/local/searxng/searxng-src
5 export SEARXNG_SETTINGS_PATH="/etc/searxng/settings.yml"
7 -python searx/webapp.py
\ No newline at end of file
6 +
7 +# activate venv
8 +source "/usr/local/searxng/searx-pyenv/bin/activate"
9 +
10 +exec python /usr/local/searxng/searxng-src/searx/webapp.py
docker/run/fs/exe/supervisor_event_listener.py new
+47
@@ -0,0 +1,47 @@
1 +#!/usr/bin/python
2 +import sys
3 +import os
4 +import logging
5 +import subprocess
6 +import time
7 +
8 +from supervisor.childutils import listener
9 +
10 +
11 +def main(args):
12 + logging.basicConfig(stream=sys.stderr, level=logging.DEBUG, format='%(asctime)s %(levelname)s %(filename)s: %(message)s')
13 + logger = logging.getLogger("supervisord-watchdog")
14 + debug_mode = True if 'DEBUG' in os.environ else False
15 +
16 + while True:
17 + logger.info("Listening for events...")
18 + headers, body = listener.wait(sys.stdin, sys.stdout)
19 + body = dict([pair.split(":") for pair in body.split(" ")])
20 +
21 + logger.debug("Headers: %r", repr(headers))
22 + logger.debug("Body: %r", repr(body))
23 + logger.debug("Args: %r", repr(args))
24 +
25 + if debug_mode:
26 + continue
27 +
28 + try:
29 + if headers["eventname"] == "PROCESS_STATE_FATAL":
30 + logger.info("Process entered FATAL state...")
31 + if not args or body["processname"] in args:
32 + logger.error("Killing off supervisord instance ...")
33 + _ = subprocess.call(["/bin/kill", "-15", "1"], stdout=sys.stderr)
34 + logger.info("Sent TERM signal to init process")
35 + time.sleep(5)
36 + logger.critical("Why am I still alive? Send KILL to all processes...")
37 + _ = subprocess.call(["/bin/kill", "-9", "-1"], stdout=sys.stderr)
38 + except Exception as e:
39 + logger.critical("Unexpected Exception: %s", str(e))
40 + listener.fail(sys.stdout)
41 + exit(1)
42 + else:
43 + listener.ok(sys.stdout)
44 +
45 +
46 +if __name__ == '__main__':
47 + main(sys.argv[1:])
docker/run/fs/ins/pre_install.sh
+3 -1
@@ -11,7 +11,9 @@ apt-get update && apt-get install -y \
11 curl \
12 wget \
13 git \
14 - ffmpeg
14 + ffmpeg \
15 + supervisor \
16 + cron
17
18 # # Configure system alternatives so that /usr/bin/python3 points to Python 3.12
19 # sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.12 1