krallin / tini

A tiny but valid `init` for containers
MIT License
9.66k stars 505 forks source link

SIGTERM not working with exec command in the entrypoint script #195

Closed kgrv-me closed 2 years ago

kgrv-me commented 2 years ago

Hi guys,

I have ENTRYPOINT [ "/sbin/tini", "--", "entrypoint.sh" ] in the Dockerfile.

In entrypoint.sh, I have just one line exec bash; in this case, using stop from Docker Desktop results in SIGKILL (137) instead of SIGTERM (143) with long shutdown. If I changed exec bash to just bash, SIGTERM worked properly with immediate shutdown. Interestingly, using exec fish works properly as well.

From researching, exec seems to be recommended to avoid improper signal handling/forwarding by replacing the middle man. However, in this case, it seems to be doing the opposite for bash. I'm assuming exec bash should work just like bash would when stopping the container.

Can someone help clarify this for me? What am I missing that exec bash doesn't work when I'm trying to stop the container? Thank you.

yosifkit commented 2 years ago

I'm assuming that you are running an interactive container similar to: docker run it --rm custom-image.

When you just run bash the process tree in the container looks like this: tini -> sh entrypoint.sh -> bash But with exec bash it looks like this: tini -> bash. tini forwards the signal to its child, but that process may or may not repeat it to its own children.

Since the entrypoint script being run by sh does not define a signal handler, then it gets the default when a running script and exits when it gets a SIGTERM, and thus its child bash is also killed when the container exits. But an interactive bash shell does not, which is why it doesn't die until it gets the later SIGKILL from docker stop.

When Bash is interactive, in the absence of any traps, it ignores SIGTERM

-https://www.gnu.org/software/bash/manual/html_node/Signals.html

kgrv-me commented 2 years ago

I'm assuming that you are running an interactive container similar to: docker run it --rm custom-image.

When you just run bash the process tree in the container looks like this: tini -> sh entrypoint.sh -> bash But with exec bash it looks like this: tini -> bash. tini forwards the signal to its child, but that process may or may not repeat it to its own children.

Since the entrypoint script being run by sh does not define a signal handler, then it gets the default when a running script and exits when it gets a SIGTERM, and thus its child bash is also killed when the container exits. But an interactive bash shell does not, which is why it doesn't die until it gets the later SIGKILL from docker stop.

When Bash is interactive, in the absence of any traps, it ignores SIGTERM -https://www.gnu.org/software/bash/manual/html_node/Signals.html

Ahhh, I assumed that bash handles signals the same both in interactive and script forms, which led me into a rabbit hole.

Is STOPSIGNAL SIGKILL in Dockerfile the only remedy to be able to utilize exec bash then? Or is there another proper/elogant way of archiving this?

My thinking is that I wouldn't want entrypoint.sh process to dangle after invoking interactive bash so doing tini -> bash looks appropriated. Is using exec bash not recommended in this case? Or am I heading into a right direction, just short of my goal?

Thank you very much for your reply!

krallin commented 2 years ago

You could experiment with the -g flag, which would have Tini send signals to the process group of your children, which I think in non-interactive bash would be the same as bash itself.

If you can’t use exec, it might be worth a try.

On Tue, 10 May 2022 at 19:46, kgrv-me @.***> wrote:

I'm assuming that you are running an interactive container similar to: docker run it --rm custom-image.

When you just run bash the process tree in the container looks like this: tini -> sh entrypoint.sh -> bash But with exec bash it looks like this: tini -> bash. tini forwards the signal to its child, but that process may or may not repeat it to its own children.

Since the entrypoint script being run by sh does not define a signal handler, then it gets the default when a running script and exits when it gets a SIGTERM, and thus its child bash is also killed when the container exits. But an interactive bash shell does not, which is why it doesn't die until it gets the later SIGKILL from docker stop.

When Bash is interactive, in the absence of any traps, it ignores SIGTERM -https://www.gnu.org/software/bash/manual/html_node/Signals.html

Ahhh, I assumed that bash handles signals the same both in interactive and script forms, which led me into a rabbit hole.

Is STOPSIGNAL SIGKILL in Dockerfile the only remedy to be able to utilize exec bash then? Or is there another proper/elogant way of archiving this?

My thinking is that I wouldn't want entrypoint.sh process to dangle after invoking interactive bash so doing tini -> bash looks appropriated. Is using exec bash not recommended in this case? Or am I heading into a right direction, just short of my goal?

Thank you very much for your reply!

— Reply to this email directly, view it on GitHub https://github.com/krallin/tini/issues/195#issuecomment-1122745529, or unsubscribe https://github.com/notifications/unsubscribe-auth/AANIHVX7OK4ZP3GX2HCLMDLVJKVI5ANCNFSM5VSFEBCA . You are receiving this because you are subscribed to this thread.Message ID: @.***>

kgrv-me commented 2 years ago

You could experiment with the -g flag, which would have Tini send signals to the process group of your children, which I think in non-interactive bash would be the same as bash itself. If you can’t use exec, it might be worth a try. On Tue, 10 May 2022 at 19:46, kgrv-me @.> wrote: I'm assuming that you are running an interactive container similar to: docker run it --rm custom-image. When you just run bash the process tree in the container looks like this: tini -> sh entrypoint.sh -> bash But with exec bash it looks like this: tini -> bash. tini forwards the signal to its child, but that process may or may not repeat it to its own children. Since the entrypoint script being run by sh does not define a signal handler, then it gets the default when a running script and exits when it gets a SIGTERM, and thus its child bash is also killed when the container exits. But an interactive bash shell does not, which is why it doesn't die until it gets the later SIGKILL from docker stop. When Bash is interactive, in the absence of any traps, it ignores SIGTERM -https://www.gnu.org/software/bash/manual/html_node/Signals.html Ahhh, I assumed that bash handles signals the same both in interactive and script forms, which led me into a rabbit hole. Is STOPSIGNAL SIGKILL in Dockerfile the only remedy to be able to utilize exec bash then? Or is there another proper/elogant way of archiving this? My thinking is that I wouldn't want entrypoint.sh process to dangle after invoking interactive bash so doing tini -> bash looks appropriated. Is using exec bash not recommended in this case? Or am I heading into a right direction, just short of my goal? Thank you very much for your reply! — Reply to this email directly, view it on GitHub <#195 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AANIHVX7OK4ZP3GX2HCLMDLVJKVI5ANCNFSM5VSFEBCA . You are receiving this because you are subscribed to this thread.Message ID: @.>

I tried that already and it didn't work due to the nature of interactive bash (great explanation and link from @yosifkit). I have decided to go with STOPSIGNAL SIGHUP in the Dockerfile such that interactive bash will forward the signal to all jobs before exiting.

Thank you all for your help, much appreciated it!