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

Does dumb_init wait for all child processes to terminate? #212

Open
richard-browne opened this issue Oct 13, 2020 · 9 comments
Open

Does dumb_init wait for all child processes to terminate? #212

richard-browne opened this issue Oct 13, 2020 · 9 comments

Comments

@richard-browne
Copy link

richard-browne commented Oct 13, 2020

Question about SIGTERM behaviour. It's not clear from your README. Suppose I have a docker container thus:

ENTRYPOINT ["/usr/bin/dumb-init", "--", "entrypoint.sh"]

#!/bin/sh
crond -b
exec /usr/sbin/nginx -g daemon off

dumb_init will forward SIGTERM to both nginx and crond. All good. But does it wait for both nginx and crond to terminate before terminating itself? Or does it wait only for nginx to terminate? I hope the answer is that it waits for all child processes to terminate.

@chriskuehl
Copy link
Contributor

It will signal them to exit when the child exits, but it does not wait for them:

dumb-init/dumb-init.c

Lines 112 to 116 in 2da4dc4

if (killed_pid == child_pid) {
forward_signal(SIGTERM); // send SIGTERM to any remaining children
DEBUG("Child exited with status %d. Goodbye.\n", exit_status);
exit(exit_status);
}

@richard-browne
Copy link
Author

richard-browne commented Oct 13, 2020

Ok so it waits for its forked child, and returns its exit code right? But does not wait for any other children in the session. Did I get that right?

Please consider a feature request, to wait for all processes in the session before terminating. Then 'docker stop' behavior is as expected. IE. Wait for the container to terminate (in my example, two processes). If it doesn't terminate within a grace period, docker sends a SIGKILL.

Without waiting for all children, you have the possibility that child processes will be forcibly closed by the kernel without time to properly clean up. See here: https://stackoverflow.com/questions/39739658/what-happens-to-other-processes-when-a-docker-containers-pid1-exits.

Thank-you for considering my feature request.

@modem7
Copy link

modem7 commented Nov 8, 2022

Do we know if this'll be implemented at all? It's been a while since an update (to the query).

@samrocketman
Copy link

Do we know if this'll be implemented at all? It's been a while since an update (to the query).

If you have bash available in your multiprocess container then you can leverage its feature to wait on backgrounded processes. Otherwise, you could have a loop waiting on all PIDs to disappear except $$ and 1 via a trap on EXIT function.

If you don’t have bash and instead have a minimal docker container you could add in statically compiled bash. I have some write ups if there’s any interest on docker practices but they don’t specifically cover this edge case.

@modem7
Copy link

modem7 commented Nov 9, 2022

If you have bash available in your multiprocess container then you can leverage its feature to wait on backgrounded processes.

I have some write ups if there’s any interest on docker practices but they don’t specifically cover this edge case.

Heya, the first part certainly sounds like my current use case.

Effectively: Alpine w/ Bash, process being called by Crond, but if the container calls an INT or similar signal, finding that the child process doesn't receive the signal.

We've tried tini, but it's only good as a zombie reaper which doesn't quite cover the use case (borgmatic-collective/docker-borgmatic#173)

@komapa
Copy link

komapa commented Feb 9, 2023

This works for us btw:

NUM_PROCESSES=${NUM_PROCESSES:-1}
if [ "${NUM_PROCESSES}" -gt 1 ]; then
  # See: https://github.com/Yelp/dumb-init#session-behavior
  # Here we use `dumb-init` to start a subshell which will run all
  # the processes and wait for them to exit/finish.
  # `dumb-init` will forward all signals to these processes and
  # `wait -n` will make sure to exit if at least one process exits prematurely.
  exec /usr/bin/dumb-init -- /bin/bash -c "for run in \$(seq ${NUM_PROCESSES}); do $* & done; \
                                           trap 'wait' SIGINT SIGTERM; \
                                           wait -n"
fi

@mitar
Copy link

mitar commented Jul 14, 2023

I made another init system for Docker, dinit, which tries to address exactly this: having multiple children processes, sending signal to them when the container (PID 1) gets the signal, and then waiting for all to finish. If some process does not finish, then it is left to the container's supervisor (i.e., Docker) to kill all processes.

@richard-browne
Copy link
Author

@mitar dinit looks like exactly what we need. Surprised this hasn't happened before now. I think not many people appreciate the nuances of init with docker.

@mitar
Copy link

mitar commented Jul 20, 2023

@richard-browne: Yea, I was also surprised that something like that does not exist yet. Especially once you realize that there are design differences when operating in a container (e.g., shutting down the whole container when one process dies so that its supervisor is informed about the death and then supervisor restarts the container if necessary, sending only TERM to processes and leaving KILL to the supervisor). Anyway, feel free to test it out and open any issues/discussions there, so that we do not fill this issue more.

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

6 participants