我想知道使用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
。但原则是一样的。