Docker/Kubernetes+Gunicorn/Celery-多工人对复制人



我想知道使用gunicorn&芹菜是。

具体来说,这些过程中的每一个都有一种内置的垂直缩放方式,对gunicorn使用workers,对芹菜使用concurrency。然后是使用replicas进行扩展的Kubernetes方法

还有一种概念是将工作线程设置为等于CPU的某些功能。Gunicorn推荐

每个核心有2-4名工人

然而,我很困惑这在K8s上意味着什么,因为CPU是一个可分割的共享资源——除非我使用resourceQuotas。

我想了解什么是最佳实践。我可以想到三个选项:

  • 对于gunicorn有单个worker,对于celenic有1的并发性,并使用副本对其进行扩展?(水平缩放)
  • 拥有gunicorn&芹菜在具有内部扩展(垂直扩展)的单个副本部署中运行。这将意味着设定相当高的工人价值&并发性
  • 一种介于1和2之间的混合方法,我们经营对工人来说价值较小的gunicorn和芹菜&并发,(比如2),然后使用K8s部署副本进行水平扩展

关于SO,有一些问题,但没有一个能提供深入/周到的答案。如果有人能分享他们的经验,我将不胜感激。

注意:对于Gunicorn ,我们使用默认的worker_classsync

这些技术并不像最初看起来那么相似。它们处理应用程序堆栈的不同部分,实际上是互补的。

Gunicorn用于扩展web请求并发性,而芹菜应该被认为是一个工作队列。我们很快就会到库伯内特。


Gunicorn

Web请求并发性主要受到网络I/O或"网络"的限制;I/O绑定";。这些类型的任务可以使用线程提供的协作调度进行缩放。如果您发现请求并发性限制了您的应用程序,那么增加gunicorn工作线程很可能是开始的地方。


芹菜

繁重的任务,例如压缩图像、运行一些ML算法,都是"繁重的";CPU绑定";任务。它们不能像更多的CPU那样从线程中获益。这些任务应该由芹菜工人卸载并并行处理。


Kubernetes

Kubernetes的用武之地在于提供开箱即用的水平可扩展性和容错性。

在体系结构上,我将使用两个单独的K8部署来表示应用程序的不同可伸缩性问题。一个是Django应用程序的部署,另一个是芹菜工人的部署。这使您能够独立地扩展请求吞吐量与处理能力。

我运行每个容器固定在单个核心上的芹菜工作程序(-c 1),这大大简化了调试并遵循Docker的";每个容器一个过程";咒语。它还为您提供了可预测性的额外好处,因为您可以通过增加副本计数来按每个核心扩展处理能力。

扩展Django应用程序部署是您需要DYOR为特定应用程序找到最佳设置的地方。再次坚持使用--workers 1,因此每个容器只有一个进程,但您应该尝试使用--threads来找到最佳解决方案。同样,只需更改副本计数,就可以将水平缩放留给Kubernetes。

HTH这绝对是我在做类似项目时必须考虑的问题。

我们用Django和Celery运行了一个Kubernetes kluster,并实现了第一种方法。因此,我对这种权衡以及我们为什么选择这种方法的一些想法。

在我看来,Kubernetes就是要横向扩展复制副本(称为部署)。在这方面,保持部署尽可能一次性使用,并随着需求的增加而增加部署(如果用完了,还可以增加pod),这是最有意义的。因此,LoadBalancer管理到Gunicorn部署的流量,Redis队列管理到Celery工作人员的任务。这确保了底层的docker容器简单而小,并且我们可以根据自己的需要单独(并自动)缩放它们。

至于你对每次部署需要多少workers/concurrency的想法,这实际上取决于你运行Kubernetes的底层硬件,需要进行实验才能正确。

例如,我们在AmazonEC2上运行集群,并尝试使用不同的EC2实例类型和workers来平衡性能和成本。每个实例的CPU越多,所需的实例就越少,每个实例可以部署的workers就越多。但我们发现,在我们的情况下,部署更多较小的实例更便宜。我们现在部署了多个m4.large实例,每个部署有3个工作人员。

有趣的一点是:gunicorn与amazon负载均衡器的结合性能非常糟糕,因此我们切换到了性能大幅提高的uwsgi。但原则是一样的。

相关内容

  • 没有找到相关文章

最新更新