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

Add timeout option to docker run #1905

Closed
benbro opened this issue Sep 17, 2013 · 66 comments
Closed

Add timeout option to docker run #1905

benbro opened this issue Sep 17, 2013 · 66 comments

Comments

@benbro
Copy link

benbro commented Sep 17, 2013

This is a feature request to add a timeout option when calling the docker run command.
The option called be -to=5000 (5000 ms).

I'm using docker to run untrusted student code in a container (C, Python...).
It will be useful to be able to set a timeout to limit how much time the container is allowed to run before it docker stops it.

Thanks

@crosbymichael
Copy link
Contributor

@benbro Thanks for the idea. What other use cases for docker would this be needed for? I am trying to decide if this should be in the scope of docker itself or if this should be handled by an external tool.

@benbro
Copy link
Author

benbro commented Sep 17, 2013

I think it is useful in any case you use a container as executable.

A user can wrap the call to docker run with a timeout in bash:
timeout 5 docker run...
but than he'll need to take care of the cleanup which might not be trivial.

When the -rm option will be added, the container might be left if docker run is stopped or exited.
If docker will handle a timeout, this issue might be minimized.

A timeout could also be useful with the remote API.
If you are executing a command using the API with an HTTP request, you probably want it to timeout after some time and cleanup.

@tianon
Copy link
Member

tianon commented Sep 20, 2013

I've given this issue quite a bit of thought over the past several days, and here's the solution I've come up with that does the equivalent of a container timeout today, including cleanup, with no changes to the docker core: (I've got this script saved as docker-run-timeout.sh in my PATH.)

#!/bin/bash
set -e

to=$1
shift

cont=$(docker run -d "$@")
code=$(timeout "$to" docker wait "$cont" || true)
docker kill $cont &> /dev/null
echo -n 'status: '
if [ -z "$code" ]; then
    echo timeout
else
    echo exited: $code
fi

echo output:
# pipe to sed simply for pretty nice indentation
docker logs $cont | sed 's/^/\t/'

docker rm $cont &> /dev/null

Here's some example output from the script:

$ docker-run-timeout.sh 10s busybox sh -c 'echo start && sleep 30 && echo finish'
status: timeout
output:
    start
$ docker-run-timeout.sh 10s busybox sh -c 'echo start && sleep 5 && echo finish'
status: exited: 0
output:
    start
    finish
$ docker-run-timeout.sh 10s busybox sh -c 'echo start && sleep 5 && echo finish && exit 42'
status: exited: 42
output:
    start
    finish

Now, we don't get real-time output from the process as it runs this way, but if this were implemented somewhere outside the shell (such as in a Go library consuming the docker API proper), that should be easier to implement than it is here.

@crosbymichael
Copy link
Contributor

I still believe this is out of the scope of docker. Docker provides all the primitives required to do what is required.

Also the -rm option has been added now.

@andreisoare
Copy link

@benbro what about running timeout inside docker?

docker run --rm ubuntu timeout 2 sh -c 'echo start && sleep 30 && echo finish'

@benbro
Copy link
Author

benbro commented May 29, 2014

@andreisoare thanks for the suggestion.
I think that a time in the host or in the container will work.
Having a timeout option as a parameter to docker run feels cleaner to me but I can live without it :)

@yoones
Copy link

yoones commented Dec 17, 2014

+1

docker run --rm ubuntu timeout 2 sh -c 'echo start && sleep 30 && echo finish'

When you run untrusted code, a timeout inside your container cannot be trusted.

Consider the following:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
  int pid;

  if ((pid = fork()) == 0)
    {
      setsid();
      sleep(5);
      printf("Done here.\n");
    }
  else if (pid)
    wait(NULL);
  return (0);
}

In this case: timeout 2 ./a.out, the child process will stay alive after the timeout.
Only a timeout on the container itself can really be effective since it can't be affected by what happens inside the container.

@tianon
Copy link
Member

tianon commented Dec 19, 2014

In the case you describe, the parent process would exit, and it would be pid 1, so the container would be destroyed and thus the child process also killed and reaped with the entire pid namespace.

@yoones
Copy link

yoones commented Dec 19, 2014

@tianon The point is that you shouldn't trust anything that's happening inside your container when you run untrusted code inside it. That's why I would prefer being able to set a timeout on the container itself.

To give you another example:

$> cat ./test.sh

#!/bin/bash

pid=$(ps ax | grep timeout | grep -v grep | cut -d ' ' -f 2)
kill -19 $pid
sleep 5
echo "Done."
kill -18 $pid

$> timeout 2 ./test.sh

@imdreamrunner
Copy link
Contributor

Thanks for the discussing. I am looking for exactly the same thing discussed in this issue.

@chopfitzroy
Copy link

@tianon would it be possible to extend your script to work with infinite loops, at the moment the container gets stuck processing the request and is not killed.

http://stackoverflow.com/questions/31266529/killing-a-docker-container-running-an-infinite-process

ie:

while True:
   print "infinite loop"

Will get stuck.

@domino14
Copy link

domino14 commented Jul 8, 2015

+1 @CrashyBang

@titpetric
Copy link

If you run a docker entrypoint, which is a cli program (and not a deamon), would a viable option of limiting execution time be with the available programs, like timelimit? ie:

timelimit -t 20 -T 10 docker run -it ubuntu bash ...?

I'm doing some long running processing from a docker image, and need to limit the execution time of each docker run. I could do it with timelimit inside (entrypoint) or outside of docker as suggested... only thing I'm worried about if some kind of cleanup (--rm=true) will work with signal 15/9 sent to docker.

@pjebs
Copy link

pjebs commented Feb 29, 2016

Could someone please reconsider implementing this feature. It is trivial for Docker to add this timeout option but would make our implementations more elegant and less error prone.

In understand that it is not strictly necessary as per @crosbymichael but it opens up possibilities like this: kubernetes/kubernetes#21925

@domino14
Copy link

it's probably not trivial...

@pjebs
Copy link

pjebs commented Feb 29, 2016

It's hard to believe it wouldn't be trivial. It seems to be a policy decision that is preventing the team.

@thaJeztah
Copy link
Member

Perhaps think of something like a "killer" / garbage-collect container, containing a cron-job, that kills any container that's started with a specific label and has been running for xx seconds

@pjebs
Copy link

pjebs commented Feb 29, 2016

I'm aware of lots of work-arounds. But they all seem like overkill considering how much more elegant a -to option would be to docker run

@thaJeztah
Copy link
Member

Implementing it in docker would involve starting timers for these containers, and implementing logic to stop those containers; it may sound trivial, but can result in having to add a lot of moving parts that has to be supported and maintained.

@pjebs
Copy link

pjebs commented Mar 16, 2016

Firstly when you type in docker ps -a you can clearly see how long a container has been running for.
Secondly we know that the user can always type in docker rm -f XXXXX to force-destroy a container.

Therefore we know both essential functionality are already implemented.
Therefore it is TRIVIAL to implement a -to (timeout) parameter to the docker run command by combining the above-mentioned functionality.

@pjebs
Copy link

pjebs commented Mar 16, 2016

(worst-case scenario) Perhaps a few hours to code and perhaps max 1 week of unit testing after it has been implemented.

@titpetric
Copy link

Fyi, timelimit wrap around docker run seems to work as expected. Why is it not good enough? Perhaps you would be fine with more detailed documentation as to what happens to the docker container if not detached and gets a kill 15/9? If 'screen' or 'tmux' is fine for putting a foreground process in the background, why is timelimit not enough to kill a process and it's children after some time?

@espadrine
Copy link

I am probably misunderstanding the situation, but isn't docker run --ulimit cpu=10 enough to stop the process after 10 seconds? (It does not work locally, but I would expect it to work.)

@ondrejhlavacek
Copy link

@DesktopMan You can run the cleanup manually afterwards.

timeout 5 docker run -it --name mycontainer --rm continuumio/anaconda3 /bin/bash
docker rm mycontainer

@DesktopMan
Copy link

The container doesn't even stop though, is there documentation on how docker run reacts to signals anywhere?

@ondrejhlavacek
Copy link

@DesktopMan you have to specify the signal which is sent from the timeout command. Fixing my previous example. This works for me:

$ timeout --signal=SIGKILL 5 docker run --rm -it alpine /bin/sh
/ # Killed
$

@DesktopMan
Copy link

Interesting. That still doesn't stop the container for me.

@ondrejhlavacek
Copy link

ondrejhlavacek commented Nov 6, 2017

@DesktopMan Interesting. You're right! The container is still running. So a docker kill or docker rm -f is required afterwards.

@saden1
Copy link

saden1 commented Dec 3, 2017

This is really a useful feature for testing purpose and any situation where ephemeral containers could be of use (i.e providing a customer a short lived sandbox environment to play with).

@soullivaneuh
Copy link

You are regularly talking about timeout 5 docker run... on command line. What about the Docker API? I'm currently trying to manage timeout with docker running with docker-php using the API, and this is not a solution.

The implementation of a timeout option on the docker API should be reconsidered.

@nevmerzhitsky
Copy link

nevmerzhitsky commented Apr 24, 2018

If the main process of a container does an infinite loop or sleep then timeout will don't stop the container in due time. Thus the solution should use docker rm/kill to stop the container. I wrote this Bash script to achieve the goal.

The main benefit over previous solutions is that if the script process will be killed then it send docker rm before dying. This reduce a risk of orphan non---rm containers on an accident in a host OS.

@pipehappy1
Copy link

It would be really handy if docker run has the timeout option. Use timeout is like in order to shutdown a computer, you need to pull the power plug off the wall instead of push some button on the machine itself.

@fulltek
Copy link

fulltek commented Jun 7, 2018

+1 @pipehappy1
It would do help

@hifall
Copy link

hifall commented Jun 27, 2018

Anyone else tried the --stop-timeout option yet? Did it work for you?

I wasn't lucky enough to make it work last time I tried.

@chrisgavin
Copy link
Contributor

@hifall That doesn't look relevant to me. It doesn't limit the time a container can run for, it just limits how long the container has to stop gracefully when it gets stopped.

@jtpavlock
Copy link

jtpavlock commented Oct 4, 2018

This may not work for everyone, but it was good enough for my use:

docker stop -t 15 $(docker run -d --stop-signal 2 --rm $(docker build -q .))

I utilized the fact that docker stop will send a SIGTERM by default, and then after a specified timeout, it will send a SIGKILL to the container. Normally, this would still stop the container once the command is issued (most things will stop to SIGTERM), but by setting --stop-signal 2 (or really any signal that isn't SIGTERM or SIGKILL) you can tell the container not to respond to a SIGTERM signal.

This means docker stop will send the SIGTERM, have no effect, and then after a timeout will send a SIGKILL which will kill the running container.

If you need to run the container interactively, the two commands (run and stop) will have to be split and run in different processes e.g. open a new shell.

@v-karbovnichy
Copy link

My need is to stop (and possibly remove) a container after a week. A team of unreliable human testers should not be responsible for container deletion.
Currently, as a workaround, I have to create some periodic job on something like Jenkins to check and stop such containers.

@renannprado
Copy link

I was looking for the feature and ended up in this issue.

My use case is the following: inside of CI/CD you might have a number of containers fired to complete CI/CD tasks. In case the process managing these containers (kind of), which in this case would be the pipeline itself, crashes/dies, potentially you can have zombie containers running forever inside of your infra - or at least until they pile up and you discover that you have to kill them.

I think it's not hard to implement a "watcher" container that would do the cleanup/garbage collection, but still would be nice to have such a flag in Docker itself.

I won't argue though about how hard or easy it is to implement such a feature, as I've never seen the Docker codebase to know, so I wouldn't underestimate the effort.

@jayaprabhakar
Copy link

@ScottG489 As @jtpavlock mentioned, there are a number of things that has to be done to use stop work as we wanted. The requirement is, when we start a container, we want to set the maximum user time, the system can run after which, it should be stopped.
Docker stop sends a SIGTERM immediately. The --time option specifies, is the time to wait to send SIGKILL. They are significantly different.

Each solution proposed here are hacks not the cleanest solution. For example, we have to run 2-3 commands, to get it to work. In @jtpavlock's solution, he explicitly catches the sigterm and ignores it. That means, you are not giving any time for the service running in the container to gracefully handle sigterm and do cleanups.

The hack I proposed has a different issue. You need to run even more commands - uuidgen, sleep, docker run and docker stop. It changes the container name :( It may be applicable to cases where you need a specific name...

@ScottG489
Copy link

Sorry I deleted my comment on you @jayaprabhakar. I was under the impression that flag was a timeout to stop, not a timeout between SIGTERM and SIGKILL and I shortly discovered this thereafter my comment. I feel like a better name could have been used for that flag.

In any case yeah, it's unfortunate these hacks are necessary, but I can see where they are coming from with wanting to be careful to not bloat on features.

@vafakaramzadegan
Copy link

I had this issue and after searching a while, I ended up in here. I couldn't find a solution to this problem, so I wrote a bash script to remove containers which have been running for more than a specific time. the code is available here:

https://github.com/vafakaramzadegan/kill-old-containers

@esabol
Copy link

esabol commented Jul 25, 2021

@vafakaramzadegan wrote:

I had this issue and after searching a while, I ended up in here. I couldn't find a solution to this problem, so I wrote a bash script to remove containers which have been running for more than a specific time. the code is available here:

https://github.com/vafakaramzadegan/kill-old-containers

You might want to use docker ps -a --format "{{.ID}}:{{.CreatedAt}}" (or docker container ls --format "{{.ID}}:{{.CreatedAt}}") or loop over each container ID and use docker inspect --format='{{.State.StartedAt}}' <CONTAINERID> | xargs date +%s -d [credit] instead of parsing "5 minutes ago" or "n days ago", depending on how accurate you want to be. Once a container is more an a minute old, those strings are just rough approximations.

@fbrodrigorezino
Copy link

I've read the comment from one of the maintainers that it's out of the scope.
I'm curious if just one person can make that call, search online, and we will find hundreds of people trying to make it work for them for different reasons. Just because it's not a necessity for you doesn't mean that it is not necessary for developers.

It would be very useful for the development environment or CI/CD process. We try all the ways not to have dangling containers lost in machines, but there are always exceptions in code/ process etc, that leave garbage behind. Sure, we can keep adding up guardrails to prevent it, but that is the point. It could be made much easier for default options. When we are talking about non controlled environment this scenarios just get worst (like devs machines)

@W2Wizard
Copy link

W2Wizard commented Aug 7, 2023

After almost a decade this is still being requested, can we please have this ?

@esabol
Copy link

esabol commented Aug 7, 2023

After almost a decade this is still being requested, can we please have this ?

I wanted this feature at first too, but the workaround is so simple and works so well: Just preface the command you are running inside the Docker container with /usr/bin/timeout SECONDS. For me, it's not worth the effort to add this feature to Docker itself.

https://man7.org/linux/man-pages/man1/timeout.1.html

@jp-clumio
Copy link

jp-clumio commented Aug 8, 2023

@esabol Please read the previous comments.

Using timeout inside the container can't be trusted in many cases, if you cannot fully trust what's running in the container.

Running the timeout on the host is reliable but it does not do the cleanup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests