如何更新 docker-compose 服务中的 celery 工作人员,但保持长时间运行的任务处于活动状态,直到它们完成

Asc*_*ion 5 deployment celery docker docker-compose

我有一个 Flask 应用程序,允许用户通过 celery 作业队列启动长时间运行的任务(有时> 1d)。Flask 应用程序及其所有依赖项(包括 celery 工作人员)都通过 docker 进行容器化,并以 docker-compose 文件启动。

我的问题是,当我使用新版本的应用程序软件更新容器映像时,我需要使用以下命令重新启动容器:

docker-compose down
docker-compose up -d
Run Code Online (Sandbox Code Playgroud)

这将取消所有长时间运行的作业,因为 docker-compose 中默认只有一个很短的超时值。按照 docker- compose 和优雅的 Celery 关闭中的建议,为 docker-compose 的优雅停止设置更长的超时值对我来说不起作用,因为无法预测作业将花费多长时间,并且更新可能需要很长时间才能完成所有任务都已完成。

我的想法是以某种方式将正在运行的容器与docker-compose控件分离,然后在分离的容器内正常关闭 celery,然后允许作业完成,但不接受新作业。然后我可以通过启动正常的容器堆栈docker-compose up -d

因此我想做:

  • 从 docker compose 中删除/重命名 celery 容器
  • 通知容器中的 celery 任务正常停止并让作业完成但不接受新作业
  • 然后启动将接受新作业的新容器

我尝试使用docker rename重命名由 docker-compose 启动的容器,但它们仍然对docker-compose down.

我的问题是,这种方法是否是处理此问题的正确方法,以及 docker-compose 是否可以实现这一点?在 docker-compose 环境中处理长期运行任务的 celery 工作人员的优雅更新的最佳实践是什么?

我发现相关但没有完全解决问题的其他问题:

docker-compose 和优雅的 Celery shutdown:答案显示了如何优雅地停止容器,但我想立即启动一个新的 celery 工作程序,以免产生停机时间。

如何优雅地重新启动 celery 工作人员?:这适用于本地安装,但我必须重新启动容器才能获取新的应用程序代码。

编辑:解决方案的新提示:

在本期我也发现了类似的情况。这里docker-compose --scale用于复制服务,然后可以找到旧服务和新服务的 ID。一旦新服务启动,人们应该能够告诉 celery 关闭并完成旧容器中正在执行的任务。如果这是解决方案,我稍后会将其添加为答案。

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

编辑:更多地考虑缩放变体。我再次遇到了长时间运行任务的问题。在我可以缩减到 1 个实例之前,观察濒临死亡的容器会很麻烦。在链接的示例中,重要的是在停止旧服务之前检查新服务是否确实启动,以便脚本可以立即缩减为单个实例。我宁愿复制该服务,但将新服务从 docker-compose 的控制中删除,这样当我缩减到 1 个容器时它就不会被杀死。这必须可以通过删除正在运行的容器的 docker-compose 标签来实现:

"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"
            }

Run Code Online (Sandbox Code Playgroud)

或者这是错误的轨道?重命名服务不会对 docker-compose 产生影响。

** 编辑 ** 无法更改正在运行的容器的标签:https://github.com/moby/moby/issues/15496 我想得越多,我想我将不得不使用普通的 docker 命令来运行 celery容器。使用 docker 命令和 shell 脚本,可以很容易地实现我需要做的事情。我仍然希望在 docker-compose 中看到解决方案。

Asc*_*ion 5

经过更多研究,我找到了解决这个问题的方法。但我不得不放弃使用的限制docker-compose

目前,我认为我需要做的事情是不可能的docker-compose,因为容器一旦启动,只要它在线,docker-compose就总是受命令控制。docker-compose原因是无法在正在运行的容器上更改标签,并docker-compose通过标签找到它控制的容器(有关详细信息,请参阅问题)。

因此,尽管可以使用:

docker-compose up -d --no-deps --scale $SERVICE_NAME=2 --no-recreate $SERVICE_NAME
Run Code Online (Sandbox Code Playgroud)

要启动更新的容器并保持当前容器运行,如下所示:

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

在长时间运行的工作完成后,我无法缩减服务规模。因为作业可能运行很长时间(> 1d),所以我可能有多个容器完成。因此,我将不得不实施大量开销来计算当前正在完成的容器,并在其中一个完成后重新缩放回适当的数量。docker-compose down总是面临着一个意外就会让他们全部倒下的危险。

但是https://github.com/docker/compose/issues/1786#末尾的 shell 脚本促使我放弃docker-compose约束并使用普通docker命令控制所有 celery 容器。有了这个,就可以轻松管理我想做的事情。我想出了以下 shell 脚本:

docker-compose up -d --no-deps --scale $SERVICE_NAME=2 --no-recreate $SERVICE_NAME
Run Code Online (Sandbox Code Playgroud)

该脚本检查给定名称的服务是否正在运行。如果不是,则启动它。如果它正在运行,它会重命名当前正在运行的容器,然后启动一个新的(可能已更新的)容器,并向旧容器发送 SIGTERM。对于 celery 来说,这是执行 a 的信号,warm shutdown这意味着它不再接受新任务,而是完成当前正在执行的任务,然后退出。如果没有任务正在运行,它将立即退出。新的芹菜工人接管所有新任务。