Pull to refresh

Comments 30

«Главными критериями для выбора способа развертывания были стабильность/надежность, и тут связке apache+mod_wsgi+nginx на данный момент равных нет.»
Вот тут можно было бы сильно поспорить с вами в сторону связки Cherokee+UWSGI+nginx (хотя и без него отлично), но толку в споре мало, стоит лишь отметить, что стабильность/надежность данной связки так же весьма и весьма на высоте.
За статью спасибо, будет полезна начинающим Django-деплоерам и не только.
равно как и fastcgi + nginx через flup. Работает уже давно так.
Угу, согласен, и uwsgi, и gunicorn, и fastcgi — способы хорошие и надежные, и используются в куче мест в продакшне. Ничего плохого про них сказать не могу. При всем при этом апач+mod_wsgi остается самым популярным способом запуска джанги (например, вот тут немного статистики: www.djangosites.org/stats/ — под апачем больше 70%, под чероки — меньше 1%. ). С ним уж точно все понятно, на все грабли давно наступили, много людей имеет опыт настройки этой связки, пакеты лежат в репозиториях операционных систем, нет необходимости разбираться с авто-запуском всего хозяйства и перезапуском процессов (завершившихся или зависших).

Я рассматривал gunicorn как альтернативу apache+mod_wsgi, но не из-за незначительных преимуществ в скорости и т.д., а из-за того, что можно было бы пользовательский апач не трогать, а gunicorn прямо в virtualenv устанавливать. Но трогать апач придется в любом случае, т.к. он не должен слушать 80 порт, так что смысла особого что-то менять сейчас пока не вижу.
Тут да… в плане популярности и перевеса в сторону, пусть относительной, но простоты конфигурации, статистическое преимущество, конечно же, явное.
Учтите, что nginx не поддерживает HTTP/1.1 для proxy_pass, так что способ хоть и самый популярный, но и самый неудачный, с точки зрения потребления ресурсов и производительности.

Также вызывает недоумение использование MySQL, вместо PostgreSQL, стандартной и рекомендуемой для django.
Мне кажется, насчет nginx Вы заблуждаетесь. Nginx не умеет keep-alive в сторону бэкенда, поэтому пишут, что он http 1.1 не поддерживает в эту сторону. Но keep-alive в сторону бэкенда и не нужен. А наоборот, противопоказан. Бэкенду лучше с запросом разобирался поскорее и ресурсы бесполезно не тратить. См., например, тут. А наружу nginx 1.1 умеет — для передачи той же статики новые соединения открываться не будут.

Насчет MySQL — так уж сложилось, что его использую. Соответственно и команды были для него. На mysql почти ничего не завязано (создание и настройку базы специально вынес из стандартного процесса установки), так что ничто не мешает написать свои fab-команды для postgres (буду рад патчам).
По ссылке верно написано: «avoiding the TCP setup/teardown overhead», а что касается «idle HTTP server just consumes RAM that an active one should be using», то:
1) это зависит главным образом от настройки, и не факт, что без keep-alive у вас ничего простаивать не будет, в конце-концов воркеры апача так и будут висеть, какое-то минимальное количество;
2) это куда лучше, чем бесконечный старт-стоп воркеров, и виделение-освобождение памяти туда-сюда, все это весьма затратные операции;
… а учитывая, что память ресурс дешевый, и то количество которое там расходуется, оно весьма скромно. Ну и да, вообще-то смешно говорить об экономии памяти, на фоне того, что вы apache запускаете.

И вообще говоря, http плохой протокол для обмена данных между веб-сервером и приложением, он прост для чтения человеком, но относительно неудобен для разбора программой, что тоже дополнительный overhead.

Популярность связки с использованием apache объясняется исключительно тем, что так проще shared-хостнигам, клиенты могут править .htaccess и прочие плюшки за счет апача, что в общем-то также сказывается негативно на производительности, но в данном случае главное объем возможностей, которые можно предоставить клиенту.
Проблема с воркерами не в том, что они висят (они и так висят в памяти при запуске через daemon mode), а в том, что при keep-alive они не могут обрабатывать соседние запросы и могут от этого закончиться.

Оверхед апача — несколько мегабайт на сайт ( grab.by/90Ax — вот пример, 2.9M RSS, это стандартный апач из дебиана без всякой настройки специальной) — это висящий в памяти управляющий процесс, от которого запускается mod_wsgi в daemon_mode. Оверхед mod_wsgi — несколько сотен килобайт на процесс, это C-код, сомневаюсь, что у fastcgi через flup (написанного на python) и т.д. оверхед меньше. О чем речь?

Апач в случае джанги выбирают из-за mod_wsgi, который умеет все: прождать и следить за процессами и тредами, втч не только запускать умершие, но и перезапускать повисшие, не инициировать интерпретатор без необходимости, перезапускать все хозяйство при изменении времени у wsgi-файла, ограничивать потребление памяти и CPU, перезапускать процессы, которые потребляют слишком много CPU и тд.
«а в том, что при keep-alive они не могут обрабатывать соседние запросы»
Это почему же вы так решили? Те http соединения которые nginx устанавливает с backend-ом никак не привязаны к соединению с клиентом, этим занимается отдельный модуль (HttpProxyModule), с тем же успехом он мог бы держать некий пул открытых соединений и их использовать, обрабатывая запросы от разных клиентов в рамках одного keep-alive соединения.

Overhead апача это не только дополнительный расход памяти на мастер-процесс и каждый воркер, это еще выполнение всей внутренней апачевской логики на каждый запрос, разбор http-заголовков, дерганье всех внутренних хэндлеров.
У mod_wsgi/daemon_mode нет дополнительного расхода памяти на каждый воркер, обусловленного апачем, т.к. воркерами (процессами и тредами) он управляет сам, апач со своими модулями и т.д. в воркеры не загружается (в отличие от того же mod_python).

Ok, с keep-alive согласен.

Я правильно т.е. понимаю, что в сухом остатке мы имеем все-таки не дикий расход памяти или какие-то другие страшные вещи, а лишь оверхед по разбору апачем заголовков и установке соединений через localhost?

Этот оверхед, конечно, есть, но в абсолютных величинах — несколько тысяч запросов в секунду через локалхост апач уж обработает. Если кто-то сделает это вдвое быстрее, выигрыш будет измеряться, похоже, в долях ms, нет?
Вопрос в том, правильный ли это подход — делать бенчмарки «тупых» хеллоу-вордов и на основе них выбирать способ запуска приложений.

blog.ianbicking.org/2010/03/16/web-server-benchmarking-we-need/

Слабо, например, представляю приложение, которому будут жать 2000 хеллоувордов/сек от mod_wsgi из теста. В то же время, можно вполне себе можно представить сервер, умерший наглухо из-за того, что 0.01% запросов вызывает сегфолты, а сервер не умеет эту ситуацию обрабатывать, постепенно забивая воркеры, пока все не застопорятся. Поэтому мне кажется, что чуть большая уверенность в надежности и проверенности (обусловленная, в частности, популярностью mod_wsgi и уверенностью в том, что автор имеет достаточно глубокие познания и умеет работать с коммьюнити) значит для реальных приложений больше, чем бенчмарки хеллоу-вордов (которые показывают только, что все популярные серверы имеют достаточную скорость).
Естественно, что задавшись целью сравнить разницу в производительности между различными wsgi-серверами, человек использовал приложение-затычку, чтобы оно вносило минимальный вклад в общую картинку, и не смешивало краски.

Если у вас медленное приложение, то, конечно, оно будет узким местом разницу вы не заметите.

Я не использую mod_wsgi для django-проектов у себя на хиленьком vds, поскольку там очень мало памяти, а apache реально очень много жрет. Приведенный вам выше скриншот одной строчки из top (?) и его интерпретация малость не адекватны. Во-первых, там все-таки 17,5Мб, и то что 15 из них выловились в swap, еще ни о чем не говорит, вероятно у вас дефицит памяти, а данный процесс как раз находится в простое. А то я вам тоже покажу:
35516 vbart 1 4 0 22828K 8K select 0:07 0.00% python
8Кб, смешно, да? =) А вот процесс который недавно обработывал запрос:
45866 vbart 1 4 0 21732K 16060K select 0:26 0.00% python

У меня большой опыт эксплуатации слабеньких vds-ок с малым количеством памяти, и он показывает, что apache на них не место. И не один я так считаю.

А для большого проекта и под высокой нагрузкой, я не буду использовать mod_wsgi, поскольку он не поддерживает асинхронный режим работы. Такой роскоши я себе позволить не могу, мне нужно получить и обработать данные от нескольких запросов к двум различным пулам серверов СУБД, если я от каждого буду ждать ответа по очереди, то пользователь получит свою страницу минимум через секунду-две, а ОС придется держать тысячи процессов.

«что 0.01% запросов вызывает сегфолты, а сервер не умеет эту ситуацию обрабатывать, постепенно забивая воркеры, пока все не застопорятся»
Это какой-то некий мифический сервер. У того же uwsgi есть «harakiri mode».
Во-первых, там все-таки 17,5Мб, и то что 15 из них выловились в swap, еще ни о чем не говорит, вероятно у вас дефицит памяти, а данный процесс как раз находится в простое.


17.5M — VIRT. Это к свопу не имеет абсолютно никакого отношения. Почитайте, например, тут: linux-mm.org/ActualMemoryFootprint

Обычно эти 17.5M вообще ни на что не влияют. Т.е. если бы там было 375M вместо 17.5M — ничего бы не изменилось. Или 1Gb. Совсем ничего. Ну это, наверное, как-то слегка влияет на работу планировщика памяти, но уж очень слегка.

Кроме одного исключения, к которому мы сейчас подойдем.

У меня большой опыт эксплуатации слабеньких vds-ок с малым количеством памяти, и он показывает, что apache на них не место. И не один я так считаю.


Вот оно, опять. Дико распространенный миф о прожорливости апача вызван вызван именно кривыми слабыми вдсками на OpenVZ. Т.к. OpenVZ настолько крив, что ограничивает именно VIRT, а не RSS. В реальных серверах учитывается RSS память, в VDS на XEN и KVM — тоже RSS. Куча софта (включая саму операционную систему — то, как она работает с потоками — выделяет в стеке место под каждый поток в VIRT памяти, порядка 8M обычно) написано в том предположении, что VIRT дается бесплатно. А на OpenVZ это не так. Поэтому любой софт, использующий потоки или, например, делающий mmap файлов — апач, mysql+innodb и т.д., на OpenVZ будет есть неприличное количество памяти. Это можно только слегка сгладить костылями, даже статью про это писал. Но проще — не использовать OpenVZ, очень вероятно, что XEN c 256метров больше памяти даст реально, чем OpenVZ на 512.

И тут в инструкции курсивом, и в документации к django-fab-deploy красным про OpenVZ предупреждаю не зря — не будет на нем конфигурация apache+mod_wsgi+nginx работать нормально. А на XEN или реальном сервере — будет.

В статье про fapws, на которую Вы ссылаетесь, автор вот тоже не разобрался ну совсем. Запустил однопоточный асинхронный fapsw и синхронную джангу за ним. Причем 1 инстанс. Джанга спрашивает базу — обработка стоит. Джанга обратилась к стороннему ресурсу через urllib, а тот не ответил — обработка встала на несколько секунд, а если таймаут не стоял, то совсем встала. Идет загрузка файла через джангу — все ждут, ничего не работает. Ну ок, выход — запустить несколько инстансов, чтоб работали примерно так же, что и апач. Так и потреблять будет как mod_wsgi без тредов, на одних процессах. Т.е. больше, чем mod_wsgi с тредами. На нормальном сервере или VDS под XEN — ужасная конфигурация, проигрывающая апачевской по всем статьям. И, кстати, бенчмарк с хеллоувордом это все не выявит. Что и требовалось доказать, как говорится. Вредные это статьи, вредные бенчмарки. Сколько вот людей ту статью причитало и времени потратило, настраивая совершенно корявую конфигурацию с уверенностью в том, что добиваются высокой производительности.

Вы вот тоже странные вещи пишете, что mod_wsgi не асинхронный и из-за этого его под высокой нагрузкой использовать не будете. У нас же про джангу речь? Нет совсем никаких преимуществ в запуске синхронной джанги через асинхронный сервер. Т.к. джанга-то синхронная. Будет ваш весь из себя асинхронный сервер обращаться к синхронной джанге, и все будут стоять и ждать, пока джанга ответит и можно будет в event loop вернуться. Для того, чтобы получать преимущества от асинхронных серверов, нужно, чтоб и код, который выполняется, асинхронным был. К джанге все это никаким боком не относится.

Джанге нужны процессы, чтоб распределять нагрузку по ядрам CPU. И треды, чтобы экономить память, обслуживать больше медленных ответов и не застопориться при этом. А асинхронность сервера ей никаким боком не нужна.

Если есть треды, то это будет плохо работать на вдсках под OpenVZ, будет казаться, что прожорливо очень. И в бенчмарке про хеллоуворд софт с тредами медленнее результат покажет, чем с 1 процессом (из-за того, что все будет CPU-bound, а треды вызовут GIL). Ну и будут опять все говорить, что вот способ X — отстой, вот эти быстрее. А реальные проекты обычно не CPU-bound, а IO-bound, и конфигурация с тредами будет надежнее, потреблять меньше памяти и обрабатывать больше запросов. Т.е. опять бенчмарки и опыт дешевых вдсок нам будет врать.

Я не к тому, что апач — панацея. Я к тому, что он работает, и работает хорошо и предсказуемо. А с остальным нужно разбираться и как минимум внимательно смотреть, что конкретно там происходит. Если знаний достаточно — вперед. А если нет — то легко начитаться бенчмарков и в лужу сесть, как вот автор статьи про fapsw3.
Про OpenVZ зря упомянули. У меня все vds-ки на XEN.

17.5M — VIRT. Это к свопу не имеет абсолютно никакого отношения. Почитайте, например, тут: linux-mm.org/ActualMemoryFootprint

По ссылке ни слова не сказано, что VIRT не имеет отношения к swap-у. И да, man top четко и ясно нам говорит, что: VIRT = SWAP + RES. Тот факт, что в него входит mmap файлы и разделяемая память, еще не отменяет того, что у вас почти все приложение сидит в свопе. Как только получит запрос, ваш RSS мигом возрастет. Что это за django-приложение которое кушает всего 2,5Мб? По-моему даже Hello world на django съест больше.

В статье про fapws, на которую Вы ссылаетесь, автор вот тоже не разобрался ну совсем. Запустил однопоточный асинхронный fapsw и синхронную джангу за ним.

Оставим автора в покое, это его тараканы. Когда я писал тот пост, то от статьи прочитал только первые несколько предложений, где он жалуется на прожорливость апача. Да, согласен с вами, данная статья не адекватна.

Вы вот тоже странные вещи пишете, что mod_wsgi не асинхронный и из-за этого его под высокой нагрузкой использовать не будете. У нас же про джангу речь?

Нет. Я конечно мог бы сделать для django асинхронную обертку, но она мне никаким боком не сдалась. Я явно указал ситуацию, когда синхронный режим работы не приемлем. Есть пара кластеров различных СУБД, и приложению надо выполнить, к примеру, 8 независимых запросов. Каждый занимает, предположим, ~100мс, если мы будем делать их по очереди, то только на запросах потеряем порядка 800мс. Единственный выход: сделать эти 8 запросов асинхронно, повесив на них обработчики, и вернуть управление application-серверу, тому же uwsgi.

Я не к тому, что апач — панацея. Я к тому, что он работает, и работает хорошо и предсказуемо.

С этим согласен.
А в общем-то, в целом Вы меня убедили, что выбранный вами метод не так уж плох.
у меня тоже несколько мифов в голове развеялось, спасибо за полезную дискуссию)
2,5Mb тут кушает не джанго-приложение, а надзирающий воркер, который запускает демон-процессы джанги с потоками. Демон-процессы кушают (оверхед mod_wsgi = несколько сот кб) + (интерпретатор питона) + (собственно приложение). При других способах запуска все то же самое, только вместо оверхеда mod_wsgi будет оверхед от другой библиотеки.

Насчет VIRT — значит в мане top неправильная формула) VIRT — это в линуксе то, что приложение себе заммапило. Память под стек в VIRT выделяется, разделяемые библиотеки тоже там учитываются. Но согласен, насчет того, что вообще никакого отношения не имеет — не прав был, имеет, своп там тоже считается. Другое дело, что у меня на серверах в свопе обычно нет ничего и куча свободной памяти еще. Так что привык, что в VIRT своп не учитывается, своп там — совсем не главное. А в OpenVZ user-level свопа-то нет вообще, а VIRT у апача может раз в 20 больше RSS быть, но черт с ним, с OpenVZ.

Асинхронность — отдельная тема, статья все-таки про django. Highload — не обязательно асинхронность. Возьмем, например, disqus. Самый нагруженный django-сайт, десятки тысяч запросов в секунду обрабатывает. БОльший опыт деплоя highload-приложений на django вряд-ли у кого-то есть. И что там внутри? HAProxy+apache+mod_wsgi+postgres.
2,5Mb тут кушает не джанго-приложение, а надзирающий воркер

И правда. Виноват, упустил сей факт из виду.

Вообще, почитал сейчас про mod_wsgi побольше, как оно устроено, понял, что заблуждался на счет прожорливости. Действительно хорошая очень штука. Мое впечатление об apache скорее больше базировалось на тех временах когда mod_python повсеместно был в ходу. Давно я от апача отказался, а негатив остался. Что ж, и у меня теперь одним мифом в голове меньше.

Однако, использовать специализированный application-сервер, типа uwsgi, мне все ж больше нравится. И проще, и когда на одной машине, можно использовать сокеты, вместо TCP. А для highload-а тут и в синхронном режиме полно всяких плюшек, даже перечислять замучаешься, можно очень здоровскую систему построить. ;-)
Да и, кстати, как поведет ваш хваленой надежности mod_wsgi если приложение словит не segfault, а впадет в бесконечный цикл, или вечный i/o wait?
Убьет процесс по inactivity timeout и возродит со следующим запросом. Работало еще в 2007 году.
Про flup, кстати, я не упоминал. Есть python-fastcgi, если нужно производительно и сурово. Есть uwsgi если нужно супер-гибко.
Использую для этого свою самописную систему, не привязанную к джанго: github.com/klen/makesite

Вкратце оно может все выше перечисленное + что касается джанго, сборка статики, добавление задач в крон, celery, сброс мемкеша при деплое, оно умеет создавать базы и пользователей для них при необходимости, оно умеет заводить ветки в git. Оно умеет обновлять проекты и это легко прикрутить к хукам систем контроля версий. Оно содержит вспомогательные скрипты для работы с проектами (типа поиск проекта и активация его виртуального окружения с автодополнением в баше). Оно может развернуть свою веб морду со списком проектов на сервере, оно может развернуть скелет базового джанго проекта из своей поставки. Оно умеет удалять проекты. Оно легко расширяемо с помощью системы темплейтов и так же легко конфигурируется. Например на сервере можно задать глобальные настройки для всех проектов или хранить настройки в своем домике. Оно вообще может еще много чего, но мне все лень написать нормальную документацию, а еще лучше серию скринкастов.
Интересная штука, добавил в djangopackages.com/grids/g/deployment/.

Тоже пол-года за документацию взяться не мог, это процентов 50 усилий как минимум) Как писать начал, куча тонкостей вылезла — я, оказывается, разные предположения делал о предварительной настройке сервера и структуре проекта, ну и не замечал, что перевод существующего проекта переусложнен.

Задачи в крон добавлять (и, что немаловажно, надежно их потом оттуда удалять), пользователей заводить, базы создавать и проекты удалять django-fab-deploy тоже умеет. На локальной машине django не требуется, к слову.

Скрипты для работы и развертывания celery, rabbitmq, redis, node.js + npm (все интегрировано с питоньим виртуаленвом), tornado, supervisord, memcached, geoip/geos, munin с плагинами и т.д. у меня для разных проектов тоже написаны, но в django-fab-deploy я это все осознанно пихать не стал. Лучше, чтоб кода было меньше, и он делал какую-то четко определенную задачу. Может, что-то вроде fab_deploy.contrib потом будет иметь смысл сделать, пока не уверен.

Вместо сброса memcached при деплое (зло как-то: один сайт поменялся, у всех кеш слетел) лучше imho в ключи версию кеша добавлять. В django 1.3 это из коробки, раньше можно было сторонние бэкенды использовать (django-newcache, django-cache-utils).

В том, из чего вырос django-fab-deploy, все изначально было прикручено к хукам системы контроля версий, но потом от этого пришлось отказаться, т.к. такой способ очень усложняет условную логику. Например, если хочется выложить изменения на сервер, но пока не делать миграции, а миграции в хуке уже прописаны. Подход с «глупым» сервером и «умным» клиентом гибче показался.
А как там можно редактировать информацию?
Зарегистрироваться и ткнуть в нужный квадрат.
django-fab-deploy не работает с Fabric 0.9.x, Fabric должен быть установлен с github'а — и урл приводится без ссылки на коммит даже? Ну и на гитхабе, судя по code.fabfile.org/projects/roadmap/fabric, идёт работа над 0.9.4 (странно, что там нет ветки под 1.0, которая также в разработке)
Я, если честно, не очень понимаю, почему 1.0 или хотя бы 0.10.0 не выпустили уже давно. Там много разных улучшений внутренних, в master они лежат давно (чуть ли не по полтора года, судя по трекеру), и которые в 0.9 не бекпортят — и, видимо, не будут.

Fabric периодически обновляю и слежу, чтоб ничего не сломалось (за пол-года никаких проблем так и не было). Но так-то да, коммит было бы хорошо вписать.
данный способ осущ. последовательное обновление на серверах а не параллельное т к Fabric не поддерживает параллельные запуски. Есть правда форк по ссылке который это умеет. По моему это нужно отметить в статье.
Я, по правде говоря, не очень понимаю, в чем тут проблема, и почему за это дело переживают так. Т.е. понятно, то удобно, когда хорошая поддержка таких штук, но:

fab stage push & fab prod push — вот и параллельный запуск готов. Это при желании элементарно автоматизируется.

Sign up to leave a comment.

Articles