Speed up docker-compose shutdown
Asked Answered
S

2

16

When I set up my application to run via docker-compose up, it takes several seconds to stop on ctrl+c. However, if I run docker kill ..., the container stops very quickly. Is there anything I can do to speed up container shutdown when killed via ctrl+c within docker-compose up?

In particular, what does it actually mean when docker-compose says it is "gracefully stopping "? Is docker-compose trying some shutdown protocol and then killing my container only after a timeout?

Straphanger answered 25/5, 2018 at 17:57 Comment(0)
I
13

what does it actually mean when docker-compose says it is "gracefully stopping "?

Basically when you do Ctrl+C you are sending a SIGINT (2) signal to the application that runs in the foreground. Most of the time, the semantics of this signal is similar to the SIGTERM (15) signal that is raised when doing docker-compose stop or more basically docker stop if you are focusing on a single-container application.

Is docker-compose trying some shutdown protocol and then killing my container only after a timeout?

Yes, the idea is that the running application can catch the SIGINT and SIGTERM signal and perform some cleanup operation before exiting.

On the contrary, the SIGKILL (9) signal (raised by docker kill for example) causes the process to terminate immediately.

You might be interested in this related SO answer where I give a toy example of entrypoint bash script that "catches" SIGINT and SIGTERM signals (but not SIGKILL, of course).

Is there anything I can do to speed up container shutdown when killed via ctrl+c within docker-compose up?

I'd say the time spent by your containers to exit when you do docker stop or so strongly depends on the implementation of the corresponding images.

At first sight, you might modify (e.g., shorten) the time offered to the containers to gracefully stop:

  • docker stop -t TIMEOUT (doc)
  • docker-compose stop -t TIMEOUT (doc)

but this would certainly not be a proper solution.

Actually, there are two typical pitfalls when one comes to rely on an entrypoint bash script:

  1. Use a bash script as a wrapper to finally call another program, but forget to use the exec builtin.
    → The recommended practice consists in prepending the last command of a bash entrypoint with exec (for example, see this line from entrypointd.sh in sudo-bmitch/docker-base).

  2. When the bash script itself should not terminate immediately, one could forget to take care of trapping the signals (including SIGINT and SIGTERM) and behave accordingly, which can also be the cause of the issue you observe. This is related to the so-called PID 1 zombie reaping problem.
    → To workaround this, you may want to run your image with the docker run flag --init, or add the docker-compose.yml parameter init (related SO Q.)

Inclinatory answered 25/5, 2018 at 19:59 Comment(0)
A
19

If you find yourself restarting containers frequently and some don't gracefully exit or have long exit times (ie. you want more than 10s before it's killed), you can now do this per-service/container with stop_grace_period

stop_grace_period specifies how long the Compose implementation MUST wait when attempting to stop a container if it doesn’t handle SIGTERM (or whichever stop signal has been specified with stop_signal), before sending SIGKILL. Specified as a duration.

For example

services:
  service_A:
    ...
    stop_grace_period: 1s  # SIGKILL after 1s
  service_B:
    ...
    stop_grace_period: 5m30s  # wait a long time before SIGKILL

This should be supported in all formats (2.0+) and the release notes suggest it's been available (in compose) from 1.10.0 (2017-01-18 which also introduced the 3.0 docker-compose.yml spec) and had an important fix in 1.18.0 (2017-12-18 "Setting stop_grace_period in service definitions now also sets the container's stop_timeout")

Astolat answered 23/4, 2022 at 5:56 Comment(2)
You've saved me weeks and month of my life. I love you <3 I always have this problem with nodejs containers, but until now I haven't found out why. It seems like either trap 'exit 1' TERM INT or exec is missing in the ENTRYPOINT+CMD chain, but I cant exactly say (otherwise I'd have fixed it by now) This "quickfix" does the trick for me.Integument
@Integument glad to help! xkcd.com/1495 naïvely, it might be waiting on active connections and either a second SIGTERM or SIGKILL will end itAstolat
I
13

what does it actually mean when docker-compose says it is "gracefully stopping "?

Basically when you do Ctrl+C you are sending a SIGINT (2) signal to the application that runs in the foreground. Most of the time, the semantics of this signal is similar to the SIGTERM (15) signal that is raised when doing docker-compose stop or more basically docker stop if you are focusing on a single-container application.

Is docker-compose trying some shutdown protocol and then killing my container only after a timeout?

Yes, the idea is that the running application can catch the SIGINT and SIGTERM signal and perform some cleanup operation before exiting.

On the contrary, the SIGKILL (9) signal (raised by docker kill for example) causes the process to terminate immediately.

You might be interested in this related SO answer where I give a toy example of entrypoint bash script that "catches" SIGINT and SIGTERM signals (but not SIGKILL, of course).

Is there anything I can do to speed up container shutdown when killed via ctrl+c within docker-compose up?

I'd say the time spent by your containers to exit when you do docker stop or so strongly depends on the implementation of the corresponding images.

At first sight, you might modify (e.g., shorten) the time offered to the containers to gracefully stop:

  • docker stop -t TIMEOUT (doc)
  • docker-compose stop -t TIMEOUT (doc)

but this would certainly not be a proper solution.

Actually, there are two typical pitfalls when one comes to rely on an entrypoint bash script:

  1. Use a bash script as a wrapper to finally call another program, but forget to use the exec builtin.
    → The recommended practice consists in prepending the last command of a bash entrypoint with exec (for example, see this line from entrypointd.sh in sudo-bmitch/docker-base).

  2. When the bash script itself should not terminate immediately, one could forget to take care of trapping the signals (including SIGINT and SIGTERM) and behave accordingly, which can also be the cause of the issue you observe. This is related to the so-called PID 1 zombie reaping problem.
    → To workaround this, you may want to run your image with the docker run flag --init, or add the docker-compose.yml parameter init (related SO Q.)

Inclinatory answered 25/5, 2018 at 19:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.