Докер/Кубернетес + Гуйкорн/Сельдерей - Несколько рабочих против реплик?
Мне было интересно, какой правильный подход к развертыванию контейнерного приложения Django с использованием gunicorn & celery был.
В частности, каждый из этих процессов имеет встроенный способ масштабирования по вертикали, используя workers
для пушки и concurrency
для сельдерея. И тогда есть подход Кубернеса к масштабированию с использованием replicas
Существует также понятие о том, что работники равны некоторой функции процессоров. Гуникорн рекомендует
2-4 рабочих на ядро
Однако я смущен тем, что это означает для K8s, где ЦП - это делимый общий ресурс - если только я не использую resoureceQuotas.
Я хочу понять, что такое "Лучшая практика". Есть три варианта, о которых я могу думать:
- Имеют ли одинокие рабочие для пушки и параллелизма 1 для сельдерея и масштабируют их с помощью реплик? (горизонтальное масштабирование)
- Попробуйте использовать пушки и сельдерей в одном реплике с внутренним масштабированием (вертикальное масштабирование). Это означало бы установление довольно высоких значений рабочих и параллелизма соответственно.
- Смешанный подход между 1 и 2, где мы запускаем пушки и сельдерей с небольшой стоимостью для рабочих и параллелизма (скажем, 2), а затем используем реплики развертывания K8s для горизонтального масштабирования.
Есть несколько вопросов по поводу этого, но никто не предлагает углубленного/продуманного ответа. Был бы признателен, если кто-то может поделиться своим опытом.
Примечание. Мы используем стандартную sync
employee_class для Gunicorn
Ответы
Ответ 1
Эти технологии не так похожи, как они изначально кажутся. Они обращаются к различным частям стека приложений и фактически дополняют друг друга.
Gunicorn предназначен для масштабирования параллелизма веб-запросов, в то время как сельдерей следует рассматривать как рабочую очередь. Скоро мы доберемся до кубернетов.
Gunicorn
Параллелизм веб-запросов в основном ограничивается сетевым вводом-выводом или "привязкой ввода-вывода". Эти типы задач можно масштабировать, используя совместное планирование, предоставляемое потоками. Если вы обнаружите, что параллелизм запросов ограничивает ваше приложение, увеличение рабочих потоков для артиллеристов вполне может стать началом.
Сельдерей
Задачи с тяжелым подъемом, например, сжимать изображение, запускать некоторый ML-алгоритм, являются задачами, связанными с "процессором". Они не могут использовать потоки столько же, сколько больше процессоров. Эти задачи должны быть разгружены и распараллелены работниками сельдерея.
Kubernetes
Там, где Kubernetes пригодится, можно установить горизонтальную масштабируемость и отказоустойчивость.
Архитектурно, я бы использовал два отдельных развертывания k8s для представления различных задач масштабируемости вашего приложения. Одно развертывание для приложения Django и другое для работников сельдерея. Это позволяет вам независимо масштабировать пропускную способность запроса и вычислительную мощность.
Я управляю работниками сельдерея, прикрепленными к одному ядру на контейнер (-c 1
), это значительно упрощает отладку и придерживается докеры "один процесс за контейнер" мантры. Это также дает вам дополнительное преимущество предсказуемости, так как вы можете масштабировать вычислительную мощность на основе -c руды, увеличивая количество реплик.
Масштабирование развертывания приложения Django - это то, где вам нужно будет DYOR, чтобы найти лучшие настройки для вашего конкретного приложения. Снова придерживайтесь использования --workers 1
поэтому для каждого контейнера есть один процесс, но вы должны поэкспериментировать с --threads
чтобы найти лучшее решение. Снова оставьте горизонтальное масштабирование до Kubernetes, просто изменив счетчик реплик.
HTH Это определенно то, что я должен был обернуть вокруг себя, работая над подобными проектами.
Ответ 2
Мы запускаем Kubernetes kluster с Django и Celery, и реализовали первый подход. Как таковые некоторые из моих мыслей об этом компромиссе и почему мы выбираем для этого подхода.
На мой взгляд, Kubernetes - это горизонтальное масштабирование вашей реплики (называемое развертыванием). В этом отношении наиболее целесообразно поддерживать развертывание как можно более единым, а также увеличивать развертывания (и контейнеры, если вы заканчиваете работу) по мере увеличения спроса. Таким образом, LoadBalancer управляет трафиком для развертываний Gunicorn, а очередь Redis управляет задачами работников Celery. Это гарантирует, что базовые контейнеры докеров являются простыми и малыми, и мы можем индивидуально (и автоматически) масштабировать их по своему усмотрению.
Что касается вашей мысли о том, сколько много workers
/concurrency
вам необходимо для развертывания, это действительно зависит от базового оборудования, на котором работает ваш Kubernetes, и требует экспериментов, чтобы получить право.
Например, мы запускаем наш кластер на Amazon EC2 и экспериментируем с различными типами экземпляров EC2 и workers
чтобы сбалансировать производительность и затраты. Чем больше у вас процессора на один экземпляр, тем меньше требуется вам экземпляров и больше workers
вы можете развернуть на один экземпляр. Но мы выяснили, что развертывание более мелких экземпляров в нашем случае дешевле. Теперь мы развертываем несколько экземпляров m4.large с тремя рабочими за развертывание.
Интересная сторона примечания: у нас была действительно плохая производительность gunicorn
в сочетании с балансирами нагрузки амазонки, поэтому мы переключились на uwsgi
с большим увеличением производительности. Но принципы одинаковы.