我想知道使用gunicorn和celery部署容器化Django应用程序的正确方法是什么。
具体来说,这些过程中的每一个都有内置的垂直缩放方式,对于workers
来说适用于青i色,对于concurrency
而言适用于芹菜色。然后是使用replicas
还有一个概念,就是将worker设置为等于CPU的某些功能。 Gunicorn建议
每核心2-4个工人
但是,我对在CPU是可分割的共享资源的K8上的含义感到困惑-除非我使用resoureceQuotas。
我想了解什么是最佳实践。我可以想到三种选择:
关于SO的一些问题,但没有一个提供深入/周到的答案。如果有人可以分享他们的经验,将不胜感激。
注意:我们对Gunicorn使用默认的worker_class sync
答案 0 :(得分:12)
这些技术与最初看起来并不相似。 它们处理应用程序堆栈的不同部分,并且实际上是互补的。
Gunicorn用于扩展Web请求并发性,而celery应该被视为一个工作队列。我们将尽快使用kubernetes。
Web请求并发主要受网络I / O或“ I / O绑定”的限制。可以使用线程提供的协作调度来缩放这些类型的任务。如果您发现请求并发性限制了您的应用程序,那么增加gunicorn worker线程可能是开始的地方。
繁重的起重任务,例如压缩图像,运行一些ML算法是“ CPU绑定”任务。他们无法从尽可能多的CPU中受益。这些任务应由芹菜工人分担和并行处理。
Kubernetes派上用场的地方是提供开箱即用的水平可伸缩性和容错能力。
在架构上,我将使用两个单独的k8s部署来表示应用程序的不同可扩展性问题。 Django应用程序的一个部署,芹菜工人的另一个部署。 这样,您就可以独立调整请求吞吐量与处理能力之间的比例。
我运行的芹菜工人固定在每个容器(-c 1
)的单个核心上,这极大地简化了调试,并遵循Docker的“每个容器一个进程”的口头禅。它还可以为您带来可预测性的额外好处,因为您可以通过增加副本数来按核扩展处理能力。
扩展Django应用部署是您需要DYOR来找到特定应用程序最佳设置的地方。
再次坚持使用--workers 1
,以便每个容器只有一个进程,但是您应该尝试使用--threads
来找到最佳解决方案。只需更改副本数即可再次将水平缩放留给Kubernetes。
HTH 在进行类似项目时,绝对是我必须全神贯注的事情。
答案 1 :(得分:9)
我们使用Django和Celery运行Kubernetes kluster,并实现了第一种方法。因此,我对这种取舍以及为什么选择这种方法的一些想法。
在我看来,Kubernetes就是要水平扩展副本(称为部署)。在这方面,最明智的做法是使部署尽可能地单次使用,并随着需求的增加而增加部署(如果耗尽则增加Pod)。因此,LoadBalancer管理到Gunicorn部署的流量,而Redis队列管理到Celery工作人员的任务。这样可以确保底层的docker容器既简单又小,我们可以根据需要单独(自动)缩放它们。
关于每个部署需要多少workers
/ concurrency
的想法,这实际上取决于您正在运行Kubernetes的基础硬件,并且需要进行实验才能正确。
例如,我们在Amazon EC2上运行集群,并尝试了不同的EC2实例类型和workers
,以平衡性能和成本。每个实例拥有的CPU越多,所需实例就越少,每个实例可以部署的workers
就越多。但是我们发现,部署更多的小型实例对我们而言更便宜。现在,我们部署了多个m4.large实例,每个部署有3个工作程序。
有趣的旁注:与亚马逊负载均衡器结合使用时,我们的gunicorn
的性能确实很差,因此我们切换到uwsgi
时性能有了很大提高。但是原理是一样的。