'Docker bash shell script does not catch SIGINT or SIGTERM
I have the following two files in a directory:
Dockerfile
FROM debian
WORKDIR /app
COPY start.sh /app/
CMD ["/app/start.sh"]
start.sh
(with permissions 755 using chmod +x start.sh
)
#!/bin/bash
trap "echo SIGINT; exit" SIGINT
trap "echo SIGTERM; exit" SIGTERM
echo Starting script
sleep 100000
I then run the following commands:
$ docker build . -t tmp
$ docker run --name tmp tmp
I then expect that pressing Ctrl+C would send a SIGINT to the program, which would print SIGINT to the screen then exit, but that doesn't happen.
I also try running $ docker stop tmp
, which I expect would send a SIGTERM to the program, but checking $ docker logs tmp
after shows that SIGTERM was not caught.
Why are SIGINT and SIGTERM not being caught by the bash script?
Solution 1:[1]
Actually, your Dockerfile
and start.sh
entrypoint script work as is for me with Ctrl+C, provided you run the container with one of the following commands:
docker run --name tmp -it tmp
docker run --rm -it tmp
Documentation details
As specified in docker run --help
:
- the
--interactive
=-i
CLI flag asks to keep STDIN open even if not attached
(typically useful for an interactive shell, or when also passing the--detach
=-d
CLI flag) - the
--tty
=-t
CLI flag asks to allocate a pseudo-TTY
(which notably forwards signals to the shell entrypoint, especially useful for your use case)
Related remarks
For completeness, note that there are several related issues that can make docker stop
take too much time and "fall back" to docker kill
, which can arise when the shell entrypoint starts some other process(es):
- First, when the last line of the shell entrypoint runs another, main program, don't forget to prepend this line with the
exec
builtin:exec prog arg1 arg2 ...
- But when the shell entrypoint is intended to run for a long time, trapping signals (at least
INT
/TERM
, but notKILL
) is very important;
{see also this SO question: Docker Run Script to catch interruption signal} - Otherwise, if the signals are not forwarded to the children processes, we run the risk of hitting the "PID 1 zombie reaping problem", for instance
{see also this SO question for details: Speed up docker-compose shutdown}
Solution 2:[2]
CTRL+C sends a signal to docker
running on that console.
To send a signal to the script you could use
docker exec -it <containerId> /bin/sh -c "pkill -INT -f 'start\.sh'"
Or include echo "my PID: $$"
on your script and send
docker exec -it <containerId> /bin/sh -c "kill -INT <script pid>"
Some shell implementations in docker might ignore the signal.
This script will correctly react to pkill -15
. Please note that signals are specified without the SIG
prefix.
#!/bin/sh
trap "touch SIGINT.tmp; ls -l; exit" INT TERM
trap "echo 'really exiting'; exit" EXIT
echo Starting script
while true; do sleep 1; done
The long sleep
command was replaced by an infinite loop of short ones since sleep
may ignore some signals.
Solution 3:[3]
The solution I found was to just use the --init
flag.
docker run --init [MORE OPTIONS] IMAGE [COMMAND] [ARG...]
Per their docs...
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | |
Solution 3 | Nickofthyme |