Search code examples
rubydockersignalsexitexit-code

Ruby exits with exit code 1 responding to TERM if running without shell


If Ruby receives the TERM signal, it usually exits with the exit code 143, which according to this source indicates the process successfully responded to that signal. But if I make the script run without a shell, the exit code is 1.

With shell:

> cat Dockerfile 
FROM ruby:alpine
CMD ruby -e "Process.kill('TERM', Process.pid)" # <- shell form

> docker build -t term_shell . > /dev/null
> docker run term_shell
Terminated

> echo $?
143

Without shell:

> cat Dockerfile
FROM ruby:alpine
CMD ["ruby", "-e", "Process.kill('TERM', Process.pid)"] # <- exec form

> docker build -t term_exec . > /dev/null
> docker run term_exec

> echo $?
1

But if I exit myself with 143, the exit code is as expected:

> cat Dockerfile
FROM ruby:alpine
CMD ["ruby", "-e", "exit(143)"] # <- exec form

> docker build -t exit_exec . > /dev/null
> docker run exit_exec

> echo $?
143

Why is that? Does the exit code when ruby receives TERM comes not from Ruby, but the shell?


Solution

  • The exit code of your second example is 1 because the call Process.kill('TERM', Process.pid) failed. ruby -e exited because of this failure, and the status code in that case is 1.

    • With CMD ruby -e "Process.kill('TERM', Process.pid)", docker executes the given command in a shell. In a running container, it means that the root process with pid 1 will be /bin/sh -c, and the ruby -e command will be executed in a child process with another pid (for example 6).
    • With CMD ["ruby", "-e", "Process.kill('TERM', Process.pid)"], docker executes directly ruby -e as the root process with pid 1.

    The PID 1 on Linux behaves differently than the normal ones. From docker documentation:

    Note: A process running as PID 1 inside a container is treated specially by Linux: it ignores any signal with the default action. So, the process will not terminate on SIGINT or SIGTERM unless it is coded to do so.

    So in your case, the TERM signal won't be sent to you process.

    You can find more information on PID 1 behavior on this article: https://hackernoon.com/my-process-became-pid-1-and-now-signals-behave-strangely-b05c52cc551c