Пример отображения динамической схемы
|
---|
![]() |
Разработка высоконагруженного или сложного проекта серьёзно отличается от "типовой" работы по целому ряду параметров:
Эти сложности могут привести к смене и стиля работы студии и технологии разработки, используемой до сегодняшнего момента.
В этой главе будут рассмотрены самые разные стороны процесса разработки сложного или высоконагруженного проекта.
Рассмотрим основные риски при работе с большими проектами:
Основной управленческий риск - это расхождение бизнес-целей и целей разработки, когда не найдено согласие между постановщиками задачи и теми, кому предстоит её решать.
К управленческим рискам можно отнести и организацию взаимодействия с заказчиком, организацию процесса производства в самой компании.
Детально управленческие риски рассмотрены в уроке Эффективная работа команды.
Забота о рисках проектирования лежит целиком на менеджере проекта. Это ещё один аргумент в пользу того, что им должен быть менеджер-технолог. Для уменьшения вероятности провала проекта необходимо составить "план управления рисками", который необходимо обсудить с клиентом. Для более точной оценки рисков необходимо провести тестирование на прототипе. Смоделируйте нагрузку и проверьте свои решения.
Типичные ошибки стратегии и тактики:
Чтобы избежать этих ошибок:
Риски разработки зависят от подготовки программистов, знания ими продукта, а также от стратегии и тактики разработки, выбранной менеджером проекта.
Типичные ошибки стратегии и тактики (убывание по важности):
Типичные ошибки при использовании собственно Bitrix Framework:
/bitrix/php_interface/dbconn.php
.Писать код "без дыр" крайне сложно и требует высокого профессионализма и большого опыта. Если в команде нет хотя бы одного такого высококлассного профессионала, способного проконтролировать написание кода остальными программистами, то использование Проактивной защиты должно стать для вас правилом.
API Bitrix Framework – защищено, написание кода в обход него всегда чревато если не текущими ошибками, то потенциальными проблемами, если вдруг обнаружится какая-то уязвимость.
Очень полезно использование сканера безопасности, который, конечно, не панацея, но позволяет выловить большинство ошибок.
Не забывайте перед сдачей проекта удалить тестовых пользователей, тестовые группы, тестовые файлы, лишние резервные копии. И обязательно использовать Монитор качества
Банально, но: для эксплуатации высоконагруженного проекта нужен хороший системный администратор, способный оптимально настроить сервер. Администратор не должен "забывать" делать резервное копирование, обновлять софт.
При эксплуатации проекта менеджер проекта не должен допускать ни ситуации, когда разработчики ходят на сервера под правами root'а, ни ситуации когда права закрыты настолько, что разработчики не могут посмотреть логи сервера.
Подводные камни разработки - чего делать нельзя
Программная архитектура веб-систем на Битриксе: от простого сайта до веб-кластера
Особенности проектирования под Битрикс
Необходимый этап для любого проекта, но если в малых ещё можно что-то делать через постоянное обращение к клиенту: "делаем так?", то в случае большого или высоконагруженного проекта без проведения этого этапа в полном объёме реализовать проект невозможно.
Анализ требований действительно необходим, когда речь идет о больших сложных проектах со сроком разработки больше месяца. Необходимо вести предварительную работу, в которую входит настройка необходимых инструментов, налаживание связи с людьми заказчика, наиболее разбирающимися в сути вопроса, а также изучение самой предметной области сайта.
В случае сложной предметной области, возможно, заказчик сам не может толком собрать такие требования. Часто клиент приходит к исполнителю с "кашей" в голове. Этап сбора и анализа требований в этом случае - это упорядочивание и формальная регистрация этой "каши". И согласование полученного с клиентом.
В случае больших и сложных проектов этап сбора и анализа требований рекомендуется проводить отдельным пунктом в договоре с отдельной сметой и оплатой. Это обезопасит как клиента, так и исполнителя от недобросовестных или непрофессиональных действий.
Требования делятся на функциональные и нефункциональные, то есть описывающие поведение системы (требуемую функциональность) и различные особенности поведения или эксплуатации системы, например, требования к удобству использования, надежности, производительности и тому подобное.
Функциональные требования - это пользовательские сценарии, которые должны быть реализованы в системе. Если они работают так как нужно клиенту, то эти требования выполнены.
Нефункциональные требования оформляются отдельно и должны учитывать экстремальные особенности которые должна выдерживать система. Одним из самых оптимальных способов выполнения нефункциональных требований на данный момент является использование веб-кластера, решающего задачи отказоустойчивости (дублирует несколько узлов), масштабируемости (можно добавлять сервера приложений и базы данных), надёжности (данные распределены по нескольким машинам).
Сбор и анализ требований может быть (в случае больших и сложных проектов - должен быть) итерационным, пошаговым. В противном случае можно потерять очень много времени на данном этапе, для сложных систем (типа Softkey) - до года. В этом случае возникают свои риски: не учесть всего и встать перед необходимостью переделки.
Надо организовать с клиентом серию встреч в доверительной атмосфере. Клиент должен увидеть что вы не "отжимаете" деньги, а пытаетесь вместе с ним понять что ему нужно, решить его задачу эффективно, дёшево, быстро и максимально просто. Заказчик должен быть уверен, что он в любой момент может прийти, увидеть доступные документы по работе над его проектом. Это называется выстроить атмосферу Agile манифеста. В этом случае клиент будет максимально открыт, сообщать всё что он знает о проблеме.
Эта же мера позволит снизить риск формализации, когда стороны переводят общение в формат e-mail с целью фиксировать всё общение на случай непонимания и претензий. Такой подход сразу лишает стороны желания разбираться и понимать. Проектирование переходит в фазу юридических перестраховок, подстилания "соломки", что резко снижает вероятность удачной реализации проекта.
Без создания доверительной атмосферы возможны попытки клиента скрыть от разработчика бизнес-процесс и цели. Это когда задачи формулируются без разъяснения для чего это надо. Заказчик обрисовывает для себя проблему, но не зная принципов работы CMS и ее возможностей, придумывает решение, которое крайне сложно реализовать. А при этом эту же проблему можно решить совершенно иначе, например, проставлением галки где-то в админке. Но заказчик не объясняя для чего это надо, заявляет требование сделать именно то, что он придумал. В итоге разработчик тратит время впустую. Заказчик тратит деньги впустую.
Очень важно "качество" менеджера, занимающегося проектом со стороны исполнителя. Менеджер должен сам "прочувствовать и вжиться" в проект, должен научиться говорить с клиентом на его языке, на языке его предметной области, стать экспертом, "хранилищем знаний" для разработчиков. Менеджер должен научить предметной области ведущего разработчика или аналитика.
Существует несколько методологий сбора и анализа требований. Существуют и разные инструменты сбора и анализа требований. Задача, которая стоит перед исполнителем - выбрать именно те методы и инструменты, которые подойдут к конкретному проекту. Максимально простые и эффективные методы и инструменты.
Есть несколько инструментов:
Это статическая модель. На неё переносятся термины из словаря и устанавливаются связи и отношения между сущностями. Здесь потребуется серьёзная логическая работа со стороны Клиента. Он должен показать и обосновать почему эта связь есть, откуда она берётся. На больших проектах таких диаграмм может быть несколько (встречается до 10) и каждая охватывает какую-то часть системы.
Пример отображения динамической схемы
|
---|
![]() |
В простом случае описываются просто цепочки действий, в более формальных случаях сложных предметных областей лучше использовать формат Сценария поведения.
Если требуется более глубокая проработка требований (это может потребоваться когда в проекте появляются какие-то алгоритмы вычисления, шифровки, работы банкоматов, корзины), то используются:
Возможно вам ещё понадобится "картинка" того, как вы будете размещать всё на серверах: Диаграмма развёртывания Deployment Diagram.
Пример диаграммы развёртывания
|
---|
![]() |
Возможна ситуация, когда требование озвучено, но не понятно ни клиенту, ни исполнителю. Это не страшно. Его надо зафиксировать не описывая. Впоследствии к нему можно вернуться.
Работа по сбору, формализации и структурирования требований не требует каких-то сложных и дорогих программных инструментов. Всё можно выполнять в обычном Excel.
Основные риски при сборе и анализе требований:
Иногда проблема возникает при недобросовестности, либо некомпетентности менеджера клиента, ведущего проект. Решение: требовать от клиента адекватного и добросовестного сотрудника.
Сбор требований - сложный этап, который крайне сложно завершить в один заход. Как правило, Клиент не может сразу высказать все свои "хотелки". Это процесс долгий в силу того, что осознание потребностей приходит в результате постоянного контакта клиента с исполнителем, в результате итерационной реализации уже сформулированных заданий.
Переход к этапу проектирования можно выполнять после получения некоторых документов из перечисленного выше списка, достаточных для осознанного начала работ по проектированию. Риск возникновения каких-то не выявленных требований, незадекларированных сущностей при этом существенно снижается.
На этапе сбора требований стало примерно понятно чего хочет клиент. Следующий шаг: создание прототипа.
Для чего делается прототипирование? Нужно проверить идею. Этап необходимый, так как позволяет оценить возможные риски, показать границы возможностей реализуемой идеи, выявить возможные сложности в реализации.
Примеры задач, где требуется создание прототипов.
Заливается 1 000 000 ценовых предложений на один сервер и проверяется как работает административная часть, как работает публичка, выбирается способ хранения данных (в случае Bitrix Framework: инфоблоки, инфоблоки 2.0 с индексами или Highloadblock), выбирается База данных и так далее.
Для создания прототипа нужен опытный программист, способный увидеть технологические риски реализации конкретной задачи. Хотя прототип принесет немалую пользу и для начинающей команды: прежде всего убережёт от ошибок, связанных с отсутствием опыта работы с той или иной технологической платформой.
Прототип надо обязательно проверять на высокие нагрузки.
На работу с прототипом уходит не более двух-трёх дней и в случае положительного отзыва можно приступать к работе. В противном случае нужно предложить клиенту альтернативные варианты решения его задачи.
Самый простой в реализации пример архитектуры - это двухуровневая конфигурация веб сервера. Однако она применима только к несложным и ненагруженным (по нынешним меркам) проектам.
Для сложных и высоконагруженных проектов одной двухуровневой схемой не обойтись, нужен отдельный этап в разработке проекта: проектирование архитектуры.
Что бывает если нет архитектурного проектирования? Программисты начинают писать код на своё усмотрение, часто даже не согласовывая друг с другом. Если в компании слабая техническая вертикаль, не контролируют работу программистов ни технический директор, ни ведущий разработчик, то получается усложнённый код, так как у программистов нередко встречается желание "прокачаться" за счёт компании.
Если чёткого проектирования нет, а программисты слабо знакомы с предметной областью (а чаще вообще не знакомы), то часто выясняется, что сделано не совсем то или совсем не то. Требуется переделка, которая требует времени и может рождать дополнительные баги. Такой цикл может повторяться несколько раз, пока не подойдёт срок сдачи проекта. Как правило именно в этот момент до всех разработчиков доходит что нужно было на самом деле сделать, но времени уже нет.
Исполнитель пытается сдать то, что сделано, что непонятно как работает. Иногда это удаётся. Но если и удалось, то при попытке что-то поменять всё "рассыпается".
Бесплатные фреймворки не гарантируют преемственности своих разработок. Разработчики могут просто начать вести новую ветку, приостановив поддержку старой, а для клиента это означает либо возрастание риска уязвимости (если оставаться на старой ветке), либо большие вложения (если переводить работу проекта на новую ветку фреймворка).
Самые распространённые виды архитектур
Наиболее простым и логичным решением по изменению архитектуры проекта являются следующие этапы "эволюции":
Переход к последней схеме позволяет в будущем проще и легче масштабировать проект и достичь более высокой производительности.
PHP-FPM – более эффективное решение:
Дальнейшим логическим развитием схемы является разнесение всех "приложений" на отдельные серверы:
Это позволяет значительно поднять общую производительность системы.
След этап развития архитектуры проекта:
Достаточно сложной категорией веб-проектов являются интернет-магазины. Для разработки магазина вам необходимо разбираться в следующих ключевых сущностях Bitrix Framework, используемых при его проектировании:
Бывают такие ситуации, когда интернет-магазин (например, магазин некоторых услуг) может обойтись без модуля Торговый каталог. Поэтому перед началом разработки необходимо сразу определиться нужен ли вам данный модуль.
Если в магазине представлены каталоги товаров, то модуль Торговый каталог должен быть изучен очень тщательно. Прежде всего вам необходимо спроектировать ценообразование: определиться какие типы цен будут использоваться и в каких валютах. Также необходимо заранее решить какой курс валют будет использоваться, как он будет браться и обновляться, как вы будете его округлять (если делать будете это сами). При проектировании структуры каталога товаров следует заранее продумать будут ли использоваться у вас торговые предложения, наборы и комплекты. Кроме того, модуль Торговый каталог позволяет настроить гибкие системы скидок. Даже, если возникнет ситуация, что вам требуются скидки, которых нет в Bitrix Framework, то разрешить ее можно с помощью API Bitrix Framework, написав собственные обработчики.
Модуль Интернет-магазин позволяет сделать:
Если ожидается, что проект будет большим, то к нагрузкам необходимо готовиться сразу. На этапе разработки следует выполнять аудит кода и оптимально использовать API Bitrix Framework, необходимо смотреть, чтобы не делались лишние запросы к базе. Кроме того, следует тщательно проектировать модели данных. Таким образом, следует использовать инфоблоки 2.0, в которых можно добавлять кастомные индексы.
Следует аккуратно работать с кешем: нельзя все кешировать. Проверяйте страницы так, чтобы они без кеша работали быстро, только потом включайте кеш, а не наоборот.
Что касается конфигурации, то настройте прекомпилятор PHP и анализируйте, что в него попадает. Проверьте в мониторе производительности, чтобы были включены все требуемые настройки PHP. Помимо этого, следует вести контроль версий и логи. Поскольку смотреть запросы - это задача программиста, то необходимо научиться понимать состояние базы данных. При работе используйте разного рода отладчики, Xdebug, XHPprof. Кроме того, рекомендуется использовать PHP-FPM для крупных проектов, потому что расходуется меньше памяти, выполняется меньше tcp/ip соединений.
Допустим, что магазин уже разработан и запущен. Рассмотрим теперь простую и эффективную стратегию достижения и поддержки высокого уровня качества обслуживания посетителей интернет-магазина.
Для веб-проектов с посещаемостью в единицы миллионов хитов в сутки нужно настроить логирование всех обращений клиентов как к страницам, так и ресурсам. Важно понимать, что система может сохранять информацию о каждом обращении клиента и включить эту возможность очень просто (это штатное средство). Если интернет-магазин развернут на веб-кластере, то несложно настроить удаленное логирование на выделенный для этих целей сервер с использованием, например, syslog-ng. Логирование всех запросов клиентов незначительно (всего на несколько процентов) снижает производительность конфигурации, однако вы сохраняете полную информацию и контроль над качеством обслуживания клиентов. Практически все случаи ошибок и зависаний интернет-магазина фиксируются, доступны для дальнейшего анализа и корректировки процесса разработки и системного администрирования. Если логирование не настроено, то можно сказать, что вы не контролируете ситуацию.
Прежде всего нужно модифицировать стандартный формат логов NGINX, Apache, PHP-FPM и добавить туда описанные ниже ключевые показатели производительности. Кроме стандартных данных (таких как URL запроса, код ответа и т.п.), важно фиксировать по каждому хиту следующие данные:
log_format main '$remote_addr - $remote_user [$time_local] "$host" "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for" -> $upstream_response_time';
LogFormat "%t \"%r\" %>s %b child:%P time-> %D" timing
access.format = "%R # %{HTTP_HOST}e # %{HTTP_USER_AGENT}e # %t # %m # %r # %Q%q # %s # %f # %{mili}d # %{kilo}M # %{user}C+%{system}C"
Процесс фиксации информации об обращении клиентов в логах организован, все ошибки попали в лог. Теперь необходимо научиться их трактовать и инициировать устранение причин их появления.
Для начала рассмотрим самые распространенные типы ошибок:
Теперь на примерах проанализируем, что происходило с обслуживаем клиентов магазина за сутки:
Total hits: 265532 200 : 264654, 99.67% 207 : 34, 0.01% 302 : 830, 0.31% 401 : 7, 0.00% 403 : 1, 0.00% 404 : 1, 0.00% 500 : 16, 0.01%Мы видим, что более 99% хитов были успешны (код 200), однако было 16 ошибок, с которыми нужно разбираться разработчикам (в логах PHP) и искать способы их устранения.
Total: 386664 0 ms: 276203, 71.43% 100 ms: 81466, 21.07% 200 ms: 13155, 3.40% 300 ms: 4282, 1.11% 400 ms: 2183, 0.56% 500 ms: 1373, 0.36% 600 ms: 968, 0.25% 700 ms: 721, 0.19% 800 ms: 586, 0.15% 900 ms: 470, 0.12% 1000 ms: 398, 0.10%Видим, что подавляющее число запросов клиентов было обслужено со временем менее 500 ms. Однако с 398 хитами более секунды нужно основательно разбираться. Страницы или сервисы могут отрабатывать единицы секунд в следующих случаях:
Total hits: 265562 0 KB: 25, 0.01% 4000 KB: 16, 0.01% 6000 KB: 67094, 25.26% 7000 KB: 123746, 46.60% 8000 KB: 61102, 23.01% 9000 KB: 3453, 1.30% 10000 KB: 1263, 0.48% 11000 KB: 890, 0.34% 12000 KB: 826, 0.31% 13000 KB: 917, 0.35% 14000 KB: 1129, 0.43% 15000 KB: 1125, 0.42% 16000 KB: 936, 0.35% 17000 KB: 798, 0.30% 18000 KB: 631, 0.24%Видно, что большинство скриптов веб-решения потребляют 6-8 МБ памяти, что немного. Однако, нередко при разработке «в сжатые сроки» получаются скрипты, которые потребляют 500МБ или единицы гигабайт памяти, что вызывает зависание сервера и общую дестабилизацию системы. Важно анализировать данную гистограмму и ставить ТЗ разработчикам на оптимизацию объема потребляемой страницами памяти в пределах, допустим 64 МБ. Таким образом, постепенно, система будет потреблять стабильно предсказуемый объем памяти и вы будете уверены, что она внезапно не впадет «в спячку» на несколько десятков минут.
Кроме того, используя для логов соответствующие bash/awk-скрипты, полезно также получить статистику по максимальным значениям: топу самых медленных страниц в сутки и топу самых затратных по памяти страниц в сутки. А также если вы используете PHP-FPM, то можете получить более подробную информацию о зависания PHP-скриптов с помощью специальной возможности логирования скриптов, которые выполняются более N секунд.
Таким образом, настроив сбор ключевой статистики по хитам клиентов интернет-магазина, нужно создать постоянно действующий бизнес-процесс по доработке и оптимизации веб-решения: раз в сутки статистика должна рассылаться всем участникам технологического процесса и при превышении заранее оговоренных показателей автоматически должен начинаться поиск, устранение ошибок и/или оптимизация кода веб-проекта. В качестве целевых критериев можно взять:
Стандартное веб приложение работает на одном сервере: собственно веб-сервер, кеширование, база данных приложения. Достаточно часто ресурсов сервера перестаёт хватать и приходится переходить на новый тарифный план или производить апгрейд сервера. Это первый шаг в долгом процессе - вертикальное масштабирование, когда добавляются ресурсы сервера. Рано или поздно происходит упор в ограничение возможностей либо хостинга, либо "железа".
Следующий шаг - разделение приложения, когда приложение делится на составные части (web, БД, кеш) и эти части работают на разных серверах. С точки зрения производительности большинству проектов бывает достаточно такого разделения. Однако такое деление не решает задачу надёжности и отказоустойчивости. Каждый узел не зарезервирован и в случае выхода из строя любого из них перестаёт работать весь проект в целом.
Следующий логический шаг - научиться масштабировать дальше. Научиться представлять любой узел системы в виде кластера серверов, которые будут взаимозаменяемы, и в случае аварии проект продолжит работу. На этом этапе возникают сложности: просто так добавить несколько серверов под БД, несколько серверов под web уже становится сложно, потому что приходится значительно переписывать, перерабатывать логику веб-приложения.
Основные задачи, которые решает веб-кластер - это задача производительности и задача отказоустойчивости:
Когда переходы на новый тарифный план или апгрейд "железа" сервера перестают решать проблемы производительности (либо становятся чрезмерно дорогими), то направление развития приложения одно: переход на кластерные технологии.
Решать задачу представления приложения в виде кластера можно с помощью следующих технологий, которые позволяют масштабировать базу данных, кеш, web и представить каждую из составляющих в виде группы взаимозаменяемых серверов:
С проектной точки зрения создание такого приложения в виде веб-кластера нужно выполнять в два шага:
На этом этапе приложение делится на составные части (web, БД, кеш), и эти части работают на разных серверах.
Этот этап не сложен в проектировании, и его выполнения с точки зрения производительности большинству проектов бывает вполне достаточно. Однако такое деление не решает задачу надёжности и отказоустойчивости. Каждый узел не зарезервирован, и в случае выхода из строя любого из них перестаёт работать весь проект в целом.
Для реализации этого этапа необходимо решить следующие задачи:
Самое простое решение - репликация, возможность которой предоставляет любая современная база данных. Чаще всего используется MySQL, так как она достаточно проста, понятна, хорошо документирована. Средствами самой базы можно организовать репликацию, добавить некоторое количество slave-серверов, которые будут получать данные с master'а, и логично вынести select и запросы на чтение на эти slave.
Схема типового веб-приложения с реплицированной базой:
Все запросы на запись отправляются на master, все запросы на чтение отправляются на slave. Это самый простой способ масштабирования.
Если этот механизм будет реализован средствами Базы данных, то на долю самого приложения остаётся диспетчеризация всех этих запросов и распределение их между серверами. Делать это лучше именно на уровне ядра приложения, потому что:
Очень часто необходимо произвести чтение с данных, которые были только что изменены. Например, первым запросом добавили пользователя, а вторым - запросили список пользователей. Если запрос на запись отправляется на мастер, а запрос на чтение списка пользователей отправляется на slave, то возможна ситуация, когда slave отстают от master'а (типовая ситуация) и в ответ приходят устаревшие данные. Для списка пользователей такое положение, возможно, терпимо, но если речь идёт о заказах интернет-магазина или банковских транзакциях, то неправильные данные - это критично. Именно приложение должно определять, какие select'ы нужно отправлять тоже на master для получения актуальных данных. Это означает чуть большую нагрузку на master, но зато это гарантирует получение корректных данных.
Приложение должно заниматься балансировкой нагрузки между slave'ми, условия которой задаются произвольным образом. В административном интерфейсе можно подключить любое количество серверов и для каждого из них задать свой вес. Возможны разные случаи, например, разные конфигурации серверов, и на тот, что помощнее, оптимально будет задать больше нагрузки. Если для этого сервера поставить больший вес, то ядро приложения будет отправлять на него больше запросов.
Не внося больших изменений в логику веб-приложения, можно добавить несколько веб-серверов, над ними поставить любой из доступных балансировщиков: от распределения по DNS и заканчивая специализированными решениями типа балансировщика Amazon'а, либо "железные" решения типа Cisco, или простейший отдельный сервер NGNIX, который распределит запросы по нескольким серверам.
Этими инструментами можно решить задачу балансировки, но нельзя решить задачи:
Вопрос с сессиями решается достаточно просто. Они выносятся в отдельное централизованное хранилище. Либо в Базе данных, либо в мемкеше.
Вопрос с синхронизацией файлов в случае небольшого проекта достаточно прост. Можно подключить единое хранилище, например, через Network File System и работать с файловой системой со всех серверов. Удобно, но не быстро.
Другой вариант: хранить файлы на каждом сервере и использовать для синхронизации сторонние утилиты. Просто, но нет возможности синхронизировать данные моментально, всё равно будет временной промежуток недоступности файлов на каких-то серверах. С ростом объёма данных этот механизм будет работать всё менее надёжно и с замедлением.
Оптимальным решением будет вынесение всего основного контента (чаще всего это документы, картинки, видео- и аудио-файлы) в какое-то отдельное централизованное хранилище. То есть полностью разделить логику кода и веб-приложение и не хранить эти данные на веб-сервере. Этим способом будет кардинально решена задача синхронизации.
Для реализации такого хранилища можно сформулировать несколько правил:
Для реализации можно использовать какой-то отдельный FTP сервер и выносить файлы туда. Но при этом не решается задача резервирования и надёжности. Для решения этих задач придётся применять другие методы, скажем, средства операционной системы, систем резервного копирования и других инструментов.
Заведомо надёжное хранилище - это облачное хранилище, которое в последнее время предоставляет всё большее число провайдеров. При этом оптимальным будет решение, когда на одном проекте одновременно можно хранить разные файлы в разных облачных хранилищах. Например, все файлы "весом" больше 100 Мб перемещать в Google Storage, а все видео - в Amazon S3.
Такое деление позволяет:
Rest API для всех популярных языков есть у всех хранилищ, проектирование системы на облаках не вызывает затруднений у квалифицированного разработчика.
Распределенный кеш данных
Работа с кешем аналогична работе с БД: можно подключить группу кеш-серверов, масштабируя группу по мере необходимости. (На практике использование более одного сервера - ситуация крайне редкая, только в случае очень больших проектов.)
Основная задача, которая решается распределением кеша по группе серверов - это отказоустойчивость. Кеш можно распределять, как и в случае с БД, по весам, которые задаются в административной части приложения.
Можно организовать распределение нагрузки на чтение между slave'ми. Но, так как все веб-сервера читают данные со всех серверов MySQL, то остаются высокие требования к связанности сети, пропускной способности, величине задержки между пакетами. В идеале все сервера должны находиться в одном дата-центре. Однако, если случится авария на уровне целого дата-центра, возникнет проблема. Такое возможно, более того, такое происходило.
Другая проблема - невозможность масштабирования между разными серверами, так как мы потеряем в скорости.
Третья проблема может возникнуть, если выйдет из строя master. В этом случае требуется заранее либо автоматизировать процедуру его восстановления, написать какие-то скрипты, которые будут превращать один из slave в master, либо оставить это администратору на ручной разбор ситуации. Правда, если авария произошла когда администратор недоступен, то проект может простаивать достаточно долго.
Необходимо резервировать сам дата-центр. По-хорошему, проект должен быть распределён географически по миру или, как минимум, по двум дата-центрам, чтобы можно было обезопасить проект даже от таких аварий.
Что можно сделать? Гео-кластер, хотя бы в упрощённом варианте. Правда, тут возникают повышенные требования к проектированию приложения.
Предлагаемая схема - весьма условная master-master репликация, так как не происходит одновременной записи в две базы. Однако в большинстве случаев задача резервирования решается полностью. Эта схема позволяет держать несколько master'ов, хотя и с рядом ограничений.
Создаются группы серверов в административном интерфейсе:
У каждой группы свой MySQL master (указан в dbconn.php). Мастеры MySQL объединены в кольцо (в минимальном варианте – 2 сервера).
Каждая группа клиентов работает с определённым сервером. Например, посетители из Европы должны попадать в европейский дата-центр и писать в master, который, находится там. Посетители из России должны попадать в российский дата-центр, и так далее. В случае аварии трафик переключается на другой дата-центр, и все посетители продолжают работать с "горячими" данными без каких-то отставаний и потерь.
Реализуется такая схема следующим образом: в самом MySQL есть возможность задать смещение для полей: auto_increment_increment и auto_increment_offset. Это обеспечивает поступление данных "стык в стык" и они не будут дублироваться. Базы в разных дата-центрах синхронны, при этом независимы друг от друга: потеря связности между дата-центрами может составлять часы, данные синхронизируются после восстановления. Таблицы БД должны иметь подобные ключи, чтобы данные не дублировались и не попадали одинаковые в разные дата-центры.
Пользователь и все сотрудники одной и той же компании работают в одном датацентре за счет управления балансировщиком. Этим исключаются сбои в подобной схеме работы master-master репликации. Сессии хранятся в базе, и объём этих данных достаточно большой. В результате были ошибки в получении данных из query кеша. Эти данные, так как пользователи направляются на определённые сервера, можно не реплицировать из-за большого трафика и возможных блокировок:
SET sql_log_bin = 0
или
replicate-wild-ignore-table = %.b_sec_session%
В результате достигнут один из приоритетов - постоянная доступность сервиса, его отказоустойчивость. Все ноды заменяемы и не зависят друг от друга, в случае аварии стартуем новые. Два дата-центра синхронизированы друг с другом и равноценно обслуживают клиентов. В случае аварии на уровне дата-центра или плановых работ с базой трафик прозрачно для клиентов переключается на рабочий дата-центр.
Вертикальный шардинг - первое, что обычно делается при возникновении нехватки ресурсов. Реализация этого разделения БД не сложна и не требует серьёзного программирования, зато появляется возможность идеально подстроить сервер для работы с одной специфической таблицей, постараться уместить ее в память, возможно, дополнительно партиционировать ее и т.д.
Работа напрямую через PHP-функции. Обеспечивает скорость разработки, гибкость, но вызывает сложность поддержки и неудобство использования. Подходит для простых проектов, не рекомендуется для больших и средних.
Работа через прослойку на основе паттерна TableModule. Работа с базой данных идёт через класс (объект), предоставляющий собой интерфейс доступа к таблице. Преимущество подхода в том, что работа с БД инкапсулируется в каком-то классе, сущности. Любые модификации идут через эту сущность. При этом упрощается развитие, поддержка и эксплуатация проекта. Недостаток: реализация сложнее, если проект разрабатывается без фреймворка, где это уже реализовано.
ORM для сложных проектов. На проектах со сложными предметными областями и сущностями иногда полезно использовать паттерны:
На этапе разработки архитектуры важно предусмотреть горизонтальное масштабирование базы данных. Позже это может быть сложно. О веб-кластере стоит задуматься уже на этапе прототипа, если проект большой и высоконагруженный. Оптимально использовать готовый фреймворк, чем самостоятельно решать вопросы:
Для слабых и средних команд рекомендуется использовать фреймворк, с которым команда знакома лучше всего. Это убережет от множества проблем и рисков, сократит затраты на разработку проекта (за счет того, что фреймворк даёт готовую архитектуру).
Сильные команды с большим опытом могут решиться на разработку «с нуля», если это позволит клиент по срокам и финансированию.
В последнее время все больше говорят про NoSQL. Технологии этого семейства начинают активно использовать известные авторитетные компании, в том числе в высоконагруженных проектах с немалыми объемами данных. Чтобы понять, применима ли эта технология к создаваемому вами проекту, надо разобраться в ваших потребностях и понять, сможет ли она их удовлетворить.
В двух словах NoSQL - это необходимость выбора в рамках проекта двух из трех принципов работы с БД: согласованность данных (Consistency), доступность (Availability), устойчивость к разделению (Partition tolerance).
Причина такой необходимости - новые бизнес-задачи, которые возникли в последнее время:
В техническом плане эти требования означают, что база должна:
При этом классические кластера БД не могут обеспечить эти возможности. Вот описания проблем, свойственных популярным БД:
Так вот, прошло не так много времени, как в начале нынешнего столетия появились NoSQL-продукты и стало возможным:
Пример того, как программисты реализуют требования бизнеса используя NoSQL. В строке с информацией о книге хранятся комментарии пользователей к ней. Что невозможно в рамках традиционной БД:
Но и у NoSQL есть свои ограничения.
Это общие для продуктов NoSQL ограничения. Но есть нюансы и в использовании конкретных реализаций. Рассмотрим их на примере Amazon DynamoDB (очень похожий на Apache Cassandra.)
Ограничения Amazon DynamoDB:
Нельзя использовать сложные WHERE, GROUP BY, не говоря уже о подзапросах: NoSQL-движки их просто эмулируют и могут выполнять очень медленно.
Можно выполнять более сложные выборки, но методом полного сканирования таблицы (table scan) и затем поэлементной фильтрации результатов на серверной стороне, что и долго и дорого.
user=john blog_post_$ts1=12 blog_post_$ts2=33 blog_post_$ts3=69
где $ts1-3
- таймстампы публикаций пользователя в блог. Это позволяет получить список публикаций за один запрос. Но работа программиста увеличивается.
CA - согласованность данных (Consistency) и доступность (Availability)
Система, во всех узлах которой данные согласованы и обеспечена доступность, жертвует устойчивостью к распаду на секции. Если у вас интернет-магазин, сервера находятся в разных дата-центрах, и в какой-то момент один из серверов "упал", то система становится неработоспособной в целом.
Такие системы возможны на основе технологического программного обеспечения, поддерживающего транзакционность в смысле ACID. Примерами таких систем могут быть решения на основе кластерных систем управления базами данных или распределённая служба каталогов LDAP.
CP - согласованность данных (Consistency) и устойчивость к разделению (Partition tolerance)
Распределённая система, в каждый момент обеспечивающая целостный результат и способная функционировать в условиях распада, в ущерб доступности может не выдавать отклик. Данные пишутся на одну машину, дублируются на другую. Если одна "упала", то можно работать со второй. Устойчивость к распаду на секции требует обеспечения дублирования изменений во всех узлах системы, в этой связи отмечается практическая целесообразность использования в таких системах распределённых пессимистических блокировок для сохранения целостности
AP - доступность (Availability), устойчивость к разделению (Partition tolerance)
Распределённая система, отказывающаяся от целостности результата. Большинство NoSQL-систем принципиально не гарантируют целостности данных. Задачей при построении AP-систем становится обеспечение некоторого практически целесообразного уровня целостности данных, в этом смысле про AP-системы говорят как о "целостных в конечном итоге" (eventually consistent) или как о "слабо целостных" (weak consistent). Наиболее распространённые системы.
Прежде чем выбирать для проекта NoSQL хранилище, оцените все возможные призы и риски:
С помощью NoSQL вы получите очень надежное, высокодоступное, поддерживающее гибкие схемы репликации современное решение. Однако платить придется жесточайшей денормализацией и усложнением логики работы приложения (в т.ч. эмулировать транзакции, жонглировать тяжелыми данными внутри приложения и тому подобное).
Если вы периодически наблюдаете близкую к 100% утилизацию диска в iostat, то пришло время задуматься над тем, правильно ли вы выбрали дисковую систему.
Самое, как кажется, очевидное решение: надо использовать более быстрые диски - SSD. Но требуется поддержка SSD в серверах (контроллер, драйверы) и это довольно дорого.
Другой подход - использовать не один, а несколько дисков: RAID.
Для высоконагруженных проектов актуальны, прежде всего software RAID, так как такие проекты, как правило, размещаются в облачных структурах. Поэтому предложим методики тестирования для определения конфигурации RAID подходящей для вашего проекта.
Пример тестирования проведём на работе с RAID 10. Именно он одновременно и быстрый, и надежный. А, вот, различных его конфигураций - достаточно много.
Собрано 5 стендов:
# mdadm --create /dev/md0 --level=10 --raid-devices=4 /dev/xvd[g-j]
# mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/xvd[g-h] # mdadm --create /dev/md1 --level=1 --raid-devices=2 /dev/xvd[i-j] # mdadm --create /dev/md2 --level=0 --raid-devices=2 /dev/md[0-1]
# mdadm --create /dev/md0 --level=10 --raid-devices=8 /dev/xvd[g-n]
# mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/xvd[g-h] # mdadm --create /dev/md1 --level=1 --raid-devices=2 /dev/xvd[i-j] # mdadm --create /dev/md2 --level=1 --raid-devices=2 /dev/xvd[k-l] # mdadm --create /dev/md3 --level=1 --raid-devices=2 /dev/xvd[m-n] # mdadm --create /dev/md4 --level=0 --raid-devices=4 /dev/md[0-3]
На всех тестовых стендах используйте однотипную файловую систему, например: ext4. Параметры монтирования:
noatime,nodiratime,data=writeback,barrier=0
Для тестов лучше использовать sysbench - на файле 256 Мб; режимы - random read, random write, random read/write; разным количеством потоков, от 1 до 16.
По тестам видно, что в чтении все сопоставимо по результатам. Рейд не дает особого преимущества. Но картинка эта - весьма искаженная, так как на результаты очень сильно повлиял файловый кэш (тестовый файл помещается в RAM целиком). По записи рейды несколько проигрывают (сказываются некоторые накладные расходы).
Чтобы определить, какая дисковая система лучше в конкретно вашем случае, необходимо чётко поставить задачу. В нашем случае выбирается дисковая система для базы. Формат хранения данных: InnoDB. Это означает, что работа в основном ожидается с большими файлами (несколько Гб) ibdata.
Значит, типичный профиль нагрузки - random read/write (чтений больше). И вот уже исходя из более понятной реальной задачи делаем новую серию тестов - на файле размером 16 Гб:
Видно, что при чтении один диск сразу упирается в потолок, и увеличение количества потоков не дает прироста производительности. А рейды из 4 дисков на нескольких потоках дают прирост производительности в 3-4 раза. Рейды из 8 дисков — в 6-7 раз.
На запись у рейдов примерно та же картина, что и с одним диском.
Типичная работа базы MySQL - random read/write, чтений больше, чем записи. Самые производительные для такой задачи: RAID 10 с большим количеством дисков.
Минус такого решения - в удвоенной стоимости дисков (что при текущей их стоимости не является критичным).
Главное преимущество - у нас есть простое решение (software RAID можно собрать как на физическом сервере, так и в "облаке") для масштабирования производительности дисковой системы.
Для программиста не важно, какой использовать framework. Framework - это просто основа, из которой бывает нередко целесообразнее собрать проект, чем писать с нуля. И Bitrix Framework здесь не исключение. Его надо понять, увидеть в целом, оценить его сложность и возможности. Но для ведущего разработчика, который выбирает модель реализации, знание системы важно, иначе трудно ожидать разумного и оправданного выбора архитектуры решения. Рассмотрим несколько вариантов архитектур, позволяющих решать определённые задачи.
Есть два варианта использования Bitrix Framework: "простой" и "сложный". Простой - обычный сайт, без магазина. Магазин - это более сложно, независимо от платформы. Если разработчик делал простые сайты, а потом начинает делать магазин, то ему, возможно, только с неделю нужно осваивать штатный функционал магазина.
Работая с Битрикс, нужно в первую очередь понимать, что можно выжать из стандартных компонентов и шаблонов, а что придётся дописывать.
Модель простого сайта и что должен предусмотреть проектировщик при реализации такого проекта.
В случае простого сайта не производится больших объёмов работ. Как правило, это: создание кастомных шаблонов компонентов, создание структуры сайта, шаблона сайта, несколько инфоблоков. Иногда создаётся кастомная форма добавления элемента инфоблоков.
Одно из главных отличий такого сайта - разделение прав доступа при создании контента с системой проверки и выпуска контента. Другое отличие - большие объёмы данных, к которым нужно обеспечить быстрый доступ и при этом гарантировать их сохранность.
Для хранения большого объёма данных рекомендуется использовать Облачное хранилище. Весь большой контент рекомендуется хранить там.
Для организации поэтапной работы над контентом рекомендуется использовать Документооборот и Бизнес процессы
Под "много данных" понимается сотни тысяч и миллионы элементов.
Такой проект "тяжёл" для административной части. Рекомендуется не использовать опцию Совместный просмотр разделов и элементов в настройках модуля Информационные блоки (и настройках самого инфоблока) и обязательно разносить инфоблоки по типам.
Обязательно для таких проектов необходимо:
В этом случае на сайте работает много сотрудников, и, как следствие этого, возникают повышенные запросы по безопасности, повышенные требования к разделению доступа.
На таком проекте практически всё делается штатными средствами, надо только понять систему уровней доступа к модулям, папкам, файлам:
Сложный проект, когда функционал выходит за стандартные возможности Bitrix Framework. Основная опасность: желание залезть в ядро Bitrix Framework и изменить его. В этом случае либо все изменения затрутся при обновлении, либо придётся отказаться от обновлений системы. Битрикс поддерживает обратную совместимость API, и любой проект может спокойно обновиться и получать новый функционал.
Кастомизируются или создаются компоненты. Создаются собственные модули с интерфейсом настройки, где хранятся общие классы, библиотеки. Определяем обработчики событий, которые могут менять поведение системы в нужную вам сторону, и пишем в техподдержку Bitrix Framework, если событий не хватает. Создаем свои страницы административного раздела.
В таких проектах ответственность лежит полностью на разработчиках, за Bitrix Framework не спрячешься, надо внимательно рассматривать вопросы производительности, безопасности, аудита.
Задача потребует нетривиального программирования. Используется модуль Веб-сервисы. Если не хватает функционала, то используя REST, SOAP, JSON, XML-RPC API, дорабатываем нужный функционал. Далее реализуете систему синхронизации.
Если данных очень много, то есть смысл создать свои таблицы в MySQL и хранить данные там.
В проектах такого уровня разработчики должны представлять себе администрирование систем. Так, при работе с веб-сервисами нужно понимать XML, TCP-IP, FTP. Как пойдут пакеты, где что пропадает. И кроме этого, в группу разработчиков надо подключать квалифицированного сисадмина как можно раньше.
Веб-кластер позволяет решать множество задач. При этом в коде вашего проекта ничего делать не нужно, это штатная функция Bitrix Framework.
При проектировании проектировщик должен рассчитать возможную нагрузку на проект и на основании этого сказать, что делать будем кластер. На этом его функции заканчиваются, всё остальное делает система. Нужно только настроить в административной части пулы ресурсов: базы, кэши, обл. хранилища.
Нужно подумать о логике восстановления на базе требований SLA. Надо понять, как сделать так, чтобы максимум 2 минуты система была недоступна. Последнее - резервное копирование. Нужен отдельный регламент.
В данном уроке мы рассмотрим вопросы, связанные не с разработкой, а именно с проектированием под Bitrix Framework.
Проектированием веб-системы может заниматься: менеджер проекта, аналитик или разработчик. При этом учитывайте, что могут возникнуть трудности в связи с тем, что менеджер старается угодить клиенту в ущерб архитектуре, аналитик может сделать слишком много моделей и объектов, а разработчик спроектирует систему так, чтобы удобно было программировать, а не использовать.
В модели проектирования должны быть отражены следующие вещи:
В техническом задании описывается структура информации проекта - это шаблон сайта (какие в нем включаемые области, типы меню и т.д.) и дерево разделов и страниц. Также описывается и структура самой веб-страницы, какие в ней используются компоненты.
В общих словах, для проектировщика система Bitrix Framework - это набор компонентов, модулей, административная и публичная части. Компоненты подразделяются на стандартные и на свои, кастомные. Модули системы также разделяются на стандартные, которые могут быть расширены кастомными обработчиками событий, и на собственные кастомные, которые могут быть связаны со своей базой данных. В административной части также, помимо стандартных страниц/разделов, могут быть кастомные страницы/разделы, формы инфоблоков, кастомные свойства и настройки инфоблоков. Публичная же часть пишется разработчиком полностью с нуля.
При составлении технического задания обратите внимание, что публичная часть модифицируется очень гибко, а административная часть модифицируется значительно сложнее и только в ограниченных местах. В ТЗ должно быть кратко описано то, что делается с помощью стандартных возможностей (можно ссылаться на официальную документацию и учебные курсы), а весь нестандартный функционал должен быть описан детально. Если необходима разработка кастомных админок (в очень редких случаях), то по ним также составляется детальное описание.
В системе Bitrix Framework права и роли играют ключевое значение. В техническом задании необходимо расписать роли, настраиваемые группы пользователей для этих ролей. Если вам не хватает возможностей стандартных уровней доступа, вы можете добавить свои уровни доступа. Все настроенные права доступа необходимо тщательно тестировать для всех групп пользователей.
Как правило, все сущности и данные представляются в Bitrix Framewok инфоблоками и некоторыми объектами из модулей. Поэтому в ТЗ вам необходимо четко определить типы инфоблоков, типы полей инфоблоков. Необходимо расписать модель сущностей, указать, как инфоблоки связаны между собой. Направление связей инфоблоков очень важно с точки зрения производительности. В связи с этим модель сущностей следует обсудить с разработчиком, чтобы не было тяжелых запросов. В случае необходимости можно прибегнуть к денормализации данных.
Также в ТЗ необходимо учесть, кто имеет права на те или иные инфоблоки и разделы. Прописать, какие и для чего используются обработчики событий при работе с инфоблоками.
При проектировании инфоблоков продумывайте, как избежать глубоких выборок. Если у вас часто будут обновляться данные, то, может быть, текущая структура инфоблоков вам не подходит, и ее необходимо заменить (обсудите с разработчиком). Кроме того, необходимо продумать сохранение целостности данных после выполнения некоторых операций, например, удаления.
В ТЗ следует подробно расписать, какие компоненты используются на страницах сайта. Кроме того, полезно указать, из каких инфоблоков тянет данные тот или иной компонент. Для нестандартных компонентов необходимо описать все их свойства. В параметрах компонентов не должно быть настроек, не связанных с логикой работы проекта. Если у компонента сложная логика работы (например, зависящая от настроек модуля), то об этом также необходимо отметить в ТЗ.
Полезные компоненты можно добавлять в свою библиотеку и потом использовать в других проектах. Если компонентов много или кода очень много, то делайте свой модуль, чтобы держать в нем общий код компонентов.
В первую очередь необходимо четко понимать, для чего нужен модуль Интернет-магазин, а для чего - Торговый каталог. Таким образом, в ТЗ должно быть указано, какие именно модули вам нужны, а также нужно ли вам много корзин.
В ТЗ обязательно должна быть подробно описана логика ценообразования, система скидок, как выполняется округление при пересчете цены. Вопросы, связанные с используемыми типами товаров (наборы, комплекты, торговыми предложениями) и остатками, также должны быть освещены в ТЗ.
Статусы заказов полезно описать с помощью диаграммы статусов (диаграммы состояний UML). Платежные системы, службы доставки и все нестандартные настройки других объектов, связанных с магазином, необходимо также добавить в ТЗ. Не следует забывать описывать и используемый формат импорта/экспорта данных магазина.
Нередко в сложных проектах нужно обеспечить взаимодействие магазина, например, с SAP или с 1С, но не по стандартному протоколу обмена. В этом случае может быть удобным использование веб-сервисов, особенно если необходимо быстро передавать изменения. Механизм работы и использование веб-сервисов полезно отображать с помощью диаграмм UML: деятельности (activity), последовательности и др.
При рассмотрении и оптимизации проекта обычно берут во внимание 2 слабо связанные технологически и социально части: фронтэнд и бэкэнд. И нередко забывают про третью ключевую составляющую - сеть.
Для типичного веб-приложения пропускная способность не особо важна (если только файлы не качать) - гораздо важнее latenсy, т.к. делается много небольших запросов по разным соединениям и TCP-окно просто не успевает раскачаться.
И разумеется, чем дальше клиент от веб-сервера, тем дольше. Но бывает что нельзя иначе или трудно. Именно поэтому придумали:
Еще можно увеличить TCP’s initial congestion window - это нередко помогает, т.к. веб-страничка отдается одним набором пакетов без подтверждения.
TCP-окошко соединения должно разогнаться сначала. Если веб-страница загружается меньше секунды - окошко может не успеть увеличиться. Средняя пропускная способность сети в мире - немного превышает 3 МБит/с. Вывод - нужно передавать через одно установленное соединение как можно больше, "разогрев" его.
Помочь тут может мультиплексирование HTTP-ресурсов внутри одного TCP-соединения: передача нескольких ресурсов вперемешку как в запросе, так и ответе. Поэтому тут можно использовать HTTP 2.0 или pipelining (но не из браузера, а из приложения напрямую).
Типичная схема работы веб-приложения состоит из следующих шагов:
Тут все понятно с точки зрения обеспечения скорости:
Главное одно - чтобы приложение было прозрачно и можно было измерить скорость прохождения запроса через разные компоненты приложения. Если этого нет - дальше можно не читать, не поможет.
Как этого добиться? Пути известны:
Если логов вас много и вы запутываетесь в них, то тогда следует агрегировать данные, смотреть процентили и распределение.
При обнаружении запроса более 0.3 секунд начинайте разбор полетов и так до победного конца.
В плане скорости тут может помочь "костылизация" - через установку обратного прокси-веб-сервера перед веб-сервером (fascgi-сервером).
Это помогает:
Ключ к пониманию появления тормозов в следующей диаграмме:
Установление TCP-соединения занимает 1 RTT. Эта величина довольно тесно коррелирует с расположением вашего пользователя относительно веб-сервера (да, есть скорость света, есть скорость распространения света в материале, есть маршрутизация) и может занимать (особенно с учетом провайдера последней мили) - десятки и сотни миллисекунд, что конечно много. И беда, если это установление соединения происходит для каждого запроса, что было распространено в HTTP/1.0.
Ради этого по большому счету и затевался HTTP 1.1 и в этом направлении развивается и HTTP 2.0 (в лице SPDY ). IETF с Google в настоящее время пытаются сделать все, чтобы выжать из текущей архитектуры сети максимум - не ломая ее. А это можно сделать максимально эффективно используя TCP-соединения, используя их полосу пропускания как можно плотнее через мультиплексирование, восстановление после потерь пакетов и др.
Поэтому обязательно нужно проверить использование постоянных соединений на веб-серверах и в приложении.
Без TLS, который изначально зародился в недрах Netscape Communications как SSL - в современном мире никуда. И хотя, говорят, Последняя "дырочка" в этом протоколе заставила многих поседеть значительно раньше срока - альтернативы практически нет.
Но не все почему-то помнят, что TLS ухудшает "послевкусие" - добавляя 1-2 RTT дополнительно к 1 RTT соединению через TCP. В nginx вообще по умолчанию кеш сессий TLS - выключен, что добавляет лишний RTT.
Поэтому следует проследить, чтобы TLS-сессии в обязательном порядке кешировались, что позволит сэкономить еще 1 RTT (а один RTT все-таки останется, к сожалению, как плата за безопасность).
Тема про известные вещи типа скорости рендеринга веб-страницы и размера изображений и JavaScript, порядка загрузки ресурсов и т.п. достаточно избита.
Если кратко ее затронуть, то необходимо кешировать ресурсы на стороне веб-браузера, но с головой. Кешировать 10МБ js-файл и парсить его внутри браузера на каждой веб-странице ни к чему хорошему не приведет.
Гораздо более острые подводные камни могут скрываться за достаточно новыми и бурно развивающимися сетевыми возможностями веб-браузера:
Это всем известный AJAX - способность браузера обращаться к внешним ресурсам по HTTP. С появлением CORS - начался совершенный "беспредел". Теперь чтобы определить причину торможения, нужно лазать по всем ресурсам и смотреть логи везде.
Технология, несомненно, взорвала возможности браузера, превратив его в мощную платформу динамического рендеринга информации. Писать о ней нет смысла, тема многим известна. Однако стоит упомянуть про ограничения:
Тем не менее, технология очень популярна и сделать ее прозрачной с точки зрения мониторинга скорости - несложно.
Как сделать веб-чат? Да, нужно как-то передавать со стороны сервера в браузер информацию об изменениях. Напрямую через HTTP - нельзя, не умеет. Только: запрос и ответ. Вот в лоб люди и решили: сделать запрос и ждать ответ, секунду, 30 секунд, минуту. Если что-нибудь пришло - отдать в ответ и разорвать соединение.
Куча антипаттернов и костылей - но технология очень широко распространена и работает всегда. Но, нагрузка на серверы при таком подходе - очень высока, и может сопоставляться с нагрузкой от основной посещаемости веб-проекта. А если обновления от сервера к браузерам распространяются часто - то может основную нагрузку превышать в разы.
Тут открывается TCP-соединение с веб-сервером, не закрывается и через него сервер передает разную информацию в UTF-8. Нельзя, правда, бинарные данные передавать оптимально без предварительного Base64 (+33% увеличение в размере), но как канал управления в одну сторону - превосходное решение. Правда не поддерживается в Internet Explorer (см. пункт выше, который везде работает).
Плюсы технологии в том, что она:
Браузер "хитрым" способом через HTTP 1.1 Upgrade меняет "тип" HTTP соединения и оно остается открытым.
Затем по соединению в ОБЕ стороны можно начать передавать данные, оформленные в сообщения (frames). Сообщения бывают не только с информацией, но и контрольные, в т.ч. типа "PING", "PONG". Первое впечатление - снова изобрели велосипед, снова TCP на базе TCP.
С точки зрения разработчика - конечно, это удобно, появляется дуплексный канал между браузером и веб-приложением на сервере. Хочешь streaming, хочешь messages. Но:
Как же отслеживать производительность Web Sockets? Со стороны клиента - сниффер пакетов WireShark, со стороны сервера и с включенным TLS - можно решить задачу через патчинг модулей для nginx, как вариант.
Так что же лучше: XMLHttpRequest, Long Polling, Server-Sent Events или Web Sockets? Успех - в грамотном сочетании этих технологий. Например, можно управлять приложением через WebSockets, а загружать ресурсы с использованием встроенного кэширования через AJAX.
Научиться измерять и реагировать на превышение заданных значений. Обрабатывайте логи веб-приложения, разбирайтесь с медленными запросами в них. Скорость на стороне клиента тоже стало возможным мерить благодаря Navigation timing API. Можно собирать данные по производительности в браузерах, отправлять их средствами JavaScript в облако, агрегировать в pinba и реагировать на отклонения. Очень полезное API.
В результате вы найдете себя в окружении системы мониторинга типа nagios, с десятком-другим автоматических тестов, на которых видно, что со скоростью работы вашей веб-системы все в порядке. А в случае срабатываний - команда собирается и принимается решение. Кейсы могут быть, например, такими:
Организация коллективной работы в рамках веб-студии - основа успеха. Решить эту задачу не сложно, всё зависит от настойчивости и дисциплинированности самого руководителя. Ему достаточно ответить на вопросы:
В коллективной работе есть много нюансов.
Творческая атмосфера. Как только в командах появляется какой-то прессинг членов команды, показуха, депрессивные настроения, то с методиками гибкой разработки можно попрощаться навсегда. Жесткие методики вроде Водопада могут ещё выдать какой-то результат, но качество работ упадёт и в этом случае.
Открытые коммуникации. Как можно проще должен быть реализован механизм коммуникации и в разработке и в проектировании. И с клиентом и между сотрудниками. Если при реализации проекта не созданы открытые эффективные коммуникации, то риски значительно возрастут.
У разработчиков всегда есть желание что-то переписать в уже созданном коде. Сделать лучше не меняя функционала. Два подхода возможны к этой проблеме:
Первый. Разрешать это делать в ограниченные сроки. Но нужно суметь подать это клиенту, так как придётся объяснять почему студия тратит время (то есть деньги клиента) на то что уже есть и работает.
Второй. Если не прислушиваться к этой потребности, то код скоро может стать не оптимальным.
Нужен баланс между обоими путями. Нельзя запрещать полностью, но и нельзя поощрять излишнюю работу и "прокачку" программиста за счёт студии. Клиенту нужно суметь объяснить необходимость рефакторинга, если такая необходимость реально возникла. (Проще поддерживать и развивать проект - основные аргументы.) Если клиент входит в положение, то это серьёзно повышает мотивацию разработчика и качество системы.
В принятии решении о рефакторинге нужно ориентироваться на мнения ведущих разработчиков, так как точность оценки объективной необходимости рефакторинга прямо пропорционально зависит от уровня профессионализма разработчиков.
Технические особенности командной работы будут рассмотрены далее.
Эффективная работа команды над проектом зависит от нескольких факторов:
Менеджер проекта - не обязательно отдельная штатная должность в рамках студии, это скорее организатор процесса, в котором он участвует сам. В небольших студиях это может быть самый опытный программист или авторитетный член команды.
В инженерных дисциплинах цель команды - работающая система (и как следствие удовлетворённый клиент). Система должна работать эффективно, дёшево решать задачи клиента. Поэтому любые позиции внутри команды, которые эту цель отодвигают (личные амбиции, желание "прокачаться" вместо решения задач) - это плохо. Нужно создать атмосферу, когда всплывают идеи, как решить задачу проще, эффективнее, прозрачнее - это хорошо. Менеджер проекта должен это понимать.
Не тот человек в качестве менеджера проекта - большой риск. Человек для этой роли - достаточно уникальный, он должен совмещать в себе понимание технологий проектирования и понимание технологий управления.
В сложных проектах командой должен управлять менеджер-технолог, понимающий техническую суть проекта. Ни в коем случае вопросы бизнеса не должны влиять на процессы разработки. Задача перед программистами должна при этом ставиться так, чтобы у них "загорелись" глаза.
Что нужно учитывать при подборе команды?
В штате команды должен быть как минимум один качественный программист. Такой, у которого "горят" глаза при виде сложной задачи. Верстальщик, умеющий вставлять простенький PHP код - не программист. Такого достаточно для сайтов-визиток, для кастомизации шаблонов компонентов Bitrix Framework. Но для сложного и крупного проекта необходим программист с высокой квалификацией, серьёзным портфолио. В крайнем случае - с высшим образованием.
И чем сложнее проект, тем больше должно быть настоящих программистов. Это не означает, что в команде не может быть неопытных разработчиков. Такие могут выполнять несложные задачи, попутно совершенствуясь в собственном мастерстве. Обучение у опытных мастеров - лучший способ вырасти в профессионала.
Отдавайте предпочтение разработчикам имеющим хотя бы онлайновые сертификаты о прохождении курсов. К сожалению, разработчики не знакомые с архитектурой и технологиями Bitrix Framework пишут неоптимальный код.
Разработчик должен уметь смотреть на систему глазами клиента и менеджера, знать административную и публичную части.Однако у программистов всегда есть соблазн залезть в "высшие материи". Попробовать технологии, которые он ещё не использовал. И в этой ситуации может возникнуть "вилка" между желаниями и возможностями. Не забывая про свободу программистов нужно постоянно держать в уме то, что проектирование должно быть проведено адекватно поставленной технической цели, разработка должна быть простой, надёжной и расширяемой. Менеджер-бизнесмен в ситуации сложного, высоконагруженного проекта может не справиться с этой задачей.
Вопрос денежного стимула не прост. С одной стороны программист с "горящими глазами" может работать достаточно долго "за идею". Однако, "обиженный" программист работает не в полную меру, а "сильно обиженный" может представить даже угрозу при слабом контроле за качеством кода и его безопасностью.
С другой стороны, у каждого есть "внутренняя оценка" своих качеств и несоответствие этой оценке (в сторону завышения) только развращает сотрудников. Деньги должны быть достойными, но не лёгкими.
Один из серьёзных факторов риска - незнание продукта и предметной области проекта. С незнанием продукта, думается всё понятно. По предметной области должна быть возможность получить чёткую, конкретную и быструю консультацию к заказчика. Это должно быть оговорено с ним (и понято им) ещё на стадии переговоров.
Другой фактор риска - недисциплинированность программистов. Она вызвана разными причинами: от незнания продукта через обычную невнимательность, до чрезмерного увлечения новыми технологиями.
Правильно организованная коллективная работа обязательно предполагает использование IDE, баг-трекеров, систем накопления информации типа Wiki, других инструментов. Об этом в курсе есть отдельная глава.
Разработчик не должен разбираться в предметной области. Иначе ему некогда будет программировать. Разработчик должен иметь возможность получать ответы на свои вопросы в идеале - сразу, "по жизни" - в течение разумного времени. Получать конкретные ответы на конкретные вопросы.
Вот, например, фраза из ТЗ: «покупатель может принадлежать группе клиентов, получающих скидку». Вопросы, которые возникнут у программиста:
Точные и формальные ответы на эти вопросы должны быть понятны разработчику перед началом работы. Эти ответы - зона ответственности менеджера проекта. Он должен сам давать ответы или найти того, кто ответит разработчику точно на возникающие вопросы по предметной области. Например, можно включить в проектную группу аналитика, эксперта или толкового представителя заказчика, который будет не спать ночами, пока в деталях не раскопает все тонкости и логику проектируемой системы. Или менеджер должен сам погрузиться в проект, предметную область и сделать эту работу сам.
Способ снижения рисков в ситуации с пониманием предметной области простой: проверьте цепочку формирования требований от менеджера, аналитика - к программисту.
Методология определяет многое: состав команды, способ взаимодействия с клиентом, порядок работы и многое другое.
При выборе методологии надо понимать самим и разъяснить клиенту, что требования к проекту у клиента будут меняться. И чем жёстче используемая методика, тем сложнее вносить изменения в требования. Клиент изначально может не осознавать части своих требований, часть требований может поменяться со временем, это объективный процесс. Поэтому, если клиент настаивает на жёстком процессе, то он должен быть готов к усложнению и удорожанию процесса разработки.
Выбор методологии зависит и от состава команды, имеющейся в распоряжении студии. Если же клиент настаивает на каком-то конкретном процессе, то придётся искать людей под него.
Чем сильнее в профессиональном плане команда, имеющаяся в распоряжении, тем более гибкие методологии можно использовать. Для высокопрофессиональных сотрудников можно использовать XP - экстремальное программирование. Вчерашние студенты в режиме XP завалят самый простой проект. Для бывших студентов чем более формализован процесс, тем работа будет успешнее. С такими сотрудниками придётся применять Водопад.
Первое. Всё максимально формализовать и упростить. (Что это значит в плане использования, например, Bitrix Framework? Использование стандартных компонентов, использование стандартной административной части, сделать всё, чтобы люди как можно меньше написали кода. Править компоненты, шаблоны, вёрстку править, настройки менять, не более.)
Второе. Построить процесс разработки максимально близкий к Водопаду. Неопытный сотрудник не может правильно оценить сложность задачи и подобрать адекватные методы её решения. Для него опытный разработчик должен всё расписать в виде подробного плана проекта с диаграммой Ганта.
Проект лучше разбить на итерации большой продолжительности (2-3 месяца) для снижения рисков. Первый этап - это создание сайта с интегрированной вёрсткой на основе стандартного функционала. То есть всё то, что можно реализовать без сложных работ, предъявить клиенту как результат первого этапа.
Второй и последующие этапы - какая-то часть сложного высоконагруженного функционала, например, объёмный каталог. Создаётся, тестируется, накатывается на сделанное, сдаётся.
В этом случае возможна меньшая формализация итерационного процесса. Можно не делать аудит кода, можно делать меньше веток контроля версий, детально не указывать параметры настроек компонентов, модулей, можно сделать более короткими итерации (примерно месяц - полтора). Для таких команд подходит RUP, Agile, Kanban.
Сильная команда постоянного состава - редкий случай. Подходят все виды процессов, выбирать которые нужно в зависимости от проекта. Сложные и большие проекты могут потребовать Водопад и для сильной команды, но в большинстве случаев сильные команды работают по технологиям XP, с элементами Agile, Scrum или Continuous Integration. Итерации могут становиться короче, примерно 2-3 недели.
Точность оценки зависит от качества проектирования и от квалификации разработчиков. Как правилно, для оценки используется технология Planning Pocker.
Технология крайне проста: оглашается задача, обсуждается и разработчики выкидывают карты с указанием времени необходимого на реализацию этой задачи. Время на картах указано в числах Фибоначи.
Большое расхождение в оценках времени исполнения говорит о слабости команды, либо неясной задаче. В слабых командах лучше не проводить коллективную оценку необходимого времени, а довериться мнению сильного разработчика. Менеджер не должен оценивать сам сложность задач. В этом случае программисты могут выдавать некачественный код, лишь бы успеть в срок. Но если разработчик недобросовестный либо хитрый, то он будет стремиться завысить срок. Менеджер должен учитывать и этот риск.
При проведении оценки задач Planning Pocker'ом менеджер должен быть уверен, что задача ясна команде. Иначе её невозможно правильно оценить. Будут названы либо завышенные сроки, из боязни не успеть, либо заниженные, из-за недооценки сложности. В обоих случаях сроки окажутся под риском срыва, а качество исполнения - невысоким.
Можно применять модификацию Planning Pocker, когда разработчики оценивают задачу по критерию: Просто\Средне\Сложно. Если у менеджера уже есть сложившаяся оценка сколько времени уйдёт на задачу каждого типа.
Разработка сложного проекта требует системных действий. Если небольшой проект с небольшим числом разработчиков (не более 3-х) можно реализовывать по принципу текущих потребностей, то со средними и большими такая организация процесса уже не будет эффективной.
Минусы работы от потребностей следующие:
Это приводит к следующим рискам:
Создание определённого алгоритма работы внутри веб-студии помогает не только в плане внутренней организации. Такой процесс упорядочивает и взаимодействие с клиентами, особенно с большими и крупными компаниями, когда от заказчика может выступать несколько человек.
Существует несколько типов процессов создания программного обеспечения. Для веб-студий можно порекомендовать:
Выбор и внедрение процесса: от водопада до Kanban/XP/RUP
Каскадная модель подходит для больших проектов с большими сроками, штатами и функционалом: сложные системы в банковской сфере, большие интернет-магазины, проекты с большим числом пользователей и так далее. ТЗ для таких проектов может занимать тысячи страниц, но для разработчиков такой процесс фактически идеален: все описано, всё завизировано и утверждено, сделано максимально всё, чтобы понять как должна работать система.
Водопад даёт качественный результат в силу чёткого следования порядку работы, отсутствию смены требований.
Минус системы: при необходимости изменений возникает большой объём «бюрократических» работ: согласование и утверждение со всеми заинтересованными лицами.
Предпочтителен в случаях когда система сложная и непонятно как она должна работать в конечном виде. Основной принцип: пошаговое приближение к конечной цели. То есть сделать от начала и до конца часть системы, внедрить, собрать фидбек, обсудить, скорректировать процесс и перейти к следующему шагу. Итерация может быть большой (2-3 месяца) или нет (2-3 недели).
Метод очень эффективен в плане взаимодействия с клиентом. Особенно с тем, кто толком не представляет чего он хочет. Улучшается обратная связь с Заказчиком – он принимает каждый этап (итерацию). Выпуск проекта в стартовой, урезанной конфигурации позволяет Заказчику самому понять куда идти дальше. И он идёт дальше вместе с вами.
Метод позволяет брать в работу приоритетные задачи и риски. Распределяется нагрузка равномерно по всему сроку, то есть проектировщики не сидят без дела, после выполнения проектирования, как было бы в методе "водопад", а после выполнения первого этапа, когда программисты только начали работу, переходят ко второму. В это время дизайнеры могут заниматься уже третьим этапом.
Затраты на проект распределяются равномерно, а не в конце проекта. Это важно для Заказчика.
Сложности метода:
Итеративный процесс имеет несколько видов: Agile, XP, Kanban, RUP и другие.
Модель подходит для веб-студий, работающих с большим количеством несложных заказов. Работа по этой модели - это работа без конечных сроков. Клиент платит за то, что студия потратила время. При доверии со стороны клиента, что программист работает эффективно и поставленную задачу реально можно было решить именно за то время, которое на неё потрачено.
Канбан позволяет сократить время прохода задачи до состояния «готовности». Количество незавершённой работы разработчика должно быть ограничено. Новая задача может ставиться только тогда, когда какая-то из существующих передаётся следующему элементу производственного цикла. Kanban (или сигнальная карточка) подразумевает, что производится некое визуальное оповещение о том, что можно задавать новую работу, так как текущий объём работ не равен принятым лимитам.
Смысл системы с точки зрения разработчика: максимально упростить бюрократию и дать человеку просто работать над небольшим числом задач.
Смысл системы с точки зрения веб-студии: снижение времени прохождения задачи через группу специалистов до её релиза. Система позволяет создать работнику такие комфортные условия работы, что бы задача проходила через все эти этапы быстро. При этом происходит визуализация задач, облегчающая контроль.
Нет планирования времени. На доске размещаются столбцы специалистов. В столбцах специалистов указывается лимит на число задач, которые "висят" на специалисте. Больше этого числа задач им нельзя ставить. Не должно быть такого, что прибегает кто-то и: "Срочно нужно сделать!", все делается только в порядке установленном на доске.
Менеджер может визуально отслеживать состояние дел: где происходит "затык" в работе, кто отстаёт и начать разбираться почему это происходит и принимать меры по устранению проблем.
На одной доске можно вести учёт всех проектов, если, например, определить для каждого проекта свой цвет задач. На карточках можно указывать время начала работы и время её завершения.
Работа организуется по следующему принципу:
Ключевой человек в технологии Scrum. Он закрывает команду от Клиента. Если Клиент - корпоративный, то с его стороны участвует несколько (до нескольких десятков) сотрудников. Доступ этих сотрудников к разработке напрямую должен быть исключён. Это делает Product owner.
Product owner выстраивает взаимодействие с Клиентом. Если Product owner "прогнётся" под наплывом требований со стороны Заказчика, Scrum становится бесполезен. Задача очень трудная, для человека с крепкой психикой.
Product owner говорит со всеми менеджерами, директорами, аналитиками и так далее и вырабатывает понимание того как должна работать создаваемая система. И он отвечает за продукт. Он знает ответы на все вопросы по продукту, что позволяет разработчикам чувствовать стабильность при работе, что завтра не поменяются требования и не придётся всё переделывать.
Для команды Product owner формирует ранжированный список "хотелок" (Product backlog) из которого и формируется список задач на конкретный цикл (Sprint backlog).
Список потребностей, сценариев использования описываются в таблице Product backlog. Список линейный, приоритезированный, предварительно оценённый на трудозатраты по простейшей схеме: простой, средний, сложный.
Product backlog позволяет легко проверять работу менеджеров: скорость работы, возможные проблемы. В том числе и в плане работы с клиентами: если Клиент не может высказать внятно свои потребности (составить backlog), то, возможно, есть смысл сменить методику работы с ним.
Срок собрания - не более одного дня. Команда с подачи Product owner'а отбирает задачи из Product backlog, оценивает их во временных единицах (как правило, это человеко-часы) на выполнение. Оценка проводится в "игровой форме" (Planning poKer), при большом разногласии в оценке может проводиться в несколько туров с периодами обсуждения.
Отобранный из Product backlog список задач на данную итерацию (Sprint). Задачи отбираются с учётом выделенного времени на Sprint и выставленных каждой задаче временных оценок.
Небольшой по срокам (1-4 недели) цикл разработки. Разработка ведётся на основе Sprint backlog. Учёт работы можно вести по аналогии с доской Kanban:
Учёт "сожжённого" времени, представленный в графической форме, позволяет судить о процессе производства. Если процесс идёт нормально, то часы "сжигаются" равномерно. Если скорость "сжигания" замедлилась или встала совсем, то на лицо проблемы, которые требуют вмешательства руководства.
В Scrum'е нет личности. Работа - коллективная, ответственность коллективная. Это порождает как преимущества, так и проблемы. Есть сотрудники - таланты, которые просто не могут работать в команде, но они нужны. Есть просто недобросовестные товарищи. И со всеми нужно работать.
Кроссфункциональность не подразумевает универсальность каждого участника команды. В команде всегда кто-от что-то лучше делает, чем другой. Под кроссфункциональностью подразумевается тесное сотрудничество между её участниками.
Размер команды для каждого проекта нужно подбирать. Оптимально - 5-7, при большем количестве возникают проблемы контроля.
Считается, что если Sprint провален, то провалила вся команда. Но в реальности провалы бывают из-за конкретного человека. Как быть в такой ситуации?
Но если команда - настоящая, слаженная команда, то этот принцип работает просто великолепно.
Авторитетный и уважаемый всеми участниками разработки человек. Его задача - следить за тем, чтобы поддерживался процесс Scrum'а. Специальность этого сотрудника в рамках проекта - не важна. Это может быть верстальщик, программист. Он следит за тем, чтобы люди не расслаблялись, не тусовались, а работали. При этом он не командует, а все вопросы решаются только за счёт авторитета.
Другая функция - устранять препятствия, возникающие перед группой разработчиков. Не боясь при этом поругаться и "повоевать" за нужное оборудование, условия работы и прочее.
Ежедневное, короткое (не более 15 минут) собрание участников для оценки сделанного накануне, задач на день, возникших проблем. Идея в том, чтобы каждый в команде знал кто чем занимается, к кому с какими вопросами обращаться, кому, если нужно, помочь.
Очень важно правильно определиться с этими условиями. Они должны быть чётко оговорены: проверено тестировщиками, проверено безопасниками, проверено сисадминами, проверено Product owner'ом и так далее.
Демонстрация клиенту результатов цикла. Контролирующее, организующие и вдохновляющее на дальнейший труд мероприятие. Клиент видит, что работа идёт, есть за что платить. Команда видит оценку своей работы.
Оценка результатов цикла самой командой, оценка механизма разработки. В целом оценивается результат и ищутся способы как сделать лучше.
Плюсы:
Минусы:
Экстремальное программирование - не методика, а несколько базовых принципов работы. В чистом виде применяется крайне редко, чаще идёт в связке с какими-то итеративными методами. Использование XP требует наличия высокопрофессиональной команды, постоянного контакта с заказчиком, способности гибко реагировать на изменения требований заказчика и, как следствие, к рефакторингу кода.
В XP многое непривычно. Для многих программистов непривычна работа в паре, непонятно требование писать тесты до написания кода, который они должны тестировать. Профессионализм исполнителей должен быть таким, чтобы разработчик мог в любой момент "въехать" в любой участок кода, поменять его и гарантировать работоспособность своих изменений в рамках всей системы.
Как элемент используется в Экстремальном программировании.
Эта практика рекомендуется для больших проектов, имеющих большой временной период для исполнения. То над чем вам предстоит работать и что поддерживать, а не просто сделать и сдать. В маленьких проектах в этой технологии просто нет необходимости, лучше использовать Kanban или SCRUM.
Требования к производству по практике Continuous Integration
Continuous Integration — от простого к сложному
Несколько советов о том как организовать внутри вашей студии процесс разработки.
Использование удобных инструментов для работы существенно ускоряет процесс. Можно программировать и в Notepad'е, но любой современный инструмент обеспечивает "малую механизацию", существенно облегчая труд разработчика:
Существует достаточно много Интегрированных сред разработки (IDE). Каждая из них имеет свои сильные и слабые стороны. На решение какую использовать в большей степени влияют привычки и вкусы конкретного разработчика, но не представляется правильным использование разных сред в рамках одной студии. Купите одну и пусть все разработчики научатся в ней работать. Необходимо стандартизировать настройки IDE в рамках вашей организации.
Кроме IDE необходимы инструменты для хранения информации по стандартам и методам работы в команде и по работе над конкретным проектам. Обязательным инструментом также можно назвать багтрекер.
Инструменты для эффективной работы команд - IDE, контроль версий, трекеры, wiki
Рассмотрим несколько схем использования:
Оптимальный результат для разработчика: отдельный выделенный сервер на котором стоит виртуальная машина Linux (либо вообще сервер на Linux), установлена IDE, папка с проектом, установлен вебсервер с ядром Bitrix Framework. В этом случае IDE может локально всё проиндексировать. В такой конфигурации чрезвычайно удобно работать одному разработчику, но над проектом работает несколько человек. Соответственно, нужно делать (желательно автоматически) deploy, нужно иметь сервера тестирования, "боевые" сервера. Как организовывать взаимодействие всего этого? Предложенная схема - для самых простых проектов.
Часто встречается схема для IDE PHP Storm: у разработчика локально размещена IDE, ядро Bitrix Framework, локально скопирован код проекта, который синхронизируется (через контроль версий, либо ftp) на удалённый сервер. На удалённом сервере размещён веб-сервер и ядро Bitrix Framework.
Плюсы схемы: подсказка по ядру работает
Минусы схемы: возникают сложности с синхронизацией ядра Bitrix Framework, так как оно синхронизируется достаточно долго, и сложности с обновлениями ядра.
Более удобная схема для IDE, поддерживающих sftp: локально у разработчика ничего нет. Проект находится либо на shared-диске, либо на удалённом сервере. Разработчик обращается к нему напрямую. В этом случае возникает проблема индексации, когда IDE должна проиндексировать всё удалённо по сети.
Последняя из возможных схем: локально хранится только код проекта, ядро Bitrix Framework не индексируется. В этом случае подсказки работают только по проекту, по ядру не работают. В этом случае лучше использовать документацию API по продукту, либо бесплатный плагин bxApiDocs.
К сожалению, веб-студии редко используют инфраструктуру отладки. Традиционно для этого используют команды echo, die. Этот вариант медленный и чреват ошибками.
В виртуальной машине BitrixVM уже установлен Xdebug. Подключить его к IDE не сложно.
Отладка методом breakpoints - классическая технология отладки, позволяющая отслеживать какие-то сложные моменты, например, рекурсии.
Система управления версиями |
Система контроля версий нужна даже одному разработчику. Она дисциплинирует, даёт устойчивость проекту. При работе нескольких разработчиков этот инструмент уже становится обязательным.
Для чего осуществляется контроль версий?
Есть несколько систем: Mercurial, Git, Subversion и другие. Нет каких-то строгих рекомендаций кому что использовать. Система выбирается под собственные предпочтения.
Один из возможных вариантов организации управлением версий для проекта среднего размера:
Каждый разработчик ведёт свою ветку на собственном виртуальном сервере. Ведущий разработчик объединяет эти ветки в единую ветку DEV. Полезно вести отдельную ветку для тестирования (TESTING). После тестирования системный администратор переносит всё на "боевой" проект
/local
, которая создана специально для работы с системой контроля версий.
Для каждого проекта желательно заводить свою Wiki, ветку в Wiki или Базу Знаний, в которой будет фиксироваться всё, что связано с реализацией проекта: от составления/обсуждения ТЗ до прямых инструкций как коммитить ветки, как выкладывать обновления на боевой сервер и так далее.
Преимущества:
Когда проект разросся и стал большим без трекера задач невозможно организовать эффективную работу. Можно использовать Универсальные списки и Бизнес-процессы в Битрикс24, но этот продукт рассчитан всё же более на решение бизнес-задач и не всегда удобны программистам. Поэтому можно порекомендовать: Redmine, Mantis, Jira
Преимущества использования баг-трекеров:
При работе нескольких разработчиков на собственных базах данных возникает проблема совмещение изменений от всех разработчиков на едином тестовом сервере.
Все изменения БД оформляются скриптом php. (Обновления самой системы Bitrix Framework реализованы подобным образом.) В скрипте проверяется текущая версия структуры БД (например, в опции COption). Если версия новая, то выполняется блок кода, модифицирующий БД, и устанавливается новое значение версии в опциях (инкремент). Скрипт коммитится наряду с "обычными" файлами. Каждый модуль подключает этот скрипт (на каждом хите, но клиентам этот вызов не уходит), и когда он приходит из репозитория, выполняются запросы и версии структуры БД выравниваются.
Скриптов может быть несколько, например, для каждого модуля отдельный. На проекте, также, могут быть разные области и удобнее будет несколькими скриптами вносить изменения. Примерный вид скрипта миграции:
$current_version = intval(COption::GetOptionInt("main", "~database_schema_version", 0)); if ($current_version < 5) // 11.5.7 { if (!$DB->Query("select TIME_ZONE_OFFSET from b_user WHERE 1=0", true)) { $updater->Query(array( "MySQL" => "ALTER TABLE b_user ADD TIME_ZONE_OFFSET int(18) null", "Oracle" => "ALTER TABLE b_user ADD TIME_ZONE_OFFSET number(18) NULL", "MSSQL" => "ALTER TABLE b_user ADD TIME_ZONE_OFFSET int NULL", )); } COption::SetOptionString("main", "~database_schema_version", 5); } if ($current_version < 6) // 11.5.7 { if (!$DB->IndexExists("b_event_message", array("EVENT_NAME"))) { $updater->Query(array( "MySQL" => "CREATE INDEX ix_b_event_message_name ON b_event_message (EVENT_NAME(50))", "Oracle" => "CREATE INDEX ix_b_event_message_name ON b_event_message (EVENT_NAME)", "MSSQL" => "CREATE INDEX IX_B_EVENT_MESSAGE_NAME ON B_EVENT_MESSAGE (EVENT_NAME)", )); } COption::SetOptionString("main", "~database_schema_version", 6); }
Возможна несколько иная реализация. Например, не подключать скрипт на каждом хите, а сделать хук в системе контроля версий на пулл.
Например создали таблицу в БД - опишите ее в Wiki, чтобы каждый программист при написании классов, обращающихся к БД знал имя таблицы, имена и параметры полей.
Решение не является разработкой компании 1С-Битрикс, все вопросы по его работе обращайте к разработчику.
В главе рассказывается об ошибках на этапе проектирования и разработки, а также даны рекомендации, как проводить тестирование и нагрузочные испытания на этапе разработки и перед сдачей проекта клиенту.
Откуда и на каких этапах берутся ошибки?
Как снизить число ошибок в данном случае:
Всех ошибок все равно не избежать, но их будет значительно меньше.
При изменении каких-либо требований помогает использование понятного модульного/объектного кода, и опять же на помощь приходят функциональные и модульные тесты, которые позволяют быстро протестировать сделанные изменения в коде.
Как снизить число ошибок в данном случае:
Тестировщику отводится отдельная роль в продукте:
Чтобы не создавать ошибок в продукте сисадмин должен знать и уметь:
Сисадмин должен обеспечивать контроль качества работы веб-проекта изнутри.
Большую ценность в тестировании представляют ранние отзывы от клиента.
Тестирование продукта как на этапе разработки, так и в процессе изменения какого-либо функционала в работающем проекте с помощью функциональных тестов очень важно. Причем, чем больше функциональных тестов покрывают систему, тем больше ошибок будет найдено на этапе тестирования и разработки.
Как проверять каждый кейс:
Полезны в ряде случаев:
В качестве инструментов модульного тестирования можно использовать: PHPUnit, SimpleTest или написать свой попроще.
Минусы:
Можно автоматизировать часть ручных тестов c помощью Selenium:
Минусы:
Попробуйте на практике, определите золотую середину.
Одним из вариантов тестирования большого проекта является привлечение сотрудников клиента к тестированию. Либо, вообще, можно попробовать продать идею запуска проекта в качестве Beta. Это позволит найти незначительные и некритичные ошибки в проекте.
Монитор качества - инструмент для проверки качества выполненного проекта перед сдачей его заказчику. Он позволяет решить задачу обеспечения прозрачного и гибкого процесса сдачи веб-проекта клиенту, повышая уровень гарантированного результата и снижая общие риски.
Монитор качества работает в продуктах «1С-Битрикс» и в каталоге веб-приложений для сайтов и корпоративных порталов «1С-Битрикс: Маркетплейс». Процедура сдачи включает обязательные и дополнительные тесты, в том числе, аудит безопасности PHP кода.
Монитор качества включает в себя:
Монитор качества дает дополнительные возможности разработчикам и клиентам:
Нагружать систему нужно, т.к при нагрузочном тестировании выявляются узкие места и ошибки, которые не могут быть выявлены при обычном тестировании на этапе разработки:
Нагрузочное тестирование нужно проводить всегда, хотя бы в базовом виде.
В зависимости от того, что хочет клиент от нагрузки, тот вывод и детальность отчета получаем в конце теста.
Пример отчета 1Гистограмма времени хитов: хитов менее 0.5 сек – 95%, хитов дольше 10 сек – 0,01%. Число ошибок < 0,01%
Среднее время хита – 0.3 сек. Пиковое использование памяти скриптами - менее 128М. Время тестирования – 48 часов. Графики: munin по CPU, памяти, диску, трафику, детальные по mysql.
Проводить нагрузочное тестирование нужно всегда на демо-данных, приближенных к реальным.
Что в таком случае происходит с системой:
Все эти процессы нельзя протестировать без нагрузки на этапе разработки.
Инструменты мониторинга:
В процессе нагрузки возможны такие случаи из-за ошибки разработчика, что NGINX файлы перестает видеть как статику и начинает отдавать через Apache, заполняя в итоге все его свободные слоты.
Инструменты мониторинга - это лог-файлы:
apachetop –f /var/log/nginx/access.log
apachetop –f /var/log/httpd/access.log
Как выявить зависания стека tcp/ip:
netstat –ntop
. show engine innodb status
. Частая ошибка в случае зависания стека tcp/ip кроется в запросах к БД.
"Зависания" memcached происходит от резкого увеличения числа соединений к memcached.
Для выявления можно использовать команду:
netstat –ntop | grep 11211
Решениями проблемы роста числа соединений могут быть:
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse echo 15 > /proc/sys/net/ipv4/tcp_fin_timeout
memcached -d -p 11211 -u memcached -m 384 -c 10240 -P /var/run/memcached/memcached.pid
В качестве примера нагрузочного тестирования опишем, как тестируется коробка "Битрикс24".
Коробочная версия Битрикс24 уже проходила нагрузочное тестирование в 2015 году, и на тот момент были получены очень неплохие результаты. Тестирование продемонстрировало, что продукт, развернутый в компании из 15 тысяч сотрудников, работает стабильно и быстро.
За прошедшее время продукт сильно изменился, получил новые возможности и сценарии. Росло и количество крупных клиентов, появилось много заказчиков из ТОП-100 Forbes/РБК. В последнее время продуктом стали интересоваться действительно очень крупные клиенты с десятками тысяч сотрудников. Только за последние пару лет Битрикс24 был запущен в НЛМК, DNS, СУЭК, Полюс Золото, Почте России, ФК Открытие и многих других.
Одним из ключевых запросов от заказчиков такого уровня является надежность и стабильность в работе, производительность решения. Корпоративные порталы в крупных предприятиях, особенно после начала пандемии, стали понемногу переходить в разряд критических для бизнеса приложений. В них заводятся информационные потоки компании, организуются цифровые сервисы для сотрудников. В целом тенденция последних лет - это превращение интранета в единое корпоративное цифровое пространство для работы и коммуникации тысяч сотрудников.
Стало понятно, что кроме опыта аналогичных внедрений нужны новые подтверждения, что "Битрикс24" может справиться с такими нагрузками. Поэтому был запущен проект нового нагрузочного тестирования.
Партнером по проекту тестирования стала компания Selectel, традиционно предоставляющая оборудование для тестов. Также в работе над тестированием вендору помогали коллеги из QSOFT.
Оборудование включало в себя физические серверы двух конфигураций:
|
Был развернут кластер, включающий 2 сервера баз данных и 3 сервера приложений (веб-серверы). Данная конфигурация была выбрана для обеспечения высокой производительности кластера с одновременным обеспечением высокой отказоустойчивости. Схема кластера и его описание приведены в детальном отчете.
Программное обеспечение серверов было сконфигурировано с помощью продукта "1С-Битрикс: Виртуальная машина". Кластерное решение реализовано на основе технологии Веб-кластер.
Указанное количество пользователей за 1 час генерируют на тестовом портале:
За сутки (24 часа) на таком портале генерируются:
Нагрузка создавалась инструментом JMeter версии 5.3.3. Данные теста записывались в [dw]InfluxDB[/dw][di]Высокопроизводительная БД, предназначенная для обработки высокой нагрузки записи и запросов.
Подробнее...[/di]. Для визуализации аналитики использовали приложение Grafana. Мониторинг серверов осуществлялся при помощи системы мониторинга Zabbix.
Для тестирования было выбрано 29 сценариев из 13 блоков, свойственных для типового интранет-портала:
Для каждого теста были подобраны веса, учитывающие работу различных пользователей на портале и долю каждого из блоков в общей нагрузке.
Была применена новая методика генерации нагрузки от пользователей. Вместо одного виртуального пользователя, перебирающего в случайном порядке типовые цепочки действий, нагрузка генерировалась большим количеством разных пользователей со своей учетной записью на портале. Генератор нагрузки авторизовывал каждого такого пользователя в системе, а затем выполнял под этим пользователем разнообразный набор сценариев. При этом между выполнением каждого нового сценария выполнялось ожидание (от 20 сек до 10 минут). Таким образом, эмулировалось максимально реальное поведение сотрудников предприятия, которые зашли в интранет-портал и работают с ним в течение дня.
Важно отметить, что не преследовалась цель максимизации показателей по сгенерированному контенту на портале за время теста (новостям, задачам, документам, сообщениям и другим). Важнее было обеспечить их соответствие реальной жизни. И тем не менее, скорость добавления информации на портал оказалась также очень высокой, что покрывает потребности многих заказчиков с большим запасом!
Демонстрационный портал, развернутый в кластерном решении из 5 серверов, обеспечил одновременную работу 30 тысяч пользователей, а это соответствует примерному профилю нагрузки для крупной корпорации, в которой работает 100 – 200 тысяч сотрудников.
При этом система обеспечила быстрый (даже по меркам интернет-проектов) отклик, для подавляющего большинства запросов не превышающий 1 сек, что, безусловно, делает работу с порталом комфортной для современного пользователя. Технология Веб-кластера вновь подтвердила свою производительность и надежность.
Ссылка на запрос подробного отчета.
Создать хорошее веб-приложение не сложно. Для этого достаточно обеспечить:
Однако при этом задачу высокой производительности никто с разработчиков не снимает. Создать "быстрое" приложение не сложно. Для этого достаточно:
Чётко поймите, что именно для клиента означает "производительный сайт" и формализуйте эти требования. Большинство клиентов ориентируется на другие ресурсы и, как правило, говорят: "быстро как там-то". Такой ответ не должен вас удовлетворять. Требования должны быть чётко формализованы: Главная - 0,5 секунд, детальная - 0,3 секунды, "Умный фильтр" не более 1, 5 секунд.
Производительность JS в этом плане - первое, с чего надо начать. JS должен работать быстро.
Второе - картинки и стили, которые тоже должны грузиться быстро. Их лучше раздавать с разных доменов, так как браузер может качать с одного домена не более чем, как правило, в 6 потоков. Большие файлы отдавать через CDN
В итоге получается что верстальщик и JS-разработчик - это основные люди, отвечающие за производительность с точки зрения клиента.
При работе с SSL рекомендуется использовать SPDY через модуль NGNIX, ускоряющий загрузку.
Коллективная разработка при неумелой организации процесса также создает проблемы, по принципу: "У семи нянек дитя - без глазу". Тем более, что тестирует это все, как правило один человек, который просто не успевает сделать проверку всего и ограничивается только проверкой работы (а не скорости работы) созданного функционала.
Другая проблема - управленческая. Если руководство не понимает проблем разработки и качества разработки и его волнуют только сроки. В этом случае практически всё делается второпях, не оптимально и "на скорую руку".
Другие ошибки:
Полезные рекомендации по клиентской производительности можно также почерпнуть тут.
Создать себе реальные проблемы на серверной стороне можно с помощью:
Для избежания проблем со статикой оптимальным решением будет использование CDN. Но, если в силу каких-то причин это неприемлемо, можно вынести статику на отдельный домен, что позволит скачивать её в несколько потоков. Либо перенести статику на отдельный сервер(ы) с быстрыми дисками.
На скорость генерации динамического контента в первую очередь влияет качество программирования. Сама по себе БД не тормозит. Тормозят запросы, которые задаются разработчиком.
При прогнозируемых больших объёмах данных рекомендуется познакомиться с теоремой CAP, NoSQL, изучить репликацию, изучить и использовать Galera.
Рассмотрим несколько типовых ошибок, которые совершают при проектировании больших проектов.
Частая ошибка начинающих студий.
За разработку берутся в общем профессиональные, но не знакомые с Bitrix Framework студии, в надежде освоить систему "по ходу". Слабое знание системы привело к тому, что написана часть функционала, которая, как потом оказалось, уже имеется в Bitrix Framework. Часть функционала показалась разработчикам "плохо" реализованной и её переписали "лучше чем в Битрикс". В последствии выяснилось, что переписанный функционал завязан на другие опции в системе. Кроме того функционал был переписан с модификацией ядра, прямыми обращениями к БД и другими стратегическими ошибками.
Результат: клиент не может управлять сайтом через стандартную админку, управление осуществляется через код, модифицированное ядро при обновлении затёрто, сайт "сломался". Уходит много времени на исправление, клиент "рвёт и мечет".
Ошибка в постановке задачи. На этапе ТЗ не отмечен факт большого числа инфоблоков и данных в них. В результате при разработке реализована неоптимальная работа с инфоблоками через АПИ. При этом, не подумав, выбрали виртуальных хостинг.
Как следствие, после загрузки данных все стало тормозить. Попытались везде внедрить кэширование – время генерации кэша > 30 секунд. Сроки - сорваны.
Одна из "ошибок роста" для веб-студии. После нескольких удачных проектов возникает ощущение, что "мы можем всё". В результате решили сделать значительно "сложнее", чем нужно по ТЗ. При этом Использовали свои таблицы в БД, а после запуска обнаружили многочисленные уязвимости типа SQL Injection.
В последствии пришел новый программист и не смог распутать решение - начал переписывать. Развивать проект получается дорого.
Ошибка самоуверенности. Возникает после продолжительной и успешной работы студии, когда научились обходить все вышеописанные ошибки.
Сложная предметная область при дефиците времени. Экономим на проектировании, надеясь на свой профессионализм. После 50% времени реализации, оказалось что нельзя добавить новое требование. Дальше возникают новые требования и новые проблемы. Проект переписывали раза 3, "Костыль на костыле" - сложно и дорого развивать.
Обжегшись на предыдущей ошибке некоторые веб-студии начинают перестраховываться.
Опять сложная предметная область, проектировали 3 недели. Получили: 10 диаграмм, 50 Use Cases, прототипы и так далее. Требования «немного» изменились и необходимо менять проекты. На внесение изменений в артефакты проектирования ушло 2 недели. Сроки летят.
Будучи уже опытными разработчиками, отлично знающими систему и успешно научившись обходить ошибки, связанные с собственным ростом, когда-то всё равно встретятся с такими клиентами.
Как правило это клиент из специфической предметной области. Заказчик в ней слабо разбирается, экспертов нет. Времени на проектирование выделили мало. В результате приходится многократно переписывать части проекта и, как следствие взаимные претензии к клиенту и от клиента.
В больших проектах существует риск управленческих ошибок.
Объемное ТЗ - большая нагрузка на менеджера проекта. Он начал терять требования и запутываться, что порождает взаимные претензии с Заказчиком. Разработчики вынуждены переписывать части веб-системы, в результате получился – "костыль на костыле". Проект дорого и сложно развивать
Эксплуатация проекта - это, как и разработка, процесс, который нужно организовывать. Но, в отличие от разработки, здесь встают совсем другие задачи и проблемы, поэтому нужны совсем другие бизнес-процессы. Соответственно, это требует создания иной команды, привлечения иных специалистов.
Варианта решения этого вопроса два: студия или сам клиент.
Мелкие клиенты с проектами, не требующими доработки, обычно уходят самостоятельно на какой-либо хостинг и доверяются его администраторам.
Вариант передачи проекта на эксплуатацию клиенту кажется предпочтительным для средних и крупных проектов. Действительно, в крупных компаниях работают, как правило, сильные системные администраторы, и кажется логичным довериться им. Но использование системных администраторов клиентов имеет свои сложности. Высококвалифицированный администратор может не знать не только Bitrix Framework, но и вообще не знать, что такое php-разработка и стек LAMP-технологий. Они могут не знать, как создать, настроить и сопровождать систему мониторинга и анализа. Это рождает свои проблемы: будут говорить, что студия написала "говнокод", мол надо переделать. Эти риски есть, и их надо учитывать.
Если проект находится на постоянной доработке студией, то возникает ещё одна проблема при эксплуатации проекта клиентом: внесение обновлений по собственно проекту, обновления Bitrix Framework и обновления серверного программного обеспечения. Необходимо выстраивать процесс выкладки изменений на сервере с тестового на боевой, процесс обновления ПО. Соответственно, встают проблемы взаимодействия с IT-службой клиента.
Если проект будет эксплуатироваться силами самой студии, то возникает другой спектр задач.
Большинство веб-студий не обладает, как правило, штатом квалифицированных системных администраторов. Имеющиеся администраторы знают понятие "хостинг", могут с правами root'а выгрузить проект на хостинг с настройками по умолчанию. На маленьких проектах это работает. Но на средних и больших проектах нужна профессиональная эксплуатация проекта: нужно разбираться в "железе", в Unix'ах. Крайне мало людей, которые могут одинаково хорошо писать код и разбираться в железе и ОС Unix.
В этом плане есть смысл максимально перенести низкоуровневое администрирование на хостера или облачного провайдера. Самое простое решение этой задачи: уйти в облако. Большая часть задач, которые нужно решать при эксплуатации проекта, уже решена облачным провайдером. Нет проблем с мониторингом сервера, нет работ по обслуживанию сервера (менять диски, мониторить температуру сервера и так далее). В дополнение предоставляются возможности, позволяющие полностью сосредоточиться на выполнении бизнес-задач: лёгкое выполнение бекапов, легко создавать репликацию, можно легко переносить машины и запускать проект на любых мощностях. В этих условиях качество услуг студии возрастает, сроки выпуска легче соблюдать и нет необходимости заниматься сложными настройками Linux'а.
Правда, возможны проблемы иного плана: заказчик российский и ему нужно обязательно, чтобы хостинг был российский. Да и заплатить за Amazon в России от юридического лица официально пока невозможно. Российские облачные провайдеры предоставляют не всегда действительно удобный сервис. В этом случае приходится решать задачу создания собственного облака и работать с ним через API.
И только в случае невозможности использования облачных технологий надо искать своего специалиста и влезать в дебри настроек веб-серверов, баз данных. В этом случае администратор должен быть в штате, а не приходящим.
При запуске проекта необходимо понимать, какой класс проекта вы запускаете: визитка, интернет-магазин, промосайт и так далее. От этого зависят требования к сайту и его размещению. То есть должно быть сформировано не только ТЗ на разработку, но и ТЗ на запуск и эксплуатацию. Такое ТЗ будет гораздо проще, чем на разработку, и должно регламентировать:
Крайне редко заказчик может выдать такие цифры, тем более не ошибившись. Задача веб-студии – помочь ему в определении этих значений.
Надо также объяснить заказчику, зачем ему нужно определиться в этих цифрах:
Отдельно нужно обговорить условия проведения технических работ на сайте. Технические работы должны проходить незаметно для клиентов:
Если проект критичен для бизнеса, если он должен быть доступен всё время, то это значит, что он должен быть доступен всегда, даже если вы сами планируете какие-то работы на нём. Как минимум, это должны быть сообщения о проведении работ с точным указанием сроков выполнения этих работ и обязательным соблюдением этих сроков.
Большая часть задач решается за счёт постоянного мониторинга.
|
Студия должна понимать сама и суметь объяснить клиентам какой вид размещения необходим конкретному проекту и почему. Неспособность объяснить это клиенту понятно, чётко и аргументировано - повод для серьёзного сомнения в компетенции студии.
Вариантов размещения немного:
Частое заблуждение: что маленький проект надо размещать на виртуальном хостинге; большой - на VPS, совсем большой - на собственном "железе". На самом деле это далеко не всегда верно. Определяющим здесь должно быть не нагрузка, а ваши требования и предпочтения.
Это - всегда "общежитие", но самый простой вариант. Хороший хостинг снимает практически всю головную боль (бэкапы, резервирование и тому подобное). Но на таком хостинге сайт всегда будет ограничен по ресурсам. Любые соседи при их повышенной активности так или иначе будут влиять на сайт. Например, на таком хостинге нет инструментов, позволяющих хорошо и аккуратно ограничить дисковую активность. В результате, если какой-то пользователь начнёт активно работать с диском, то при достаточности всех остальных ресурсов, вы всё равно ощутите "тормоза" на своём сайте.
У вас минимум возможностей по кастомизации серверной части. В случае аварии у вас нет средств по восстановлению, кроме забрасывания писем в ТП.
Экономика. Считается, что "облачное" решение - более дешёвое, чем собственный сервер. На самом деле, при прямом сравнении аналогичных конфигураций "облачное" решение будет дороже. Более того, есть платежи, которые изначально могут быть не учтены, например, стоимость трафика, которая, как правило, считается отдельно. Есть определённая сложность расчетов при оплате "по потреблению": при резком росте нагрузки (DDoS, «хабраэффект», ошибки в разработке) возможны значительные расходы (в разы больше запланированных).
Реальная экономия использования облака кроется в других местах:
Масштабируемость. Считается, что масштабируемость в облаке намного выше, чем в случае собственного сервера. С одной стороны это так, но не надо забывать про способность к масштабированию самого приложения. Например, если приложение сконфигурировано таким образом, что БД потребляет определённый объём памяти, веб-сервер потребляет определённый объём памяти, то увеличение памяти на виртуальной машине не даст прироста производительности. Требуется переконфигурирование приложения. Это возможно не для всех приложений.
Вертикальное масштабирование - это увеличение ресурсов на одной машине (или апгрейд физического сервера). С таким масштабированием может работать большинство приложений. Максимум, что может потребоваться - поменять настройки в конфигурационном файле (выделить больше памяти, например), перезапустить и продолжить работу.
Надёжность. Считается, что "облако" надёжнее. Тем не менее, VPS перегружается гораздо чаще, чем собственный сервер. При правильной конфигурации сервер может работать годами. И грамотный дата-центр всегда страхуется по электропитанию не только через UPS, но и дизельными генераторами.
Виртуальная машина может перегружаться достаточно часто, что всегда является неприятным моментом, особенно, если это касается машины, на которой работает База данных. Более того, перезапуск такой машины может оказаться процедурой не тривиальной, особенно, если бьются файлы или теряются данные.
Облачные провайдеры подвержены всё тем же рискам, что и обычные провайдеры: природные, человеческие факторы, катаклизмы.
Само по себе облако не надёжнее, но позволяет построить более надёжную инфраструктуру, так как ресурсов машин больше и можно заранее спроектировать и зарезервировать машины или сервисы и тем себя подстраховывать на случай каких-то аварий. В "облаке" эти машины можно конфигурировать произвольным образом, включать и отключать тогда, когда это необходимо не платя за ненужные мощности. В случае собственного сервера это означало бы покупку нового сервера со всеми вытекающими отсюда затратами и рисками.
Ресурсы. Распределение ресурсов в "облаке" так же не гарантировано. В лучшем случае провайдер будет обещать 95% времени выделять обещанные ресурсы, но 100% гарантии не даст никто.
Дополнительные сервисы. "Облако" предоставляет сервисы, которые, при размещении на собственных серверах пришлось бы реализовывать самостоятельно. Например, облачные хранилища для статической информации. Такие хранилища доступны (99.99%), надежны (99.999999999%), поддерживают версионность и шифрование. Благодаря ему снижается стоимость эксплуатации, оно может использоваться совместно с CDN, снижается нагрузка на web-узлы, сайту становится легко переезжать, его легко бэкапить, производится синхронизация контента между множественными web-узлами, ускоряется рендеринг страниц в браузере.
Кроме того доступны сервисы:
Несмотря на все сложности и риски "облачное" размещение выбирать можно и нужно, если учитываются все риски. Сравните плюсы и минусы размещения в "облаке".
|
Если вы решили размещать проект на собственном сервере и самим заниматься администрированием, то вам необходимо изучить Курс для хостеров. Этот курс даст основную информацию по правильной настройке сервера и базы данных.
В этой главе опишем некоторые моменты настроек, которые не всегда ясно освещаются в документации на веб-сервер и базу данных. Данная глава – «сын ошибок трудных» наших администраторов.
Настройки «по умолчанию» - это далеко не всегда хорошо. На них можно начать работать, но с ними трудно обеспечить нужную производительность и надёжность. Однако неграмотное изменение настроек по умолчанию может дать обратный результат: не ускорение, а замедление работы. Типовые ошибки конфигурации:
Их надо использовать. Один из самых быстрых - Zend Optimizer+, правда он и один из самых непрозрачных. APC на 5-10% медленнее, но надёжнее, реже падает, и работать с ним проще.
Подключая прекомпилятор, обязательно смотрите, сколько памяти ему выделяется, насколько полно она используется. У всех прекомпиляторов есть сторонние или внутренние средства диагностики и мониторинга, которые рисуют диаграммы, показывают скрипты, которые попали в кеш, как эффективно используется память. Это поможет вам использовать прекомпилятор максимально эффективно.
Возможно использование вместо Apache модуля PHP-FPM. Этот вариант обладает с одной стороны неудобством: если у вас много логики, завязанной на файл .htaccess , то эту логику надо перенести отдельно в конфиг NGINX. Это делается один раз, но потом останутся только удобства.
Пример конфига:
location ~ \.php$ { root /var/www/chroot/var/www/html; fastcgi_intercept_errors on; fastcgi_pass backend; fastcgi_index index.php; include fastcgi_params; fastcgi_param DOCUMENT_ROOT /var/www/html; fastcgi_param SCRIPT_FILENAME /var/www/html/$fastcgi_script_name; fastcgi_param SERVER_NAME $host; fastcgi_split_path_info ^(.+\.php)(.*)$; fastcgi_param PATH_INFO $fastcgi_path_info; }
Apache при каких-то ошибках не умеет сам себя рестартовать, нужны внешние обработчики. А на больших нагрузках достаточно часто могут возникать ошибки вида segmentation fault. PHP-FPM может делать рестарт внутренними средствами:
; рестартовать при ошибках emergency_restart_threshold = 1 emergency_restart_interval = 10
Первое - количество ошибок за интервал времени, после которого надо перезапустить сервер;
второе - тот самый интервал.
Можно регулировать, сколько коннектов можно держать, пока основные процессы заняты обработкой каких-то данных. Можно установить фиксированное количество процессов в системе исходя из известного нам объёма памяти и данных о том, сколько занимает в ней один процесс.
Пример конфига:
pm = static pm.max_children = 30 pm.start_servers = 30 pm.min_spare_servers = 30 pm.max_spare_servers = 30
Ведение лога медленных запросов: автоматическое включение лога медленных запросов, выполняющихся больше заданного времени с указанием адреса страницы и функции PHP, на которой началось торможение.
request_slowlog_timeout = 5 slowlog = /opt/php/var/log/www.slow_access.log
Лог медленных запросов - самый простейший инструмент увидеть проблемные места быстро. В идеале лог должен быть пустым, либо в нём должны быть запросы, о которых вам известно и которые вы допускаете быть долгими.
PHP-FPM очень хорошо подходит для ограничения прав доступа, если на одном сервере работает несколько проектов. В этом случае для каждого проекта можно выделить свой пул, выделить свой chroot. В отличие от open_basedir, он никаким образом не снижает производительность, успешно разделяя пользователей.
; не open_basedir в php.ini chroot = /var/www/chroot
Более того, можно задать разные пулы, разные конфигурации под разные сервера.
Примерный чек-лист, который должен быть выполнен при настройке веб-сервера:
Особенности работы с Базой данных заключается в том, что всегда приходится искать компромисс между быстротой и надёжностью. Либо будет быстро, но не надёжно, либо надёжно, но медленно, либо остановиться надо на каком-то среднем варианте.
Узкие места в работе с БД: CPU, RAM, Диск, то есть всё. Если чего-то не хватает, то их надо добавлять. В "облаке" используются медленные диски, однако так же как и на обычном сервере можно использовать программный RAID, что позволяет снимать все узкие места.
На платформе Amazon'а проводился эксперимент. Использовался одиночный диск, RAID10 из 4-х дисков и из 8-и дисков. При многопоточной загрузке больших файлов тесты sysbench показали что производительность RAID гораздо выше чем одиночного диска. На иллюстрации показана работа с одиночным файлом 16 Гб в режиме random read/write. При увеличении количества потоков единичный диск почти сразу достигает «потолка», производительность RAID растет.
Диагностировать проблемы можно стандартными инструментами: команды top (загрузка процессора и памяти), free (свободная память) и iostat –x 2 (что происходит с дисками, как они утилизированы, есть ли очередь из запросов и какое время обработки запросов).
Если памяти достаточно, то для временных таблиц MySQL лучше использовать именно память, создавая tmpfs, а для хранилища всех данных использовать RAID.
Можно использовать различные подсистемы хранения данных в СУБД MySQL, например Percona Server, который:
Fast Shut-Down
, Buffer Pool Pre-Load
).Другие подсистемы (MariaDB и другие) имеют свои преимущества. Выбирайте подходящую вам (или более знакомую вам) подсистему.
Сбалансированность БД, так же как и в случае с веб-сервом, должна быть максимальной. Оставить параметры по умолчанию - одна из частых ошибок. Другая ошибка - выбор значений параметров "на глазок", когда администратор не достаточно квалифицирован.
MySQL использует глобальные буферы и буферы для одного коннекта:
Максимально возможное использование памяти: глобальные буферы + буферы подключений * максимальное число коннектов. Для подсчёта вручную можно взять нужные цифры из конфигов MySQL, либо воспользоваться утилитой mysqltuner.
Основная ошибка при настройке: конфигурация настроена на потребление памяти больше, чем есть в распоряжении виртуальной машины (сервера). Это означает, что при возросшей нагрузке, когда вырастет число соединений, запросы от БД будут просто "убиты" операционной системой по превышению. Именно поэтому значения нужно подбирать исходя из ваших потребностей и возможностей виртуальной машины (сервера).
На малых и средних невысоконагруженных проектах над выбором типа можно не задумываться. MyISAM достаточно быстрая, в ряде случаев быстрее чем InnoDB, при этом работать с ней проще. Но если проект нагружен, большой или требователен к надёжности, то нужно использовать InnoDB:
Как хранить базы и таблицы: в одном едином файле ib_data или в разных файлах. По умолчанию всё храниться в едином файле. Если проект такой, что объём данных у него постоянно меняется (таблички создаются/удаляются, базы создаются/удаляются), то эти операции при хранении в одном файле выполняться будут быстро, но при этом команда DROP DATABASE не будет реально освобождать место. В итоге база дорастает до лимитов диска и нужно заново делать импорт/экспорт базы, чтобы сбросить этот файл, либо увеличивать диск, либо куда-то переносить данные.
Если все таблички хранятся в разных файлах (параметр innodb_file_per_table равен 1), то работать с ними, с точки зрения администрирования проще, DROP DATABASE реально удаляет данные, но в MySQL, к сожалению, это дорогая и тяжелая операция в силу ряда внутренних причин. Узким местом в этом случае станет процессор.
Поэтому надо оценить часто ли на проекте будут создаваться/удаляться базы данных, делаться реорганизация таблиц, выполнение ALTER TABLE и других подобных вещей, если нет, то смело используйте единый файл.
Временные таблички хранить в памяти, а размер временных таблиц, если память позволяет лучше делать больше. До этого лимита временные таблицы не будут создаваться на диске, а будут в памяти.
# временные таблицы – в памяти tmpdir = /dev/shm # размер временных таблиц в памяти max-heap-table-size = 64M tmp-table-size = 64M
Используйте параметр skip-name-resolve, который позволяет отправлять чуть меньше DNS запросов.
Многие, видя, что в Bitrix Framework join'ов много, увеличивают параметр join-buffer-size, считая что он будет эффективно использоваться. Увеличение этого параметр не всегда полезно, так как это количество памяти, которые сразу же будет выделяться на запрос, не смотря на то, используются ли в нём индексы. Соответственно, увеличивая этот буфер можно просто съесть всю память в системе без какого либо увеличения эффективности.
Аналогично с параметром query-cache-size. Полезный инструмент, когда выполняется один и тот же запрос на не изменяющемся наборе данных, то результат выполнения попадает в кеш и очень быстро отдаётся из кеша при аналогичном запросе. При превышении кешем размера более чем, по эмпирическим данным, 500 мегабайт, этот кеш работает только хуже. Так как в этом случае возникают уже внутренние блокировки в самом MySQL. Это покажет SHOW PROCESS LIST, который выведет список запросов в состоянии waiting for query cache log.
При грамотно настроенном сервере и грамотно спроектированном приложении query-cache-size можно не использовать, либо использовать с небольшими значениями: 64 или 128 Мб. Если данных столько, что они вытесняют всю память из query cache, то есть смысл перемотреть логику приложения и структуры данных уже в самих таблицах.
Основная сущность в InnoDB - это buffer pool, где хранятся все структуры данных с которыми мы работаем. Всегда нужно стараться установить такое значение параметра, чтобы все данные с которыми мы работаем (или хотя бы "горячие данные") помещались в buffer pool. При этом не забывать про сбалансированность по памяти, не допускать того, что бы произошёл уход в swap.
Если buffer pool получается у нас достаточно большим, то эффективнее с точки зрения производительности использовать такой параметр как innodb_buffer_pool_instances. Это запуск нескольких buffer pool'а в MySQL. В этом случае БД работает с ними эффективнее процентов на 5-10%.
# желательно – по объему таблиц innodb-buffer-pool-size = 4000M # если buffer pool > 1Gb innodb_buffer_pool_instances = 4 innodb_log_file_size = 512M innodb_log_buffer_size = 32M # на быстрых дисках; можно экспериментировать innodb_read_io_threads = 16 innodb_write_io_threads = 16 innodb_io_capacity = 800
Основные параметры, на которые надо обращать внимание при работе с базой. Нужно уметь пользоваться командой SHOW ENGINE INNODB STATUS. Это позволяет видеть, что у нас происходит с InnoDB и buffer pool, обращая особое внимание на секцию BUFFER POOL AND MEMORY, где Buffer pool hit rate должен стремиться к 1, то есть все запросы должны обрабатываться из buffer pool, а не браться с диска.
Так же значение имеет секция TRANSACTIONS, где отображаются транзакции, которые выполняются долго, или висят, или откатываются, всё это показывает что с ними что-то не так.
Нужно уметь пользоваться командой SHOW STATUS \G, которая показывает что происходит с query cache, с временными таблицами.
Нужно уметь пользоваться командой SHOW PROCESSLIST, которая показывает текущие запросы.
Нужно включать логирование медленных запросов, что бы не думать и не гадать что у нас происходит в системе, какая страница тормозит и на чём именно она тормозит. Его легко найти (команда slow.log) и легко изучать (EXPLAIN). Однако из EXPLAIN'а не всегда понятно что же медленного происходит в базе.
Если долгих запросов нет, но всё равно не понятно как работает БД: быстро, не быстро. Тут прежде всего надо определить критерии оценки.
После нахождения медленного запроса включается mysql> SET PROFILING=1;, выполняется запрос. Сохраняете профиль этого запроса и вы смотрите таймингами, что у вас в этом запросе происходило и что тормозило: передача по сети, оптимизация, проблемы на уровне SQL.
mysql> SHOW PROFILES; +----------+------------+---------------------------------+ | Query_ID | Duration | Query | +----------+------------+---------------------------------+ | 1 | 0.09104400 | SELECT COUNT(*) FROM mysql.user | +----------+------------+---------------------------------+ 1 row in set (0.00 sec)
В тестовом запросе самым долгим было Opening tables, дольше всего открывали таблицу с диска.
Профилирование - очень полезный инструмент, позволяющий понять что в базе может тормозить.
Если с базой всё хорошо (долгих одиночных запросов нет), то можно использовать mysql> SELECT * FROM INFORMATION_SCHEMA.QUERY_RESPONSE_TIME;
(в стандартном MySQL такого нет, есть в расширениях, например в Percona Server). Это гистограмма времени запросов, которые выполняются с шагом в 1 порядок начиная с 0.000001 сек. Определите для себя критерии оценки. Например: соотношение быстрых и медленных запросов. Надо при этом определить что такое "быстрый" и что такое "медленный" запрос. Пусть быстрый будет тот, что выполняется быстрее, чем 0,01 секунды, а медленный - всё остальное. Если это соотношение - 2-3 %, то система функционирует нормально.
На больших проектах, где идёт большая работа с базой, где много обращений, такой инструмент очень удобен и показателен:
mysql> SELECT * FROM INFORMATION_SCHEMA.QUERY_RESPONSE_TIME; +----------------+-------+----------------+ | time | count | total | +----------------+-------+----------------+ | 0.000001 | 0 | 0.000000 | | 0.000010 | 2011 | 0.007438 | | 0.000100 | 12706 | 0.513395 | | 0.001000 | 4624 | 1.636106 | | 0.010000 | 2994 | 12.395174 | | 0.100000 | 200 | 6.225339 | | 1.000000 | 33 | 5.480764 | | 10.000000 | 1 | 2.374067 | | 100.000000 | 0 | 0.000000 | | 1000.000000 | 0 | 0.000000 | | 10000.000000 | 0 | 0.000000 | | 100000.000000 | 0 | 0.000000 | | 1000000.000000 | 0 | 0.000000 | | TOO LONG | 0 | TOO LONG | +----------------+-------+----------------+ 14 rows in set (0.00 sec)
Если позволяет логика приложения можно разделить одну установку mysqld на несколько и разнести данные по ним. Этот способ имеет смысл только при достаточном количестве ресурсов на физическом сервере (многоядерные системы, гигабайты RAM). Несколько установок лучше используют ресурсы, это проверено тестами.
Два теста с помощью sysbench: первым тестом грузим в 100 потоков одну установку; вторым тестом грузим параллельно 50 потоков на один инстанс, 50 потоков на второй. Во втором тесте в среднем получаем на 15% больше запросов в секунду.
Минус такого метода - некоторая сложность администрирования.
Описанное разделение не всегда можно использовать. Оно возможно, если на одном сервере работает несколько проектов и возможно логическое разделение по разным базам.
Один из полезных параметров - sync_binlog (особенно, если используется репликация). Это запись всех изменений базы, записанные в бинарный лог. Если важно, что бы репликация была надёжной и в случае падения какой-то машины у нас никакие данные не потерялись, то этот параметр должен быть выставлен в 1 (то есть запись ведётся постоянно). В обычном окружении такой значение кардинально снизит производительность базы. Но если sync_binlog вынести на отдельный диск, то его можно смело включать, производительность базы падать не будет.
# самый надежный sync_binlog = 1 # hint: писать на отдельный раздел log-bin = /mnt/binlogs/mysql/mysqld-bin
Формат binlog-format чаще используется mixed. MIXED фактически является псевдоформатом. Этот формат по умолчанию работает как STATEMENT. Но если в SQL запросе встречается функция, которая корректно логгируется только в ROW, то формат временно (в это запросе) переводится в него и после выполнения возвращается назад. Такое решение позволяет использовать преимущества одновременно обоих форматов STATEMENT и ROW.
binlog-format = mixed
Сброс логов buffer_pool'а - innodb-flush-log-at-trx-commit можно выставить в значении 2. В этом случае логи будут сбрасываться достаточно часто, но не на каждый коммит.
# сбрасываем log buffer на каждый commit, flush на диск – # раз в секунду innodb-flush-log-at-trx-commit = 2
Параметры sync_master_info, sync_relay_log и sync_relay_log_info существенно влияют на производительность. Для надёжности их надо выставлять в значении 1, но производительность упадёт. Можно установить значение 0, но надо иметь в этом случае опыт восстановления данных, возможности диагностики проблем другими средствами и всё равно понимать, что вы идёте на определённый риск.
Параметр max-connect-errors влияет на надёжность косвенным образом. По умолчанию у него стоит не высокое значение (10). Это параметр означает, что если с определённого хоста пришло 10 коннектов с ошибками, то этот хост блокируется до перезагрузки базы, либо ручного выполнения команды flush-hosts. Другим и словами, если возникла какая-то сетевая проблема между веб-сервером и базой (проблема частичная, например, коннект проходит, но не проходит авторизация, пакеты или ещё что-то), то после 10 попыток обратиться к базе хост блокируется. То есть проект перестаёт работать. Если на проекте нет мониторинга, то возникшая ночью такая проблема до утра сделает сайт неработающим.
Это глупая и мелкая ошибка и можно смело ставить это значение больше, которое позволит обойти такие мелкие сетевые проблемы, и проект будет работать.
На производительность БД влияют:
Мониторинг "узких" мест:
Рекомендации:
Есть такой миф из XP/TDD, что можно полностью покрыть веб-приложение модульными и функциональными тестами, написать большое количество Selenium-тестов, кликающих по системе снаружи и без страха вносить в систему доработки и выкладывать на рабочий сервер через процесс непрерывной интеграции.
Вот несколько причин ошибочности данного суждения:
Тем не менее, тесты нужны. Для ключевых вещей, для системных модулей тесты нужно писать и поддерживать в актуальном состоянии. Для остальных задач можно написать тест-планы и тестировать с помощью тестировщиков все страницы веб-решения перед каждым обновлением, тщательно наблюдая за появлением ошибок в логах и получая обратную связь от пользователей.
Таким образом, для поддержания работоспособности проекта в сети, необходимо использовать и unit-тесты, и функциональные тесты, и Selenium-тесты, и вариант процесса постоянной интеграции с системой контроля версий и труд тестировщиков. При этом вы понимаете, что ошибки обязательно будут просачиваться на боевые сервера и их нужно научиться быстро находить и устранять.
Нагрузочное тестирование - обязательный этап сдачи проекта, оно является важнейшей процедурой подготовки крупного проекта к открытию, а также позволяет определить предел работоспособности созданного проекта именно на выбранном оборудовании.
Зачастую, простые корректировки конфигурации могут ускорить проект в 5-10 раз и сделать его устойчивым к стрессовым нагрузкам, поэтому:
Подходящими инструментами для нагрузочного тестирования являются:
В данной главе содержится информация, как на боевых серверах во время нагрузки эффективно отлавливать узкие места в производительности больших веб-приложений на PHP, а также искать и устранять «нестандартные» ошибки». Эта информация пригодится системным администраторам и разработчикам, обслуживающим сложные веб-проекты, а также менеджерам, которые хотят выстроить эффективный и быстрый процесс поиска и устранения узких мест и ошибок проектов на PHP.
Вообще, существует огромное количество инструментов для нагрузочного тестирования, как opensource, так и коммерческих. Остановимся на наиболее часто используемых и расскажем об их основных возможностях.
Бесплатный
Официальный сайтСамый часто используемый, т.к входит в состав Apache.
ab [options] [http[s]://]hostname[:port]/path
где основные необходимые options
:
-c
concurrency - количество одновременных запросов к серверу (по умолчанию 1); -n
requests - общее количество запросов (по умолчанию 1).
В результате команды получаем такой отчет:
Concurrency Level: 10 Time taken for tests: 0.984 seconds Complete requests: 100 Failed requests: 0 Write errors: 0 Total transferred: 3725507 bytes HTML transferred: 3664100 bytes Requests per second: 101.60 [#/sec] (mean) Time per request: 98.424 [ms] (mean) Time per request: 9.842 [ms] (mean, across all concurrent requests) Transfer rate: 3696.43 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 1 2 3.6 1 23 Processing: 63 94 21.5 90 173 Waiting: 57 89 21.6 84 166 Total: 64 96 21.5 92 174
Плюсы:
Минусы:
Бесплатный
Официальный сайт.Немного сложнее ab и необходимые задачи выполняет гораздо лучше.
В файле-сценарии задаются URL-ы и запросы тестирования. Если сценарий большой по объему, то можно вынести все запросы в отдельный файл и в команде указать этот файл при тестировании:
# cat urls.txt # URLS file for siege # -- http://www.bitrix24.ru/ http://www.bitrix24.ru/support/forum/forum1/topic3469/?PAGEN_1=2 http://www.bitrix24.ru/register/reg.php POST domain=test&login=login http://www.bitrix24.ru/search/ POST < /home/www/siege_post_data # siege -f urls.txt -c 10 -r 10 -d 3
В команде указывается количество пользователей -с
, количество повторений -r
и задержку между хитами -d
.
Результат можно выводить в log-файл или сразу в консоль в режиме реального времени:
HTTP/1.1 200 0.44 secs: 12090 bytes ==> GET / HTTP/1.1 200 0.85 secs: 29316 bytes ==> GET /support/forum/forum1/ HTTP/1.1 200 0.85 secs: 29635 bytes ==> GET /support/forum/forum1/ HTTP/1.1 200 0.34 secs: 12087 bytes ==> GET / [...] done. Transactions: 100 hits Availability: 100.00 % Elapsed time: 12.66 secs Data transferred: 1.99 MB Response time: 0.64 secs Transaction rate: 7.90 trans/sec Throughput: 0.16 MB/sec Concurrency: 5.02 Successful transactions: 100 Failed transactions: 0 Longest transaction: 1.06 Shortest transaction: 0.31
Также можно взять из access-log веб-сервера URL-ы, по которым ходили реальные пользователи и эмулировать нагрузку реальных пользователей.
Плюсы:
Минусы:
Бесплатный
Официальный сайтОсновные возможности:
Результаты выводятся в графическом виде:
Плюсы:
Минусы:
Бесплатный
Официальный сайтОсновные возможности:
С помощью собственных скриптов, которые обрабатывают логи работы, можно выводить различные отчеты по тестированию:
Плюсы:
Минусы:
Платный
Официальный сайтОсновные возможности:
Отчет можно вывести как таблицей, так и графиком:
Плюсы:
Минусы:
Входит в лицензию продукта
Официальный сайтОсновные возможности:
Отчет о тестировании выводится в виде таблицы и графиков:
Итак, в результате тестирования были получены логи и графики нагрузки. А дальше, используя эти логи и графики, нужно понять, где узкие места, все ли устраивает и какие существуют способы повышения эффективности работы проекта.
Клиент может не знать всех терминов и инструментов тестирования. Нужно объяснить клиенту результаты тестирования понятным ему языком: «было столько-то, стало столько-то» и «хорошо стало или плохо», а также предложить ему решение проблемы, если она есть.
Нагрузили систему, и через некоторое время она перестала отвечать. Причина – безнадежно «заDDOSили» инструментами тестирования.
Чтобы это не произошло, нужно создавать разумную, похожую на боевую нагрузку:
В итоге пришли реальные пользователи на другие страницы сайта и … система перестала отвечать.
Поэтому при нагрузочном тестировании:
Здесь можно выявить различные ошибки. Например, верстальщик забыл добавить css-файл, NGINX отдает страницу с 404-й ошибкой через Apache средствами PHP (ошибка администратора). В итоге все слоты Apache заняты обработкой 404-й ошибки, попутно еще загружая сам сайт - получился типичный DDOS сайта. Решение этой проблемы - отдавать легкую статичную страницу с 404-й ошибкой сразу средствами NGINX.
Одним из основных инструментов анализа тестирования являются логи:
Также важная составляющая тестирования. Графики позволяют наглядно показать как вела себя система. Тестирование желательно проводить не менее суток.
Используя инструменты munin, cacti, pnp4nagios, просматриваем графики:Полезно написать несколько скриптов обработки логов:
Инструментов для этого достаточно, например: awk, bash, php, а также можно воспользоваться готовыми скриптами из блога «Битрикс» на Хабрахабре или связаться с нами по e-mail: serbul@1c-bitrix.ru.
Пример скрипта обработки лога на awk:cat /var/log/www.access.log | awk -F# '{ zone = int($10/100)*100; hits[zone]++; } \ END { for (z in hits) {printf("%8s ms: %8s,%6.2f% ",z,hits[z],hits[z]/total*100);{s="";a=0;while(a++<int(hits[z]/total*100)) s=s"*";print s} } }' \ total="$TOTAL" - | sort -n
Для правильного проведения теста и интерпретации результатов нужно знать, как работает система.
Очень важно понять, как проходит хит клиента к PHP:
Рассмотрим эти шаги подробнее:
Статика отдается, как правило, очень быстро. NGINX проксирует запрос (к upstream), ждет и возвращает статусы:
200 ОК 500 Internal Server Error 502 Bad Gateway 503 Service Unavailable 504 Gateway Timeout
Нужно понять, какие статусы ошибок и когда возвращаются:
Если программист ошибся в коде - это обычно находится легко. Но бывают ошибки, возникающие из-за превышения потребления памяти скриптом (memory-limit) - желательно не превышать потребление памяти больше 512Мб. Такие системные скрипты могут не только сами завершаться, но и вытеснять, например, MySQL или Apache в swap, а если сервисы начнут уходить в swap, то происходит общая деградация системы и уменьшение производительности в целом.
Для отслеживания используются инструменты - Apache mod_status, php-fpm pm.status_path, ps/top.
Apache не может принять запрос от NGINX, он занят запросами в БД, запрос уходит в swap.
Инструменты для отслеживания занятых очередей: apache mod_status, netstat, atop/sar.
Основной причиной часто являются задержки в БД или в коде разработчик обращается на внешний сокет, который недоступен, и из-за этого скапливаются очереди между NGINX и Apache/php-fpm. Одним из решений может являться установка таймаута на работу с сокетом.
Способы отлавливания ошибок обращения с БД:
define("ERROR_EMAIL", "admin@site.ru, support@site.ru");
На этом этапе идет уже чистая отладка PHP:
Альтернативный логу медленных запросов php-fpm инструмент - Pinba, который рисует графики выполнения страницы или ее частей php.
На данном этапе:
После запуска нагрузки важно проверить, чтобы система работала в штатном режиме. Не перегружена ли текущая веб-система:
apachetop –f <лог nginx>, <лог apache>
- скорость запросов, есть ли проброс статики с Nginx на Apache - здесь в логе можно увидеть и поправить ошибки, если они есть. show processlist
– должно быть пусто или почти пусто.Если все в норме, то оставляем тестироваться проект на сутки. Если есть какие-то ошибки, то не нужно тратить время на тест, а нужно выявлять ошибки и после устранения начать тест заново.
Считаем процент ошибок в логах NGINX:
Нужно выяснить причины каждой ошибки. Очень важно исключить 50х ошибки.
Ошибки типа Segmentation fault (SIGSEGV) исправляются так:
-g
);Связываем с логом NGINX.
Связываем с логом NGINX.
Опираясь на технические результаты тестирования, важно правильно и понятно преподнести выводы теста клиенту.
Например, на основе данных построения страницы можно показать еще такой расчет:
90% хитов уложились в 2 сек, а 10% хитов - выполнялись более 2 сек при среднем времени всех хитов 0.3-0.4.
99% хитов уложились в 5 сек, 1% выполнялись более 5 сек и т.д.
В данной главе рассмотрим на примере, как готовить нагрузку в Jmeter-е и нагрузить систему до 10 миллионов и более хитов.
Независимо от типа проекта, всегда существует человеческий фактор и вероятность ошибок. Нагрузочное тестирование позволяет выявить и устранить потенциальные проблемы, что существенно снижает риски в будущем.
Это важный момент. Например, запустили нагрузочное тестирование, оно прошло успешно, результатом остались довольны. Но не учли тот факт, что в проекте есть каталог с базой данных на 1 млн. объектов. Запустили проект, пошла реальная нагрузка, и системе понадобился экспорт/импорт каталога - и в итоге система может стать неустойчивой или вообще перестанет отвечать на запросы пользователей. Поэтому во время нагрузочного тестирования нужно учитывать подобные критические сценарии и включать их в тест.
Важно различать пик посещаемости и общую посещаемость в сутки - пик будет всегда больше.
Рассмотрим пример графика посещаемости за неделю:
Получаем по логам сервера за сутки – 1,5 млн. хитов к динамическим данным. Среднее количество запросов в секунду высчитывается как 1,5 млн./86400 = 17 запросов/сек, но на графике в пике видим 34 запроса/сек.
Поэтому всегда нужно готовиться к пикам выше среднего в 2-3 раза, а точнее - смотреть по графикам и логам сервера, т.е брать для нагрузочного тестирования количество запросов в секунду, исходя из пикового значения нагрузки проекта, а не из среднего.
Важно знать, не перегружена ли текущая веб-система при нагрузочном тестировании. При тестировании, например, нужно следить, не заполнены ли все процессы Apache (mod_status), не допускать скопления очередей на NGINX, запросы к базе данных не должны «висеть» (show processlist
) - все это может привести к недостоверным результатам тестирования.
Например, эта перегруженная веб-система может «отбрасывать» запросы:
Примерное количество хитов, которое создает пользователь в секунду - 10. Определить примерную величину можно также по веб-аналитике текущего сайта или с помощью данных внешних источников (например, Google Analytics).
Если тестируется портал компании, то общее количество хитов в сутки считается, исходя из количества сотрудников и продолжительности рабочего дня. Можно также добавить некоторое количество хитов «про запас».
Цепочки - это, по сути, поведение клиентов на сайте, куда будут ходить пользователи/сотрудники. Варианты цепочек берутся из аналитики или придумываются самим (на основе типичного поведения пользователя на сайте данной тематики). Глубокое исследование не требуется, только лишь основное поведение.
Пример цепочки 1:
1.1 Главная 1.2 Список новостей 1.3 Детальная новости 1.4 Поиск по сайту
Пример цепочки 2:
2.1 Детальная каталога 2.2 Авторизация 2.3 Корзина 2.4 Мастер заказа 2.5 Личный кабинет
Далее нужно распределить потоки виртуальных пользователей по цепочкам (опять же не нужно глубокое исследование, только лишь основные крупные доли).
К примеру, получилось такое распределение путей следования посетителя по сайту:
70% - Главная 20% - Каталог – Корзина – Мастер заказа 5% - Результаты поиска – Описание товара – Корзина 5% - Новости – Новость детально
Во время авторизации работают БД, сети и т.д, таким образом, создается нагрузка, причем приближенная к боевой.
Создание уникальных пользователей для тестирования также важно, и это также позволяет приблизиться к реальной работе проекта. Все эти данные можно хранить в CSV-файлах и подключать к Jmeter при тестировании. Количество создаваемых пользователей - примерно в 2-3 раза больше прогнозируемого количества посетителей.
Для сценария нагрузочного теста понадобится количество потоков в каждой цепочке. Для этого предварительно нужно рассчитать, сколько хитов в данной цепочке клиент сделает за сутки.
Пример распределения путей следования посетителя по сайту:
1.1 Главная Пауза ~ 30 сек + случайная пауза 1.2 Список новостей Пауза ~ 30 сек + случайная пауза 1.3 Детальная новости Пауза ~ 30 сек + случайная пауза 1.4 Поиск по сайту Пауза ~ 30 сек + случайная пауза -> в начало
В итоге получается, что один клиент, ходящий по цепочке, создаст нагрузку примерно 3000 хитов\сутки (86400/30).
Зная пропорции распределения путей следования посетителя по сайту (см. ранее) и имея для тестирования общее количество хитов в сутки (из технического задания), подбирается число нагрузочных потоков (виртуальных пользователей) в каждой цепочке.
Пример расчета:
Пусть за сутки нужно протестировать проект в 5 млн. хитов. В цепочке производится 3000 хитов в сутки одним пользователем.
Тогда получается примерное количество потоков в каждой цепочке
70% - Главная: 3500000 хитов \ 3000 = 1167 потоков. 20% - Каталог – Корзина – Мастер заказа: 1000000 хитов \ 3000 = 333 потока. 5% - Результаты поиска – Описание товара – Корзина: 250000 хитов \ 3000 = 84 потока. 5% - Новости – Новость детально: 250000 хитов \ 3000 = 84 потока.
Если нужно в тесте увеличить общее количество хитов в сутки (к примеру до 10 млн. в сутки), то также, зная пропорции, можно легко подобрать число нагрузочных потоков (виртуальных пользователей) в каждой цепочке.
Итак, зная составляющие нагрузочного тестирования, приступаем к созданию плана нагрузочного тестирования в Jmeter.
Тестовый пакет Jmeter представляет собой модульную систему, он очень удобен и функционален. Документацию по работе с программой можно прочитать на сайте разработчика. Рассмотрим наиболее часто используемые элементы тест-плана Jmeter
Использование глобальных переменных очень удобно - это позволяет управлять настройками всех цепочек централизованно.
Число потоков при создании цепочки как раз задается глобальной переменной CHAIN1_USERS
, а общее время теста TOTAL_TIME
.
Настройка запроса в цепочке проста - задается название цепочки, путь для запроса, метод GET\POST, редиректы, KeepALive и другие необходимые данные.
Для эмуляции реальной работы пользователя используется время паузы между хитами Constant Delay Offset и время случайной паузы (небольшое отклонение от постоянной паузы) Random Delay Maximum.
Случайные страницы вносятся в цепочку для того, чтобы немного отклониться от предполагаемого пути следования посетителя по сайту. Для этого устнавливается в цепочке контрол Random Controller и в нем уже создаются варианты случайных запросов.
Cookies играют важную роль, практически, на любом сайте. Они участвуют в авторизации и их использование необходимо включать в тест. Также желательно активировать опцию Clear cookies each iteration, что позволит после прохождения потока цепочки сбрасывать авторизацию.
Очень удобно хранить логины/пароли, поисковые фразы, ID элементов каталога в CSV-файлах, которые создаются заранее и подключаются в настройках цепочки выбором соответствующего пункта меню.
Далее задается имя файла с данными и название переменной.
Которая потом указываются при создании запроса цепочки.
Важной момент: желательно выставить опцию Recycle on EOF в значение True для того, чтобы при окончании значений переменных в файле начиналось чтение его заново и т.д.
Например, для задания авторизации при тестировании проектов на Bitrix Framework нужно использовать переменные:
USER_LOGIN = $(login)
;USER_PASSWORD = $(password)
;AUTH_FORM = Y
.Таким образом, с помощью Jmeter можно создавать как простые, так и сложные сценарии нагрузочного тестирования систем.
[ds]Jmeter[/ds][di]Это инструмент для проведения нагрузочного тестирования, разрабатываемый Apache Software Foundation.
Интересна возможность создания большого количества запросов с помощью нескольких компьютеров при управлении этим процессом с одного из них. Архитектура, поддерживающая плагины сторонних разработчиков, позволяет дополнять инструмент новыми функциями.
В программе реализованы механизмы авторизации виртуальных пользователей, поддерживаются пользовательские сеансы. Организовано логирование результатов теста и разнообразная визуализация результатов в виде диаграмм, таблиц и т. п.
Подробнее...[/di] умеет создавать огромную нагрузку в десятки миллионов хитов в сутки. Так как программа написана на Java, то узким местом для создания большой нагрузки на проект может быть как железо нагрузочной машины, так и пропускающая способность сетей передачи данных.
Поэтому для создания огромной нагрузки в десятки миллионов хитов можно использовать распределённую нагрузку, кластер из нескольких машин:
JMETER_HOME/bin/jmeter-server
т.е пробрасываются рабочие порты Jmeter, который запущен на машинах кластера, на порты управляющей машины (например 60001, 60002, 60003 и т.д.).
/bin/jmeter.properties
указываются «виртуальные» серверы кластера, которые получились после проброса портов с машин кластера, участвующих в распределенном тестировании:
remote_hosts=127.0.0.1:60001, 127.0.0.1:60002, 127.0.0.1:60003
Также для запуска не через GUI-интерфейс Jmeter можно воспользоваться консольной командой:
jmeter -n -t script.jmx -R server1,server2...где:
Разрабатывая проект, нужно сразу думать о том, как мониторить его состояние в период эксплуатации. Перезапуск Apache и MySQL помогает почти всегда, но не является "правильным" выходом, так как не диагностируется и не исправляется проблема. А о проблемах, при отсутствии системы мониторинга, узнаётся либо от клиента, либо от посетителей, либо от собственного руководства, что совсем не является признаком качественной работы студии.
Цель этой главы - понять, какой бизнес-процесс нужно выстроить, чтобы взять процесс эксплуатации под контроль. Высоконагруженные проекты имеют тенденцию со временем деградировать, подчиняясь закону энтропии. Тем не менее, проект можно поддерживать, делать его простым, прозрачным и удобным. Он будет развиваться, приносить и пользу и радость.
Система запускается "как есть" и по инерции может поработать год-два без вмешательства. Но невыполнение (не полное выполнение) вышеописанных требований ведёт к деградации системы.
Деградация может быть остановлена, а может быть ускорена. К сожалению, достаточно часто происходит второе в силу:
Создать идеальную систему не сложно. Сложно в условиях постоянного давления со стороны бизнеса её поддерживать в должном состоянии. (А давление заключается в постоянном внесении изменений в проект.) Но к этому надо стремиться, этого надо добиваться:
Если нет своего администратора, то придётся обращаться либо к услугам аутсорсинга, либо использовать внешние системы типа Яндекс.Метрики, которые смогут вам сообщить хотя бы о недоступности вашего сервиса.
Для стабильной эксплуатации системы нужно:
Несколько принципов организации системы мониторинга:
Нужно научиться оценивать как проект работает. Есть много внешних инструментов мониторинга работы проекта в режиме реального времени. Они могут быть очень точными, они могут собирать много информации, но они все работают "снаружи" проекта и показывают симптомы, а не причины.
Полноценные данные с пониманием всех происходящих процессов может дать только само веб-приложение. Для этого нужно научиться мониторить процессы средствами ядра продукта и платформы, которая используется в приложении.
Собственный сервер нуждается в постоянном контроле за его комплектующими. Можно разделить контроль по видам ресурсов: Критический и важный. Критический - это те, сбой которых гарантировано приведёт к отказу бизнес-приложения. Важные ресурсы - это те, наблюдение за которыми позволит предсказать возникновение проблем.
Критические ресурсы:
Важные ресурсы:
Чем мониторить?
Мониторинг самой ОС выполняется по следующим параметрам:
Полезна представляемая vmstat'ом информация о памяти, которая в Unix системах устроена не просто. В этих системах просто нет свободной памяти, она вся занимается под буфер и кеш. То есть не стоит опасаться малых значений в колонке free. Но должны беспокоить малые значения в колонках buff и cache - это означает, что система не умещается в памяти., нужны изменения в конфигурации.
Полезно контролировать колонку st: наличие в ней значений, отличных от 0 говорит о том, что другая виртуальная машина потребляет время вашего процессора. Ваша виртуальная машина в этом случае будет тормозить. К сожалению, виртуализация может иногда "красть" ваши процессорные ресурсы. Это актуально для облачного размещения.
При мониторинге серверного ПО обратите внимание на:
В мониторинге MySQL надо тестировать:
Если вы видите, что запросов больше 0,1 секунды много, то, значит, база тормозит. Для каждого проекта "правильный" график будет свой, в зависимости от задач проекта. В Munin можно создать свой плагин, который будет по базам данным выводить табличку запросов, например, запросов длительностью более 0,1 секунды:
При мониторинге сети очень удобна команда netstat. Она позволяет видеть что у вас в сети: кто висит на memcache, кто на php, кто на MySQL.
Можно также использовать утилиты:
Для каждого бизнес-скрипта, который обязательно должен выполниться (и, если не выполниться, то сообщить об ошибке) нужно вести лог работы скрипта. Nagios при этом должен следить за обновлением этого лога с заданным интервалом. Если файл не обновился, значит скрипт не отработал.
Кроме лога работы можно вести лог ошибок. В Unix есть поток ошибок, который направляется в другой файл. Этот лог должен быть, в идеале, пустой. Однако в нём могут появляться самые непрогнозируемые данные: например, сторонняя программа убила процесс вашего приложения.
Необходимо так же мониторить:
Делается это с помощью Pinba. Если число ошибок превышает указанные значения в этих параметрах, то необходимо уведомлять администратора по SMS.
Для данных тестов полезно написать плагин для Munin, который будет формировать графики по этим параметрам. Например, среднее время выполнения скрипта:
Или, расход скриптами памяти:
Гистограммы распределения времени хитов, памяти, кодам ответа по времени строятся из логов (awk-скрипт) или Pinba. Смысл этих графиков - визуальное представление состояния вашего приложения: тормозит оно или нет. Например, число запросов с продолжительностью с шагом в 100 ms:
Помимо мониторинга рекомендуется использовать аналитику. Аналитика - это тенденции развития. По её данным можно прогнозировать развитие/деградацию системы. Самый простой пример: по тенденции роста данных вы можете прогнозировать срок, когда переполнится место на дисках и подготовиться к этой ситуации заранее, не доводя систему до остановки.
Для аналитики можно использовать собственные решения, но лучше всё же стандартные. Это лучше делать по тем же причинам, что и для систем мониторинга. Munin, например, кроме стандартных вещей (количество запросов, нагрузка на CPU), позволяет мониторить другие параметры, которые могут быть критичны: скорость генерации страниц, соотношение быстрых и медленных запросов к базе данных. Munin позволят выводить всё это в графики:
Далее рассмотрим, что необходимо анализировать.
В дисковой подсистеме один из ключевых параметров - Чтение и запись в секунду байт. 10 МБ/cек - это нормальная нагрузка, 100 МБ/cек - это уже повод для беспокойства. Графики за день и за неделю:
Гистограмма Утилизация дисков поможет увидеть когда происходит повышенная утилизация диска и попытаться разобраться в причинах.
Можно использовать стандартные графики, не создавая собственных плагинов. Сеть - это обычный TCP\IP. График нормальной, текущей ситуации:
Если что-то пошло не так, то появляются другие кривые, syn_sent, например.
График трафика позволяет также увидеть тенденции изменений. Если, например, трафик обращений к БД за год растёт, то может разработчики чего-то недооптимизировали.
На стандартном графике использования памяти можно увидеть тот самый swap, уход в который не желателен для системы, красный цвет:
На иллюстрации: запущенное приложение стало потреблять память - зелёное на графике - и выдавало всю память из операционной в swap, чем убило несколько приложений (белое на графике). Какой-то скрипт убил несколько MySQL серверов с репликацией. На графике видно почему это произошло.
График так же необходим для контроля за расходом памяти. Зелёную зону на графике желательно держать в районе 70%, не более. Операционой системе нужно оставлять какое-то место для буфера и файлов. Регулировать этот показатель можно с помощью, например, Apache MaxClients или MySQL buffers.
Своппинга надо избегать. На графике чётко видно, что система в какой-то момент ушла в swap, администратор что-то сделал не так. Либо, что-то случилось с системой. А что случилось можно понять по графику использования памяти.
Графики нагрузки показывают запущенные процессы (зелёное) и "зависшие" либо в сети, либо на дисковых операциях ввода-вывода (синие).
Чем ниже зелёный график, тем меньше система нагружена. С параметром Load average сложно сказать о конкретных параметрах. 20 или 40? Это сильно зависит от числа процессоров. (20 на 8-и ядерной системе - это нормально, по опыту.) Какое значение параметра показательно для вас вы почувствуете сами по поведению вашей системы. Когда ей "плохо" - она "тупит", значит вы не должны допускать превышения того значения, когда это происходит.
Если эти параметры постоянно "скачут", то значит есть ошибки в настройке софта, слишком много процессов запущено. Процессы запускают потоки. Проще начинать оценку с числа потоков, а потом переходить к числу процессов. Если не поставлена верхняя граница потоков для Apache или MySQL, то со временем система "зависает". Рекомендуется ставить в Nagios тесты, которые ограничивают это число.
Memcached мониторится бесплатным плагином. Зелёное - место в memcached занятое приложением, оранжевое - число элементов (ключей кеша) которое хранится. Видно сколько живёт кеш и какой размер он занимает.
График команд показывает число запросов memcached в секунду - интенсивность использования. Можно судить насколько memcached востребован.
Важно смотреть какие типы запросов идут в Базу данных. Это позволяет выбирать настройки MySQL. Например, если много select, то можно попытаться использовать Query Cache, а если много update, то его нет смысла использовать.
Потоки в MySQL. Явный всплеск - это перегрузка для системы. Надо следить за такими моментами, уметь их анализировать и недопускать: конфигурировать число серверов или PHP-FM.
Query Cache вызывает много споров когда он нужен, а когда - нет. Чтобы понять нужен он или нет вашему приложению, лучше всего настроить мониторинг и анализировать. Сколько запросов идёт из кеша, а сколько в кеш. И по соотношению этих запросов можно принять решение нужен этот вид кеша или нет. Без аналитики сделать такой вывод сложно.
График заполнения Query Cache данными показывает насколько используется память.
График медленных запросов показывает когда возникли эти запросы. Это позволяет локализовать проблему и решить её.
График сортировки позволяет судить о качестве разработки. Если разработка не качественная, то возникают в большом количестве scan, merge passes, range. Если работа идёт по индексам, то всего этого не будет в таком объеме.
Теория Predictive maintenance достаточно сложна, требует для понимания серьёзной теоретической математической подготовки. Сложна ещё и потому, что с этими сферами математики большинство не сталкивается в обычной жизни. Одновременно с этим Predictive maintenance - это просто, не смотря на обилие кажущейся сложной информации. Это тот случай, когда практика позволяет компенсировать недостаток знаний.
Покрыть веб-проект сеткой тестов недостаточно для предотвращения аварий, выявления атак и превентивного реагирования на изменение условий эксплуатации. Новый подход - анализ метрик и предотвращение аварий (выявление угроз) с помощью алгоритмов машинного обучения. Это позволит не только сократить расходы на обеспечение отказоустойчивости, но и реагировать раньше возникновения проблем.
Считается, что с помощью машинного обучения можно работать эффективнее, чем по классическим алгоритмам. Но не везде и не всегда. Машинное обучение лучше в случаях очень сложных задач, решение которых классическими методами либо крайне сложно, затратно, либо невозможно вообще.
Машинное обучение имеет достаточно высокий уровень вхождения: надо достаточно хорошо понимать много дисциплин: высшую математику, матстатистику, теорию вероятности и так далее.
При этом описание информации зачастую крайне научное, для специалистов по математике и совершенно не приспособлено для ответов на простые вопросы. Например, стоит вопрос классификации: как понять спам - не спам? Прямого и простого описания алгоритма модели для такой классификации не найти.
Тем не менее, несмотря на сложность вхождения, решение есть. Для этого достаточно понимать не так много, это:
Анализ и интерпретацию результатов Predictive maintenance желательно поручить выделенному сотруднику или роли. Обязанности аналитика:
Далее рассмотрим основные шаги по внедрению.
Изначально сложно сказать какая информация будет вам нужна, какая нет. Поэтому рекомендуем собирать всё.
Где собирать информацию? Хиты на сайте - логи, события, привязанные к cookie (человеку) через "счетчик". Использовать логи работы, мультиканальность, если надо понять как люди движутся между вашими системами.
Допустим, собираем данные о покупателе магазина, всё что есть: пол, возраст, статус, пути на сайте, обращения в техподдержку, счетчики (клики, звонки, обращения), средние данные по счётчикам за квартал, месяц, день. То есть нужно собрать информацию о том, что в динамике делает пользователь.
В контексте системного администрирования, эксплуатации, нужно собирать метрики системы, падение которой будет предсказываться:
Какие метрики критически необходимы? Гарантированной ответ дать крайне сложно, если вообще возможно. Как правило собирается всё, чтобы потом из имеющегося набора получить что-то нужное. Однако последние исследования в области нейронных сетей позволяют решить эту проблему - нейронная сеть сама выделит необходимые метрики для анализа автоматически.
| ![]() |
Для анализа собранных данных нужен инструмент. Можно написать своё, а можно использовать готовые решения для создания моделей: Rapidminer, SAS, SPSS и другие. Свои инструменты можно создать на готовых библиотеках: Spark MLlib (scala/java/python) если много данных или scikit-learn.org (python) если мало данных.
Rapidminer - очень удобный инструмент. Модель в нём собирается из "кубиков", есть несколько сот операторов для обучения и статистики. Инструмент позволяет изменять диапазоны параметров, может строить графики. Типовые трудозатраты создания модели в нём - 1-2 часа.
Примерные сроки программирования если выбрано создание собственного инструмента, при достаточном опыте, - небольшие. Модель вообще не должна быть большой. Создание модели в коде из 5 строк - это 10 минут.
Обучение модели - минуты, десятки минут. Очень редко (использование опорных векторов, нейронные модели) и при необходимости - часы, дни, особенно при использовании DeepLearning.
Оценка качества модели - десятки минут с использованием метрик AUC (area under curve), Recall/Precision, Cross-validation. Методика оценки проста: делается бинарный классификатор и доказывается что он работает точнее обычного гадания Да/Нет. Если работает на 10-15% лучше, то модель годна, можно опробовать её "в бою". Данного результата достаточно в 80% случаев.
Если данных много, то обработка происходит долго. Для ускорения можно использовать векторизацию текста, LSH (locality sensetive hashing), word2vec.
Первое что нужно сделать - научиться визуализировать полученные данные: строить графики, гистограммы и так далее. Визуализация позволяет понять как у вас распределены данные и что у вас с ними происходит.
Как правило инструменты для этого есть у всех. Это Munin, Cacti, InfluxDB. Если размерностей много, то можно их сжать (PCA, SVD). Перед началом использования сложных инструментов всегда надо попробовать решить задачу простыми средствами.
Простейшие и первейшие это гистограммы: время загрузки страницы, вызов метода API, время жизни ключей в redis.
Если рассыпать много шариков по полу, то где-то они будут в одиночестве, а где-то соберутся в кучи. Это в двухмерной плоскости. Если плоскость трёхмерная, то получатся "облачка". Вот эти "облачка" и кучи и есть кластеры.
Кластерный анализ нужен когда есть несколько (больше чем три) векторов измерений. Например:
Эти данные кластеризуются, то есть ищутся "облачка" и получается типизация нагрузки на серверы.
Виды кластерного анализа:
Для нашей задачи вполне достаточно использовать K-means.
Кейсы кластерного анализа:
Кластерный анализ – оценки на программирование:
На выходе нужно получить список кластерных групп, лучше с визуализацией.
Метрики качества кластеризации достаточно просты: кластера должны быть явно видны. Есть строгие математические понятия: плотность и прочие, но в нашем случае достаточно простой визуализации с явным выделением кластеров.
Классификация и кластеризация - разные вещи. Кластеризация - автоматическая группировка данных в многомерном пространстве. Классификация - обучение компьютера распределять сущности по классам.
Два вида классификации:
|
Собраны метрики с серверов (например, 50 плоскостей), раскидываем точки в 50-имерном пространстве. С помощью машинного обучения строим гиперплоскость (плоскость в 50-имерном пространстве). Далее системе говорится, что если вектор попадет в какое-то место этой плоскости, то сервер упадёт через час. И это сбывается в 70% случаев.
В рамках задачи безопасной и эффективной эксплуатации можно предложить такие кейсы:
В чём разница между человеческим реагированием и машинным? Машина предотвращает эти ошибки, человек реагирует на уже совершённые ошибки. Есть специалисты, которые могут отслеживать работу и квалифицировано предотвращать проблему. Но такие люди редки и дороги. Машинное обучение - замена таким дорогим специалистам.
Не усложняйте себе работу. Начинать всегда лучше с самой простой модели, например, Naive Bayes - простейшего байесовского классификатора, который работает хорошо в 80% случаев. В случае неудовлетворительной работы этой модели можно попробовать Метод опорных векторов (support vector machine), которая обучается медленнее, но работает точнее.
Если и в этом случае не получился приемлемый результат, то можно попробовать поменять ядро и, в самом крайнем случае, попробовать нейронные сети, но это не очень-то приветствуется в силу сложности, трудозатратности и других трудностей.
Всё в целом выглядит просто: есть данные, есть оттренированная модель, которая научилась решать вашу задачу (когда зависнет сервер), определили качество модели, можно выходить "в бой".
Модель нужно ставить на самое критичное место в вашей эксплуатации.
Данные нужно периодически обновлять: раз в неделю, раз в месяц - частота зависит от ваших задач. После смены данных нужно тестировать модель заново.
То, что мы получили в ходе анализа, нужно перевести в практическую плоскость
Регрессия - попытка предсказать некоторую величину. Например, расходы на "железо", объём траффика, число запросов к API и так далее. На вход подаются вектора, на выходе получается значение.
Принцип работы - аналогичный: выявление атрибутов, далее выбор алгоритма (Spark MLlib может не сработать в этой задаче, на scikit-learn требуется 1-2 дня).
Это попытка объяснить суть принятых решений. В нашем случае: почему сервер повиснет через 3 часа? Дерево решений поможет понять какие именно атрибуты из собираемых влияют на решаемую проблему. Например: одновременно, увеличивается нагрузка на процессор, уменьшается нагрузка на диск и зигзагообразно скачет трафик - предвестники того, что сервис выйдет из строя.
На основании собранных данных (хиты, логи, анкетирование) строится дерево решений. В Rapidminer полчаса уйдёт на это, в Spark MLlib чуть больше.
Когда приходится "чинить" - это плохо. Это кроме всего прочего - большой удар по репутации. Реактивный подход - это первый, начальный шаг в вашей работе и чем раньше вы закончите с ним и перейдёте к предупреждению проблем - тем лучше.
Проактивный подход - это когда вы на основе построенной модели начинаете принимать решения задолго до проявления их последствий. Это существенно снижает стоимость поддержки. Далее необходимо найти причину проблемы и исправить её.
Стратегия расходов. Тратите мало денег - система падает, тратите много - система не падает. А где оптимум? Оптимум -это место на графике где денег тратиться не много по отношению к уровню получаемой стабильности работы системы.
Правильно сделанные и настроенные модели экономят вам не только деньги, но и людей.
В последнее время все чаще проявляется проблема блокировки сессий во время эксплуатации проектов на PHP. По умолчанию PHP создает файл для сессии, и процесс его блокирует. Остальные процессы, пытающиеся открыть сессию, выстраиваются в очередь. Логика приложения, особенно, если она сложная, не всегда позволяет эффективно ограничить время блокировки конкурирующих за сессию процессов. Ситуация усугубляется еще тем, что 3-5 подобных клиентов способны быстро забить зависшими и простаивающими в ожидании процессами PHP worker'ы и веб-проект «зависает».
В качестве решения проблемы можно использовать достаточно простой скрипт на AWK. Скрипт выбирает процесс, вызвавший коллапс PHP worker'ов из группы висящих блокировок, и прерывает его:
lsof | awk ' /sess/ { load_sessions[$9]++; if (load_sessions[$9]>max_sess_link_count){ max_sess_link_count = load_sessions[$9]; max_sess_link_name = $9; }; if ($4 ~ /.*uW$/ ){locked_id[$9]=$2}; } END { print max_sess_link_count, max_sess_link_name,locked_id[max_sess_link_name]; if (locked_id[max_sess_link_name] && max_sess_link_count>10) { r=system("kill "locked_id[max_sess_link_name]); if (!r) print "Locking process "locked_id[max_sess_link_name]" killed" } }'
При переносе бизнеса в интернет каждый час простоя несет в себе потерю заказов и репутации, неэффективность рекламных кампаний и переиндексаций поисковыми роботами, т.е. стоит зачастую немалых денег.
Сайт должен работать стабильно, без ошибок и задержек. По результатам исследования компании GoMez, занижение скорости загрузки страниц на 1 секунду снижает конверсию на 7%, а количество просмотров - на 11%.
Обеспечение отказоустойчивости критично для проектов, ключевым моментом деятельности которых является высокий уровень доверия со стороны пользователей, Business critical application: веб-сервисы, использующиеся в работе, CRM, бухучет, таск-менеджмент, почта, Битрикс24 и так далее.
Ключевым моментом отказоустойчиваости является система мониторинга.
В контексте отказоустойчивости техническое задание на сайт должно включать в себя:
Основные принципы разработки и эксплуатации, обеспечивающие высокую отказоустойчивость:
$upstream_response_time
;
ini_set('xdebug.collect_params', 3); xdebug_start_trace(); … xdebug_stop_trace(); TRACE START [2013-01-29 14:37:13] 0.0003 114112 -> {main}() ../trace.php:0 0.0004 114272 -> str_split('Xdebug') ../trace.php:8 0.0007 117424 -> ret_ord('X') ../trace.php:10 0.0007 117584 -> ord('X') ../trace.php:5 0.0009 117584 -> ret_ord('d') ../trace.php:10 …
Также о причинах и способах устранения медленной загрузки страниц вы можете прочитать в блоге.
define("LOG_FILENAME", "/var/log/db_error.log");
Многие проекты обходятся командами var_dump и echo. Однако, по мере усложнения проектов, разработчики скорее всего установят на локальных серверах что-то типа XDebug или Zend Debugger, что разумно и действительно упрощает отладку веб-приложения, если научиться этими штуками грамотно пользоваться.
Очень часто оказывается полезным XDebug в режиме сбора трейса. Иногда без трейса отловить ошибку или узкое место в производительности просто невозможно. Тем не менее, есть одно весомое "Но" - эти инструменты создают серьезную нагрузку на веб-проект и включать их на боевых серверах - опасно. Инструменты помогут вам «вылизать» код на локальных серверах, но «выжить на бою», к сожалению, не помогут.
Очень полезно, если нагрузка позволяет, включить на боевых серверах лог-файлы nginx, apache, php-fpm, вести свои лог-файлы бизнес-операций (создание особенных заказов, экспорт из 1С-Битрикс в SAP и т.п.).
Если у вас веб-кластер или просто много серверов с PHP - попробуйте собирать логи с этих машин на одну машину, где их централизованно мониторить. Используйте для этого, например, syslog-ng. Имея лог ошибок PHP со всех серверов кластера на одной машине, вам не придется бегать при аварии искать их по машинам.
Существует отличный и полезный "боевой" профилировщик xhprof. Он практически не создает на нагрузку на PHP (проценты).
Он позволяет увидеть профиль выполнения хита:
А также критический путь, что же тормозило-то на бою:
Причем он хорошо сочетается с встроенным инструментом отладки платформы Bitrix Framework (если у вас веб-решение на этой платформе): xhprof можно запускать на бою в пиках нагрузки, а Монитор производительности - когда нагрузка не зашкаливает или на серверах разработки/тестирования.
Если у вас веб-кластер, нередко удобно автоматизировать процесс динамической профилировки. Суть проста: профилировщик постоянно включен, после исполнения страницы мы проверяем время хита и если оно больше, к примеру, 2 секунд, сохраняем трейс в файл. Да, это добавляет лишних 50-100 мс, поэтому можно включать систему при необходимости либо включать по каждому, например, десятому хиту.
Файл auto_prepend_file.php://Включаем профайлер по переменной или куке, хотя можно включать сразу if (isset($_GET['profile']) && $_GET['profile']=='Y') { setcookie("my_profile", "Y"); } if (isset($_GET['profile']) && $_GET['profile']=='N') { setcookie("my_profile", "",time() - 3600); } if ( !( isset($_GET['profile']) && $_GET['profile']=='N') && ( $_COOKIE['my_profile']=='Y' || ( isset($_GET['profile']) && $_GET['profile']=='Y') ) && extension_loaded('xhprof') ) { xhprof_enable(); }Файл dbconn.php:
//Время выполнения хита получаем из пинбы, но можно в принципе напрямую из PHP (http://php.net/manual/ru/function.microtime.php) //Forcing storing trace, if req.time > N secs $profile_force_store = false; $ar_pinba = pinba_get_info(); if ($ar_pinba['req_time']>2) $profile_force_store=true; ... if ($profile_force_store || ( !(isset($_GET['profile']) && $_GET['profile']=='N') && ( $_COOKIE['my_profile']=='Y' || ( isset($_GET['profile']) && $_GET['profile']=='Y' )) && extension_loaded('xhprof') ) { $xhprof_data = xhprof_disable(); $XHPROF_ROOT = realpath(dirname(__FILE__)."/perf_stat"); include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_lib.php"; include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_runs.php"; // save raw data for this profiler run using default // implementation of iXHProfRuns. $xhprof_runs = new XHProfRuns_Default(); // save the run under a namespace "xhprof_foo" $run_id = $xhprof_runs->save_run($xhprof_data, "xhprof_${_SERVER["HTTP_HOST"]}_".str_replace('/','_',$_SERVER["REQUEST_URI"]));
Таким образом, если хит оказался дольше 2 секунд, мы его сохраняем во временную папку на сервере. Затем, на машине мониторинга можно обойти сервера и забрать с них трейсы для централизованного анализа (пример для виртуальных хостов в амазоне):
#!/bin/bash HOSTS=`as-describe-auto-scaling-groups mygroup | grep -i 'INSTANCE' | awk '{ print $2}' | xargs ec2-describe-instances | grep 'INSTANCE' | awk '{print $4}'` for HOST in $HOSTS ; do scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p -i /home/trace_loader/.ssh/id_rsa "trace_loader@${HOST}:/tmp/*xhprof*" /my_profiles done
Теперь у вас есть собранные в одном месте трейсы запросов более 2 секунд. Напишите веб-интерфейс для их просмотра в отсортированном по дате порядке, что займет минут 30. Часть бизнес-процесса готова - каждый трейс является фактически ТЗ на оптимизацию веб-приложения, можно подключать к ним специалистов из разработки.
Иногда профилировщик и лог-файлы не позволяют определить причину проблемы. И на серверах разработки при отладке (XDebug) воспроизвести эту же проблему не получается. Остается одно - понять, что происходит в момент наступления события на боевом сервере. Тут часто неоценимую помощь оказывает трассировщик системных вызовов, например strace. Только не нужно впадать в ступор при виде системных вызовов Linux. Немного усилий и вы поймете идею и сможете отлавливать на боевых серверах самые трудноуловимые ошибки и узкие места.
man 2 open man 2 sendto man 2 nanosleep
Если у вас нет этих мануалов, их можно поставить так (на CentOS6):
&yum install man-pages
Посмотреть последнюю документацию по системным вызовам можно также в Интернете или из консоли:
man syscallsПотратив немного времени на понимание как работает PHP на уровне операционной системы, вы получите в руки секретное оружие, позволяющее быстро найти и устранить практически любое узкое место высоконагруженного веб-проекта.
Нужно правильно «прицепить» трассировщик к процессам PHP. Процессов часто запущено много, в разных пулах, они постоянно гасятся и поднимаются (нередко для защиты от утечек памяти задают время жизни процесса например в 100 хитов):
ps aux | grep php root 24166 0.0 0.0 391512 4128 ? Ss 11:47 0:00 php-fpm: master process (/etc/php-fpm.conf) nobody 24167 0.0 0.6 409076 48168 ? S 11:47 0:00 php-fpm: pool www1 nobody 24168 0.0 0.4 401736 30780 ? S 11:47 0:00 php-fpm: pool www1 nobody 24169 0.0 0.5 403276 39816 ? S 11:47 0:00 php-fpm: pool www1 nobody 24170 0.0 1.0 420504 83376 ? S 11:47 0:01 php-fpm: pool www1 nobody 24171 0.0 0.6 408396 49884 ? S 11:47 0:00 php-fpm: pool www1 nobody 24172 0.0 0.5 404476 40348 ? S 11:47 0:00 php-fpm: pool www2 nobody 24173 0.0 0.4 404124 35992 ? S 11:47 0:00 php-fpm: pool www2 nobody 24174 0.0 0.5 404852 42400 ? S 11:47 0:00 php-fpm: pool www2 nobody 24175 0.0 0.4 402400 35576 ? S 11:47 0:00 php-fpm: pool www2 nobody 24176 0.0 0.4 403576 35804 ? S 11:47 0:00 php-fpm: pool www2 nobody 24177 0.0 0.7 410676 55488 ? S 11:47 0:00 php-fpm: pool www3 nobody 24178 0.0 0.6 409912 53432 ? S 11:47 0:00 php-fpm: pool www3 nobody 24179 0.1 1.3 435216 106892 ? S 11:47 0:02 php-fpm: pool www3 nobody 24180 0.0 0.7 413492 59956 ? S 11:47 0:00 php-fpm: pool www3 nobody 24181 0.0 0.4 402760 35852 ? S 11:47 0:00 php-fpm: pool www3 nobody 24182 0.0 0.4 401464 37040 ? S 11:47 0:00 php-fpm: pool www4 nobody 24183 0.0 0.5 404476 40268 ? S 11:47 0:00 php-fpm: pool www4 nobody 24184 0.0 0.9 409564 72888 ? S 11:47 0:01 php-fpm: pool www4 nobody 24185 0.0 0.5 404048 40504 ? S 11:47 0:00 php-fpm: pool www4 nobody 24186 0.0 0.5 403004 40296 ? S 11:47 0:00 php-fpm: pool www4
Чтобы не гоняться за меняющимися ID процессов PHP, можно поставить им временно короткое время жизни:
pm.max_children = 5 pm.start_servers = 5 pm.min_spare_servers = 5 pm.max_spare_servers = 5 pm.max_requests = 100
Затем запустить strace так:
strace -p 24166 -f -s 128 -tt -o trace.log
То есть trace запускается как рутовый процесс и при появлении новых потомков он автоматически начнет отслеживать их тоже.
Теперь желательно в лог файлы PHP начать писать PID процесса, чтобы можно было быстро сопоставить долгий хит из лога с конкретным процессом трейса:
access.log = /opt/php/var/log/www.access.log access.format = "%R # %{HTTP_HOST}e # %{HTTP_USER_AGENT}e # %t # %m # %r # %Q%q # %s # %f # %{mili}d # %{kilo}M # %{user}C+%{system}C # %p"
Дождёмся когда появится жертва, например долгий хит:
[24-May-2012 11:43:49] WARNING: [pool www1] child 22722, script '/var/www/html/myfile.php' (request: "POST /var/www/html/myfile.php") executing too slow (38.064443 sec), logging - # mysite.ru # Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0 # 24/May/2012:11:43:11 +0400 # POST # /var/www/html/myfile.php # # 200 # /var/www/html/myfile.php # 61131.784 # 6656 # 0.03+0.03 # 22722
Системные вызовы - это низкоуровневые «элементарные» операции, которыми программа общается с операционной системой. На них зиждется все IT-мироздание с разными языками программирования и технологиями. Системных вызовов не очень много (аналогично небольшому числу команд процессора и бесчисленному количеству языков и диалектов выше по стеку).
Команды PHP веб-проекта выполняются строго друг за другом в рамках одного процесса. Поэтому в трейсе нужно отфильтровать процесс подозрительного хита - 22722 из примера выше:
cat trace.log | grep '22722' | less
Время хита известно из лога, осталось посмотреть что творилось при построении страницы и почему висело так долго (трейс немного отредактирован). Видны запросы к БД и ответы:
24167 12:47:50.252654 write(6, "u\0\0\0\3select name, name, password ...", 121) = 121 24167 12:47:50.252915 read(6, "pupkin, 123456"..., 16384) = 458
Обращения к файлам:
24167 12:47:50.255299 open("/var/www/html/myfile.php", O_RDONLY) = 7
Взаимодействие с memcached:
24167 12:47:50.262654 sendto(9, "add mykey 0 55 1\r\n1\r\n", 65, MSG_DONTWAIT, NULL, 0) = 65 24167 12:47:50.263151 recvfrom(9, "STORED\r\n", 8192, MSG_DONTWAIT, NULL, NULL) = 8 ... 24167 12:47:50.282681 sendto(9, "delete mykey 0\r\n", 60, MSG_DONTWAIT, NULL, 0) = 60 24167 12:47:50.283998 recvfrom(9, "DELETED\r\n", 8192, MSG_DONTWAIT, NULL, NULL) = 9
И вот видим, например, почему хит завис:
22722 11:43:11.487757 sendto(10, "delete mykey 0\r\n", 55, MSG_DONTWAIT, NULL, 0) = 55 22722 11:43:11.487899 poll([{fd=10, events=POLLIN|POLLERR|POLLHUP}], 1, 1000) = 1 ([{fd=10, revents=POLLIN}]) 22722 11:43:11.488420 recvfrom(10, "DELETED\r\n", 8192, MSG_DONTWAIT, NULL, NULL) = 9 22722 11:43:11.488569 sendto(10, "delete mykey2 0\r\n", 60, MSG_DONTWAIT, NULL, 0) = 60 22722 11:43:11.488714 poll([{fd=10, events=POLLIN|POLLERR|POLLHUP}], 1, 1000) = 1 ([{fd=10, revents=POLLIN}]) 22722 11:43:11.489215 recvfrom(10, "DELETED\r\n", 8192, MSG_DONTWAIT, NULL, NULL) = 9 22722 11:43:11.489351 close(10) = 0 22722 11:43:11.489552 gettimeofday({1337845391, 489591}, NULL) = 0 22722 11:43:11.489695 gettimeofday({1337845391, 489727}, NULL) = 0 22722 11:43:11.489855 nanosleep({0, 100000}, NULL) = 0 22722 11:43:11.490155 nanosleep({0, 200000}, NULL) = 0 22722 11:43:11.490540 nanosleep({0, 400000}, NULL) = 0 22722 11:43:11.491121 nanosleep({0, 800000}, NULL) = 0 22722 11:43:11.492103 nanosleep({0, 1600000}, NULL) = 0 22722 11:43:11.493887 nanosleep({0, 3200000}, NULL) = 0 22722 11:43:11.497269 nanosleep({0, 6400000}, NULL) = 0 22722 11:43:11.503852 nanosleep({0, 12800000}, NULL) = 0 22722 11:43:11.516836 nanosleep({0, 25600000}, NULL) = 0 22722 11:43:11.542620 nanosleep({0, 51200000}, NULL) = 0 22722 11:43:11.594019 nanosleep({0, 102400000}, NULL) = 0 22722 11:43:11.696619 nanosleep({0, 204800000}, NULL) = 0 22722 11:43:11.901622 nanosleep({0, 409600000}, NULL) = 0 22722 11:43:12.311430 nanosleep({0, 819200000},22722 11:43:13.130867 <... nanosleep resumed> NULL) = 0 22722 11:43:13.131025 nanosleep({1, 638400000}, 22722 11:43:14.769688 <... nanosleep resumed> NULL) = 0 22722 11:43:14.770104 nanosleep({1, 638400000}, 22722 11:43:16.408860 <... nanosleep resumed> NULL) = 0 22722 11:43:16.409048 nanosleep({1, 638400000}, 22722 11:43:18.047808 <... nanosleep resumed> NULL) = 0 22722 11:43:18.048103 nanosleep({1, 638400000}, 22722 11:43:19.686947 <... nanosleep resumed> NULL) = 0 22722 11:43:19.687085 nanosleep({1, 638400000}, 22724 11:43:20.227224 <... lstat resumed> 0x7fff00adb080) = -1 ENOENT (No such file or directory) 22722 11:43:21.325824 <... nanosleep resumed> NULL) = 0 22722 11:43:21.326219 nanosleep({1, 638400000}, 22722 11:43:22.964830 <... nanosleep resumed> NULL) = 0 22722 11:43:22.965126 nanosleep({1, 638400000}, 22722 11:43:24.603692 <... nanosleep resumed> NULL) = 0 22722 11:43:24.604117 nanosleep({1, 638400000}, 22722 11:43:26.250371 <... nanosleep resumed> NULL) = 0 22722 11:43:26.250580 nanosleep({1, 638400000}, 22722 11:43:27.889372 <... nanosleep resumed> NULL) = 0 22722 11:43:27.889614 nanosleep({1, 638400000}, 22722 11:43:29.534127 <... nanosleep resumed> NULL) = 0 22722 11:43:29.534313 nanosleep({1, 638400000}, 22722 11:43:31.173004 <... nanosleep resumed> NULL) = 0 22722 11:43:31.173273 nanosleep({1, 638400000}, 22722 11:43:32.812113 <... nanosleep resumed> NULL) = 0 22722 11:43:32.812531 nanosleep({1, 638400000}, 22722 11:43:34.451236 <... nanosleep resumed> NULL) = 0 22722 11:43:34.451554 nanosleep({1, 638400000}, 22722 11:43:36.090229 <... nanosleep resumed> NULL) = 0 22722 11:43:36.090317 nanosleep({1, 638400000}, 22724 11:43:36.522722 fstat(12, 22723 11:43:36.622833 <... gettimeofday resumed> {1337845416, 622722}, NULL) = 0 22722 11:43:37.729696 <... nanosleep resumed> NULL) = 0 22722 11:43:37.730033 nanosleep({1, 638400000}, 22724 11:43:39.322722 gettimeofday( 22722 11:43:39.368671 <... nanosleep resumed> NULL) = 0 22722 11:43:39.368930 nanosleep({1, 638400000}, 22722 11:43:41.007574 <... nanosleep resumed> NULL) = 0 22722 11:43:41.007998 nanosleep({1, 638400000}, 22722 11:43:42.646895 <... nanosleep resumed> NULL) = 0 22722 11:43:42.647140 nanosleep({1, 638400000}, 22720 11:43:43.022722 fstat(12, 22720 11:43:43.622722 munmap(0x7fa1e736a000, 646) = 0 22722 11:43:44.285702 <... nanosleep resumed> NULL) = 0 22722 11:43:44.285973 nanosleep({1, 638400000}, 22722 11:43:45.926593 <... nanosleep resumed> NULL) = 0 22722 11:43:45.926793 nanosleep({1, 638400000}, 22722 11:43:47.566124 <... nanosleep resumed> NULL) = 0 22722 11:43:47.566344 nanosleep({1, 638400000}, 22722 11:43:49.205103 <... nanosleep resumed> NULL) = 0 22722 11:43:49.205311 nanosleep({1, 638400000}, 22719 11:43:49.440580 ptrace(PTRACE_ATTACH, 22722, 0, 0
Сначала шли обращения к memcached: запись/чтение сокета 10, затем сокет был успешно закрыт (22722 11:43:11.489351 close(10) = 0
), затем в коде был вызов nanosleep
в цикле и условие выхода из цикла не срабатывало по причине предварительно закрытого сокета. В результате причину удалось быстро определить и поправить код.
Веб-проект на PHP, добавляются фичи, клиенты довольны. Но нагрузка постоянно растет. В один прекрасный день начинают появляться загадочные ошибки, которые программисты не знают как исправить. "Ломается" серверный софт, например связка Apache-PHP - а клиент получает в ответ на запрос страницу о регламентных работах.
Достаточно часто встречаются проекты, которые сталкиваются с подобным классом "ошибок" серверного софта, и в команде не всегда знают, что делать. В логе Apache часто появляются сообщения о нарушении сегментации (segmentation fault). Клиенты получают страницу об ошибке, а веб-разработчик с сисадмином ломают себе голову, играются с разными версиями PHP/Apache/прекомпилятора, собирают PHP из исходников с разными опциями снова и снова. А это баги не PHP, а их кода.
[Mon Oct 01 12:32:09 2012] [notice] child pid 27120 exit signal Segmentation fault (11)
В данном случае бесполезно искать подробную информацию в логе ошибок PHP - ведь "упал" сам процесс, а не скрипт. Если заранее не сделать на NGINX симпатичную страничку о регламентных работах, то клиенты увидят аскетичную ошибку 50*.
Вспомним теорию. Что такое signal? Это средство, которое операционная система использует, чтобы сказать процессу, что он не прав. Например, берет и, нарушая законы математики, делит на 0, или насильственными действиями вызывает переполнение стека. В данном случае мы видим сигнал с номером 11 и названием SIGSEGV. Список сигналов можно посмотреть, выполнив kill -l
.
Теперь найдем причину, за что же убили процесс PHP? Для этого нужно настроить создание дампа памяти процесса в момент "убийства" или coredump. Как только в следующий раз процесс будет убит операционной системой, ядром будет создан файл. Место размещение и название файла можно настроить. Если вы в консоли, просто наберите man 5 core
.
Например, можно складывать файлы в папочку так:
echo "/tmp/httpd-core.%p" > /proc/sys/kernel/core_pattern
Однако, скорее всего, в вашей системе отключена генерация coredump-файлов. Ее можно включить, вставив в начало скрипта запуска веб-сервера строку:
ulimit -с unlimited
Или, чтобы сделать настройку постоянной, отредактировать файлик /etc/security/limits.conf
. Туда можно вставить:
apache - core -1
Необходимо также для Apache настроить папку для coredump-файлов (/etc/httpd/conf/httpd.conf
):
CoreDumpDirectory /tmp
Перезапустите Apache:
service httpd restart
Тестируем и вручную завершаем процесс:
ps aux | grep httpd … kill -11 12345
Проверка: в файле /var/log/httpd/error_log
должно быть что-то вроде такого:
[Mon Oct 01 16:12:08 2012] [notice] child pid 22596 exit signal Segmentation fault (11), possible coredump in /tmp
В /tmp
теперь видим файл с названием типа /tmp/httpd-core.22596
. Вы научились получать дамп памяти завершившегося процесса. Теперь ждем, когда процесс будет завершён естественным образом.
--enable-debug, -g
для gcc при компиляции), то потеряется много полезной информации. Даже если PHP собран из исходников без этой опции, но исходники лежат рядом, этого может хватить для анализа.Открыть coredump можно утилитой gdb. Обычно открывают coredump так:
gdb путь_к_выполняемому_файлу_веб-сервера путь_к_coredump
Разобраться, как работает отладчик, не займет много времени. Можно за пару часиков поглотить один из самых занимательных учебников, а можно попросить это сделать сисадмина. Все уважающие себя разработчики на C в unix умеют пользоваться этим отладчиком. Но, к сожалению, их может не быть в вашей команде. И есть еще одно неприятное "НО".
Компилированный в байткод скрипт PHP это не совсем программа на C. Нужно, правда совсем немного, разобраться во внутренностях движка Zend. А именно - нужно найти в трейсе последний вызов функции execute, перейти в этот frame стека и исследовать локальные переменные (op_array
), а также заглянуть в глобальные переменные движка Zend:
(gdb) frame 3 #3 0x080f1cc4 in execute (op_array=0x816c670) at ./zend_execute.c:1605 (gdb) print (char *)(executor_globals.function_state_ptr->function)->common.function_name $14 = 0x80fa6fa "pg_result_error" (gdb) print (char *)executor_globals.active_op_array->function_name $15 = 0x816cfc4 "result_error" (gdb) print (char *)executor_globals.active_op_array->filename $16 = 0x816afbc "/home/yohgaki/php/DEV/segfault.php"
В op_array
можно запутаться, поэтому полезна команда просмотра типа этой структуры:
(gdb) ptype op_array type = struct _zend_op_array { zend_uchar type; char *function_name; zend_class_entry *scope; zend_uint fn_flags; union _zend_function *prototype; zend_uint num_args; zend_uint required_num_args; zend_arg_info *arg_info; zend_bool pass_rest_by_reference; unsigned char return_reference; zend_uint *refcount; zend_op *opcodes; zend_uint last; zend_uint size; zend_compiled_variable *vars; int last_var; int size_var; zend_uint T; zend_brk_cont_element *brk_cont_array; zend_uint last_brk_cont; zend_uint current_brk_cont; zend_try_catch_element *try_catch_array; int last_try_catch; HashTable *static_variables; zend_op *start_op; int backpatch_count; zend_bool done_pass_two; zend_bool uses_this; char *filename; zend_uint line_start; zend_uint line_end; char *doc_comment; zend_uint doc_comment_len; void *reserved[4]; } *
Процесс отладки заключается в хождении между фреймами стека (frame N
), переходе в каждый вызов функции execute и исследовании ее локальных аргументов (print name
, ptype name
). Чем меньше номер фрейма, тем вы глубже. Иногда полезно зайти в гости в экстеншн PHP и посмотреть, где произошла ошибка и почему (хотя бы попытаться понять причину).
(gdb) frame #номер# (gdb) print op_array.function_name $1 = 0x2aaab7ca0c10 "myFunction" (gdb) print op_array.filename $2 = 0x2aaab7ca0c20 "/var/www/file.php"
Если разбираться во внутренностях движка Zend особого времени нет, то просто запомните, что переходя между фреймами стека вызовов с помощью команды frame #N#
, нужно смотреть только определенные элементы этой структуры, и вы точно сможете установить в каком файле PHP была вызвана функция PHP, какую функцию она вызвала и т.п. Так можно добираться до причины Segmentation Fault или другой ошибки, "убившей" процесс. И объясните программистам в чем причина, и они ее поправят.
Ошибки можно свести в группы:
pcre
, входит в рекурсию и вызывает себя несколько тысяч раз. Можно либо настроить параметры библиотеки или добавить процессу побольше стека (/etc/init.d/httpd
):
ulimit -s «ставим значение больше»А текущее значение можно посмотреть командой:
ulimit -aСправка по команде
man ulimit
, далее ищем ulimit. Если вы не можете получить coredump, то можно подключиться к запущенному процессу и изучить его. Пока вы внутри процесса, его выполнение приостанавливается (ps aux | grep apache | grep 'T '
, Он будет в состоянии трейсинга.). Когда покинете его, он снова продолжит выполняться. Подключиться можно так:
gdb -p ид_процесса
Составим чеклист для менеджера для борьбы с загадочными серверными ошибками, в которых не могут разобраться ни веб-разработчики, ни сисадмины:
Использование веб-кластерных технологий позволяет иметь несколько серверов веб-приложений, несколько баз данных, несколько memcached-серверов (на примере 1С-Битрикс: Веб-кластер):
Кластеризация составляющих сайта, благодаря балансировке нагрузки между нодами кластера, позволяет минимизировать время простоя системы. Кроме того, веб-кластер позволяет подключить одну из нод в качестве бэкап-сервера и отменить использование данной ноды для распределения нагрузки от посещений сайта.
Помимо аппаратных, существуют и программные решения балансировки нагрузки между серверами:
Для определения наиболее подходящей конфигурации необходимо проанализировать характер нагрузки на веб-приложение с помощью различных инструментов мониторинга (munin, zabbix, apache server-status).
Высокая нагрузка на процессоры, невысокая/средняя нагрузка на СУБД, редкая смена контента.
Подключенные к веб-кластеру ноды заводятся под балансировщик нагрузки, в результате чего по ним будет распределена нагрузка на процессоры.
Для синхронизации контента подойдет csync2, nfs/cifs-сервер на одной из нод веб-кластера.
Снизить нагрузку на master-базу путем добавления slave-нод. С учетом их мощности распределить между ними нагрузку с помощью параметра Процент распределения нагрузки.
При дальнейшем увеличении нагрузки на СУБД:
Большой объем часто перестраиваемого кэша, контент постоянно обновляется.
Для эффективной работы с кэшем запускаем на каждой ноде сервер memcached и выделяем каждому по несколько GB памяти (в зависимости от требований приложения).
Для синхронизации контента рекомендуется использовать выделенный nfs/cifs-сервер, либо ocfs/gfs или аналоги.
Веб-кластер от 1С-Битрикс позволяет использовать:
# chkconfig memcached on # service memcached start
Каждый веб-сервер кластера можно добавить в общий веб-кластер для мониторинга в административном разделе Настройки > Веб-кластер > Группа серверов > Веб-сервера:
На каждом из серверов необходимо настроить страницу, на которой будет отображаться статистика веб-сервера Apache (с помощью модуля mod_status). Если используется 1С-Битрикс: Веб-окружение, необходимо:
ExtendedStatus On SetHandler server-status Order allow,deny Allow from 10.0.0.1 Allow from 10.0.0.2 Deny from All
Location
- адрес, по которому будет доступна статистика;Allow from
определяют, с каких ip-адресов статистика будет доступна для просмотра.Apache
с помощью команды:
# service httpd reload
Location~ ^/server-status$ { proxy_pass http://127.0.0.1:8888; }
# service nginx reload
RewriteCond %{REQUEST_FILENAME} !/bitrix/urlrewrite.php$добавьте:
RewriteCond %{REQUEST_URI} !/server-status$
После внесения всех необходимых изменений, адрес server-status'а можно добавить в конфигурацию кластера.
Обеспечение производительности проекта в период его эксплуатации требует постоянного внимания. Проблемы могут возникать разные, но в любом случае их можно отнести к проблемам на клиентской или серверной стороне.
Тестирование локально, проведённое при разработке проекта, - это хорошо, но далеко не всё. Нужны замеры в реальных условиях. Можно собирать статистику: по скорости рендеринга javascript в браузере клиента, DNS резолвингу, TCP соединениям и вообще, всё, что можно собрать внутри браузера клиента. С помощью Pinba эта статистика может выводиться в удобном виде для анализа. Pinba сохраняет данные по онлайн агрегации хитов за последние 15 минут (по умолчанию).
Пример регистрации в pinba браузерной статистики из Navigation Timing API
|
---|
pinba_timer_add( array( 'jstimer'=>'responseEnd-responseStart', 'jshost'=>$jshost, 'jsip'=>$_SERVER["REMOTE_ADDR"], 'jsua'=>$_SERVER["HTTP_USER_AGENT"], ), (intval($_REQUEST["responseEnd"]) - intval($_REQUEST["responseStart"]))/1000 ); pinba_hostname_set( strval(substr($_REQUEST["host"],0,128)) ); pinba_script_name_set( strval(substr($script,0,128)) ); pinba_request_time_set($timef); |
Все эти данные можно получить средствами js внутри браузера клиента и передать в MySQL хранилище Pinba:
Одним запросом в Pinba можно увидеть у каких клиентов сейчас, в каких подсетках тормозит DNS, тормозит TCP или рендер тормозит и так далее. Разработчик одним взглядом видит то, что видят сейчас видит обобщённый клиент его проекта.
Далее из Pinba можно можно выбрать данные:
Рендер у Клиентов:
select avg(round(timer_value/req_count,3)) from bx24_cps_js_performance_host where tag2_value='domContentLoadedEventStart-responseStart';
Рендер в браузерах:
select substring(tag2_value,1,100) as ua, avg(round(timer_value/req_count,3)) as avg_time,count(*) from bx24_cps_js_performance_jstimer_ua where tag1_value='domContentLoadedEventStart-responseStart' group by ua order by count(*) desc limit 10;
DNS-скорость:
&select tag2_value, avg(round(timer_value/req_count,3)) as avg_ip_time, count(*) from bx24_cps_js_performance_jstimer_ip where tag1_value='domainLookupEnd-domainLookupStart' group by tag2_value order by count(*) desc limit 20;
Топ коннектов:
mysql> select tag2_value, avg(round(timer_value/req_count,3)) as avg_ip_time from bx24_cps_js_performance_jstimer_ip where tag1_value='connectEnd-connectStart' group by tag2_value order by avg_ip_time desc limit 20; +-----------------+-----------+ | 176.60.226.9 | 7.6150000 | | 119.32.156.237 | 3.9420000 | | 79.133.133.97 | 3.2835000 | | 78.138.133.2 | 2.4352500 | | 195.38.55.178 | 1.9980000 |
Информация, получаемая таким образом должна приводить к выводам. Такому анализу и нужно научиться. Можно анализировать данные вручную, а можно написать тесты и получать смс по критичным результатам этих тестов. Такие уведомления позволят реагировать на проблемы быстрее. Ставьте тесты на определённые пороговые значения, рисуйте графики и вы осчастливите клиента.
Научитесь быстро локализовывать источник проблем, это могут быть:
Если на клиентской стороне все в порядке, то реальная проблема на серверной стороне. Тут возможны две группы причин:
Диагностика проблем со статикой - самое простое. Возможно:
Решение проблем статики
Оптимально - использование CDN. Не, если в силу каких-то причин это неприемлемо, можно вынести статику на отдельный домен, что позволит скачивать статику в несколько потоков. Либо перенести статику на отдельный сервер(ы) с быстрыми дисками.
И обязательный мониторинг, позволяющий ловить проблемы сразу по их возникновению.
Очень редко, но бывает, что статика "пробивается" через двухуровневую конфигурацию на Apache/PHP-FPM, то есть NGNIX вместо того, чтобы отдавать статику, начинает передавать запросы на Apache. Возможные причины такого явления - неправильная вёрстка, неправильная настройка NGNIX. Для локализации проблемы можно использовать тесты (apachetop). Также проблему можно увидеть на странице /server-status
сервера Apache.
Эти ошибки берутся из-за back-end'а. Такие ошибки нужно по возможности прятать от клиента. Поэтому сделайте красивую страницу с уведомлением пользователей и разбирайтесь с проблемой: смотрите логи.
При проблемах с Базой данных основной инструмент диагностики - логи. Постоянный мониторинг тестами Percona XtraDB, например:
Данные выводятся в виде общей статистики, сразу видно когда начались проблемы:
При больших объёмах данных рекомендуется познакомиться с CAP, NoSQL, изучить репликацию, посмотрите про Galera.
Сама по себе БД не тормозит. Тормозят запросы, которые задаются разработчиком. Ищите решение в них.
Если тормозит PHP, для выявления проблем используйте:
Полезно измерять не только среднее значение показателя, но и оценивать распределение значений. На гистограмме времени отработки или использования памяти можно увидеть много интересного: например 95% быстрых хитов и 5% очень очень медленных. Не менее полезен анализ распределения по процентилям.
В связи с бурным ростом и усложнением Front-End аяксами, табами в браузере и тому подобным, все чаще проявляется проблема блокировки сессий во время эксплуатации сайтов на PHP. PHP по умолчанию создает для сессии файл и процесс эксклюзивно его блокирует. Остальные процессы, пытающиеся открыть сессию - выстраиваются в очередь. Не всегда логика приложения, особенно если она сложная, позволяет эффективно ограничить время блокировки конкурирующих за сессию процессов. Ситуация усугубляется еще тем, что 3-5 подобных клиентов способны быстро забить зависшими и простаивающими в ожидании процессами PHP-worker'ы и сайт "повисает".
К сожалению, разработчики/сисадмины не всегда могут сразу понять, что дело в блокировке сессии — и ищут проблемы в других частях проекта, теряя время.
Рассмотрим, что происходит внутри операционной системы, если одновременно попытаться открыть в браузере (можно в разных вкладках) один засыпающий файл и несколько просто стартующих сессию скриптов:
<?php session_start(); sleep(30);// только для одного скрипта ?>
Страницы будут дожидаться освобождения сессии (30 секунд) и займет это много времени, при этом будут забиты слоты веб-сервера. Примерно то же самое случается, когда AJAX запускает в сессии веб-клиента тяжелую задачу и остальные AJAX и другие элементы интерфейса зависают в ожидании (либо когда открывается несколько вкладок под одной авторизацией).
Процессы веб-сервера, в данном случае httpd, но то же самое происходит и с PHP-FPM — пытаются эксклюзивно заблокировать файл сессии, что видим с помощью lsof:
lsof -n | awk '/sess_/' httpd 7079 nobody 52uW REG 8,1 2216 809832 /tmp/sess_f629a13b4b0920a21042c86d17f4a6a6 httpd 10406 nobody 52u REG 8,1 2216 809832 /tmp/sess_f629a13b4b0920a21042c86d17f4a6a6 httpd 10477 nobody 52u REG 8,1 2216 809832 /tmp/sess_f629a13b4b0920a21042c86d17f4a6a6 httpd 10552 nobody 52u REG 8,1 2216 809832 /tmp/sess_f629a13b4b0920a21042c86d17f4a6a6 httpd 11550 nobody 52u REG 8,1 2216 809832 /tmp/sess_f629a13b4b0920a21042c86d17f4a6a6 httpd 11576 nobody 52u REG 8,1 2216 809832 /tmp/sess_f629a13b4b0920a21042c86d17f4a6a6
Обратите внимание на 4 колонку. Число - это номер дескриптора файла в процессе, а дальше - тип блокировки. "uW" - веб-сервер заблокировал файл эксклюзивно для записи. Остальные - в ожидании. Как только процесс 7079 закончит свою работу, блокировку "uW" возьмет другой процесс. В это время, понятно, выстраивается очередь и веб-интерфейс заметно тормозит. Если несколько процессов заблокируют сессию на единицы секунды - интерфейс вообще "замрёт".
Посмотрим теперь с другой стороны, чем занимаются процессы:
ps -e -o pid,comm,wchan=WIDE-WCHAN-COLUMN | grep httpd 7079 httpd - 10406 httpd flock_lock_file_wait 10477 httpd flock_lock_file_wait 10552 httpd flock_lock_file_wait 11550 httpd flock_lock_file_wait 11576 httpd flock_lock_file_wait
Во второй колонке видим, что все, кроме одного, заняты в функции flock_lock_file_wait.
strace -p 10406 Process 10406 attached - interrupt to quit flock(52, LOCK_EX)
Они заняты в системном вызове c запросом эксклюзивной блокировки.
LOCK_EX Place an exclusive lock. Only one process may hold an exclusive lock for a given file at a given time.
Чтобы постоянно отслеживать на веб-серверах появление такого "паровозика", забивающего PHP-воркеры, можно использовать скрипт, написанный на AWK:
/sess_/ { load_sessions[$9]++; if (load_sessions[$9]>max_sess_link_count){ max_sess_link_count = load_sessions[$9]; max_sess_link_name = $9; }; if ($4 ~ /.*uW$/ ){ locked_id[$9]=$2 }; } END { print max_sess_link_count, max_sess_link_name,locked_id[max_sess_link_name]; if (locked_id[max_sess_link_name] && max_sess_link_count>3) { # r=system("kill "locked_id[max_sess_link_name]); # if (!r) print "Locking process "locked_id[max_sess_link_name]" killed" system("ls -al "max_sess_link_name); } }
Запускается так:
lsof -n | awk -f sess_view.awk 5 /tmp/sess_f629a13b4b0920a21042c86d17f4a6a6 24830
Скрипт отображает длину "паровозика" и процесс — создающий затор.
Понятно, что на реальном сайте не должно быть подобных проблем - нужно сделать все (переделать логику работы с сессией, написать кастомные хандлеры PHP, принять другие меры), чтобы у клиента по возможности ничего не тормозило, а вы как системный администратор - спали крепко и долго.
Если по каким-либо причинам переделка логики и прочие меры предпринять сложно (невозможно), то можно раскомментировать kill
и уничтожать процессы веб-сервера, создающие коллапс. Но правильнее с собранной подобным образом через cron в файлик статистикой обратиться к разработчикам и договориться о рефакторинге.
Объем данных многих популярных сайтов быстро растет. Кроме проблемы резервного копирования возникает и проблема быстрой раздачи контента клиентам, чтобы все могли его, контент, качать на высокой скорости. Для системного администратора задача даже редкого, ежедневного резервного копирования такого объема файлов крайне сложна, а менеджер веб-проекта просыпается в холодном поту от мысли о предстоящей профилактике дата-центра на 6 часов.
В статье хочу разобрать частые кейсы дешевого и дорогого решения данной задачи — от простого к сложному. В конце статьи расскажу как задача решена в нашем флагманском продукте — всегда полезно сравнивать opensource-решения с коммерческими, мозгам нужна гимнастика.![]() | NGINX или аналогичный обратный прокси в режиме двухуровневой конфигурации позволяет эффективно раздавать файлы, особенно по медленным каналам. При этом серьезно снижается нагрузка на сервер и повышается в целом производительность веб-приложения: NGINX раздает кучу файлов, Apache или PHP-fpm обрабатывают запросы к серверу приложений. Такие веб-приложения живут хорошо, пока объем файлов не увеличивается до, скажем десятков гигабайт. Когда несколько сотен клиентов начинают одновременно скачивать файлы с сервера, а памяти для кэширования файлов в ОЗУ недостаточно: диск, а потом и RAID просто умрёт. |
Примерно на этом этапе умирания первого RAID нередко начинают сначала фиктивно, затем физически раздавать статические файлы с другого, желательно не отдающего много лишних cookies, домена. Всем известно, что браузер клиента имеет ограничение на число коннектов в одному домену сайта, и когда сайт вдруг начинает отдавать себя с двух и более доменов, то скорость его загрузки в браузер клиента существенно возрастает.
Чтобы более эффективно раздавать статику с разных доменов, ее выносят на отдельный сервер(а) статики. Полезно на этом сервере использовать режим кэширования NGINX и "быстрый" RAID (чтобы побольше клиентов требовалось для постановки диска «на колени»).
Делают домены что-то типа:
www.mysite.ru
img.mysite.ru
js.mysite.ru
css.mysite.ru
download.mysite.ru
и т.п.
При дальнейшей работе веб-проекта организаторы начинают понимать, что раздавать из своего дата-центра даже с очень быстрых дисков или даже SAN - не всегда эффективно:
Становится понятно, что нужно быть как можно ближе к клиенту и отдавать клиенту столько статики, сколько он хочет, на максимально возможной быстрой скорости, а не сколько мы можем отдавать со своего сервера(ов). Эти задачи в последнее время начала эффективно решать технология CDN.
Веб-проект постепенно распухает. Копится все больше и больше файлов для раздачи. Они хранятся централизованно в одном ДЦ на одном или группе серверов. Делать резервную копию такого количества файлов становится все дороже, дольше и сложнее. Полный бэкап делается неделю, приходится делать снепшоты, инкременты и прочие вещи, увеличивающие риск потери данных.
Что будет, если:
В инфраструктуру вложено уже столько денег, а риски, причем немалые, всё ещё остаются.
Для проекта с большим бюджетом есть вариант развернуть в нескольких дата-центрах свою распределенную файловую систему. Вариант достаточно сложный для реализации и администрирования, но если есть профессионалы, то вполне реализуем.
Одним из наиболее эффективных решений является использование возможностей известных облачных провайдеров. Они дают возможность хранить неограниченный объем ваших файлов в облаке с очень высокой надежностью по очень привлекательным, особенно при большом объеме данных, ценам. Провайдеры организовали на своих мощностях описанные выше распределенные файловые системы. Эти системы - высоконадежные и размещенные в нескольких дата-центрах, разделенных территориально. Более того, эти сервисы, как правило, тесно интегрированы в сеть CDN данного провайдера. Т.е. вы будете не просто удобно и дешево хранить свои файлы, но и их максимально удобно для клиента раздавать.
Для этого нужно настроить на своем веб-проекте прослойку или виртуальную файловую систему (или аналог облачного диска). Среди бесплатных решений можно выделить FUSE-инструменты (для linux) типа s3fs.
Чтобы перевести текущее веб-приложение на эту дешевую и очень эффективную с точки зрения бизнеса технологию, нужно внести ряд изменений в существующий код и логику приложения.
Последний вариант реализован "из коробки" в модуле Облачные хранилища, который позволяет перенести хранение файлов вместо локального сервера веб-сайта в "облака". Модуль позволяет хранить данные отдельных модулей системы в разных облачных хранилищах. Это реально диверсифицирует файловое хранилище. Можно распределить файлы в зависимости от адреса или типа клиента. Можно распределить файлы в зависимости от типа информации: кто-то эффективно отдает легкую статику, кто-то тяжелый контент.
Считается, что не так уж много в Рунете проектов, которым требуется CDN: новостные агентства, сайты с видео контентом и подобные им проекты с большими файлами. Считается, что сайты поменьше (которых подавляющее большинство: интернет-магазины, блоги, корпоративные сайты и т.д.) вполне могут обойтись и без этого сервиса, так как это дорого и сложно.
Например, в компании CDNvideo тарифы начинаются от 3000 руб. в месяц с трафиком от 1 Тб. Для большинства проектов такая дополнительная нагрузка - чрезмерна.
Само подключение, вроде бы, не должно вызвать сложностей (настроить DNS, заменить локальные ссылки на новые). Но надо не забыть заменить все ссылки (они могут генерироваться динамически приложением, быть жестко прописаны на страницах, могут указываться в файлах стилей (css) и скриптов (js)). Надо сохранить удобство разработки и отладки, работая на девелоперских серверах с локальными копиями. Надо обновлять кэш CDN при обновлении файлов. В общем, технически все не слишком однозначно.
На обычном сайте нет "тяжелого" контента, который достоин CDN. Даже самые прогрессивные интернет-магазины, размещающие на сайте, например, видео-отзывы от клиентов, скорее всего просто ставят ссылки на YouTube, а не хранят все у себя.
Тем не менее, «подвинуть» поближе к посетителю можно весьма многое: все картинки, файлы стилей (css), javascript (js) и т.п. — по сути, практически весь статический контент можно вынести в CDN. А это, в среднем, до 80% объема трафика.
Помимо ускорения собственно сетью CDN за счет раздачи контента с ближайших к посетителям серверов, сайт, включивший CDN, получает дополнительное ускорение на клиентской стороне (во время рендеринга страницы браузером) за счет распределения запросов по разным доменам.
Все современные браузеры имеют лимит на количество одновременных соединений к одному домену (обычно - не более 6). Таким образом, даже если сервер может отдавать страницы с максимальной скоростью, а скорость клиентского подключения достаточно высока, то все равно загрузка всех элементов страницы (картинок, скриптов, файлов стилей) будет осуществляться максимум в 6 потоков.
При подключении CDN локальные ссылки заменяются на разные домены, и уже лишь каждый отдельный домен имеет ограничение на число соединений. Их же реальное максимальное число становится в несколько раз больше (по числу доменов, с которых осуществляется загрузка файлов).
Какая практическая польза от выигранных секунд? Например, для интернет-магазина?
На быстром сайте выше конверсия и больше просмотров страниц. Чем дольше загружается страница сайта, тем больше вероятность того, что самые нетерпеливые посетители не дождутся ее загрузки, уйдут с сайта и больше на него не вернутся.
Есть разные исследования, в которых предпринимаются попытки посчитать таких потенциальных клиентов, которых «недополучает» сайт. Например, инфографика от 5coins говорит о том, что 25% посетителей сайта покидают страницу, загрузка которой занимает более 4 секунд:
Специалисты компании GoMez, проанализировав 150 миллионов хитов на 150 сайтах, получили такие цифры: замедление загрузки страницы на 1 секунду снижает конверсию на 7%, а количество просмотров - на 11%.
Интеграция платформы с CDN для пользователя выглядит максимально просто: сначала нужно через стандартную систему обновлений установить новый модуль «Облачные сервисы Битрикс», а затем в административной панели сайта в разделе Настройки > Облачные сервисы Битрикс > Ускорение сайта (CDN) включить и настроить модуль.
После этого ссылки на статические файлы сайта (картинки, файлы стилей css, скрипты js) будут заменены: вместо локальных URL'ов будут использоваться служебные имена серверов сети CDN. При этом не потребуется вносить никакие изменения в DNS и не нужно заботиться о сбросе кэша CDN при обновлении файлов.
Непосредственно для раздачи контента в дальнейшем используется сеть российского провайдера CDN - CDNvideo, в партнерстве с которым реализована эта услуга.
Модуль Ускорение сайта (CDN) является бесплатной дополнительной опцией для любой активной коммерческой лицензии на «1С-Битрикс: Управление сайтом». Никаких дополнительных платежей помимо стоимости самой лицензии или ее продления.
Для каждой отдельной лицензии определены ежемесячные лимиты трафика, который может быть передан через узлы CDN конечным пользователям.
Источник контента - origin - должен быть один. 1С-Битрикс организовала кусочек собственной облачной инфраструктуры (отказоустойчивой и масштабируемой):
Это "облачко" является таким ориджином для сети CDN, проксируя через себя трафик подключенных клиентов и решая несколько важных задач:
Модуль Ускорение сайта (CDN), работающий непосредственно на клиентском сайте:
Непосредственно эксплуатацией проекта должна заниматься IT-служба клиента, но на основе ваших рекомендаций и инструкций по настройке проекта, его обновления и мониторингу. Таким образом, вам необходимо грамотно выстроить процесс взаимодействия с IT-службой.
В данном уроке мы рассмотрим как выстроить эффективный процесс взаимодействия партнера с IT-службой клиента, объясним какие могут быть политические и технические риски и как с ними правильно работать, объясним какие бывают IT-службы и как с ними взаимодействовать.
Для начала рассмотрим какие могут быть преграды для построения эффективного взаимодействия в зависимости от представителей клиента:
Обычно IT-служба клиента представляет из себя:
Но нашим наблюдениям на деле чаще всего приходится работать со вторым типом: сисадминистраторами и людьми, которые занимаются эксплуатацией.
Поведение IT-служб может быть достаточно разнообразным: одни пытаются показать, что им никто не нужен, что они могут все сами сделать, другие - это сплошная бюрократия, но есть и адекватные службы. Такие IT-службы ориентируются на результат, хотят сделать все быстро и просто. Они не стремятся создавать огромные ТЗ, доверяют партнеру. В таких службах работают опытные и грамотные специалисты, в случае необходимости готовые даже подсказать решения и вместе обсудить поставленную задачу.
Как правильно вам поступать и действовать?
Рассмотрим более детально ваши действия для каждого конкретного типа сотрудника IT-службы:
Таким образом, для эффективного взаимодействия с IT-службой необходимо наладить открытый процесс работы. Если вы вступите на тропу войну, то проект будет скорее всего «завален». Старайтесь чаще встречаться, работать на позитиве, идите на контакт с клиентом и выстраивайте с ним рабочий диалог. Очень полезно обмениваться своим опытом работы. Даже если имеются какие-либо проблемы, то их нельзя скрывать, наоборот, нужно их поднять и совместно разрешить.
Введенный в эксплуатацию проект необходимо развивать вместе с клиентом. Чтобы правильно выстроить процесс работы, необходимо знать как должны передаваться изменения на «боевой» проект, как при этом избежать ошибок и как же правильнее обновлять проект.
Полезно иметь несколько тестовых серверов, причем тестовая конфигурация должна быть точной копией «боевого» сервера: версии Bitrix Framework, PHP, СУБД и другого ПО проекта должны совпадать. При наличии некоторых различий могут возникать ошибки в работе проекта, которые будет находить клиент.
Рассмотрим схему процесса эксплуатации и развития проекта:
На схеме кратко показано, что в первую очередь на тестовом сервере необходимо выполнить обновление (при наличии) системного ПО и системы Bitrix Framewok. Затем из системы контроля версий (репозитария) мы передаем обновление кода и базы данных тестового проекта на сервер тестирования. После чего выполняем модульное, функциональное и нагрузочное тестирование.
На «боевом» сервере обязательно выполняем резервное копирование. Затем ставим те же обновления системного ПО и Bitrix Framewok, что и на тестовый сервер. После чего из системы контроля версии передаем обновления кода и базы данных «боевого» проекта.
Для хорошей работы вашего проекта необходимо использовать дистрибутивы только стабильных версий. Кроме того, их следует выбирать с увеличенным сроком поддержки, поскольку при смене ПО могут возникнуть ошибки и сбои в работе проекта.
Не следует использовать «левые» репозитории пакетов и кастомные сборки, которые могут сломать систему при обновлении. Программное обеспечение не следует собирать и из исходников, если не собираетесь его поддерживать.
Доступ на сервер не должно быть ни у кого, кроме администратора. Программное обеспечение не должны устанавливать несколько человек. Должен быть наведен порядок с правами доступа к серверам.
Перед тем, как установить обновления программного обеспечения на «боевой» сервер, необходимо установить и проверить их на тестовом сервере. При этом полезно изучить логи пакетов обновлений, поскольку всегда нужно быть готовым к тому, что при обновлениях может сломаться проект. Для подстраховки можно сделать LVM shapshot. Конфигурации серверов необходимо держать под контролем версий (например, использовать пакет etckeeper).
Прежде чем приступать к обновлению системы Bitrix Framework, обязательно сделайте «горячий» бекап «боевого» проекта. Для максимально быстрого восстановления проекта можно сделать бекап через LVM shapshot файлов и базы данных. Сперва обновляем Bitrix Framework на тестовом проекте, проводим тестирование (см. схему выше) и только после удачного его завершения обновляем Bitrix Framework на «боевом» проекте при минимальной посещаемости.
В крайне маловероятном, но возможном случае нарушения работоспособности проекта после обновления по технологии SiteUpdate, постарайтесь как можно быстрее обратиться в техническую поддержку компании "1С-Битрикс" и предоставить доступ для восстановления, либо откатитесь к предыдущему состоянию, восстановив данные из бекапа.
При обновлении кода проекта всегда следите за тем, чтобы в стабильную ветку попадал только тщательно протестированный на тестовом сервере код. Нужно предусмотреть, чтобы в скриптах создания объектов Bitrix Framework не использовались внешние ключи, атрибут AUTO_INCREMENT
. Кроме того, нужно следить за синхронностью систем, иначе при отсутствии зависимостей объекты Bitrix Framework будут некорректно создаваться на «боевом» сервере. Все изменения файлов должны быть залогированы, а также для скриптов создания объектов Bitrix Framework должны вестись подробные логи работы и ошибок. Все скрипты обновлений должны быть привязаны к релизам и храниться в системе контроля версий.
Для выполнения модульного тестирования на крупном проекте, работающего на системе Bitrix Framework, желательно иметь Unit-тесты для своих модулей, классов, библиотек. Компоненты проще тестировать руками либо частично использовать программу Selenium. Но при этом не нужно целиком и полностью покрывать веб-проект модульными тестами, необходимо найти баланс между разными видами тестирования.
При проведении функционального тестирования нарисуйте графики зависимостей: укажите что от чего зависит и где искать. Это необходимо, чтобы не приходилось после любого изменения перетестировать весь проект полностью. Для сложного функционала пишите и актуализируйте подробные варианты тестирования («test cases») либо проводите тестирование вместе с аналитиком. Простые задачи по функциональному тестированию можно выполнить с помощью Selenium.
Для проведения нагрузочного тестирования на тестовом сервере следует держать большой набор данных, приближенный к «боевому». Нагрузочное тестирование необходимо выполнять для измененных компонентов, страниц, сервисов. Тестовые планы сохраняйте в системе контроля версий. Автоматически проверяйте настройки кеширования компонентов, автокеширования и другие параметры, сильно влияющие на производительность. Используйте на проекте программы Munin, Cacti и анализируйте их графики, чтобы иметь возможность корректировать конфигурации серверов.
Важной задачей для владельцев веб-проектов является качественная и надежная защита от хакерских атак, взлома и кражи хранящейся на сайте информации.
Само по себе API системы Bitrix Framework взломоустойчиво. Потенциально опасный код возникает при интеграции, в кастомных страницах, компонентах и модулях. Обычно разработчику не хватает опыта аудита и взлома кода. Написание устойчивого к взлому кода требует не только глубоких теоретических знаний, но и многолетней практики.
Комплекс защитных мероприятий проектов, работающих на системе Bitrix Framework, реализуется с помощью модуля Проактивная защита. Модуль имеет следующие инструменты для обеспечения безопасности:
Данные инструменты страхуют разработчика, значительно снижают последствия ошибок и усложняют взлом. Но, несмотря на это, они не всегда позволяют устранить сам источник уязвимости. Для этого необходимо использовать инструмент аудита безопасности PHP-кода, который доступен в разделе Безопасность на странице Монитор качества:
В основе теста по проверке безопасности кода лежит статический taint-анализ. Запустив данный тест, вы сможете просмотреть в отчете найденные потенциальные уязвимости (при наличии) и тем самым усилить защиту проекта от взлома.
Инструмент идентифицирует следующие возможные уязвимости:
Проводить мониторинг состояния проекта с точки зрения безопасности помогает сканер безопасности. С его помощью вы можете выполнить внешнее сканирование окружения проекта, найти потенциальные уязвимости в коде, проверить настройки сайта и убедиться в правильности настроек всех систем безопасности.
Также для обеспечения высокого уровня безопасности вашего проекта следует придерживаться следующих правил:
На любом проекте хранится большое количество важных данных. Особую ценность представляют данные пользователей: заказы, персональная информация, документы и т.п. Потеря части данных может нанести сильный удар по репутации клиента, а потеря всех данных пользователей может обернуться закрытием бизнеса клиента. При этом пострадает ваша репутация партнера и ваш бизнес. Чтобы избежать таких плачевных последствий, необходимо грамотно организовать систему резервного копирования.
Чем крупнее и важнее проект, тем чаще должно выполняться это копирование и не нужно надеяться на хостера. При этом нужно иметь стратегию копирования.
Облачные структуры предоставляют свое API, позволяющее выполнять копирование быстро и надежно (например, бекап файловой системы в 500 Гб в течение нескольких минут), делать snapshot виртуальной машины. Snapshot - это самое современное, самое мощное на данный момент средство копирования. Без «облака» настройка такого резервирования - задача непростая даже для высококвалифицированного персонала.
Рассмотрим общие принципы, которыми вы должны руководствоваться при организации резервного копирования для крупного проекта:
Бекап должен отвечать следующим требованиям:
Если вы будете следовать данным принципам и иметь отлаженные процессы работы с резервными копиями, то вы всегда сможете быстро восстановить работоспособность проекта или отдельных его элементов, даже в случае критических сбоев, в том числе с потерей данных.
Бекап файлов должен храниться на отдельном сервере. Если проект небольшой, то обычно бекап файлов делается с помощью программы tar (плюс утилита gz или bz2). Альтернативой могут быть rsync, csync2, rdiff-backup.
Если на проекте очень много файлов, то для создания бекапа файлов следует использовать LVM snapshot, облачный механизм snapshot'ов для Amazon (см. пример), snapshot в хранилище типа NetApp и т.п. При этом обратите внимание, что для создания snapshot'ов подходят только те файловые системы, которые поддерживают возможность "замораживания" (fsfreeze).
Кроме того, иногда бывает полезным вынести редко меняющиеся файлы в облако (например, Amazon S3, Google Cloud Storage) и время от времени делать бекап самого облака.
Если на сервере хранится большое количество бекапов, то для удобной работы с ними целесообразно иногда пользоваться пакетом bacula.
Репликация – это не бекап, но может выполнить некоторые его функции, например, при использовании pt-slave-delay.
Логические (mysqldump) и бинарные (Percona Xtrabackup или MySQL Enterprise Backup) бекапы используются для восстановления отдельных баз или таблиц, поврежденных в случае некорректных операций в системе или ошибок пользователей.
Логический бекап MySQL должен быть всегда и его лучше делать со slave-сервера, чтобы не нагружать сервер с «боевой» базой данных. При этом необходимо отслеживать синхронность его данных (mixed mode replication, --sync-binlog, pt-table-checksum). Используя опцию --single-transaction, можно сделать целостный бекап без блокировки таблиц, причем иногда прямо с «боевой» базы данных. Кроме того, рекомендуется сохранять позицию бинарного лога в бекапе (опция --master-data=2). Сами бинарные логи полезно хранить на отдельном диске, поскольку они очень важны при восстановлении.
Бинарный бекап можно делать и с «боевого», и со slave-сервера (делается он не очень быстро, но гораздо быстрее логического). Кроме того, он позволяет сохранять инкременты, т.е. его можно делать чаще одного раза в сутки. Восстановление из бинарного бекапа достаточно долгое, зачастую удобно пользоваться инкрементальными snapshot'ами дисков базы данных. Но все-таки такой бекап позволяет довольно быстро поднять новый slave-сервер (значительно быстрее, чем из логического бекапа).
Кроме того, можно удобно и быстро сделать копию базы данных, используя механизм snapshot'ов (LVM snapshot'ы, snapshot'ы в Amazon и NetApp). Общая схема действий такова: блокируются все таблицы базы, сбрасываются изменения и делается freeze файловой системы, затем снимается snapshot, «размораживается» файловая система и снимается блокировка с таблиц. Снимать snapshot'ы с базы данных можно достаточно часто: хоть каждые полчаса и даже чаще. Но обязательно необходимо проверять, что база восстанавливается из такого snapshot'а. Время восстановления обычно быстрее, чем для логического бекапа, может составлять от нескольких минут до часов (в зависимости от ситуации). Кроме того, используя, например, сервис Amazon, можно делать snapshot'ы всего сервера целиком и разворачиваться очень быстро в случае сбоя.
В этом уроке рассмотрим возможные проблемы и их решения.
Проблема: разрушились диски или файловая система на master-сервере БД (при этом fsck не помогает).
Решение. Делаем slave-сервер master-сервером. Редактируем необходимым образом файл dbconn.php (можно скриптом или переключаем IP). Далее выполняем одно из перечисленных ниже действий:
CHANGE MASTER TO MASTER_LOG_FILE=…, MASTER_LOG_POS=…
).Таким образом, всегда полезно иметь рядом «горячую» копию БД, на которую можно быстро переключиться.
Проблема: случайно испорчена часть данных на master-сервере БД.
Например, в результате ошибки администратора или разработчика удалены поля в таблице заказов посетителей. Конечно, испорченные данные уже есть и на slave-сервере.
Решение. В приложении необходимо заблокировать часть функционала. Написать, что в ближайшее время всё будет починено, и выполнить следующие действия:
Проблема: случайно испорчена большая часть данных на master-сервере БД.
Например, в результате ошибки администратора или разработчика испортилось большое количество данных (заказы, счета, каталог). Конечно, на slave-сервер изменения уже ушли.
Решение. Необходимо заблокировать сайт. Написать, что в ближайшее время всё будет восстановлено, и выполнить следующие действия:
Иногда использование логического бекапа и бинарных логов позволяет быстрее развернуть БД. Желательно проводить учения по восстановлению системы.
В уроке будут рассмотрены архитектурные решения на EBS-дисках для обеспечения надежности и производительности веб-проектов и конкретные техники резервного копирования, которые используются в повседневной работе
Из официальной документации Amazon по EBS-дискам до конца не ясно, надежнее ли они RAID1 или нет - всякие разговоры про функциональную зависимость объема диска и времени последнего снапшота диска - не внушают оптимизм:
Во время аварии в европейском ДЦ Amazon у нас из софтварного RAID10 вылетели сразу 2 диска из-за отключения питания в датацентре. В сети также иногда появляются жутковатые сообщения о том, что диск EBS внезапно "сломался", клиенту прислали письмо на тему "ваши данные потерялись, к сожалению" и официально рекомендуется сделать снапшот как раз за минуту перед отказом диска. Такое впечатление, что механизм снапшотов появился именно для "подстраховки" от выхода из строя не очень надежных EBS-дисков.
Публичные виртуальные машины, которые используются для создания собственных приватных образов, имеют только один EBS-диск, не состоящий в рейде и на котором находится корневой раздел. А загрузить машину с корневым разделом на софтварном рейде - мягко сказать, непросто и требует времени.
Тем не менее, за полгода в Amazon у нас не вылетел ни один диск (у нас их около 50), не считая отключения питания после удара молнии, что позволяет относиться к ним как к более-менее надежным RAID1 дискам (репликация на одно устройство, расположенное в том же датацентре), уступающим в надежности s3 (реплицируется дополнительно на 2 устройства в разных датацентрах, и вообще это не блочное устройство), но в 10 раз более надежным, чем обычные жесткие диски.
После миграции в Amazon можно столкнуться с достаточно невысокой производительностью EBS-дисков, что особенно заметно на машинах с MySQL.
"Подсмотрев" идею в RDS (Amazon автоматически, в зависимости от суммарного объема EBS-дисков, размещают информацию базы данных на софтварном рейде) и, использовав смекалку, можно перенести данные на созданный из EBS-дисков софтварный RAID10 (mdadm в Linux):
cat /proc/mdstat Personalities : [raid10] md0 : active raid10 sdo[0] sdj[8](S) sdi[7] sdh[6] sdg[5] sdn[4] sdm[3] sdl[2] sdk[1] 629145344 blocks 64K chunks 2 near-copies [8/8] [UUUUUUUU]
Для увеличения уровня отказоустойчивости используются софтварные рейды.
После миграции данных на рейды проблемы с производительностью дисковых подсистем на машинах перестали беспокоить.
Стоит упомянуть, что относительно недавно появились более быстрые SSD-backed диски, которые интересно потестировать на производительность.
EBS-диски, как оказалось, нельзя одновременно смонтировать на несколько виртуальных машин для использования кластерной файловой системы типа OCFS2 или GFS2. Поэтому для кластеризации контента можно использовать утилиту csync2, форсированную надстройкой для "быстрой" синхронизации часто обновляемых тяжелых папок на базе inotify.
Диски виртуальной машины совсем несложно бекапить, т.к. именно для этого создан инструмент создания снапшотов блочного устройства:
ec2-create-snapshot vol-a5rtbvwe
Операция стартует очень быстро (секунды), а загрузка снапшота в s3 продолжается определенное время, в зависимости от объема диска и загруженности Amazon - до десятков минут.
Мы взрослые люди и понимаем, что целостный снапшот диска может быть если:
umount -d /dev/sdh
Иначе мы получаем снапшот диска "как бы после внезапного выключения питания" и диск нужно будет полечить (fsck
) при загрузке машины - что, благодаря, современным журналируемым файловым системам (ext3, ext4, xfs и др.) происходит незаметно и часто быстро за счет проигрывания журнала.
Мы тестировали создание снапшота диска с "живой" (без размонтирования) ext3 и xfs - после некоторой паузы для проигрывания журнала диск (созданный из снапшота) монтируется и работает.
В качестве файловой системы, поддерживающей "приостановку", мы выбрали xfs. Эта файловая система рекомендуется Amazon и используется утилитами создания целостного снапшота типа ec2-consistent-backup:
xfs_freeze (-f | -u) mount-point
Действительно, если после xfs_freeze -f
сделать снапшот диска, то он монтируется без проигрывания журнала очень быстро.
В CentOS 6 (и видимо в других дистрибутивах с "обновленным" ядром) стало возможным "фризить" также дефолтные файловые системы типа ext3/ext4 командой fsfreeze.
Если мыслить логически, то нужно:
К сожалению, в документации к API Amazon нет информации, сколько времени должно пройти между 2) и 3). Однако опытным путем было найдено (и аналогично делается в данной утилите), что достаточно получить идентификаторы снапшотов из вызова API для каждого диска рейда и файловую систему рейда можно "разморозить" - и при сборке рейда он подхватится без ребилдинга. Иначе - начнет ребилдиться.
Вот так сделали мы:
bxc_exec("/usr/bin/ssh remote_xfs_freezer@hostname".escapeshellarg("sudo /usr/sbin/xfs_freeze -f /xfs_raid_path")); ... ec2-create-snapshot - для каждого диска рейда ... bxc_exec("/usr/bin/ssh remote_xfs_freezer@hostname".escapeshellarg("sudo /usr/sbin/xfs_freeze -u /xfs_raid_path")); ... ec2-create-volume --snapshot id_of_snapshot ... ec2-attach-volume volume_id -i instance_id -d device ... mdadm --assemble /dev/mdN /dev/sd[a-z]
Можно сбросить данные MySQL на диск: FLUSH TABLES WITH READ LOCK, зафризить файловую систему: xfs_freeze -f /path
, затем выполнить API вызов снятия с дисков снапшота ec2-create-snapshot
, но есть и другой, полностью устраивающий вариант - асинхронная репликация MySQL в другой ДЦ. Интересным предоставляется создание снапшотов базы с использованием xtrabackup - без вышеописанных "танцев с бубнами".
Если отдельно бекапить диски, группы дисков в рейдах, то, при наличии конфига - процесс работает стабильно и прозрачно. Однако, если вам нужно иногда (например, в ДЦ ударила на выходных молния) переезжать между AZ Amazon (датацентрами) - проще ввести уровень абстракции выше ID инстансов, снапшотов и дисков и оперировать категориями ролей и образов (AMI - Amazon Machine Image) машин. Через API можно назначать объектам Amazon тэги и фильтровать по ним выборки.
Мы определили роли: веб-машина, СУБД, балансировщик, машина мониторинга и наши скрипты бекапов работают примерно так:
Однако, гораздо проще бекапить работающую виртуальную машину в образ (AMI image). При этом в образе сохраняются ее настройки и пути к создаваемым снапшотам, из которых будут созданы диски машины, присоединяемые к ней при старте:
ec2-create-image id_of_instance [--no-reboot] ... ec2-run-instances ami_image_id
Как говорится, почувствуйте разницу! Можно создать образ с машины с RAID10 из 9 дисков - одной командой, не отвлекаясь на ID ее дисков и снапшотов - вся необходимая для воссоздания машины информация записывается в образ AMI автоматически.
Единственный минус - неясно, на какое время нужно лочить диски/рейды работающей машины, чтобы создать из нее образ AMI. Опытным путем было установлено, что достаточно 2-5 секунд - если меньше, то диски/рейды начинают восстанавливаться при создании новой машины. Также очень удобно то, что поднять машину из бекапа в образ (AMI) можно в другом датацентре (AZ).
У себя мы создаем бекап всех машин нашей кластерной конфигурации (сотни гигабайт) в образ AMI два раза в сутки.
Очистка "устаревших" образов, вместе с их снапшотами производится скриптом:
|
---|
function bxc_delete_old_instance_backups( AmazonEC2 $ec2, $instanceRole = '', $bkpsMaxCount = 3) { if (!$instanceRole || !is_object($ec2) || $bkpsMaxCount<1 ) { bxc_log_error("Bad input params: ".__FUNCTION__); return false; } $response = $ec2->describe_images(array( 'Filter' => array( array('Name' => 'tag:Role', 'Value' => 'Image:'.$instanceRole), array('Name' => 'state', 'Value' => 'available'), ) )); if ( !$response->isOK() ) { bxc_log_amazon_error($response, __FUNCTION__.":".__LINE__); return false; } if ( isset($response->body->imagesSet->item) ) { $images = array(); $snaps = array(); foreach ( $response->body->imagesSet->item as $image ) { $images[ (string) $image->name ] = (string) $image->imageId; if ( isset($image->blockDeviceMapping->item) ) { foreach ( $image->blockDeviceMapping->item as $disk ) { $snaps[ (string) $image->name ][] = $disk->ebs->snapshotId; } } } if ($images) { krsort($images); } else { bxc_log("No images with tag:Role=Image:$instanceRole"); return true; } if ( count($images) <= $bkpsMaxCount ) { bxc_log("Nothing to delete"); return true; } $imagesToDelete = array_slice($images, $bkpsMaxCount, count($images)-$bkpsMaxCount, true); foreach ($imagesToDelete as $imageName=>$imageId ) { $r = $ec2->deregister_image($imageId); if ( $r->isOK() ) { bxc_log("Image deleted ok: ".$imageId); sleep(5);//Important. Wait to ami really be deleted ;-) foreach ( $snaps[$imageName] as $snapId ) { $rr = $ec2->delete_snapshot( $snapId ); if ( $rr->isOK() ) { bxc_log("Snapshot deleted ok: ".$snapId); } else { bxc_log_amazon_error($rr, __FUNCTION__); bxc_log_error("Cannot delete snapshot: ".$snapId); } } } else { bxc_log_amazon_error($r, __FUNCTION__); bxc_log_error("Cannot delete image: ".$imageId); } } return true; } else { bxc_log("No images with tag:Role=Image:$instanceRole"); return true; } return false; } |
Amazon AWS имеют неплохую админку, однако многие вещи можно сделать только через API. Для Linux можно установить инструменты командной строки для работы с API Amazon, которые довольно хорошо документированы. Мы используем такие инструменты:
ls /opt/aws/ AutoScaling-1.0.39.0 ElasticLoadBalancing-1.0.14.3 ec2-api-tools-1.4.4.1 env-set.sh
Последний скрипт устанавливает переменные окружения для работы инструментов. Обратите внимание, практически каждый веб-сервис Amazon имеет свой набор утилит, которые нужно скачать и установить отдельно. Для управления машинами, например, документация находится тут, а утилиты командной строки - тут.
Пока неплохо себя показывает в работе официальный SDK для работы с API Amazon для PHP (вышеприведенный скрипт использует именно эту библиотеку).
С EBS-дисками Amazon можно эффективно работать, объединив их в софтварный рейд. К сожалению, нельзя замонтировать EBS-диск к нескольким виртуальным машинам, для работы с кластерной файловой системой типа OCFS2, GFS2. Бекапы как дисков, так и рейдов EBS делаются несложно, необходимо только понять основные принципы и обойти подводные камни. Удобно бекапить машины целиком, а не по отдельному диску - это позволит быстро развернуть конфигурацию в соседнем датацентре, если в ваш ДЦ ударит молния или врежется самолет.
Рассмотрим технику настройки резервного копирования файлов и MySQL/InnoDB/XtraDB в приложениях, развернутых в облаке, на примере Amazon Web Services.
Представим себе крупный интернет-магазин, который хранит бизнес-информацию как в базе данных, так и на дисках. Новые файлы появляются ежеминутно, а общий размер контента составляет сотни гигабайт. Как делать резервные копии в таком случае?
Прежде всего разберемся в технологиях хранения данных. Для хранения данных виртуальных машин сервис Amazon предлагает виртуальные блочные устройства EBS. Такие диски очень просто создаются и подключаются к серверу в 2 клика, максимальный размер диска - 1ТБ (по умолчанию есть ограничение на 5000 дисков и 20ТБ, но его увеличивают по первой просьбе). Что же касается производительности EBS-дисков, то сразу можно сказать, что они медленнее «железных» дисков (это видно даже на простых операциях типа копирования папок с диска на диск, распаковке архивов и т.д.).
Для успешной работы с дисками сервиса Amazon полезно создать программный RAID (делается это довольно просто, работает долго и практически не ломается). RAID может особенно пригодится, если EBS-диск внезапно выйдет из строя. Кроме того, миграция данных с EBS-дисков на программный RAID решает проблемы с производительностью.
Для резервного копирования Amazon предлагает нам LVM и snapshot'ы в режиме "copy-on-write". Snapshot'ы блочного устройства можно делать столько раз, сколько необходимо. При этом:
Таким образом, мы можем делать snapshot'ы большой папки часто меняющего контента хоть каждые 5 минут и они будут надежно сохранены в Amazon S3. Если нам потребуется откатить, например, 1ТБ изменяемых данных на 5 минут назад, то мы с легкостью сделаем это сделаем следующим образом:
Разумеется, технически невозможно мгновенно передать 1ТБ данных из Amazon S3 в SAN, где живут EBS-диски. Поэтому, хотя блочное устройство и становится доступным операционной системе, данные на него будут заливаться в фоне определенное время и, возможно, скорость работы с диском поначалу будет не очень высокой. Но, тем не менее, такой подход позволяет удобно делать инкрементальный бекап большого объема данных и откатывать их на любую точку (например, на неделю назад с шагом в 5 минут).
Кроме возможности создания snapshot'ов с EBS-дисков, можно напрямую отправлять файлы в Amazon S3. В этом случае удобна в использовании утилита s3cmd, позволяющая синхронизировать деревья файловой системы с облаком в обоих направлениях (передаются только изменения на базе расчета md5 на локальном диске и хранения md5 объекта внутри Amazon S3 в ETag).
Для хорошей производительности мы объединили EBS-диски в RAID'ы, но как же делать бекап RAID'а? Можно воспользоваться специальной утилитой ec2-consistent-snapshot (или в своих скриптах повторить ее логику) и выполнить следующие действия:
Для подключения сохраненного RAID'а лучше написать скрипт, подключающий диски к машине и запускающий из них программный RAID. Сохраненный в snapshot'ы вышеуказанным способом RAID прекрасно «поднимается», не проигрывая журнал файловой системы.
Иногда удобнее сделать snapshot всей машины целиком. Выполняется это с помощью команды CreateImage, которая позволяет сделать snapshot в двух режимах: с остановкой машины или без. Но обратите внимание, что в последнем случае возможна порча данных на дисках или RAID'ах.
После создания snapshot'а машины появляется объект AMI (Amazon Machine Image), имеющий ссылки на сохраненные snapshot'ы каждого своего диска. Можно одной командой запустить из этого объекта сервер со всеми дисками/RAID'ами (AWS API call RunInstances). Рабочие сервера можно не только бекапить целиком, но и разворачивать из бекапа целиком со всеми RAID'ами одной командой. Однако есть серьезный «подводный камень»: команда CreateImage совершенно непрозрачна и неясно, сколько времени она снимает snapshot'ы со всех дисков сервера (скажем, секунду или 10 секунд?). Поэтому необходимо тщательно тестировать скрипт перед его использованием.
Инкрементальный бекап MySQL с помощью сервиса Amazon делается следующим образом:
FLUSH TABLES WITH READ LOCK
.UNLOCK TABLES
.Теперь у нас имеется объект AMI с «горячим» бекапом MySQL.
Таким образом, довольно просто делать инкрементальный бекап сервера MySQL в Amazon S3 хоть каждые 5 минут с возможностью его быстрого ввода в использование. Если сервер используется в репликации, то он восстановит базу данных как правило без особых проблем при условии, что вы не забыли в настройках задать консервативные настройки репликации (либо базу можно довольно быстро вернуть в работу вручную):
sync_binlog = 1 innodb_flush_log_at_trx_commit = 1 sync_master_info = 1 sync_relay_log = 1 sync_relay_log_info = 1
Для системного администратора имеются специальные утилиты, получающие REST-методы API сервиса Amazon. Поэтому, чтобы написать скрипт по основным действиям с сервисом Amazon, необходимо для каждого используемого веб-сервиса скачать утилиты и вызовы к ним прописать в bash-скрипте. В качестве примера рассмотрим скрипт, меняющий у сервера аппаратное обеспечение («железо»):
#!/bin/bash #Change cluster node hw type #Which node to change hardware? NODE_INSTANCE_ID=$1 #To which hw-type to change? #Some 64-bit hw types: t1.micro (1 core, 613M), m1.large (2 cores, 7.5G), m1.xlarge (4 cores, 15G), #m2.xlarge (2 cores, 17G), c1.xlarge (8 cores, 7G) NODE_TARGET_TYPE='c1.xlarge' #To which reserved elastic ip to bind node? NODE_ELASTIC_IP=$2 ec2-stop-instances $NODE_INSTANCE_ID while ec2-describe-instances $NODE_INSTANCE_ID | grep -q stopping do sleep 5 echo 'Waiting' done ec2-modify-instance-attribute --instance-type $NODE_TARGET_TYPE $NODE_INSTANCE_ID ec2-start-instances $NODE_INSTANCE_ID ec2-associate-address $NODE_ELASTIC_IP -i $NODE_INSTANCE_ID
Для разработчиков имеются библиотеки на разных языках для работы с API сервиса Amazon (например, AWS SDK for PHP - библиотека для работы из PHP).
Многие успешные стартапы рано или поздно сталкиваются с работой с большими объемами данных и должны быть готовы - знать инструменты и алгоритмы. Благо теорию до нас проработали Google, Facebook*, Twitter и даже Amazon приготовил веб-сервисы, помогающие противостоять таким задачам.
Если, вдруг, возникла задача на резервное копирование Bigdat'ы? Можно использовать алгоритм map-reduce (от Google) или бесплатный инструмент кластерных параллельных вычислений - hadoop, используемый в Facebook*, Twitter, Yahoo и много ещё где (от единиц машин до тысяч в кластере).
* Социальная сеть признана экстремистской и запрещена на территории Российской Федерации.
Логика этих механизмов очень проста:
Далее все это загружается в кластер, расширяется на кучу машин (настраивается), параллельно выполняется и получаем выполненную в N раз быстрее задачу. При этом проверяется состояние каждой задачи, каждой машины, если что, задачи рестартуются. Что важно, активно используется локальная кластерная файловая система для обмена данными между нодами кластера hdfs.
Имеется 10 млн. файлов, хранящиеся в бакете s3. Необходимо измененные файлы скопировать в другой бакет s3 за разумное время. Как?
Если в проекте есть 10 млн. файлов, то нужно пробежать по 10 млн файлов и определить, изменился ли каждый файл. А если изменился, то нужно скопировать в другой бакет.
То есть нужно для каждого файла выполнить операцию условного копирования (E-tag или Update-time) из бакета А, в бакет Б - по http. Сами данные - не перемещаются, только обращения к API. (Но в перспективе может возникнуть потребность и файлы переносить в другой регион на другие сервера.) Батчей на копирование в API S3 (put copy) - нет, но даже если бы были, ненамного ускорили бы.
Дополнительно можно попробовать:
Но даже в этом случае максимум, что можно получить из нескольких параллельных процессов на одном сервере, создавая заметную нагрузку - неделя срока на каждое резервное копирование.
И ещё сложность в том, что нужно это настроить, администрировать, запускать и останавливать.
Что предлагает Amazon
В связи с востребованностью клиентами кейса для:
Amazon предложил веб-сервис, оптимально заточенный под резервирование больших объёмов данных:
При такой схеме оплата идёт только за время использования железных серверов, но несколько (~20%) выше, чем за обычные железки. Это из-за того, что автоматической настройкой, интеграцией с S3, файероволами и обновлением кластерного софта занимается сам Amazon. В принципе клиент может сам этим заниматься и не доплачивать 20%, но придется всю автоматику написать и постоянно обслуживать.
От клиента требуется разработать простейший алгоритм, положить его в виде файлика и входные данные в S3. Дальнейшая работа по бекапу - автоматическая.
Созданы необходимые элементарно простые mapper, reducer, конфигуратор.
Подготовил код создания списка файлов (во много потоков, конечно, но занимает полчасика и нагрузку особую не создает). Включил в крон:
/home/bitrix/bxc/cron_jobs/bkp_s3_folder_hadoop.php
Пример команды запуска кластера в Amazon
|
---|
/opt/aws/emr/elastic-mapreduce --create --stream \ --name bx24_s3_bkp_$D \ --step-name bx24_s3_bkp_$D \ --with-termination-protection \ --step-action CANCEL_AND_WAIT \ --ami-version '2.4.2' \ --bootstrap-action 's3://***/code/bkp_s3_folder_hadoop_bootstrap.sh' \ --bootstrap-action 's3://elasticmapreduce/bootstrap-actions/configure-hadoop' \ --args "-m,mapred.map.max.attempts=20,-m,mapred.tasktracker.map.tasks.maximum=15,-m,mapred.task.timeout=600000" \ --input 's3://***/input/' \ --mapper 's3://***/code/bkp_s3_folder_hadoop_mapper.php' \ --reducer 's3://***/code/bkp_s3_folder_hadoop_reducer.php' \ --output 's3://***/output_'$D \ --log-uri 's3://***/logs/' \ --num-instances 5 \ --master-instance-type m1.small \ --slave-instance-type m1.xlarge \ --key-pair '***' |
Скрипт формирует, затем выгружает список файлов в облако и стартует кластер hadoop (6 машин):
/home/bitrix/bxc/cron_jobs/bkp_s3_folder_hadoop_launcher.sh
Необходимо также подправить настройки виртуальной машины Amazon, объем памяти и число воркеров. При старте мы в bootstrap дописываем несколько своих настроек. После всех этих действий желательно провести тестирование на большом объёме файлов, например 10 миллионов:
Этим способом можно сократить время бэкапа 10 млн. файлов до 16 часов вместо 7 дней. То есть можно делать резервное копирование чаще. А если файлов станет 20 миллионов, то надо просто изменить лишь одну циферку в скрипте запуска кластера hadoop.
К организации технической поддержки проекта необходимо подойти грамотно. С одной стороны недостаточно просто завести электронный ящик, на который клиенты будут присылать свои вопросы, а вы будете на них отвечать. С другой стороны нельзя сильно углубляться в аспекты ITIL/ITSM, которые могут быть применимы для очень крупных центров технической поддержки, а в случае обычных проектов процесс организации техподдержки будет очень затяжным (в худшем случае, техподдержка так и не будет налажена). Самое главное - это понять зачем нужна служба техподдержки и в том, как она будет организована. В этом заинтересованы все стороны: клиент, заказчик и исполнитель-разработчик.
Начнем с того, что организация техподдержки подразумевает учет того, что хочет (ожидает) обычный клиент от службы техподдержки. Клиенту важно, чтобы все его вопросы были оперативно и эффективно разрешены. Ответы на свои вопросы он ожидает увидеть в полной, точной и понятной формулировке. А если проблема требует выполнения каких-либо действий со стороны клиента, то он желает получить набор готовых инструкций для выполнения.
С точки зрения разработчика необходимо:
Это могут быть консультации по функционалу или по back-end разработанного решения, по работе с административной частью. Вопросы могут быть как по технической части как самого решения, так и связанные с инфраструктурой (и относится к тому, кто эту инфраструктуру поддерживает). Также это могут быть срочные вопросы, связанные с аудитом производительности и безопасности, или обработка каких-то аварийных ситуаций. Все это стоит зафиксировать, рассмотреть вместе этот круг вопросов и по возможности стараться за него не выходить.
Это могут быть: зоны ответственности по инфраструктуре, по резервным копиям, по тестовой среде, по среде разработки. Если вы размещаете инфраструктуру не на своих ресурсах, то всегда определяйте кто отвечает за код сайта, за само приложение, за его контент.
Например, в компании-заказчике работает 100 тыс. человек и, если к вам, как к разработчику, будет обращаться каждый из этих человек, то техподдержку оказывать будет очень тяжело. От компании должны быть представлены контактные лица, которые будут с вами работать. Также зафиксируйте интерфейсы для обращения (телефон, электронная почта, тикетная система) и выделите наиболее приоритетный и удобный с обеих сторон способ.
Все вышеперечисленное следует зафиксировать в едином документе под названием, например, регламент работы технической поддержки. Он может быть зафиксирован в виде договора, в виде дополнительного соглашения или в виде просто странички на сайте, с который вы все согласитесь. Главное, чтобы этот регламент был фиксированный, чтобы в случае каких-либо спорных ситуаций не было ощущения, что одна сторона помнит про одни договоренности, а другая - про другие.
Все входящие обращения в техподдержку обязательно должны быть зафиксированы:
При организации техподдержки заранее обозначьте какие данные от клиента вы хотите получить, когда он обращается к вам с вопросом. Чем полнее и корректнее он сформулирует вопрос, тем быстрее он получит на него ответ По типовым вопросам, которые обязательно у вас накопятся со временем, составьте базу знаний и FAQ, сделайте каталогизатор и удобную форму поиска. Обязательно показывайте пользователям вашу базу знаний, чтобы они имели возможность какие-то типовые вопросы решать самостоятельно.
Кроме того, необходимо уделять должное внимание и сотрудникам техподдержки. Они должны обладать грамотной речью, а также хорошей дикцией, если техподдержка оказывается в устном виде. Вам следует прорабатывать сценарии разговоров, шаблоны ответов, чтобы ускорить реакцию на обращение. Желательно, чтобы в техподдержке был руководитель или старший по смене, который проверял бы выборочно ответы сотрудников, контролируя по необходимости отдельные моменты. Также важно регулярно проводить внутренние тестирования с целью определения компетенции сотрудников.
Сотрудник техподдержки должен обладать максимумом информации, чтобы предоставлять клиенту актуальные данные, ориентировать его по срокам решения трудоемких проблем. Поэтому у вас должны быть созданы внутренние регламенты по взаимодействию сотрудников техподдержки с другими отделами: системами мониторинга, системными администраторами и разработкой.
Немаловажным аспектом работы службы техподдержки является внутренняя система мотиваций. Оценивать работу сотрудников можно по совокупности или по отдельным критериям, например:
Анализируя показатели, вы сможете определить требуются ли изменения в организации работы технической поддержки.
Что же касается технической стороны вопроса, то сейчас очень много тикетных систем, помогающих организовать службу техподдержки. В Bitrix Framework имеется собственный инструмент - модуль Техподдержка, который включает в себя публичный интерфейс для создания обращений, удобную работу с тикетами и их историей и много других возможностей (подробное описание смотрите в главе Техподдержка курса Администратор. Модули).
Рассмотрим примеры тестирования крупных проектов
Клиент обратился за помощью, чтобы разобраться, почему иногда начинает «тормозить» веб-проект, при этом большую часть времени все хорошо. Дополнительно ситуация усугубляется тем, что доступ к административной части и на сервера клиент дать не может.
Были проведены следующие мероприятия:
Таким образом, работа проекта стала прозрачной. Все факты неадекватного поведения фиксируются для дальнейшего анализа. После чего проводятся работы по устранению проблемных мест.
Было проведено нагрузочное тестирование веб-кластера, при котором были получены достаточно хорошие результаты: при нагрузке 25 сайтов одновременно (они в одной базе данных - многосайтовость на веб-кластере), с помощью Яндекс.Танк система уверенно держит ~600 запросов в секунду (52 млн. хитов в сутки) на одной базе данных и трех веб-интерфейсах за балансировщиком при требуемых в ТЗ 300 хитах в сек.
Но при этом были отмечены следующие риски и даны рекомендации:
В процессе общения с клиентом были выявлены следующие проблемы в работе проекта: зависание сессии, периодически система работает медленно, проблемы с работой административной части, скопилось много файлов. При этом никаких систем аналитики на проекте нет.
Предприняты были следующие меры:
Все это делалось для того, чтобы сделать систему клиента прозрачной для анализа, чтобы рекомендовать, как настроить все их сервера, чтобы затем обсудить список приоритетных проблем, совместно найти корень каждой и устранить причину.
Использование облаков особо актуально для высоконагруженных проектов. К этому предрасполагают следующие ключевые особенности "облачных систем":
Эти характеристики позволяют получить услуги с высоким уровнем доступности и низкими рисками неработоспособности, обеспечить быстрое масштабирование вычислительной системы благодаря эластичности без необходимости создания, обслуживания и модернизации собственной аппаратной инфраструктуры.
По модели развертывания "облака" можно разделить на следующие категории:
Частное облако (private cloud) – инфраструктура, предназначенная для использования одной организацией, включающей несколько потребителей (например, подразделений одной организации), возможно также клиентами и подрядчиками данной организации. Оно может находиться в собственности, управлении и эксплуатации как самой организации, так и третьей стороны (или какой-либо их комбинации), и оно может физически существовать как внутри, так и вне юрисдикции владельца.
Публичное облако (public cloud) – инфраструктура, предназначенная для свободного использования широкой публикой. Публичное облако может находиться в собственности, управлении и эксплуатации коммерческих, научных и правительственных организаций (или какой-либо их комбинации). Оно физически существует в юрисдикции владельца — поставщика услуг.
Общественное облако (community cloud) – вид инфраструктуры, предназначенный для использования конкретным сообществом потребителей из организаций, имеющих общие задачи (например, миссии, требований безопасности, политики, и соответствия различным требованиям). Оно может находиться в кооперативной (совместной) собственности, управлении и эксплуатации одной или более из организаций сообщества или третьей стороны (или какой-либо их комбинации), и оно может физически существовать как внутри, так и вне юрисдикции владельца.
Гибридное облако (hybrid cloud) – это комбинация из двух или более различных облачных инфраструктур (частных, публичных или общественных), остающихся уникальными объектами, но связанных между собой стандартизованными или частными технологиями передачи данных и приложений (например, кратковременное использование ресурсов публичных облаков для балансировки нагрузки между облаками).
Рассмотрим архитектуру сервиса Битрикс24 как пример реализации объемного проекта на базе Bitrix Framework.
В процессе разработки самой идеи сервиса было сформулировано несколько бизнес-задач:
Исходя из этих бизнес-требований сформировались два больших фронта работ:
![]() | Традиционное устройство веб-приложений очень плохо масштабируется и резервируется. В лучшем случае возможно разнести по разным серверам само приложение, кэш и базу. Можно как-то масштабировать веб (но при этом необходимо решить вопрос синхронизации данных на веб-серверах). Кэш и база масштабируются уже хуже. А о распределенном гео-кластере (для резервирования на уровне датацентров) речь вообще не идет. |
Платформа "1С-Битрикс" включает в состав модуль Веб-кластер, который обладает следующими возможностями:
Модуль Облачные хранилища решает проблему синхронизации статического контента.
То есть штатные инструменты платформы Bitrix Framework позволяют без дополнительных усилий получить базовый функционал сервиса. То есть вопрос программной платформы был решён.
Собственное или арендуемое оборудование требует достаточно серьезных вложений в инфраструктуру на старте проекта. Масштабировать физические сервера достаточно сложно (долго и дорого). Администрировать (особенно в разных ДЦ) - неудобно. Кроме этого необходимо создание всех сопутствующих сервисов "с нуля".
На этапе старта невозможно максимально полно оценить те объемы регистраций, приток новых клиентов. Не хотелось покупать лишнее оборудование и переплачивать за него. Хотелось платить только за реальное потребление. Хотелось прийти к схеме, которая балансировалась не с пиками нагрузки в течение месяца, а в течение дня. Пик идёт днём: мы добавили нужное число машин, ночью отключили. В результате платим только за реальное потребление.
Поэтому было выбрано "облачное" размещение.
Сайты компании 1С-Битрикс работают в Amazon AWS достаточно давно. Это "облако" удобно тем, что есть множество уже готовых сервисов, которые можно просто брать и использовать в своем проекте, а не изобретать собственные велосипеды: облачное хранилище S3, Elastic Load Balancing, CloudWatch, AutoScaling и многое другое.
В очень упрощенном виде вся архитектура "Битрикс24" выглядит примерно так:
Каждый портал, заведённый в Битрикс24 имеет собственную базу внутри MySQL. На одном сервере может быть размещено до 30000 баз данных. Битрикс24, по сути, очень похож на shared hosting, когда на одном сервере размещено достаточно много баз данных клиентов, создающих разную нагрузку. В этих условиях очень важно обеспечить высокое качество работы для всех клиентов, которые там размещаются.
В такой архитектуре главный минус - избыточность приложения, это дорого по ресурсам.
Плюсы такой архитектуры
Опытным путём выяснилось, что удобнее использовать 4 инстанса на одном сервере. И на каждый инстанс выделять по два отдельных диска, без Raid, один диск для данных, второй - для логирования. В итоге получается 8 дисков: 4 с данными, 4 диска под binlog'и. Такая конфигурация значительно лучше утилизирует ресурсы сервера, более эффективная нагрузка на каждый диск.
Вынос binlog'ов на отдельный диск вызван тем, что использование отдельного диска не затрагивает производительность дисков с данными. Это позволяет достаточно часто производить записи в лог, то есть логировать большее число параметров и действий.
Сам MySQL в плане производительности и эффективности работает с 30000 базами на 4-х инстансах лучше, чем на одном "большом" инстансе.
Приложение (веб) масштабируется в проекте не вертикально (увеличение мощности сервера), а горизонтально (добавление новых машин).
Для этого используется связка Elastic Load Balancing + CloudWatch + Auto Scaling. Все клиентские запросы (HTTP и HTTPS) поступают на один или несколько балансировщиков Amazon (Elastic Load Balancing). Рост и снижение нагрузки отслеживается через CloudWatch. AutoScaling добавляет и удаляет машины.
Есть две интересные метрики:
В качестве основной характеристики используются данные о загрузке машин (EC2), так как latency может варьироваться не только из-за реальной нагрузки, но и по каким-то иным причинам: сетевые задержки, ошибки в приложении и другим. В таком случае возможны «ложные» срабатывания, и тогда крайне неэффективно будут добавляться новые машины.
В процессе разработки сервиса проводились долгие эксперименты с разными пороговыми значениями. В результате выбраны следующие: автоматически стартуют новые машины, если EC2 превышает 60% в течение 5 минут, и автоматически останавливаются и выводятся из эксплуатации, если средняя нагрузка менее 30%. Если верхний порог ставить больше (например, 70-80%), то начинается общая деградация системы - пользователям работать некомфортно (долго загружаются страницы). Нижний порог меньше — система балансировки становится не очень эффективной, машины долго могут работать вхолостую.
При проектировании возникло несколько задач, которые необходимо было решить:
Решилось это следующим образом: выбрана конфигурация без Apache. Есть только PHP-FPM + NGINX. У каждого клиента создаётся свой домен.И создан новый модуль для PHP который:
В описанной выше схеме веб-ноды по сути становятся «расходным материалом». Они могут в любой момент стартовать и в любой момент гаситься. А это значит, что никакого пользовательского контента на них быть не должно, он должен храниться либо в базе данных, либо в облачном хранилище. А на серверах должны в лучшем случае использоваться только временные файлы и директории.
Именно поэтому при создании каждого нового портала в Битрикс24 для него создается персональный аккаунт в Амазоне для хранения данных в S3. Тем самым данные каждого портала полностью изолированы друг от друга.
При этом само хранилище S3 очень надежно:
Амазон, например, говорит о том, что их архитектура S3 устроена таким образом, что они готовы обеспечить доступность на уровне двух девяток после запятой. А вероятность потери данных – одна миллиардная процента. Информации о потерянных данных в облаке Амазона пока ещё никто не обнародовал.
Сервис Битрикс24 сейчас размещается в шести дата-центрах: по два в России, в Европе и в Америке. Этим решаются сразу три задачи: во-первых, распределяется нагрузка, во-вторых, резервируются все сервисы - в случае выхода из строя одного из ДЦ, траффик просто переключается на другой, в-третьих выполняются требования европейского и российского законодательства.
База в каждом ДЦ является мастером относительно слейва во втором ДЦ и одновременно слейвом - относительно мастера.
Важные настройки в MySQL для реализации этого механизма: auto_increment_increment
и auto_increment_offset
. Они задают смещения значений для полей auto_increment
для того, чтобы избежать дублирования записей. Грубо говоря, в одном мастере - только четные ID, в другом - только нечетные.
Каждый портал (все зарегистрированные в нем сотрудники), заведенный в Битрикс24 в каждый конкретный момент времени работает только с одним ДЦ и одной базой. Переключение на другой ДЦ осуществляется только в случае какого-либо сбоя.
Базы в разных дата-центрах синхронны, но при этом независимы друг от друга: потеря связности между ДЦ может составлять часы, данные синхронизируются после восстановления.
Одним из важнейших приоритетов при проектировании сервиса "Битрикс24" была постоянная доступность сервиса и его отказоустойчивость.
В случае аварии на одной или нескольких веб-нодах Load Balancing определяет вышедшие из строя машины и, исходя из заданных параметров группы балансировки (минимально необходимое количество запущенных машин), автоматически восстанавливается нужное количество инстансов.
Если теряется связность между дата-центрами, то каждый дата-центр продолжает обслуживать свой сегмент клиентов. После восстановления связи, данные в базах автоматически синхронизируются.
Если же выходит из строя полностью дата-центр, или же, например, происходит сбой на базе, то весь трафик автоматически переключается на работающий дата-центр.
Если это вызывает повышенную нагрузку на машины, то CloudWatch определяет возросшую утилизацию CPU и добавляет нужное количество машин уже в одном дата-центре в соответствие с правилами для AutoScaling.
При этом приостанавливается мастер-мастер репликация. После проведения нужных работ (восстановительных - в случае аварии, или плановых - например, точно по такой же схеме был осуществлен переход со стандартного MySQL на Percona Server (см. далее) - при этом без какого-либо downtime'а для пользователей сервиса) база включается в работу и восстанавливается репликация.
Если все прошло штатно, трафик снова распределяется на оба дата-центра. Если при этом средняя нагрузка стала ниже порогового значения, то лишние машины, которые поднимались для обслуживания возросшей нагрузки, автоматически отключаются.
Для хранения данных в базе рассматривалось несколько разных вариантов.
Первым кандидатом на использование в проекте был Amazon Relational Database Service (Amazon RDS) - облачная база данных. Есть поддержка MS SQL, Oracle и, главное, MySQL.
Сервис хороший, но оказался неподходящим для конкретного проекта.
Причины: |
---|
|
Рассмотрев еще несколько вариантов, было решено начать со стандартного MySQL. На этапе проектирования и закрытого бета-тестирования сервиса попутно рассматривались различные форки. Самими интересными, на наш взгляд, оказались Percona Server и MariaDB.
В итоге, в качестве основного сервера баз данных, для проекта была выбрана Percona Server.
Ключевые особенности, которые оказались важны для проекта:
Еще одним важным моментом было то, переход со стандартного MySQL на Percona Server вообще не потребовал изменения какого-либо кода или логики приложения.
В качестве системы хранения данных в MySQL была выбрана "улучшенная версия" InnoDB - XtraDB:
Amazon Virtual Private Cloud (Amazon VPC) позволяет организовать приватные изолированные сети в публичном облаке Amazon. Amazon VPC предоставляет полный контроль над сетевой средой, включая выбор собственного диапазона адресов, создание подсетей, и конфигурации таблиц маршрутизации и сетевых шлюзов.
Также Amazon VPC позволяет легко настроить сетевую конфигурацию. Например, можно создать публичную подсеть для фронтэнд веб-серверов, которая будет иметь доступ к Интернету, и разместить бэкэнд (базы данных или серверы приложений) в частной подсети без доступа в Интернет.
Система позволяет использовать несколько уровней безопасности, включая группы безопасности и списки контроля доступа к сети, для упрощения управления доступом к Amazon EC2 для каждой подсети.
Кроме того, можно создать VPN подключение (Hardware Virtual Private Network) между корпоративной сетью и вашей Amazon VPC и использовать "свое облако" как расширение корпоративной сети.
Рассмотрим небольшой схематичный пример использования Amazon VPC для организации работы веб-приложения в нем с доступом клиента из его локальной сети через VPN.
С точки зрения Amazon, выделение частного облака начинается с создания VPC (CIDR /16) и его разбиения на более мелкие подсети. Затем с подсетями работают остальные технологии частного облака: файерволы, шлюзы, балансировщики, роутеры, соединения VPN и т.д.
Облако клиента начинается с его изолированной подсети в Amazon с машинами и другими объектами инфраструктуры. Облако соединяется с локальной сетью компании клиента через VPN-шлюз. Создаем сначала один раз VPC для всех клиентов - 10.0.0.0/16.
Важно при создании VPC активировать использование DNS-сервера Amazon для внутренних и внешних IP-адресов.
Затем делим VPC на подсети /26 для каждого клиента - меньше подсеть создать нельзя, т.к. внутренний балансировщик работает только с подсетями больше /27 и в подсети должно быть не меньше свободных 20 адресов (а также резервируется 4 начальных адреса и один последний).
Подсеть создается только внутри одной AZ (датацентра), поэтому нужно для каждого клиента создать 2 подсети - по одной в каждом ДЦ. Изолировать подсети клиентов можно через ACL, которые работают на уровне подсетей.
Для того чтобы иметь возможность обновлять софт на машинах клиентов в каждой из двух подсетей, нужно организовать доступ из подсетей в интернет.
Есть 2 пути:С точки зрения повышения безопасности правильнее вариант 2 - чтобы на машины не было прямого доступа через интернет, даже с определенных адресов. Также придется поднять еще один VPN-шлюз Amazon и ходить в "облака" клиентов через него.
Чтобы клиент мог получать доступ к веб-приложению, которое находится в "частном облаке", по привычному URL без установки на свои ПК клиентской части VPN - необходимо настроить тоннель через маршрутизатор на стороне клиента и на стороне Amazon.
Для Amazon это стандартная возможность. Для клиента же придется произвести соответствующие настройки в его среде.
Для отказоустойчивости, масштабируемости при нагрузке и переключении автоматическом при аварии ДЦ - можно настроить внутренний балансировщик, обслуживающий 2 подсети "облака клиента" в двух датацентрах.
Естественно, в "облаке" уже должна быть собрана и настроена инфраструктура автоматического масштабирования и управления нагрузкой.
В состав Amazon Web Services входят наиболее востребованные сервисы для организации различных бизнес-решений в одном месте (облаке).
Список некоторых интересных сервисов AWS:
За всей технологичностью сервисов скрывается простота и легкость управления. Амазон сделали действительно, по сравнению с «не облачным» набором сервисов и услуг, платформу для быстрого и удобного разворачивания бизнес-решений.
В плане глобальной инфраструктуры, дата-центры Амазон расположены в различных географических точках мира.
Дата-центры, расположенные в какой-либо одной точке мира, у Амазон называются «регионом» (Region). На данный момент имеются 9 регионов:
В свою очередь, каждый регион делится на «зоны доступности» (Availability Zone). Зона доступности – это конкретное физическое местоположение внутри региона, спроектированное таким образом, чтобы оно было изолировано от проблем в других зонах в том же регионе (т.е. выход из строя оборудования в одной зоне никак не повлияет на работоспособность оборудования в других зонах), и в то же время между зонами одного региона существует быстрое высокоскоростное сетевое соединение.
Такое построение инфраструктуры позволяет, например, сделать свой сервис ближе для целевой аудитории, расположив его в том же регионе. Для обеспечения максимального уровня отказоустойчивости сервиса, его можно расположить в разных зонах доступности (на случай проблем в одной конкретной зоне доступности).
Еще одна отличительная особенность сервисов от Амазон – это надежность. Большинство высокоуровневых сервисов Амазон имеют встроенные механизмы для обеспечения отказоустойчивости и высокой доступности. Также Амазон позволяет делать логический и физический бэкап данных на случай аварии.
Например, для сервиса Amazon S3 гарантируется 99.99999999% сохранности данных и их 99,99% доступности в течение года.
Например, при использовании образов виртуальных машин в "облаках", в качестве основного сервера баз данных вместо обычного MySQL можно использовать его форк Percona Server:
В качестве же системы хранения данных в MySQL также можно использовать "улучшенную версию" InnoDB - XtraDB:
В облаке, так же, как и для обычного MySQL, для серьезных проектов, можно и нужно использовать репликацию.
В качестве мониторинга и аналитики, в дополнение к тому, что предлагает сам облачный провайдер, можно использовать все те же привычные сервисы, которые используются в случае "не облака": nagios, munin и прочие.
При использовании "облаков" все-таки существуют некоторые нюансы. Например, стоит упомянуть проблему скорости.
Самое простое в этом случае - организовать RAID. Например, Amazon позволяет организовать "софтварный" рейд довольно просто, который работает долго и практически не ломается.
Также стоит отметить, что если "облако" позволяет, то можно использовать специальные быстрые диски. У Amazon, например, есть диски с Provisioned IOPS.
Существует два основных метода запуска базы данных в "облаке":
Так же можно приобрести хостинг базы данных, в случае если база данных не предоставляется как сервис. Например, облачный провайдер Rackspace предлагает такую услугу для баз данных MySQL.
Источник: Wikipedia
Использование "облака" является не только модным трендом, но может приносить реальную пользу для проектов. В продуктах Bitrix Framework имеется целый ряд таких "облачных" сервисов которые на практике приносят пользу:
CDN "1С-Битрикс":
Облачные хранилища позволяют перенести хранение файлов вместо локального сервера веб-сайта в "облака".
Выгоды от использования облачных хранилищ:
Основные особенности резервного копирования в облачное хранилище от "1С-Битрикс":
Возможно хранение резервной копии в облачном хранилище, предоставляемое компанией "1С-Битрикс" или же в собственном облачном хранилище.
Инспектор сайтов периодически проверяет доступность и работоспособность сайта и сообщает обо всех неполадках через E-mail или push-уведомления для мобильных устройств.
Инспектор сайта отслеживает 4 параметра, очень важных для работы любого веб-проекта, и особенно интернет-магазина:
Можно выделить следующие типы мониторинга самого сайта:
Мониторинг доступности - отслеживание того, работает ли он или нет. Наиболее оптимальным для простого мониторинга является десятиминутный интервал: большинство пользователей попытаются вернуться на сайт в течение 1-2 часов, а за это время можно как обнаружить проблемы, так и эффективно их устранить без особого вреда для бизнеса компании.
Мониторинг проблем - отслеживание нескольких параметров сайта с частотой не менее раза в минуту и из нескольких географических точек (чтобы максимально покрыть минутный интервал проверками и установить возможные проблемы, связанные с географией пользователей).
Среди возможных критериев проверки можно выделить проблемы с:
Этот метод особенно хорош, когда требуется отловить какую-то «плавающую» ошибку, а несколько независимых сервисов либо точек проверки позволяют добиться частоты проверки вплоть до раза в 10 секунд - а это более чем достаточно, чтобы обнаружить все, что необходимо.
Мониторинг может быть не долговременным (до обнаружения и исправления проблем) либо периодическим (в целях профилактики проблем).
Мониторинг работоспособности - сюда может относиться любой сложный функционал, который может быть затронут какими-либо изменениями на сайте, например, кабинет интернет-банка. В этом случае необходимо настраивать цепочки проверок либо задавать сложные условия для проведения проверок того или иного функционала сайта.
При построении отказоустойчивой распределенной инфраструктуры кроме обеспечения нескольких уровней надежности обычно закладывают и несколько уровней мониторинга системы. Среди них можно выделить:
Munin выдает большое количество информации о состоянии требуемого сервера. К наиболее часто проверяемым моментам относят:
Примеры реальной системы (загрузка и база данных) |
---|
![]() ![]() ![]() ![]() |
Nagios как решение для мониторинга безусловно хорош. Но нужно быть готовым к тому, что кроме него придется использовать еще собственные скрипты и (или) Pinba (или аналогичное решения дл вашего языка программирования). Pinba оперирует UDP-пакетами и собирает информацию о времени выполнения скриптов, объеме памяти и кодах ошибок. В принципе, этого достаточно для создания полной картины происходящего и обеспечения требуемого уровня надежности сервиса в автоматическом режиме.
На уровне внутреннего мониторинга уже можно принимать решения о выделении дополнительных мощностей (если это возможно автоматически — то достаточно просто отслеживать средний уровень загрузки процессора на серверах приложений или базы данных, если это производится в ручном режиме — то можно высылать письма или jabber-сообщения) или их отключении. Также в случае возникновения аномального количества ошибок (обычно это происходит при отказе оборудования либо ошибки в новой версии веб-сервиса, и что является причиной, всегда можно установить за счет дополнительных проверок) можно слать уже экстренные уведомления по смс или звонить по телефону.
Также очень удобно настроить автоматическое добавление (или удаление) тестов при увеличении точек проверки (например, серверов ли пользовательских сайтов) с заданными шаблонами: например, проверка главной страницы, распределение времени выполнения PHP, распределение использования памяти для PHP, число nginx и PHP ошибок.
Мониторинг на уровне облачной инфраструктуры предлагает не такое большое количество провайдеров, и он является, скорее, информационным: реальные решения принимаются либо на основе внутренних данных, либо внешнего состояния системы. На промежуточном уровне можно только собирать статистику или подтверждать внутреннее состояние инфраструктуры.
Для Amazon CloudWatch здесь доступны следующие возможности проверки:
Также можно добавлять свои собственные метрики.
Уже по результатам мониторинга промежуточного (на уровне балансировщиков) можно принимать обоснованное решение о выделении или закрытии машин (инстансов) в кластере.
Здесь выбор решений очень большой. Если требуется отслеживать состояние серверов по всему миру, то лучшее решение — это Pingdom. Для российских реалий подойдет PingAdmin, Monitorus или WEBO Pulsar. Особенно удобно настроить проверку из нескольких географических точек и дергать удаленный скрипт уведомления, если сервис не доступен в течение 1-2 минут. Если при этом есть какие-либо проблемы внутри, то можно сразу переключаться на план "Б" (выключать неработающие сервера, отсылать уведомления и т.д.).
К дополнительным плюсам внешнего мониторинга можно отнести проверку реального времени ответа на стороне сервера (или реальных сетевых задержек). По этому параметру также можно настроить уведомления. Как дополнительная возможность в случае использования CDN: можно отслеживать полное время загрузки страниц сервиса и отключать или включать CDN для разных регионов.
Не стоит забывать и принимать во внимание некоторые нетипичные, но важные параметры, которые тоже необходимо отслеживать:
Некоторые дополнительные материалы, которые могут помочь вам при создании и эксплуатации высоконагруженных проектов.
В ряде случаев при создании своих проектов имеет смысл не использовать какие-либо фреймворки, сервера и так далее, а написать свой код, например, тот же веб-сервер. Такое решение будет самым оптимальным, так как вы сможете учесть все нюансы именно вашей задачи. Это не сложно, но и не просто. Это требует глубокого понимания как устроена операционная система, какие бывают сетевые протоколы, как правильно взаимодействовать с ядром операционной системы через интерфейс системных вызовов.
Обработкой сетевых сокетов должно заниматься ядро операционной системы и уведомлять вас о наступлении события:
На данный момент доминируют другие методы обработки соединений: создаётся куча потоков или процессов (работают медленнее и потребляют значительно больше памяти). Это работает в случаях когда дешевле купить еще одну "железку" чем учить программиста асинхронной обработке демультиплексированных сокетов. Однако когда нужно решить задачу эффективно на текущем оборудовании, сократив издержки на 1-2 порядка, иного способа как научиться понимать суть сетевых процессов и программировать в соответствии с их законами - нет.
Считается, что асинхронная обработка демультипрексированных сокетов - это гораздо сложнее с точки зрения программирования, чем 50 строк в отдельном процессе. Но это не так. Даже на заточенном немного на другие задачи PHP написать быстрый сервер совсем просто.
Рассмотрим пример написания сервера на PHP.
В PHP есть поддержка BSD-сокетов. Но это расширение не поддерживает ssl/tls. Поэтому нужно использовать интерфейс потоков streams. За этим интерфейсом можно увидеть сетевые сокеты и довольно эффективно с ними работать.
Полный исходный код сетевого сервера приводить не будем, отобразим его ключевые части. Сервер устойчиво держит без перекомпиляции PHP до 1024 открытых сокета в одном процессе, занимая около 18-20 МБ и работая в одном процессе операционной системы, загружая "одно" ядро процессора. Если пересобрать PHP, то select может работать с гораздо большим числом сокетов.
Задачи ядра сервера:
То есть в ядро сервера отправляются задания на работу сокетами (например, ходить по сайтам и собирать данные и тому подобное) и ядро в одном процессе начинает выполнять сотни заданий одновременно.
Задание это объект в терминологии ООП типа FSM. Внутри объекта имеется стратегия. Например: "зайди по этому адресу, создай запрос, загрузи ответ, распарси и т.п. возвраты в начало и в конце запиши результат в NoSQL". То есть можно создать задание от простой загрузки содержимого, до сложной цепочки нагрузочного тестирования с многочисленными ветвлениями - и это все будет жить в объекте задания.
Задания в данной реализации ставятся через отдельный управляющий сокет на 8000 порту - пишутся json-объекты в tcp-сокет и затем начинают свое движение в сервером ядре.
Главное: не позволить серверному процессу заблокироваться в ожидании ответа в функции при чтении или записи информации в сетевой сокет, при ожидании нового соединения на управляющий сокет или где-нибудь в сложных вычислениях/циклах. Поэтому все сокеты заданий проверяются в системном вызове select и ядро ОС уведомляет нас лишь тогда, когда событие случается (либо по таймауту).
while (true) { $ar_read = null; $ar_write = null; $ar_ex = null; //Собираемся читать также управляющий сокет, вместе с сокетами заданий $ar_read[] = $this->controlSocket; foreach ($this->jobs as $job) { //job cleanup if ( $job->isFinished() ) { $key = array_search($job, $this->jobs); if (is_resource($job->getSocket())) { //"надежно" закрываем сокет stream_socket_shutdown($job->getSocket(),STREAM_SHUT_RDWR); fclose($job->getSocket()); } unset($this->jobs[$key]); $this->jobsFinished++; continue; } //Задания могут "засыпать" на определенное время, например при ошибке удаленного сервера if ($job->isSleeping()) continue; //Заданию нужно инициировать соединение if ($job->getStatus()=='DO_REQUEST') { $socket = $this->createJobSocket($job); if ($socket) { $ar_write[] = $socket; } //Задание хочет прочитать ответ из сокета } else if ($job->getStatus()=='READ_ANSWER') { $socket = $job->getSocket(); if ($socket) { $ar_read[] = $socket; } //Заданию нужно записать запрос в сокет } else if ( $job->getStatus()=='WRITE_REQUEST' ) { $socket = $job->getSocket(); if ($socket) { $ar_write[] = $socket; } } } //Ждем когда ядро ОС нас уведомит о событии или делаем дежурную итерацию раз в 30 сек $num = stream_select($ar_read, $ar_write, $ar_ex, 30);
Далее, когда событие произошло и ОС уведомила нас, начинаем в неблокирующем режиме обработку сокетов. В коде ниже можно еще немного оптимизировать обход массива заданий, индексировать задания по номеру сокета и выиграть примерно 10мс.
if (is_array($ar_write)) { foreach ($ar_write as $write_ready_socket) { foreach ($this->getJobs() as $job) { if ($write_ready_socket == $job->getSocket()) { $dataToWrite = $job->readyDataWriteEvent(); $count = fwrite($write_ready_socket , $dataToWrite, 1024); //Сообщаем объекту сколько байт удалось записать в сокет $job->dataWrittenEvent($count); } } } } if (is_array($ar_read)) { foreach ($ar_read as $read_ready_socket) { ///// command processing /// //Пришло соединение на управляющий сокет, обрабатываем команду if ($read_ready_socket == $this->controlSocket) { $csocket = stream_socket_accept($this->controlSocket); //Тут упрощение - верим локальному клиенту, что он закроет соединение. Иначе ставьте таймаут. if ($csocket) { $req = ''; while ( ($data = fread($csocket,10000)) !== '' ) { $req .= $data; } //Обрабатываем команду также в неблокирующем режиме $this->processCommand(trim($req), $csocket); stream_socket_shutdown($csocket, STREAM_SHUT_RDWR); fclose($csocket); } continue; /// ///// } else { //Читаем из готового к чтению сокета без блокировки $data = fread($read_ready_socket , 10000); foreach ($this->getJobs() as $job) { if ($read_ready_socket == $job->getSocket()) { //Передаем заданию считанные данные. Если сокет закроется, считаем пустую строку. $job->readyDataReadEvent($data); } } } } } }
Сам сокет инициируется также в неблокирующем режиме, важно установить флаги, причем оба! STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT:
private function createJobSocket(BxRequestJob $job) { //Check job protocol if ($job->getSsl()) { //https $ctx = stream_context_create( array('ssl' => array( 'verify_peer' => false, 'allow_self_signed' => true ) ) ); $errno = 0; $errorString = ''; //Вот тут происходит временами блокировочка в 30-60мс, видимо из-за установки TCP-соединения с удаленным хостом, надо глянуть в исходники, но снова ... лень $socket = stream_socket_client('ssl://'.$job->getConnectServer().':443',$errno,$errorString,30,STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT,$ctx); if ($socket === false) { $this->log(__METHOD__." connect error: ". $job->getConnectServer()." ". $job->getSsl() ."$errno $errorString"); $job->connectedSocketEvent(false); $this->connectsFailed++; return false; } else { $job->connectedSocketEvent($socket); $this->connectsCreated++; return $socket; } } else { //http ...
Код самого задания: оно должно уметь работать с частичными ответами/запросами. Для начала сообщим ядру сервера что мы хотим записать в сокет.
//Формируем тело следующего запроса function readyDataWriteEvent() { if (!$this->dataToWrite) { if ($this->getParams()) { $str = http_build_query($this->getParams()); $headers = $this->getRequestMethod()." ".$this->getUri()." HTTP/1.0\r\nHost: ".$this->getConnectServer()."\r\n". "Content-type: application/x-www-form-urlencoded\r\n". "Content-Length:".strlen($str)."\r\n\r\n"; $this->dataToWrite = $headers; $this->dataToWrite .= $str; } else { $headers = $this->getRequestMethod()." ".$this->getUri()." HTTP/1.0\r\nHost: ".$this->getConnectServer()."\r\n\r\n"; $this->dataToWrite = $headers; } return $this->dataToWrite; } else { return $this->dataToWrite; } }
Теперь пишем запрос, определяя сколько еще осталось.
//Пишем запрос в сокет до того момента, когда полностью запишем его тело function dataWrittenEvent($count) { if ($count === false ) { //socket was reset $this->jobFinished = true; } else { $dataTotalSize = strlen($this->dataToWrite); if ($count<$dataTotalSize) { $this->dataToWrite = substr($this->dataToWrite,$count); $this->setStatus('WRITE_REQUEST'); } else { //Когда успешно записали запрос в сокет, переходим в режим чтения ответа $this->setStatus('READ_ANSWER'); } } }
После получения запроса, читаем ответ. Важно понять, когда ответ прочитан полностью. Возможно понадобиться установить таймаут на чтение.
//Читаем из сокета до момента, когда полностью прочитаем ответ и совершаем переход в другой статус function readyDataReadEvent($data) { ////////// Successfull data read ///// if ($data) { $this->body .= $data; $this->setStatus('READ_ANSWER'); $this->bytesRead += strlen($data); ///// ////////// } else { ////////// А тут мы уже считали ответ и начинаем его парсить ///// ////////// redirect if ( preg_match("|\r\nlocation:(.*)\r\n|i",$this->body, $ar_matches) ) { $url = parse_url(trim($ar_matches[1])); $this->setStatus('DO_REQUEST'); } else if (...) { //Так мы сигнализируем ядру сервера, что задание нужно завершить $this->jobFinished = true; ... } else if (...) { $this->setSleepTo(time()+$this->sleepInterval); $this->sleepInterval *=2; $this->retryCount--; $this->setStatus('DO_REQUEST'); } $this->body = ''; ...
В последнем фрагменте кода можно иерархически направлять FSM по заложенной стратегии, реализуя различные варианты работы задания.
Решить задачу одновременной работы с сотнями и тысячами заданий и сокетов всего в одном процессе PHP можно просто и лаконично. Если поднять для данного сервера сколько процессов PHP, сколько ядер на сервере, то можно вести речь о тысячах обслуживаемых клиентов. Серверный процесс PHP при этом потребляет всего около 20МБ памяти.
Решение технических задач достаточно часто требует нового, незашоренного взгляда на проблему и способы её решения. Рассмотрим пример того, как типичную бэкендовую задачу можно решить с помощью инструментов фронтенда.
Скорость работы сайта - постоянная забота разработчика. И если раньше основное внимание уделялось бэкенду, то в последнее время всё больше приходится смотреть на фронтенд, с точки зрения клиента, для которого есть единственный критерий: как быстро сайт открывается.
В процессе работы над проблемой скорости сайта возникла задача создания сервиса, который будет показывать пользователю работу его сайта, что с его сайтом происходит. Это в конечном счёте было реализовано в виде сервиса Скорость сайта.
В качестве инструмента учёта при решении задачи используется Navigation Timing API, который позволяет на клиентской стороне очень подробно разобрать каждый запрос: на какой этап сколько времени ушло. Инструмент поддерживается всеми современными браузерами, очень удобен и информативен. Его можно в виде простого счётчика встроить на сайт и получить все необходимые данные, обработать их и показать пользователю в виде диаграмм, графиков.
Все данные получаемые через Navigation Timing можно собрать, обработать, но куда это сохранить? Сохранить можно клиенту на его сайт, в его локальную базу. Но таким образом решая задачу производительности мы добавляем нагрузку на клиентскую Базу данных. Это неправильно, надо создавать свой собственный сервис, где мы за клиента агрегировали бы эти данные, делали выборки и отдавали клиенту только готовые графики.
Оценивая ориентировочную нагрузку, которая могла лечь на такой сервис, подсчитали:
Все эти данные нужно сохранить, агрегировать и выдать пользователю.
Сервис требовал решения многих проблем, но сейчас рассмотрим только решение задачи, когда нужно хит от JS скрипта быстро получить, быстро сохранить и сообщить клиенту о том, что данные получены.
Сделать это можно с помощью сервиса Kinesis от Amazon'а. Это - некий большой буфер, задача которого как раз: очень быстро принять некий набор данных, так же быстро сказать пользователю: всё принято. А потом из этого буфера можно любыми воркерами, на любом удобном языке программирования достать, обработать данные и построить на этом какую-то аналитику.
Установка перед Kinesis NGINX и использование его как некий буфер для складирования данных в Kinesis не позволяла решить задачу:
Варианты решения:
Решение на бэкенде: добавить PHP-FPM, написать простой скрипт в несколько строк, который делает нужные запросы и отвечает ровно так как нужно. Это в целом очень неудачное решение, когда для решения простой задачи использовался сложный и тяжёлый инструмент, который расходует много памяти, запросы с него получаются блокирующими (сколько воркеров PHP есть, столько запросов и можно отправить, остальные выстраиваются в очередь.)
Это приемлемый компромисс для невысоконагруженного сервиса, где не идут хиты пачками. Администратор своего проекта может раз в день, раз в неделю эту проверку выполнить. Компромисс на первое время устраивает, но в будущем могут возникнуть ситуации, когда такое решение работать уже не будет, либо потребует дополнительных мощностей, что сделает его достаточно "дорогим".
Но наше внимание привлёк новый, неизвестный нам модуль Lua. Реально это оказался очень простой инструмент. Кусок кода можно написать прямо в конфиге NGINX'а, либо вынести в отдельный файл и подключить в конфиге NGINX'а. По сути - это некий скриптовый язык, который может реализовать самую сложную кастомную логику. Плюсы Lua:
| ![]() |
Ниже приведён не полный код (базовые функции), который был реализован для проксирования и складывания запросов в Kinesis:
http { lua_package_cpath "/usr/lib64/crypto.so;;"; server { location /bx_stat { lua_need_request_body on; rewrite_by_lua ‘ local data = "" if ngx.var.request_method == "POST" then data = ngx.req.get_body_data() else data = ngx.var.query_string; ngx.req.set_uri_args(""); end ... local crypto = require "crypto" local kDate = crypto.hmac.digest("sha256", date_short, "AWS4" .. secret_key, true) ... ngx.req.set_header("x-amz-date", date_long) ngx.req.set_header("Authorization", "AWS4-HMAC-SHA256 Credential= ... ‘; proxy_set_header Host kinesis.eu-west-1.amazonaws.com; proxy_pass https://kinesis.eu-west-1.amazonaws.com/;
В коде подключается внешняя библиотека для реализации функции шифрования, отбираются параметры, которыми подписывается запрос, генерируется собственно авторизационный запрос в Kinesis, указывается куда дальше проксируется запрос (на амазоновский хост). Так можно избавится от бэкенда и всю логику реализовать на фронтенде.
Требуется при этом произвести дополнительные настройки на стороне NGINX'а:
worker_processes 4; events { worker_connections 10240; } worker_rlimit_nofile 100000; proxy_hide_header Access-Control-Expose-Headers; proxy_hide_header x-amz-id-2; proxy_hide_header x-amzn-RequestId; proxy_hide_header Content-Type; proxy_method POST;
Так как запросов много, то изменено число коннектов, обрабатываемых одним воркером, оцениваем сколько нам нужно будет открытых файлов и открытых соединений. Убраны заголовки из ответа, чтобы не было видно, что данные отправляются в Амазон. И установлено, что данные отправляются в Kinesis через POST, так как он принимает данные только так.
Так как реально тестирование началось с 1000 хитов в секунду, то пришлось настраивать параметры сети в операционной системе. Эти параметры по умолчанию в Linux стоят очень скромные, не ориентированные под высокую нагрузку, и почти всегда приходится их перенастраивать.
В описываемом случае очень быстро закончился диапазон портов исходящих соединений. С помощью файла /var/log/messages
можно легко отследить что происходит с сетью и изменить параметры под потребности. Вот результат настроек, после которых всё заработало как это требовалось:
/etc/sysctl.conf (man sysctl) # диапазон портов исходящих коннектов net.ipv4.ip_local_port_range=1024 65535 # повторное использование TIME_WAIT сокетов net.ipv4.tcp_tw_reuse=1 # время пребывания сокета в FIN_WAIT_2 net.ipv4.tcp_fin_timeout=15 # размер таблиц файрволла net.netfilter.nf_conntrack_max=1048576 # длина очереди входящих пакетов на интерфейсе net.core.netdev_max_backlog=50000 # количество возможных подключений к сокету net.core.somaxconn=81920 # не посылать syncookies на SYN запросы net.ipv4.tcp_syncookies=0
По результатам начальной работы потребовались дополнительные настройки: по логу Kinesis'а стало заметно, что всё ломается на 1000 хитов. Оказалось, что фронтенд со всем справлялся, но, несмотря на то, что увеличен диапазон исходящих портов, их всё равно не хватает. Помогло то, что Kinesis поддерживает keepalive соединения, и то, что NGINX тоже поддерживает эти соединения на бэкенде. Мы в proxy_pass прописали не один конкретный хост, а прописали upstream, в котором указали нужный хост, а через keepalive прописали сколько соединений держать открытыми и не закрывать.
upstream kinesis { server kinesis.eu-west-1.amazonaws.com:443 max_fails=0 fail_timeout=10s; keepalive 1024; } proxy_pass https://kinesis/; proxy_http_version 1.1; proxy_set_header Connection "";
На изучение и разработку всего этого ушло не более недели в режиме не полной занятости.
Всё реально заработало на виртуальной машине с 2-я ядрами и 4-мя Гб оперативной памяти, совершенно спокойно обрабатывая нагрузку. Для локализации failover надо просто добавить вторую машину, что сделать очень просто, так как там нечему падать: там нет базы данных, нет нагрузки на диски. Можно также быстро, при необходимости отмасштабировать, поставив сколько нужно машин с балансировщиком.
Решение этой проблемы позволило решить и другие задачи, позволяющие отвязаться от структуры Амазона и увеличить одновременно, скорость доступа для клиентов Битрикс24. В сервисе всё работает хорошо, но RTT не ускорить, если сервера в Ирландии или в США, а клиент в России.
Решение было таким: поставить балансировщик в России, который по максимуму запросы клиентов обрабатывает сам, то есть отдаёт всю статику. Кроме того Композитный кеш тоже отдаётся с этого же балансировщика. Там же настроен SPDY и так далее. Часть из этого списка решалось стандартными средствами NGINX. Но вот сброс кеша по разным условиям не решался штатным функционалом в NGINX'е.
К этой задаче был применён всё тот же подход с использованием Lua. Можно удалять кеш прямо из файловой системы без всяких подзапросов или чего ещё либо. Задача решалась через header_filter_by_lua - удаление кэша высчитыванием пути к файлам и удалением через Lua os.remove В документации к модулю расписано как хранится кеш (задаётся глубина хранения, высчитывается MD5 от ключа, берётся последний символ, если это вложения 1, 2, 3 уровня). Все пути высчитываются, затем файлики удаляются. Всё сделано на нескольких строчках кода. Мы получил то что хотели:
Есть библиотека Lua-resty, которая предоставляет интерфейсы для работы с:
То есть можно написать код, который непосредственно из NGINX'а будет соединяться с базой, выполнять запросы и получать ответы. Это можно применять в конфигурациях, в которых есть очень много условий.