Как я могу обновить работника сельдерея в docker -компонентной службе, но сохранять активным долго выполняющуюся задачу до ее завершения - PullRequest
1 голос
/ 21 апреля 2020

У меня есть приложение flask, которое позволяет пользователям запускать долго выполняющиеся задачи (иногда> 1d) через очередь заданий сельдерея. Приложение flask и все его зависимости, включая рабочих из сельдерея, контейнеризируются через docker и начинаются с docker -композитного файла.

Моя проблема в том, что когда я обновляю образы контейнеров новой версией прикладного программного обеспечения, мне нужно перезапустить контейнеры с помощью:

docker-compose down
docker-compose up -d

Это отменит все долго выполняющиеся задания, так как там по умолчанию используется только короткое значение тайм-аута в docker -compose. Установка более длительного значения тайм-аута для постепенного останова с помощью docker -создание, как предложено в docker -создание и постепенное отключение сельдерея не работает для меня, так как нет способа предсказать, как долго задания займет, и обновление может занять очень много времени, пока все задачи не будут завершены.

Моя идея состояла в том, чтобы каким-то образом отсоединить работающий контейнер от элемента управления docker-compose, а затем выполнить постепенное отключение сельдерея внутри отсоединенного контейнера, что затем разрешает заданиям завершаться sh, но не принимает новые рабочие места. Тогда я мог бы запустить обычный стек контейнеров через docker-compose up -d.

Таким образом, я хотел бы сделать:

  • удалить / переименовать контейнер сельдерея из docker compose
  • сигнализировать задачу сельдерея в контейнере, чтобы она корректно остановилась и дала заданию завершиться sh, но не приняла новые задания
  • , затем запустила новые контейнеры, которые будут принимать новые задания

I пытался использовать docker rename для переименования контейнеров, запущенных docker -compose, но они все равно реагируют на docker-compose down.

Мой вопрос заключается в том, является ли этот подход правильным способом справиться с этим и возможно ли это даже с docker -compose? Какова была бы лучшая практика для обработки изящных обновлений работников сельдерея с долгосрочными задачами в docker -композиционной среде?

Другие вопросы, которые я нашел, которые связаны, но не решают проблему полностью:

docker - сложное и изящное отключение сельдерея : ответ показывает, как остановить контейнеры изящно, но я хочу начать новый работник сельдерея немедленно, чтобы не было времени простоя.

Как изящно перезапустить работников сельдерея? : Это работает для локальной установки, но мне нужно перезапустить контейнеры, чтобы получить новый код приложения.

РЕДАКТИРОВАТЬ : новые подсказки к решению:

В этой проблеме я обнаружил похожую ситуацию. Здесь docker-compose --scale используется для дублирования сервиса, после чего можно найти идентификаторы старого и нового сервиса. Как только новая служба будет запущена, вы сможете сообщить сельдерею о завершении работы и завершить выполнение sh в старом контейнере. Если это решение, я добавлю его в качестве ответа позже.

https://github.com/docker/compose/issues/1786#

EDIT : больше думать о варианте с масштабированием , Здесь снова у меня проблема с долго выполняющимися задачами. Было бы обременительно наблюдать за умирающим контейнером, пока я не смогу вернуться к 1 экземпляру. В примере, приведенном в ссылке, важно было только убедиться, что новый сервис действительно работает, прежде чем остановить старый, чтобы скрипт мог сразу же вернуться к одному экземпляру. Я предпочел бы дублировать сервис, но убрать новый сервис из-под docker -композиции, чтобы он не был убит при масштабировании до 1 контейнера. Это должно быть возможно путем удаления docker -композиционных меток работающего контейнера:

"Labels": {
                "com.docker.compose.config-hash": "44e0bbd2a10e28bcad071a42315e65ed4d89f2d815a08aed4f3133b05b9d9f71",
                "com.docker.compose.container-number": "1",
                "com.docker.compose.oneoff": "False",
                "com.docker.compose.project": "karmada_docker_upgreat",
                "com.docker.compose.project.config_files": "docker-compose_test.yml",
                "com.docker.compose.project.working_dir": "/home/USERNAME/git/karmada_docker_upgreat",
                "com.docker.compose.service": "karmada_celery_kalibrate_worker",
                "com.docker.compose.version": "1.25.0"
            }

Или это неправильный путь? Переименование службы не имеет значения для docker -compose.

** EDIT ** Метки нельзя изменить для работающего контейнера: https://github.com/moby/moby/issues/15496 Чем больше я думаю о Я думаю, что для запуска контейнеров сельдерея мне придется использовать обычные команды docker. С помощью команд docker и сценария оболочки было бы легко добиться того, что мне нужно сделать. Я все еще хотел бы видеть решение в docker -compose.

1 Ответ

0 голосов
/ 23 апреля 2020

После долгих исследований я нашел решение этой проблемы. Но мне пришлось отказаться от ограничения использования docker-compose.

В настоящее время я думаю, что то, что мне нужно сделать, невозможно с docker-compose, потому что контейнер, который когда-то начинался с docker-compose, всегда будет контролироваться командами docker-compose, пока он находится в сети. Причина в том, что метки не могут быть изменены в запущенных контейнерах и docker-compose находит контейнер, которым он управляет, через метки (подробности см. В вопросе).

Таким образом, хотя можно использовать:

docker-compose up -d --no-deps --scale $SERVICE_NAME=2 --no-recreate $SERVICE_NAME

для запуска обновленного контейнера и оставить текущий запущенным, как предлагается здесь:

https://github.com/docker/compose/issues/1786#

У меня нет средств для масштабирования сервисов после завершения долгосрочной работы. Поскольку задания могут выполняться очень долго (> 1d), у меня может быть несколько контейнеров, заканчивающихся. Таким образом, мне пришлось бы реализовать огромные накладные расходы для подсчета контейнеров, которые в настоящее время заканчиваются, и масштабирования обратно до соответствующего числа, когда один из них сделан. Всегда с опасностью, что случайный docker-compose down может уничтожить их всех.

Но сценарий оболочки ближе к концу https://github.com/docker/compose/issues/1786# побудил меня отказаться от контраста и контроля docker-compose все контейнеры сельдерея с обычными командами docker. С этим легко управлять тем, что я хотел сделать. Я придумал следующий сценарий оболочки:

startup () {
  SERVICE_NAME=${1?"Usage: docker_update <SERVICE_NAME> <COMMAND>"}
  COMMAND=${2?"Usage: docker_update <SERVICE_NAME> <COMMAND>"}
  docker run \
         -d \
         --name $SERVICE_NAME \
         SOME_DOCKER_IMAGE \
         $COMMAND
}

update () {
  SERVICE_NAME=${1?"Usage: docker_update <SERVICE_NAME> <COMMAND>"}
  COMMAND=${2?"Usage: docker_update <SERVICE_NAME> <COMMAND>"}
  echo "[INFO] Updating docker service $SERVICE_NAME"
  OLD_CONTAINER_ID=$(docker ps --format "table {{.ID}}  {{.Names}}  {{.CreatedAt}}" | grep $SERVICE_NAME | tail -n 1 | awk -F  "  " '{print $1}')
  OLD_CONTAINER_NAME=$(docker ps --format "table {{.ID}}  {{.Names}}  {{.CreatedAt}}" | grep $SERVICE_NAME | tail -n 1 | awk -F  "  " '{print $2}')

  TEMP_UUID=`uuidgen`
  TEMP_CONTAINER_NAME="celery_worker_${TEMP_UUID}"

  echo "[INFO] rename $OLD_CONTAINER_NAME to $TEMP_CONTAINER_NAME"
  docker rename $OLD_CONTAINER_NAME $TEMP_CONTAINER_NAME

  echo "[INFO] start new/updated celery queue"
  startup $SERVICE_NAME $COMMAND

  echo "[INFO] send SIGTERM to $TEMP_CONTAINER_NAME for warm shutdown"
  docker kill --signal=SIGTERM $TEMP_CONTAINER_NAME

#  Optional waiting for the container to finish
  echo "[INIT] waiting for old docker container to finish"
  docker wait $TEMP_CONTAINER_NAME
}

SERVICE_NAME=${1?"Usage: docker_update <SERVICE_NAME>"}
COMMAND=${2?"Usage: docker_update <SERVICE_NAME> <COMMAND>"}
echo "[INFO] checking if this service already runs"
docker ps --format "table {{.ID}}  {{.Names}}  {{.CreatedAt}}" | grep $SERVICE_NAME

if [ $? -eq 0 ]
then
  echo "[INFO] CONTAINER with name $SERVICE_NAME is online -> update"
  update $SERVICE_NAME $COMMAND
else
  echo "[INFO] CONTAINER with name $SERVICE_NAME is **not** online -> starting"
  startup $SERVICE_NAME $COMMAND
fi

Сценарий проверяет, запущена ли служба с данным именем. Если это не так, это начинает. Если он работает, он переименовывает текущий запущенный контейнер, затем запускает новый (возможно, обновленный) и отправляет SIGTERM старому. Для сельдерея это сигнал для выполнения warm shutdown, что означает, что он больше не принимает новые задачи, а завершает те, которые выполняет в данный момент, и затем завершает работу. Если задача не запущена, она немедленно завершается. Новый сельдерей берет на себя все новые задачи.

...