Dave
Dave

Reputation: 997

Why can't I always kill a docker process with Ctrl-C?

I have a script which I want to optionally run within a container. I have observed that if I run an intermediate script it can be killed with Ctrl-C, however if I do not then it can't.
Here is an example:
test1.sh:

#!/bin/bash

if [ "${1}" = true ]; then
  while true; do echo "args: $@"; sleep 1; done
else
  docker run --rm -it $(docker build -f basic-Dockerfile -q .) /test2.sh $@
fi

test2.sh:

#!/bin/bash

/test1.sh true $@

basic-Dockerfile:

FROM alpine:3.7

RUN apk add --no-cache bash
COPY test1.sh test2.sh /
ENTRYPOINT ["bash"]

Running ./test1.sh true foo bar will happily print out true foo bar, and running ./test1.sh foo bar will do the same in a container. Sending Ctrl-C will kill the process and delete the container as expected.
However if I try to remove the need for an extra file by changing /test2.sh $@ to /test1.sh true $@:
test1.sh

#!/bin/bash

if [ "${1}" = true ]; then
  while true; do echo "args: $@"; sleep 1; done
else
  docker run --rm -it $(docker build -f basic-Dockerfile -q .) /test1.sh true $@
fi

then the process can no longer be terminated with Ctrl-C, and instead must be stopped with docker kill.

Why is this happening?

Docker version 18.06.1-ce running on Windows 10 in WSL

Upvotes: 0

Views: 4185

Answers (2)

vozman
vozman

Reputation: 1416

You can also you can also use exec to replace the current shell with a new one which can be stopped with ctrl-c For example start.sh script which starts nginx server and runs uwsgi

#!/usr/bin/env bash

service nginx start
uwsgi --ini uwsgi.ini

should changed to

#!/usr/bin/env bash

service nginx start
exec uwsgi --ini uwsgi.ini

After theese changes ctrl c will stop the container

Upvotes: 3

Ignacio Millán
Ignacio Millán

Reputation: 8026

That's a common misunderstanding in docker but it's for a good reason.

When a process run as PID 1 in Linux it behaves a little different. Specifically, it ignores signals as SIGTERM (which you send when hitting Ctrl-C), unless the script is coded to do so. This doesn't occur when PID > 1.

And that's why your second scenario works (The PID 1 is script2.sh, which delegates the signal in script1.sh, which stops because it is not PID1) but not the first one (script1.sh is PID 1 and thus it doesn't stop with SIGTERM).

To solve that, you can trap the signal in script1.sh and exit:

exit_func() {
        echo "SIGTERM detected"            
        exit 1
}
trap exit_func SIGTERM SIGINT

Or tell docker run to init the container with a different process as PID 1. Specifically, if you add --init to docker run with no more arguments, it uses a default program, tini, prepared to handle these situations:

docker run --rm -it --init $(docker build -f basic-Dockerfile -q .) /test1.sh true $@

Upvotes: 6

Related Questions