<p>So we have a kubernetes cluster running some pods with celery workers. We are using python3.6 to run those workers and celery version is 3.1.2 (I know, really old, we are working on upgrading it). We have also setup some autoscaling mechanism to add more celery workers on the fly.</p>
<p>The problem is the following. So let's say we have 5 workers at any given time. Then lot of tasks come, increasing the CPU/RAM usage of the pods. That triggers an autoscaling event, adding, let's say, two more celery worker pods. So now those two new celery workers take some long running tasks. Before they finishing running those tasks, kubernetes creates a downscaling event, killing those two workers, and killing those long running tasks too.</p>
<p>Also, for legacy reasons, we do not have a retry mechanism if a task is not completed (and we cannot implement one right now).</p>
<p>So my question is, is there a way to tell kubernetes to wait for the celery worker to have run all of its pending tasks? I suppose the solution must include some way to notify the celery worker to make it stop receiving new tasks also. Right now I know that Kubernetes has some scripts to handle this kind of situations, but I do not know what to write on those scripts because I do not know how to make the celery worker stop receiving tasks.</p>
<p>Any idea?</p>

<p>I wrote a <a href="https://blog.dy.engineering/hpa-for-celery-workers-6efd82444aee" rel="nofollow noreferrer">blog post</a> exactly on that topic - check it out.</p>
<p>When Kubernetes decide to kill a pod, it first send SIGTERM signal, so your Application have time to gracefully shutdown, and after that if your Application didn't end - Kubernetes will kill it by sending a SIGKILL signal.</p>
<p>This period, between SIGTERM to SIGKILL can be tuned by <code>terminationGracePeriodSeconds</code> (more about it <a href="https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#hook-handler-execution" rel="nofollow noreferrer">here</a>).</p>
<p>In other words, if your longest task takes 5 minutes, make sure to set this value to something higher than 300 seconds.</p>
<p>Celery handle those signals for you as you can see <a href="https://docs.celeryq.dev/en/stable/userguide/workers.html#stopping-the-worker" rel="nofollow noreferrer">here</a> (I guess it is relevant for your version as well):</p>
<p>Shutdown should be accomplished using the TERM signal.</p>
<blockquote>
<p>When shutdown is initiated the worker will finish all currently
executing tasks before it actually terminates. If these tasks are
important, you should wait for it to finish before doing anything
drastic, like sending the KILL signal.</p>
</blockquote>
<p>As explained in the docs, you can set the <code>acks_late=True</code> <a href="https://docs.celeryq.dev/en/stable/reference/celery.app.task.html#celery.app.task.Task.acks_late" rel="nofollow noreferrer">configuration</a> so the task will run again if it stopped accidentally.</p>
<p>Another thing that I didn't find documentation for (almost sure I saw it somewhere) - Celery worker won't receive a new tasks after getting a SIGTERM - so you should be safe to terminate the worker (might require to set <code>worker_prefetch_multiplier = 1</code> as well).</p>
