Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot kill or detach a docker container using Ctrl-C or Ctrl-\ #2838

Closed
vmalloc opened this issue Nov 24, 2013 · 70 comments

Comments

@vmalloc
Copy link

commented Nov 24, 2013

Hi,

Running docker 0.6.7 on Ubuntu 13.04.

I built a container whose CMD executes a uwsgi process (also tried with other executables). I run without detaching, and sigproxy is True.

Hitting Ctrl-C seems to have no effect over the running container and the only way to leave it or kill it is with docker kill and docker stop...

Is this a known issue?

@lhazlewood

This comment has been minimized.

Copy link

commented Nov 25, 2013

Starting with docker 0.6.5, you can add -t to the docker run command, which will attach a pseudo-TTY. Then you can type Control-C to detach from the container without terminating it.

If you use -t and -i then Control-C will terminate the container. When using -i with -t then you have to use Control-P Control-Q to detach without terminating.

Test 1:

$ ID=$(sudo docker run -t -d ubuntu /usr/bin/top -b)
$ sudo docker attach $ID
Control-C
$ sudo docker ps

The container is still listed.

Test 2:

$ ID=$(sudo docker run -t -i -d ubuntu /usr/bin/top -b)
$ sudo docker attach $ID
Control-C
$ sudo docker ps

the container is not there (it has been terminated). If you type Control-P Control-Q instead of Control-C in the 2nd example, the container would still be running.

A pull request to fix the docs for the Hello World daemon sample is here:

#2845

I'm unaware of where you saw the recommendation to Control-C outside of this example. If you saw this reference somewhere else, can you please submit a new pull request to fix the docs you referenced?

You might also find this mailing list thread helpful.

@alexlarsson

This comment has been minimized.

Copy link
Contributor

commented Nov 25, 2013

@lhazlewood Well, -t -d lets you kill the container with ctrl-c in the docker attach, however the problem is:

$ ID=$(docker run -d ubuntu bash -c "while true; do echo foo; sleep 5; done")
$ docker attach $ID

In this case ctrl-C does nothing. I expected it to kill the "docker attach" process (not the container).

@alexlarsson

This comment has been minimized.

Copy link
Contributor

commented Nov 25, 2013

If the daemon was not started with -t, i think docker attach should default to -sig-proxy=false

@vmalloc

This comment has been minimized.

Copy link
Author

commented Nov 25, 2013

From what I can gather you need both -t and -i for Ctrl-C to work as expected...

@alexlarsson

This comment has been minimized.

Copy link
Contributor

commented Nov 25, 2013

@vmalloc That depends on what you expect. I expect to be able to detach from "docker attach" with ctrl-c, not kill the daemon. That is not currently possible without manually specifying -sig-proxy=false. If you accidentally do this the only way out is to kill the docker attach process from another terminal.

@alexlarsson

This comment has been minimized.

Copy link
Contributor

commented Nov 25, 2013

Ah, sorry I was a bit confused above. If you run "docker run -d -t" and the docker attach, then ctrl-c does detach, not kill the daemon. The problem is that if you forgot the -t, and then ever use docker attach you end up with something you can't detach without killing it from a different terminal.

@lhazlewood

This comment has been minimized.

Copy link

commented Nov 25, 2013

@alexlarsson Yes, the two test cases I show above show what happens w/ and w/o -i.

@dmp1ce

This comment has been minimized.

Copy link

commented Jan 27, 2014

Yes, I am experiencing the same issue. An easy way to reproduce is docker run busybox sleep 60 and then CTRL-C all you want. The command will not terminate or detach until the 60 seconds is up. I would expect a CTRL-C to send a SIGTERM to the sleep command, which should stop the docker instance.

@lhazlewood

This comment has been minimized.

Copy link

commented Jan 27, 2014

Also see #2855

@cpuguy83

This comment has been minimized.

Copy link
Contributor

commented Aug 14, 2014

To have ctrl+c stop the container you must use -it
To detach from the container you should use ctrl+pq

Closing.

@cpuguy83 cpuguy83 closed this Aug 14, 2014

@lhazlewood

This comment has been minimized.

Copy link

commented Aug 14, 2014

Why was this closed? Can you please explain the reason?

I asked because it is obvious that the majority of the community does not want the current default behavior.

Also note that ctrl+pq does not work on Mac OS X running Ubuntu in VirtualBox.

@cpuguy83

This comment has been minimized.

Copy link
Contributor

commented Aug 14, 2014

@lhazlewood Because the issue wasn't about ctrl+pq it was about getting out of attach with ctrl-c, which is not supported since "attach" is litterally attaching you to the running process and should be expected behavior.
If you just want the stdout+stderr streams you should use docker logs not attach.
I see this as a training issue and not a Docker issue.

If we want to discuss changing ctrl-pq, and I know I've seen other issues around it that's fine.
I'm not sure that we can change it at this point.
We talked about introducing a configurable command for it but was implemented server side and just wasn't ideal and implementing it client-side is difficult.

And I write all this in the friendliest of tone, I'm terse by nature, sorry about that.

@rolkar

This comment has been minimized.

Copy link

commented Jun 18, 2015

I am VERY confused by all this.

The default behaviour when being in a shell prompt is that ^C kills the running foreground process. And if no such process exists, it just do nothing, except maybe show a new prompt. ^D on the other hand do exit the shell, without stopping the machine. This is what I expect, because this is what everyone else do.

So, why do Docker do it differently? And why do I read this strange discussion? Am I missing something?

@cpuguy83

This comment has been minimized.

Copy link
Contributor

commented Jun 19, 2015

@rolkar Because there are two foreground processes in this case.
The process you're attached to in the container and the docker client.

I personally think this should behave in much the same way as SSH, though with a different escape sequence.
FYI SSH's escape sequence is <enter>~.

@Vanuan

This comment has been minimized.

Copy link

commented Mar 7, 2016

Still don't get it. Why Ctrl-C doesn't send SIGINT?

@rolkar

This comment has been minimized.

Copy link

commented Mar 8, 2016

@Vanuan - because the process is running in a docker daemon and you only attach to that daemon's tty. Lets say that ^C would kill the process that the daemon has started. Then you would kill the container daemon. Not unreasonable. Maybe what you and I want (in most cases). But, it is not obviously the right thing to do. And some have other opinions.

@Vanuan

This comment has been minimized.

Copy link

commented Mar 8, 2016

Yeah, I figured it out. Docker actually sends SIGINT, but the kernel ignores it because process id is 1. And since the process doesn't have its own SIGINT handler, nothing happens.

@cpuguy83

This comment has been minimized.

Copy link
Contributor

commented Mar 8, 2016

The kernel doesn't ignore it, the process does.

@Vanuan

This comment has been minimized.

Copy link

commented Mar 8, 2016

@cpuguy83
So you're saying that if pid is not 1, SIGINT is sent to the parent process?

@cpuguy83

This comment has been minimized.

Copy link
Contributor

commented Mar 8, 2016

@Vanuan The signal is always sent to the process you specified, but some processes don't load signal handlers when they are pid 1, so the signal gets ignored.
The only way to kill it is to kill -9, in which case kernel terminates the process rather than the process terminating itself.

@Vanuan

This comment has been minimized.

Copy link

commented Mar 9, 2016

Doesn't appear to be true:

// main.c
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

void catch_interrupt(int sig) {
  printf("Interrupting...\n");
}

int main() {
  signal(SIGINT, catch_interrupt);
  sleep(10);
  return 0;
}
$ docker run -it --rm -v `pwd`:/src -w /src iron/gcc:dev gcc main.c -o main
$ docker run -it --rm -v `pwd`:/src -w /src iron/gcc:dev ./main
^CInterrupting...

Same setup with signal commented out:

$ docker run -it --rm -v `pwd`:/src -w /src iron/gcc:dev ./main
^C^C^C^C^C
# nothing happens

As you see, signal handler is loaded. And even though PID remains to be "1", it responds to Ctrl-C and terminates (though we didn't ask it to terminate, we only handled it). But when there's no signal handler registered, it doesn't respond to Ctrl-C and nothing happens.

@cpuguy83

This comment has been minimized.

Copy link
Contributor

commented Mar 9, 2016

@Vanuan Yes, this is exactly as I said.
The signal is still sent to the program, the program just doesn't respond to it.

@Vanuan

This comment has been minimized.

Copy link

commented Mar 9, 2016

I referred to this part:

some processes don't load signal handlers when they are pid 1

Does it mean something else? Signal handlers are always loaded when they're registered.

But it appears that there are some default signal handlers which are not loaded if pid = 1 AND signal handler is not registered. Is that what you meant?

@cpuguy83

This comment has been minimized.

Copy link
Contributor

commented Mar 9, 2016

@Vanuan exactly.

@johshoff

This comment has been minimized.

Copy link

commented Mar 9, 2016

I'm still confused about this. Example run:

docker run -i $IMAGE
ping google.com
PING google.com (216.58.217.46) 56(84) bytes of data.
64 bytes from 216.58.217.46: icmp_seq=1 ttl=61 time=31.5 ms
^C64 bytes from 216.58.217.46: icmp_seq=1 ttl=61 time=31.5 ms
^C^C^C^C^C^C^C^C64 bytes from 216.58.217.46: icmp_seq=1 ttl=61 time=31.5 ms

So ctrl+c doesn't send SIGINT to either the docker process or the ping process. Is docker stop my only option here? (ctrl+p, ctrl+c does also not work. I'm on OSX)

@cpuguy83

This comment has been minimized.

Copy link
Contributor

commented Mar 10, 2016

@johshoff It depends on how ping was started by the image.
Is it running inside a /bin/sh? If so, the signal is sent to /bin/sh, which ignores signals when run as pid 1.

@johshoff

This comment has been minimized.

Copy link

commented Mar 10, 2016

Thanks, @cpuguy83, that makes it clearer. ping was indeed running under /bin/sh.

@Vanuan

This comment has been minimized.

Copy link

commented Sep 19, 2017

Cool! Is there a corresponding option for compose?
Looks like there's not: docker/cli#51
So for swarm and if you use docker-compose you'd still need tini.

@thaJeztah

This comment has been minimized.

Copy link
Member

commented Sep 19, 2017

It's possible to enable the init option as a default (by setting a daemon option); then all container, including those started as part of a service get --init. There's a PR in progress docker/cli#479

@esodot

This comment has been minimized.

Copy link

commented Jan 30, 2018

Can't detach with CTRL+P,CTRL+Q in my Ubuntu 17.04 OS. Arrrghh

@khs1994 khs1994 referenced this issue Mar 19, 2018

Closed

Ctrl + c can't stop command lnmp-php #280

2 of 2 tasks complete

dsingleton added a commit to dsingleton/unit-tracker that referenced this issue Mar 25, 2018

Dockerise the project
Add a basic Dockerfile, using a lightweight node image to speed and size.
Structure the Dockerfile to take advantage of layer caching.

Dockerfile based on:
- https://nodejs.org/en/docs/guides/nodejs-docker-webapp/
- https://docs.docker.com/engine/reference/builder/

About the Alpine base image:
- https://github.com/mhart/alpine-node
- https://medium.com/@iamnayr/a-multi-part-analysis-of-node-docker-image-sizes-using-yarn-vs-traditional-npm-2c20f034c08f

Structuring npm install for layer caching:
- http://bitjudo.com/blog/2014/03/13/building-efficient-dockerfiles-node-dot-js/

Docker run command:
- `--rm` to destroy the container after it exits. Not doing this can cause
  unused containers to build up and consume resources
  jupyter/docker-stacks#476 (comment)
- `-it` so Control-C can terminate the container
  `-i, --interactive                    Keep STDIN open even if not attached`
  `-t, --tty                            Allocate a pseudo-TTY`
  See moby/moby#2838
- `-p` to map the port of the server inside the container to the host machine

Good background on Docker principles and details:
- https://github.com/wsargent/docker-cheat-sheet
@felipekm

This comment has been minimized.

Copy link

commented Apr 6, 2018

1 year later we still can't Ctrl+C a docker container

Now it's 2 years 🎂
Still getting the error on MacOSX.

@thaJeztah

This comment has been minimized.

Copy link
Member

commented Apr 6, 2018

@felipekm not helpful: what problem are you running into? Have you read this thread?

@felipekm

This comment has been minimized.

Copy link

commented Apr 6, 2018

Yes I have @thaJeztah, I'm just trying to kill a process initialized bydocker run -ti <image_id>.

@montanaflynn

This comment has been minimized.

Copy link

commented Apr 6, 2018

@felipekm in another terminal type docker ps and then docker stop <container_id>

If you want Ctrl+C to work you'll need to change your docker container to respond to SIGINT.

@aalexgabi

This comment has been minimized.

Copy link

commented Apr 13, 2018

Update: This has been answered here #37200

Real world use-case: running Docker container with tests inside CI with:

docker run -ti my-image my-test-script

The CI does not support TTY so I get:

the input device is not a TTY
stdin: is not a tty
ERROR: Job failed: exit status 1

How do I propagate a SIGTERM up to the container entrypoint when I don't have a TTY?

My desired process tree:

ci-runner
  bash
    docker run -ti my-image my-test-script

CI runner will send a SIGTERM to the bash script that will propagate to the docker run but since there is no TTY support it can't run with -ti.

This works in my terminal.

@tsl0922

This comment has been minimized.

Copy link

commented Apr 28, 2018

@aalexgabi This what I used on CI (jenkins):

# Fix docker signal proxy issue without tty
function docker() {
    case "$1" in
        run)
            shift
            if [ -t 1 ]; then # have tty
                command docker run --init -it "$@"
            else
                id=`command docker run -d --init "$@"`
                trap "command docker kill $id" INT TERM
                command docker wait $id
            fi
            ;;
        *)
            command docker "$@"
    esac
}

# export to sub-shell
export -f docker
@guiambros

This comment has been minimized.

Copy link

commented Jun 3, 2018

@felipekm Try adding --init when starting your container. This will start Tini as PID 1, and Tini will ensure your application gets SIGINT (instead of being intercepted by /bin/sh).

More details in the thread above.

@rayfoss

This comment has been minimized.

Copy link

commented Jul 3, 2018

In summary:

  1. docker run -it won't be enough if the program you run doesn't handle it. specially problematic if it's not your program... such as is the case when another program interprets your program in a limited way.
  2. you can make ENTRYPOINT ["/sbin/tini", "--"] the entry point... instead of your app.
  3. use docker run -it --init which will solve all your problems.

@guiambros that is so useful it's going in my aliases.

@Vanuan

This comment has been minimized.

Copy link

commented Jul 4, 2018

Also for swarm mode:

The corresponding compose change was recently merged, so in the next version you'd be able to do this:

version: '3.7'
services:
  myservice:
    image: myimage
    init: true

Finally you won't have to modify images just to add tini so that your containers aren't killed (exit code 137) when not responding to SIGTERM. Especially useful for databases to prevent data loss.

@thaJeztah

This comment has been minimized.

Copy link
Member

commented Jul 4, 2018

Finally you won't have to modify images just to add tini so that your containers aren't killed (exit code 137) when not responding to SIGTERM. Especially useful for databases to prevent data loss.

Just adding this as additional information (some of which is mentioned in earlier comments);

While using --init (or adding tini in your image) can be a quick way to help with images that don't handle signals properly, be sure you're not "masking" an actual problem with the image.

Tini provides the following features;

  1. reaping of processes; any (Linux) process that spawns child processes is also responsible for reaping those processes once the parent process is terminated. This means that tini should not be needed, unless the container's main binary violates this rule (unfortunately, there's some examples out there).
  2. it is started as PID-1, which means that the container's main process now becomes PID-2. PID-1 is "special"; killing PID-1 means killing the "machine" (or in context of Docker; killing the container). Some processes therefore behave different if they are executed as PID-1; they will ignore (some) signals, to prevent being killed (as that would kill the machine, which is usually not desirable, unless you want to shutdown or reboot).

If you know that the process running inside the container is a "bad actor", and doesn't handle reaping, using --init is definitely a good choice to get you going. Be aware though, that you're effectively running into a bug; be sure to report the issue with the maintainer/publisher of the software you're running in the container: perhaps they're not aware of this situation, and can fix it.

If you're using --init because of 2., this may be because the image you're running was not well-designed; Is the container's main process actually the main process, or is it running in a shell?

For example; the following CMD will start a shell (/bin/sh or /bin/bash, depending on the image) in which mysqld is started;

CMD /usr/sbin/mysqld

Because of this, /bin/sh (not mysqld) has become the container's main process (PID-1), and any signal sent to the container will be handled by /bin/sh (and not forwarded to mysqld). Using --init will "resolve" that situation (/bin/sh now runs as PID-2, and mysqld runs as PID-3), but still runs an unnescessary shell process in the container.

To make the container's process run without a shell, use the JSON ("exec" form). For example, below is the CMD for the official mysql image;

CMD ["mysqld"]

But what if you need to run some commands when starting the container (before running the container's main process), such as setting permissions, or doing some setup the first time the container is run?

These steps can be done in an entrypoint-script. The entrypoint script can run a shell, perform initialization (see for example the init steps in the official WordPress entrypoint script), and at the end switch to the container's main process using exec.

When using exec, the current process is replaced with another process, so when using exec at the end of the entrypoint script, the container's main process will run as PID-1, and not be running inside a shell; here's the last line of the WordPress entrypoint script;

exec "$@"

The Dockerfile uses both an ENTRYPOINT and CMD, in which case CMD is used as parameter for the ENTRYPOINT;

ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["apache2-foreground"]

exec "$@" will thus default to exec apache2-foreground, but will be replaced with the command that's used to start the container (for example docker run mysql echo "hello", will run the entrypoint script, then exec echo "hello")

TL;DR; the --init option is safe to use, and may help your situation, but be sure you're not tapering over the actual problem in the image 😄

@aalexgabi

This comment has been minimized.

Copy link

commented Jul 13, 2018

Update: This has been answered here #37200

@rayfoss @thaJeztah Again --init and -ti are not a solution when you don't have a TTY see #37200

I would like docker run myScriptImage to be the exactly the same as running ./myScript except that it runs in a isolated and controlled environment. I would like that Ctrl-C works out of the box as for "any other script". It would be ideal if in any bash script I could replace any system command with an equivalent docker run myEquivalentCommand and have it work the same way as if I launched a the command locally (stdin, stdout, stderr, exit code, input buffering, output buffering, handling of signals etc.).

@aalexgabi

This comment has been minimized.

Copy link

commented Jul 13, 2018

Update: This has been answered here #37200

@tsl0922 Thank you but that's what I'm trying to avoid doing. I feel that I might be doing a command called wrap-docker-run-as-if-is-a-regular-program what will handle all that using bash traps instead of copy-pasting that code in all ci pipelines (there are 30 or so.)

@Vanuan

This comment has been minimized.

Copy link

commented Jul 15, 2018

@aalexgabi

--init and -ti are not a solution when you don't have a TTY see #37200

TTY has nothing to do with being able to send a signal or catch it. Please read my messages in that issue thread you mentioned.

@bidiu

This comment has been minimized.

Copy link

commented Sep 5, 2018

One question: wonder what's the solution used by official images...say nginx. Nginx container can be terminated using SIGINT when not in detach mode.

@Vanuan

This comment has been minimized.

Copy link

commented Sep 5, 2018

@bidiu First of all, it uses exec form:

CMD ["nginx", "-g", "daemon off;"]

Secondly, nginx handles signals by its own.
And finally, it uses
STOPSIGNAL SIGTERM so that when you send SIGINT to docker in interactive mode or run docker stop container it would send SIGTERM, which would be handled by nginx to terminate itself

@Vanuan

This comment has been minimized.

Copy link

commented Sep 5, 2018

You could figure out it yourself by looking at its Dockerfile:
https://github.com/nginxinc/docker-nginx/blob/master/mainline/alpine/Dockerfile

@bidiu

This comment has been minimized.

Copy link

commented Sep 5, 2018

@Vanuan
Aha, didn't know the directive STOPSIGNAL before, and also not familiar with modes other than "exec form". Thanks for your clarification😊

@Vanuan

This comment has been minimized.

Copy link

commented Sep 5, 2018

So there's shell form and exec form. Shell form is used like this:

CMD nginx

Exec form like this:

CMD ["nginx"]

In first case the shell handles your signal, in the second case the nginx process

@vprasanth

This comment has been minimized.

Copy link

commented Oct 30, 2018

For folks coming here after not being able to stop a node process with a SIGINT, I found this other issue also very helpful in understanding this behaviour.

Amdingo added a commit to Amdingo/fleapday that referenced this issue Mar 14, 2019

adds -d
 thanks to moby/moby#2838
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.