Нагрузка и ресурсы, как оно есть.
Год назад я делал нудный доклад о нагрузке, с ним можно ознакомиться здесь:
http://vimeo.com/7631344 .
Вкратце обозначу основные положения из него:
- По стратегии предоставления ресурсы могут быть гарантированными и разделяемыми. Между ними нет холивара — они или такие, или такие. Со своими особенностями.
- Ресурсы должны быть поделены по функциональности. Чем более полно выделены отдельные по смыслу функционирования сервисы, тем легче справиться с контролем ресурсов. С «кучей» всегда разобраться сложнее.
- Разделяемые ресурсы должны работать на полной производительности. Нельзя «тормозить» ресурс. Требуется оптимально выбрать «точку входа» и ограничить её и только её.
Проблема.
В нашем случае мы рассматриваем виртуальный UNIX-хостинг с разделяемыми ресурсами. Т.е. множество различных сайтов на одном сервере или группе серверов.

Одной из самых ярких проблем виртуального хостинга является неконтролируемая, не поддающаяся разумной формализации посещаемость, в особенности её резкие скачки. В результате — периодический взрывной рост соединений с веб-сервером, вытекающая из этого нагрузка на базу данных и/или диск, замедление выдачи результатов по сумме причин. Обратная связь — рост соединений с веб-сервером из-за замедляющейся обработки.
Такое в 90% случаев происходит по вине различных роботов, как «белых» (Yandex, Google и проч.), так и «серых» (другие индексирующие сервисы — sape, linkfeed и им подобные) и даже (о боже!) «черных» — роботов, ворующих контент. «Черные роботы» обычно пытаются сдернуть контент «за раз», поэтому приходят на сайт в десятки одновременных потоков, чем создают немалые проблемы.
Ситуация почти всегда усугубляется тем, что очередь желающих попасть к веб-серверу в момент наплыва соединений к какому-нибудь сайту растёт. Даже когда «виновник» затихает, освободившиеся ресурсы резко пытается занять скопившаяся «очередь». Это приводит к так называемой «зыби». В моей практике нередки были проблемные случаи даже при отношении один сервер — три сайта.
Установка «тонкого прокси» (например, nginx) перед веб-сервером устраняет некоторое количество симптомов проблемы. Сама проблема остаётся.
«Вынос» базы данных на отдельную машину снижает порядок негативного эффекта, но не устраняет его, и не во всех случаях экономически оправдан.
Есть вариант кэширования страниц сайтов. Но будем реалистами — большинство разработчиков не знают ничего о среде исполнения своих разработок, знать не хотят и не будут. Примерно на пятом сайте вы уже забудете, что куда кэшируется, что не кэшируется никогда, кто только что поменял структуру сайта или что-нибудь ещё.
Теория.
Очевидно, что в описываемой ситуации «точкой входа» является соединение с веб-сервером, и ограничивать надо именно его. Сразу возникает вопрос — что мы хотим «сказать» пришедшему «лишнему» соединению? Если мы ему хоть что-то «скажем», будь то ошибка 503 или сброс соединения, мы в итоге будем иметь «битые» сайты у неопределённого числа посетителей в хаотичном порядке. Из готовых решений у нас есть только limit_req в nginx с высоким burst. Но возникает проблема — как подобрать параметр скорости запросов? Можно, конечно, доработать limit_conn, на параметр burst из limit_req. Но у обоих решений есть проблема — ограничивается точка входа на «тонкий прокси».
На наш взгляд, точка входа «тонкого прокси» является не самым удачным местом для ограничения. У готовых же решений для apache проблема общая — они что-то, да возвращают. Доработка невозможна даже теоретически — не держать же обработчик апача в ожидании «разрешения», это сводит на нет весь смысл затеи.
Предыстория
Мы предоставляем услугу хостинга приложений на языке python посредством mod_wsgi. Апач запускает для каждого сайта некоторое количество специальных обработчиков, которые постоянно находятся в памяти. Каково же было наше удивление, когда мы осознали, что особо не замечаем эффекта «наплыва» на таких сайтах (например —
http://twihoo.ru, который является достаточно большим по посещаемости, но не самым крупным по нагрузке на сервер!).
Присмотрелись внимательнее. Понятно — апач общается с обработчиками по unix-сокету, и все «лишние» соединения просто ждут, когда их обработают. В итоге получается «размазывание» нагрузки по времени без лишних телодвижений. Эта практика сильно уменьшила наш религиозный страх перед постоянно находящимися в памяти процессами пользователей, и мы начали искать решение.
Эврика!
Решение пришло совершенно неожиданно. Я участвовал в настройке сервера нашим партнёрам и хотел поставить им несколько отдельно настроенных веб-серверов apache. Заглянув в сценарий запуска apache из коллекции пакетов FreeBSD, я вдруг обнаружил, что там предусматривается такая возможность «из коробки». Сайты у партнёров раскрученные, посещаемые, тяжёлые. Партнёры требовательны и полностью зависят от качества работы сайтов. Учитывая наш опыт хостинга WSGI, соблазн провести эксперимент «в бою» победил.
«Каждому свой apache» оказалось очень удачной мыслью. Совсем недавно планировщик FreeBSD зависел от количества процессов (по умолчанию до версии 7.1), но мы, как прогрессивная компания, уже использовали новые версии FreeBSD с новым планировщиком. Эксперимент показал, что при наличии ресурсов 3-4 процесса апача выдерживают достаточно большую посещаемость даже «тяжёлых» сайтов. Такое решение имеет целый ряд очевидных преимуществ перед специализированными решениями вроде mod_peruser и mod_itk — отсутствие невнятно работающего кода этих модулей, возможность управлять количеством модулей апача, возможность делать специализированные настройки этих модулей.
До настоящего времени мы использовали для хостинга apache 1.3.x с немного переделанным самым первым патчем Дмитрия Котерова — на каждый запрос apache делал vfork(), потом setgid() и setuid(), после обслуживания запроса — _exit(). Новый способ даёт нам возможность сэкономить процессорное время, затрачиваемое на эти процедуры.
Но есть и минус — требования к количеству постоянно занимаемой памяти. Современные машины виртуальной памяти, особенно унаследованные из ОС mach, очень сложные. Сосчитать и даже прикинуть заранее, сколько физической памяти будет реально задействовано, не представляется возможным.
К слову сказать, похожую схему выделенных веб-серверов используют как некоторые российские, так и некоторые зарубежные хостинг-провайдеры. Например: MediaTemple с услугой GridContainers (
http://mediatemple.net/webhosting/gs/features/containers.php). Правда несколько сложнее и с виртуализацией, но общая идея похожа.
Через тернии к звёздам.
Мы приняли волевое решение
рискнуть. Целый месяц мы писали программу сборки конфигураций по данным панели, реагирование на изменения, запуск и останов веб-сервера, автоматическое отслеживание состояния каждого из сотен запущенных веб-сереров, ротация журналов, изменения конфигурации «тонкого прокси» — nginx. Заодно мы причёсывали и оптимизировали все задействованные в перестройке узлы.
Первый провал ожидал нас с… php. Мы пытались собрать php 5.2.x статически с апачем, чтобы сократить время старта веб-сервера и, теоретически, издержки во время исполнения. К сожалению, без хитрой правки внутри системы сборки php этого сделать не удалось. Адекватно поправить уже не позволяло время, и нам пришлось временно отказаться от затеи. Кстати, если есть желающие, в ЖЖ описана эта проблема, можно попробовать её решить годными методами:
community.livejournal.com/ru_root/1884339.html.
Но наконец-то система подготовлена. Мы перевели на неё несколько «своих» сайтов. Результат воодушевил — трёх обработчиков апача с лихвой хватило для тяжёлого сайта на drupal посещаемостью в ~1200 уников. В качестве особого теста мы попробовали небольшую «dos-атаку» на данный сайт (10000 запросов по 100 одновременных) — сайт замедлил работу но не оказал почти никакого влияния на работу остальных сайтов. Особых заметных подвижек в распределении памяти мы не диагностировали. Наш же сайт хостинга, который мы тоже перевели на новую систему, вообще не появлялся в выводе top. И вот, за три ночи я радостно перевёл все остальные сервера на новую систему. Почему ночью? Потому что ночью сервера обычно пустуют, и за счёт этого все переделки делаются наиболее быстро и незаметно.
Наступило утро понедельника…
Полный провал.
Сервер с наименьшим количеством сайтов стал работать заметно лучше, но заметно медленнее. На сервере с i386-архитектурой ОС не диагностировалась нехватка памяти, но вдруг mysql занял под себя все обращения к диску и почти парализовал его. Самый же мощный сервер сделал гигабайтный своп и тоже почти парализовал диски.
Подобное состояние «перегруженного» сервера имеет свои плюсы. Стрессовые ситуации вообще способствуют умственной работе. Мы сразу отловили всех, кто создаёт временные таблицы в mysql на сотни мегабайт, мы отловили несколько сайтов, делавших несколько сотен апдейтов баз в секунду. Мы полностью привели в порядок все настройки серверов mysql. Заодно поправили для себя утилиту mytop, до которой ранее руки не доходили:
schors.livejournal.com/652161.html. Но ничего не дало ощутимых улучшений. В порыве лёгкой паники мы попробовали сделать большие буфера для записи журналов апача — буфера для частых записей вообще полезны. Практика показала нулевой результат при данных обстоятельствах.
К концу дня мы готовы были дать отмашку на откат всего обратно. Но откат — это сдача позиций, поскольку мы так и не поняли, что происходит. Это значит, новая система и решение проблемы появятся ещё не скоро. Мы ещё немного потянули время. Почти сутки были потрачены на полную диагностику системы и воскурение всех возможных мануалов и руководств.
Результаты в теории оказались банальными — всё-таки не хватает памяти. Не вся память хочет бывать в дисковом свопе, не все программы при нехватке памяти сообщают об этом (например, mysql с временными таблицами и кэшами), используя другие инструменты. Посчитав стоимость дополнительной оперативной памяти, мы не задумываясь решились на обновление оборудования. И угадали.
Двукратное увеличение памяти решило всё. Хочу отметить, что это всё после выглядит странно — изначально было ясно, что может памяти не хватить. Но всё было неясно. Могло не хватить чего угодно — мог не справиться планировщик, мог не справиться процессор, памяти могло не хватить никакой вообще, могло не хватить обработчиков apache, могло не хватить буферов, могло не хватить диска. Вообще, последнее нас и сбило с толку. Вместо того, чтобы провести прямую зависимость между утилизацией памяти и дисковой активностью, мы бросились решать проблему «что же так грузит диск» смотря на то что делают с ним программы.
И тут неожиданно в спину нам ударил… php. Собранный статикой php 5.2.11 вдруг отказался нормально работать с модулем gd (графика). Причём он вообще отказался работать на том срезе библиотек, который у нас был. Магическими методами за бессонную ночь нам удалось привести в рабочее состояние только php с динамически загружаемыми расширениями. Эта проблема находится так:
www.google.ru/#hl=ru&newwindow=1&q=gd-png+fatal+libpng+error+freebsd+imagecreatefrompng&lr=&aq=f&oq=&fp=7b635a504e075d30. Понять, на какой зависимости это происходит, так и не удалось. Проблема открыта.
На пути к мировому господству.
Итог оказался потрясающим. Мы выравняли нагрузку. Представляем показательный график в «попугаях» Load Average. В районе 44-ой недели чётко видно вдруг выравнявшуюся 15-ти минутную нагрузку («холмики» сгладились), которая с апгрейдом оборудования в конце недели предсказуемо упала.
Мы выиграли процессорное время за счёт отказа от постоянных vfork(). На графике видно, что использование cpu заметно упало в начале 44-ой недели, после апгрейда оборудования в конце недели изменения менее заметны.
Остался один вопрос — не перестарались ли мы с ограничением в три обработчика веб-сервера apache на пользователя? В среднем по больнице, каждый пользователь размещает по два сайта. Но у некоторых нередко бывает и десять. Мы внимательно следим за сообщениями нашего мониторинга. На каждый запущенный apache у нас стоит проверка соединения с таймаутом в 60 секунд. Так мы определяем, что сайт точно «лёг». Прошло уже две недели. Срабатывание мониторинга исчисляется единичными случаями. Что характерно — буквально у трёх клиентов из сотен. Обычно это происходит или от нестандартных наплывов желающих что-нибудь «качнуть», или иногда во время бэкапа базы данных, или от допущенных в процессе обновления сайта ошибок.
Новая система принесла нам и приятные неожиданности. Мы получили «честный top». Программа top показывает процессы в системе, по умолчанию сортируя их по некоему странному параметру — процент использования процессора. Он вычисляется на фиксированном промежутке времени ядром по достаточно хитрому оценочному алгоритму и на самом деле не является в чистом виде процентом. В случае, когда один процесс веб-сервера может обрабатывать соединения к сайтам разных пользователей, мы видим «остаточный» процент от прошлого использования процесса. В случае, когда мы делаем vfork(), мы вообще видим только те процессы, что успевают попасть в опрос. В новой системе такого очевидно не происходит. Те процессы, которые появляются на верху top'а, действительно самые требовательные к ресурсам.
Вот так, перешагнув через подсознательный страх нового, переделав чуть менее чем полностью функциональную архитектуру хостинга, мы избавились от «эффекта понедельника».
комментарии (75)
Сейчас столкнулись с аналогичными проблемы, но после прочтения появились некоторые оригинальные выходы из нашей ситуации.
По неттопам, возможно, вам стоит обратиться в www.webdc.ru?
Хочется такой домашней и свойское размещение серверов, для небольших эксперементов, почти как вы.
Еще хотел спросит, думаю уйти из masterhost (по причине питона), насколько реально у вас получить 5 gb?
И что за странность в тарифе PARMA ,CALMA. Calma стоит дороже — и кол-во сайтови баз 2, PARAMA дешевле и кол-во сайтов и баз 10 O_o?
просто при цене 400 рублей я сейчас имею 5gb, 5 доменов, бд 10. или — это лучше в частном порядке? ^_^?
Про дополнительное место напишите в support@diphost.ru
А по неттопам вот тут представитель webdc признается, что косвенно у них есть такое: habrahabr.ru/blogs/hosting/74283/#comment_2143112
Просто щас да же по работе пристыковатся некуда, а дома ремонт, так что то же нет возможности.
На PARMA возможен один ознакомительный python-сайт (с малым количеством выделенных ресурсов)
Вернее тогда так: налетай, разберай!
Буду пробовать.
Это всё не избавляет на самом деле нас от проблем. И оборудование подгорает, и вышеописанный способ 100% ничего не защищает. Всякое бывает. Вплоть до «пропустили момент когда уже пора новый сервер». Хотя, теперь конечно легче :)
Извините, накипело!
Просто с Мажордомо получилось очень плохая ситуация. Тестовый период всё работало хорошо, сайт открывался шустро и я был всем доволен. Оплатил 2 месяца хостинга.
Через 2 недели после оплаты, сайт стал еле-еле грузиться ака *.народ.ру в давние времена. На 3 день тормозов я написал в суппорт, по этому поводу. Суппорт ответил, что на их стороне всё хорошо, а проблемы с моим каналом. Ответил я уже с логом трэйсроута и объяснением, что сайт тормозит из-под любого провайдера Москвы (qwerty, corbina, стрим и неизвестный провайдер на работе). Суппорт посоветовал мне сменить мой тяжелый свежеустановленный webasyst shop-script на что-то более легкое. На этом мой разговор и все дела с Мажордомо закончились.
Но мы наверное вам не будем интересны. У нас небольшая посещаемость 600 человек, 7000 просмотров/день.
У нас проблемы с поставщиками, поэтому рекламы мы боимся. Товара на всех не хватит.
Чисто интуитивно мне кажется, что тут можно сэкономить. Заодно и акселератор для php можно будет использовать.
А может и не оказаться, ага, я только предполагаю :)
может и правда таких сайтов уже нет. раньше много видел
В общем — шило на мыло имхо.
P.S. Да я и «там» как-то не очень долго ждал :) Ещё при мне начали 6-ку ставить
1. Сколько в среднем клиентов (т.е. dedicated-апачей) у Вас на одной машине?
2. Каковы примерно характеристики машины (память, проц)?
2. P4 Celeron 960 RAM 4Gb перекочевал в Core2Quad RAM 8Gb. SATA в обоих. Я специально поэтому обратил внимание на то, что ситуация начала устаканиваться ещё на старом оборудовании и что процессор вообще практически тут роли не сыграл (хотя какую-то сыграл, конечно).
1. Не могли бы Вы чуть подробнее расписать, почему решение этой проблемы на уровне reverse proxy Вас не устраивает в теоретическом смысле? (Про то, что в nginx нет требуемой функциональности, и про то, что limit_conn не годится, я понимаю.)
2. Пробовали ли Вы на каждом из dedicated-апачей запускать peruser-, itk- или dklab-патчи, дабы сравнить, делает ли погоду нагрузка от лишнего fork() или нет? У меня есть некоторые подозрение, что fork() здесь как раз погоды не должен делать. (Вывод «Мы выиграли процессорное время за счёт отказа от постоянных vfork()» меня не совсем убедил, т.к. процессор мог выиграться не из-за отказа от fork(), а из-за того, что Вы ограничили число воркеров на особо «жрущего» клиента; раньше процессор «жрался» 20 воркерами такого клиента, а теперь их у него только 3, вот и выигрыш.)
А вообще, я бы с удовольствием с Вами как-нибудь выпил кофе, было бы интересно пообщаться.
Хабросообщество! Если ты знаешь инструмент, который позволяет ставить запросы браузеров в очередь, когда достигнут лимит на определенное число коннектов к backend-ам для одного виртуального хоста, напиши пожалуйста, сюда, что это за инструмент.
Разумеется то, что mod_access работает только под apache версии 1.3.хх и для каждого запроса требуется процесс является существенным ограичением для его использования, но при существующих обьемах памяти это уже не такая проблема, как это было лет 5 назад.
Во многих случаях связка nginx>>apache+mod_accell>>apache+mod_php является очень выигрышным вариантом.
Такую схему, использовал и Сысоев, сейчас не помню для каких целей, можно порыться в его рассылке, года 2-3 назад было про это.
Как и в любом другом случае плюсов и минусов может быть больше, чем я в состоянии сейчас вот так вот заметить, но в целом такая схема вполне рабочая.
2. С памятью сложный вопрос. Это уже всё равно больше чем обычно, а посчитать сложно. Там же память не суммируется.
В старой схеме типа как у Петерхоста у меня приходили уведомления — типа 5% процессора занимаешь!!111 Нехороший чувак!
Нужна какая-то тулза показывать сколько воркеры мои стоят, а сколько пашут :) Ну и следующий шаг должен быть — варьировать воркеры взависимости от тарифного плана.
1. Если бы оно было, то, возможно, это бы немножко оттянуло такое решение. Но я не зря тут же в тексте упомянул про конфигурацию. Да, я вижу в рассылке письмо и ровно такой ход мысли у меня был и я чуть сам не написал в начале осени такой же вопрос. В нашем варианте мы получили возможность регулировать Zend и APC — можем давать, можем не давать. Не зря был упомянут и питон — мы рискнули и в принципе вписались по внутреннему бизнес-плану в окупаемость резидентных процессов, да ещё и с совершенно очевидным эффектом. Был необоснованный страх. И запускать свои я до сих пор никому не дам (эти-то имеют не подконтрольное пользователю регулирование). Совершенно не факт, что регулирование на уровне прокси не будет по производительности равно или даже дороже чем лишнее соединение. Да, я думаю сейчас апачу ещё сокет ввернуть на приём, и вообще настанет счастье. Т.е. некая второстепенная сумма причин отвела нас от мысли с nginx. А, ну и проблема привилегий. Осенью 2003 года это был «рывок», когда я зажмурившись воткнул vfork(). Ни разу не пожалел, хотя это грязный хак и все вокруг морщили носы.
2. peruser/itk не пробовали. Коллеги, хоть и используют, cтрашно ругаются. Но (1) — смысла не было. Патч от dklab… keepalive лишает нас nginx… а он в свою очередь нас спасает. Согласен про весьма спорную оценку выигрыша cpu. Но… коллеги говорят, что даже отказ от getpwd() «на глаз» заметен на хостинге (делается перед vfork() для setcontext() (или как она там точно называется)). Да и не такой уж и апгрейд процессора, чтобы уж ужас что. И сразу после апгрейда, мы клиентов на серверах увеличили (утилизировали один из старых, раскидав клиентов). И проблемы нет теперь вообще — т.е. нет клиентов, которые говорят, что они что-то там теряют, и мониторинг ничего такого не показывает. Хотя, это может быть действительно следствие демпфирования нагрузки, по аналогии со случайной задержкой в cron. Мы послезавтра уже на ХостОбзор, а потом я могу банально протестировать по результату BSD-аккаунтинга/времени. Пусть даже ab на «hello world», для оценки потянет.
Мда, чувствую, накопилось причин заглянуть в Москву :) И с Бобуком уже год встречаюсь, и вообще давно не был :) Я думаю, это будет декабрь :)
getpwuid() конечно, для setusercontext()
Если да, мне кажется, при такой схеме работы, сбор данных аккаунтинга будет немного затруднен, т.к. они могут не совсем актуально отображать реальное положение вещей у того или иного пользователя. При использовании патча Дмитрия появляется один вкусный плюс — процесс апача пользователя завершается после обработки реквеста и мы моментально имеем в аккаунтинге процессорное время, затраченное на обработку этого реквеста (ядро собирает эти данные и дописывает их в файл аккаунтинга только при завершении процесса). Таким образом, собирая данные раз в N минут мы смело можем утверждать, что пользователь употребил столько-то процессорного времени в такой-то промежуток реального времени.
В Вашем случае, данные о потреблении ресурсов попадут в аккаунтинг в некое неопределенное время, когда рестартанет апач или отдельный воркер пользователя, отработав какое-то количество реквестов.
P.S. Поправьте, если я где-то ошибаюсь.
Да, как раз в том докладе я говорил о невнятной ситуации с аккаунтингом даже с патчем Котерова. Естественно, сейчас он врёт совсем и требуется придумывать другие способы. Естественно, надо взвесить «за» и «против». Но как Вы видите из графиков и моих утверждениях, что сервера забиты чуть менее чем полностью согласно плана, проблема cpu на вообще не беспокоит. Если что-то будет «пожирать» его в заметных количествах — сработают все другие виды мониторинга.
У аккаунтинга есть и вторая проблема, которая вполне явно пробегает в моём докладе. Что я с ним буду делать? Для грубой оценки ситауции он помогает. Но и сейчас у меня появились инструменты для этой оценки. Но опыт показывает, что единственное что я могу сделать — это нажать красную кнопку «выключить». Ибо цифра не раскрывает суть проблемы. А хочется результат — или исправление, или апсейл, но апсейл опять же решающий проблему. В случае получения голой цифры, я ничего не могу сказать по проблеме и по путям её решения.
С новой же системой, у меня появились вполне понятные инструменты. Во-первых, это память. Если её не хватает — понятно, что нужно больше и я, и клиент будут понимать, что оплачивается и к какому вполне конкретному результату это приводит. Во-вторых, это количество воркеров. Если всё тормозит, понятно в каких местах искать, смотреть логи, отделять «тормоза» от нехватки воркеров от тормозов от каких-то задержек программ и итерационными методами приходить к решению. С одной стороны, я не имею цифры, с другой стороны, я всё разложил по полочкам.
Как-то так.