django - minikube for production




Docker/Kubernetes+Gunicorn/Apio-¿Trabajadores múltiples frente a réplicas? (2)

Me preguntaba cuál era el enfoque correcto para implementar una aplicación Django en contenedores utilizando gunicorn y apio.

Específicamente, cada uno de estos procesos tiene una forma integrada de escalar verticalmente, utilizando workers para gunicorn y concurrency para el apio. Y luego está el enfoque de Kubernetes para escalar usando replicas

También existe esta noción de establecer trabajadores igual a alguna función de las CPU. Gunicorn recomienda

2-4 trabajadores por núcleo

Sin embargo, estoy confundido a qué se traduce esto en K8s, donde la CPU es un recurso compartido divisible, a menos que use las cuotas de recursos.

Quiero entender cuál es la mejor práctica. Hay tres opciones que se me ocurren:

  • ¿Tiene trabajadores solteros para gunicornio y una concurrencia de 1 para apio, y los escala usando las réplicas? (escala horizontal)
  • Haga que gunicorn y apio se ejecuten en un solo despliegue de réplica con escala interna (escala vertical). Esto significaría establecer valores bastante altos de trabajadores y concurrencia respectivamente.
  • Un enfoque mixto entre 1 y 2, donde ejecutamos gunicornio y apio con un valor pequeño para trabajadores y concurrencia (digamos 2), y luego usamos las réplicas de Despliegue de K8 para escalar horizontalmente.

Hay algunas preguntas sobre SO alrededor de esto, pero ninguna ofrece una respuesta profunda y reflexiva. Agradecería si alguien puede compartir su experiencia.

Nota: Usamos la sync worker_class predeterminada para Gunicorn


Ejecutamos un kuberner de Kubernetes con Django y Celery, e implementamos el primer enfoque. Como tal, algunos de mis pensamientos sobre esta compensación y por qué elegimos este enfoque.

En mi opinión, Kubernetes tiene que ver con la escala horizontal de sus réplicas (llamadas implementaciones). En ese sentido, tiene más sentido mantener sus implementaciones lo más posible, y aumentar las implementaciones (y los pods si se agotan) a medida que aumenta la demanda. Por lo tanto, LoadBalancer administra el tráfico a las implementaciones de Gunicorn, y la cola Redis administra las tareas a los trabajadores de Celery. Esto garantiza que los contenedores de la ventana acoplable subyacente sean simples y pequeños, y podemos escalarlos individualmente (y de manera automática) según lo creamos.

En cuanto a su idea sobre cuántos workers / concurrency necesita por implementación, eso realmente depende del hardware subyacente en el que ejecutan sus Kubernetes y requiere experimentación para hacerlo bien.

Por ejemplo, ejecutamos nuestro clúster en Amazon EC2 y experimentamos con diferentes tipos de instancia y workers EC2 para equilibrar el rendimiento y los costos. Cuanta más CPU tenga por instancia, menos instancias necesitará y más workers podrá implementar por instancia. Pero descubrimos que implementar instancias más pequeñas es, en nuestro caso, más barato. Ahora implementamos múltiples instancias de m4.large con 3 trabajadores por implementación.

Nota interesante: hemos tenido un rendimiento realmente malo de gunicorn en combinación con los balanceadores de carga de Amazon, por lo que cambiamos a uwsgi con grandes aumentos de rendimiento. Pero los principios son los mismos.


Estas tecnologías no son tan similares como parecen inicialmente. Se dirigen a diferentes partes de la pila de aplicaciones y en realidad son complementarias.

Gunicorn es para escalar la concurrencia de solicitudes web, mientras que el apio debe considerarse como una cola de trabajadores. Llegaremos a los kubernetes pronto.

Gunicorn

La concurrencia de la solicitud web está limitada principalmente por la E / S de la red o el "límite de E / S". Estos tipos de tareas se pueden escalar utilizando la programación cooperativa proporcionada por subprocesos. Si encuentra que la concurrencia de solicitudes está limitando su aplicación, aumentar los subprocesos de trabajadores de Gunicorn puede ser el lugar para comenzar.

Apio

Las tareas de trabajo pesado, por ejemplo, comprimir una imagen, ejecutar algunos ML, son tareas "vinculadas a la CPU". No pueden beneficiarse de subprocesos tanto como más CPU. Estas tareas deben ser descargadas y paralelizadas por los trabajadores del apio.

Kubernetes

El objetivo de Kubernetes es proporcionar escalabilidad horizontal y tolerancia a fallos de forma inmediata.

Arquitectónicamente, usaría dos implementaciones de k8s separadas para representar las diferentes preocupaciones de scalablity de su aplicación. Un despliegue para la aplicación Django y otro para los trabajadores del apio. Esto le permite escalar de forma independiente el rendimiento de la solicitud en comparación con la potencia de procesamiento.

Ejecuto trabajadores de apio clavados en un solo núcleo por contenedor ( -c 1 ), lo que simplifica enormemente la depuración y se adhiere al mantra "un proceso por contenedor" de Docker. También le brinda el beneficio adicional de la previsibilidad, ya que puede escalar la potencia de procesamiento por núcleo incrementando el recuento de réplicas.

La escala del despliegue de la aplicación Django es donde deberá DYOR para encontrar la mejor configuración para su aplicación en particular. Nuevamente, --workers 1 usando --workers 1 para que haya un proceso único por contenedor, pero debe experimentar con - --threads para encontrar la mejor solución. De nuevo, deje la escala horizontal a Kubernetes simplemente cambiando el número de réplicas.

HTH Definitivamente es algo que tuve que envolver cuando trabajé en proyectos similares.