25

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?

6 Answers 6

9

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 not KILL) 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}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks @ErikMD the first suggestion was exactly my problem. I was starting catalina.sh without exec and not reacting to Ctrl+C
This is not answering the question. The OP did the mistake of not using the wait built-in, but there is more to that.
6

The reason is that bash will not handle signals until the foreground process terminates, the sleep 10000 in your case. Your traps do work, but you would have to wait 10000 seconds first.

Your fix is

#!/bin/bash
pid=
trap 'echo SIGINT; [[ $pid ]] && kill $pid; exit' SIGINT
trap 'echo SIGTERM; [[ $pid ]] && kill $pid; exit' SIGTERM
echo Starting script
sleep 10000 & pid=$!
wait
pid=

Also you probably want to replace trapping specific signals with trapping any exit

trap 'echo EXIT; [[ $pid ]] && kill $pid; exit' EXIT

These and more are very well explained at http://mywiki.wooledge.org/SignalTrap#When_is_the_signal_handled.3F

1 Comment

Shouldn't the trap commands be single-quoted? (due to the kill "$pid" inside)
4

The solution I found was to just use the --init flag.

docker run --init [MORE OPTIONS] IMAGE [COMMAND] [ARG...]

Per their docs...

enter image description here

1 Comment

This fixed my issue with the stop button being unresponsive in Intellij IDEA when running/debugging in a Docker container.
1

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.

5 Comments

CTRL+C sends a signal to docker running on that console. That makes sense, however i'm still confused why SIGTERM is not being caught
You are setting a trap inside the container but sending the signal from its outside. It's not caught because it never reaches there
From what I understand: docker stop sends SIGTERM to the main docker process. The main docker process is bash /app/start.sh. Therefore, the bash process should be receiving SIGTERM, which I expect would write SIGTERM to stdout then exit. However, that does not seem to be happening.
That's right. You could try creating a file instead of an echo trap "touch term.tmp; exit" SIGTERM to check if the signal is trapped. Docker is not probably showing the echo if it ever happens.
Just tried, the file isn't being created either. It seems like the signal simply isn't being caught by the script
1

Try with something like the following script as your ENTRYPOINT:

#!/bin/bash

# Start the main process and save its PID
# Use exec to replace the shell script process with the main process
exec my_main_process &
pid=$!

# Trap the SIGTERM signal and forward it to the main process
trap 'kill -SIGTERM $pid; wait $pid' SIGTERM

# Wait for the main process to complete
wait $pidh

Source: https://www.linkedin.com/pulse/propagating-sigterm-signal-main-process-kubernetes-pod-chidambaram/

Comments

1

For anyone following the following and still not catching the signals, it may be that your entry command is not running in exec form (i.e. CMD ["/app/start.sh"] with square braces in your dockerfile!), see below answer: https://stackoverflow.com/a/72308559/2939369

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.