Захват Heroku SIGTERM в воркерах Celery, чтобы изящно завершить работу воркера

Я провел массу исследований по этому вопросу, и я удивлен, что еще нигде не нашел хорошего ответа на этот вопрос.

Я запускаю большое приложение на Heroku, и у меня есть определенные задачи сельдерея, которые очень долго обрабатываются и в конце задачи сохраняют результат. Каждый раз, когда я переустанавливаю Heroku, он отправляет SIGTERM (и, в конечном итоге, SIGKILL) и убивает моего работающего воркера. Я пытаюсь найти способ для рабочего экземпляра корректно завершить работу и повторно поставить себя в очередь для обработки позже, чтобы в конечном итоге мы могли сохранить требуемый результат, а не потерять поставленную в очередь задачу.

Я не могу найти способ, чтобы рабочий правильно прослушивал SIGTERM. Самое близкое, что я получил, которое работает при запуске python manage.py celeryd напрямую, но НЕ при эмуляции Heroku с использованием мастера, это следующее:

@app.task(bind=True, max_retries=1)
def slow(self, x):
    try:
        for x in range(100):
            print 'x: ' + unicode(x)
            time.sleep(10)
    except exceptions.MaxRetriesExceededError:
        logger.error('whoa')
    except (exceptions.WorkerShutdown, exceptions.WorkerTerminate) as exc:
        logger.error(u'retrying, ' + unicode(exc))
        raise self.retry(exc=exc, countdown=10)
    except (KeyboardInterrupt, SystemExit) as exc:
        print 'retrying'
        raise self.retry(exc=exc, countdown=10)
    else:
        return x
    finally:
        logger.info('task ended!')

Когда я запускаю эту задачу сельдерея, работающую в мастере, и нажимаю Ctrl + C, происходит следующее:

^CSIGINT received
22:20:59 system   | sending SIGTERM to all processes
22:20:59 web.1    | exited with code 0
22:21:04 system   | sending SIGKILL to all processes
Killed: 9

Таким образом, ясно, что ни одно из исключений сельдерея, ни исключения KeyboardInterrupt или SystemExit, которые я видел в других сообщениях, должным образом не перехватывают SIGTERM и не завершают рабочий процесс.

Как правильно это сделать?


person jdotjdot    schedule 26.04.2015    source источник
comment
celery.readthedocs.org/en/latest/ userguide/, по-видимому, указывает, что основной рабочий процесс всегда будет перехватывать SIGTERM.   -  person Raghuram Onti Srinivasan    schedule 04.05.2015
comment
Верно, так есть ли способ, чтобы главный рабочий передал его детям?   -  person jdotjdot    schedule 04.05.2015
comment
Это проблема, для которой я также никогда не находил отличного решения. Я склонен обрабатывать это в логике приложения, следя за тем, чтобы мои задачи были идемпотентными, и отслеживая запущенные и завершенные задачи, чтобы я мог автоматически перезапустить данную задачу при запуске моего приложения.   -  person Brock Haywood    schedule 26.08.2015
comment
Кто-нибудь решил это уже? Я также пытаюсь найти решение для этого - мне нужно правильно остановить выполнение задач перед развертыванием, чтобы они были либо полностью завершены, либо перенесены на после перезапуска развертывания.   -  person T.Poe    schedule 23.11.2018


Ответы (3)


Начиная с версии >= 4, Celery поставляется со специальной функцией только для Heroku, которая поддерживает эту функцию из коробки:

$ REMAP_SIGTERM=SIGQUIT celery -A proj worker -l info

источник: https://devcenter.heroku.com/articles/celery-heroku#using-remap_sigterm

person xavriley    schedule 08.08.2019

сельдерей, к сожалению, не предназначен для чистого завершения работы. КОГДА-ЛИБО. Я серьезно. рабочие процессы сельдерея отвечают на SIGTERM, но если задача не завершена, рабочие процессы будут ждать завершения задачи и только после этого завершатся. В этом случае вы можете отправить его SIGKILL, если воркеры не отключатся в разумное время, но в этом случае будет потеря информации, т.е. вы можете не знать, какие задания остались незавершенными.

person pbhowmick    schedule 27.04.2015
comment
Поскольку вы можете сохранить результат задачи, поэтому должно быть возможно проверить статус задачи и т. Д. На прикладном уровне и восстановить ситуацию. - person Dwight Gunning; 03.05.2015

Вы можете использовать acks_late или task_acks_late.

Задачи будут подтверждены из очереди после выполнения задачи, а не непосредственно перед ней. Таким образом, задача будет возрождаться, если рабочий корректно завершит работу.

person Mesut Tasci    schedule 13.12.2017
comment
просто из любопытства, как брокер (скажем.. rabbitmq) различает подтвержденное сообщение (задачу), потому что: а) задача все еще обрабатывается б) рабочий умер, его следует доставить повторно - person Marcos Modenesi; 09.12.2020