I'm running a very basic example of Python loop inside a Windows docker container, that I would like to gracefully stop.
The script is started this way in my dockerfile:
CMD [ "python.exe" , "./test.py"]
In the docker documentation it's said a SIGTERM signal is sent to the main command, so I'm trying to catch it this way:
import signal
import time
import logging, sys
class GracefulKiller:
kill_now = False
def __init__(self):
signal.signal(signal.SIGINT, self.exit_gracefully)
signal.signal(signal.SIGTERM, self.exit_gracefully)
def exit_gracefully(self,signum, frame):
self.kill_now = True
if __name__ == '__main__':
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
killer = GracefulKiller()
while True:
time.sleep(1)
logging.info("doing something in a loop ...")
if killer.kill_now:
break
logging.info("End of the program. I was killed gracefully :)")
In theory the signal should be caught by the handler, the boolean should toggle and the loop should exit and display my very last log line. It doesn't, it's just stopping the whole thing at the moment the signal is emitted (or 2-3 seconds later rather)
C:\Users\Administrator\Documents\Projects\test>docker-compose up
Recreating test_1 ... done
Attaching to test_1
test_1 | INFO:root:doing something in a loop ...
test_1 | INFO:root:doing something in a loop ...
test_1 | INFO:root:doing something in a loop ...
test_1 | INFO:root:doing something in a loop ...
test_1 | INFO:root:doing something in a loop ...
test_1 | INFO:root:doing something in a loop ...
test_1 | INFO:root:doing something in a loop ...
test_1 | INFO:root:doing something in a loop ...
test_1 | INFO:root:doing something in a loop ...
test_1 | INFO:root:doing something in a loop ...
test_1 | INFO:root:doing something in a loop ...
test_1 | INFO:root:doing something in a loop ...
test_1 | INFO:root:doing something in a loop ...
test_1 | INFO:root:doing something in a loop ...
Gracefully stopping... (press Ctrl+C again to force)
Stopping test_1 ... done
My last line log is never reached. Does anyone knows what's going on ? Is it a python specific issue, docker specific or Windows specific?
Also I tried to inspect the stopped container with docker logs, the last log isn't here either. Tried to add a sleep after it, same result.
Thanks,
docker rm -f The final option for stopping a running container is to use the --force or -f flag in conjunction with the docker rm command. Typically, docker rm is used to remove an already stopped container, but the use of the -f flag will cause it to first issue a SIGKILL.
Just Stopping the Container TL;DR: press ctrl+c then ctrl+d - that means, keep the ctrl key pressed, type a c, and let go of ctrl. Then the same with ctrl and d. If there's a non-shell process running, the combination is ctrl+c to interrupt it. Then you can exit the shell, or the container might exit already.
Press Ctrl-P, followed by Ctrl-Q, to detach from your connection. You'll be dropped back into your shell but the previously attached process will remain alive, keeping your container running.
If you would like to keep your container running in detached mode, you need to run something in the foreground. An easy way to do this is to tail the /dev/null device as the CMD or ENTRYPOINT command of your Docker image. This command could also run as the last step in a custom script used with CMD or ENTRYPOINT.
this seems to still be pervasive when using docker-compose up
in current versions which i've been investigating on Raspbian all morning (and led me here).
However running your example with docker-compose 2.1.1
via docker-compose up
with the following configs shows that the last line of your python code is actually called, you just can't see it:
services:
grace_test:
container_name: grace_test
build: .
FROM python:3.8-slim-buster
# setup WORKDIR
ADD . /grace_test
WORKDIR /grace_test
CMD ["python", "test.py"]
import signal
import time
import logging, sys
class GracefulKiller:
kill_now = False
def __init__(self):
signal.signal(signal.SIGINT, self.exit_gracefully)
signal.signal(signal.SIGTERM, self.exit_gracefully)
def exit_gracefully(self,signum, frame):
self.kill_now = True
if __name__ == '__main__':
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
killer = GracefulKiller()
while True:
time.sleep(1)
logging.info("doing something in a loop ...")
if killer.kill_now:
break
logging.info("End of the program. I was killed gracefully :)")
Use docker-compose logs
to inspect logs after Ctrl-C
:
$ docker-compose logs
grace_test | INFO:root:doing something in a loop ...
grace_test | INFO:root:doing something in a loop ...
grace_test | INFO:root:doing something in a loop ...
grace_test | INFO:root:doing something in a loop ...
grace_test | INFO:root:End of the program. I was killed gracefully :)
Just catch KeyboardInterrupt
and that's it.
if __name__ == '__main__':
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
try:
while True:
time.sleep(1)
logging.info("doing something in a loop ...")
except KeyboardInterrupt as ex:
print('goodbye!')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With