<p>When deleting a pod or deploying a new version of a pod kubernetes should theoretically send a <code>SIGTERM</code> to the process running and then wait <code>gracePeriodSeconds</code> (30 by default) seconds before it sends a <code>SIGKILL</code>.</p>
<p>I have encountered the problem that this first <code>SIGTERM</code> never seems to be sent. The default settings in my cluster were never changed (kill is sent as expected after 30 seconds), so my assumption is that there might be something wrong, permissions or similar, with my Dockerfile (see below).</p>
<p>I've excluded there being an error in the graceful shutdown logic catching the <code>SIGTERM</code> in the executable by <code>kubectl exec</code>-ing into the pod and using <code>kill -15</code> on the process which works as expected.</p>
<p>The Dockerfile looks as follows:</p>
<pre><code>FROM debian:bullseye-slim AS app

ARG USERNAME=app
ARG USER_UID=1000
ARG USER_GID=$USER_UID
RUN apt update &amp;&amp; apt install -y libssl-dev zstd ca-certificates pkg-config

RUN groupadd --gid $USER_GID $USERNAME \
    &amp;&amp; useradd --uid $USER_UID --gid $USER_GID -m $USERNAME
WORKDIR /home/$USERNAME

ARG RELEASE_DIR
ARG SERVICE 

USER $USERNAME

COPY $RELEASE_DIR .

EXPOSE 8080

ENV CMD=./${SERVICE}
CMD ${CMD}
</code></pre>
<p>Is there something blatantly wrong here? Or does kubernetes require some additional config to actually send the termination signal as expected?</p>

<p>For termination to work correctly, you need to ensure your application is the main container process.  With the <a href="https://docs.docker.com/engine/reference/builder/#shell-form-entrypoint-example" rel="noreferrer">shell form</a> <code>CMD</code>, the command your container runs is <code>/bin/sh -c '${CMD}'</code>, and depending on what's in that environment variable and what <code>/bin/sh</code> actually is, that shell wrapper might keep running as the main container process and intercept the termination signal.</p>
<p>The same mechanisms apply in both plain Docker and Kubernetes and you should see a similar issue if you <code>docker stop</code> the container locally.  This may be easier to debug and iterate on.</p>
<p>The easiest way to work around this is to use the <a href="https://docs.docker.com/engine/reference/builder/#exec-form-entrypoint-example" rel="noreferrer">exec form</a> of <code>CMD</code> that looks like a JSON array.  Since this won't run a shell, it also can't do variable expansions, and you'll have to spell out what you want the command to actually be</p>
<pre class="lang-bash prettyprint-override"><code>CMD [&quot;./service&quot;]
</code></pre>
<p>This is still easy to override at runtime, and you in fact don't need that <code>CMD</code> at all:</p>
<pre class="lang-bash prettyprint-override"><code># instead of `docker run -e CMD='...'`
docker run --rm my-image \
  ls -l /home/app
</code></pre>
<pre class="lang-yaml prettyprint-override"><code># or in a Kubernetes pod spec
command:
  - /home/app/another_app
args:
  - --option
</code></pre>
<p>You can probably similarly remove pretty much all of the <code>ARG</code> declarations in the Dockerfile (the name or numeric uid of the container user shouldn't matter, for example, and the compiled application filename and host build path are usually fixed) which will simplify the setup.</p>
