Несмотря на то, что мы предпочитаем писать про микросервисы, Kubernetes и прочее из области cloud native, нам хорошо известен и другой мир — гораздо более реальный, если посмотреть «в массе», например, на интернет-магазины (даже весьма успешные). В нём нет автоматического provisioning и масштабирования, сложной балансировки нагрузки и прочих красивых технических решений. Зато есть «Чёрная пятница», которая уже завтра, а значит — времени на подготовку практически не осталось. Конечно, наш рецепт №1 по качественной подготовке к ней (а заодно и получению целого букета дополнительных плюсов) — миграция на микросервисную архитектуру и Kubernetes, но предположим, что по какой-то причине этот вариант не подходит (до завтра его всё равно не реализовать).

Эта статья — список более-менее быстрых действий для оптимизации типовой инфраструктуры интернет-магазина (рассматриваются примеры с nginx, Apache, PHP, MySQL) с целью её подготовки к высоким нагрузкам. Они могут быть весьма очевидны для опытных системных администраторов, однако наверняка окажутся полезными для тех, кто глубоко ещё не погружался в эти вопросы, а их актуальность стремительно нарастает. Итак, попробуем выжать максимум из того, что есть в инфраструктуре, или хотя бы взять на заметку те основные вопросы, которыми стоит озаботиться перед следующими всплесками нагрузки.

1. Разбейте инфраструктуру и настройте мониторинг


Не имея средств отслеживания даже базовых показателей нагрузки (CPU, память, дисковый ввод/вывод, занятость интернет-канала и т.п.), действия по оптимизации будут во многом осуществляться «вслепую».

Если вся инфраструктура представляется одним неподъёмным монолитом, когда ключевые службы (веб-сервер, СУБД, вспомогательные сервисы) не разбиты по отдельным серверам/виртуальным машинам, то начинать нужно с её соответствующей реорганизации. Это сделает собираемую статистику гораздо более наглядной и полезной: переключаясь между основными графиками разных виртуальных машин, станет ясно, каким компонентам инфраструктуры не хватает процессора или памяти.

Примечание: Пример дополнительного преимущества такого разграничения — закончившиеся ресурсы для СУБД приведут не к полной недоступности сайта (когда веб-сервер вообще не отвечает), а к выдаче информативной ошибки (или даже минимально функциональной заглушки — подробнее см. ниже). Для этого, правда, понадобится ещё корректно настроить взаимодействие приложения/веб-сервера с СУБД: подключение к СУБД и получение данных не должно быть слишком долгим, т.к. иначе это приведёт к появлению множества «зависших» (в ожидании) процессов веб-сервера (и, возможно, превышению максимального числа его процессов) и забиванию ресурсов соответствующего сервера/ВМ.
Как это решается?
В простейшем случае для Apache, PHP и MySQL необходимо подобрать разумное значение max_connections, близкое к максимально возможному числу одновременных процессов веб-сервера + других источников обращений (cron-скриптов). Если это значение будет слишком маленьким, то даже нетребовательные к ресурсам запросы к СУБД перестанут выполняться, а если слишком много — оперативная память в один момент может закончиться. Отчасти в этом поможет использование легендарного скрипта mysqltuner.pl.

Дополнительно для борьбы с «висящими» процессами стоит обратить внимание на директиву mysql.connect_timeout в PHP (/etc/php5/apache2/php.ini) и системную переменную wait_timeout в MySQL. Разумеется, помимо настройки таймаутов в сервисах необходимо предусмотреть их корректную обработку приложением, чтобы в случае их срабатывания выводилась правильная заглушка или производились какие-то другие действия.

Очевидное и готовое (быстрое в развёртывании, хорошо документированное) Open Source-решение для статистики и мониторинга ключевых системных показателей (как минимум нужно оперативно узнавать о заканчивающихся ресурсах) — Zabbix. Как и все другие продукты, он не идеален, и существует ряд альтернатив (например, таких), но для решаемых задач отлично подходит (и, пожалуй, можно говорить, что в значительной степени он укрепился как стандарт де-факто).



По-хорошему, конечно, помимо графиков общих системных показателей полезно настроить более полезные для бизнеса значения (от количества обрабатываемых запросов к динамическому контенту до числа совершаемых заказов).

Альтернативный путь — готовые сервисы, с которыми может быть проще и удобнее в установке и использовании, но за это придётся платить (пример — разрабатываемый в России okmeter).

К мониторингу основных значений, собираемых выбранной системой, полезно добавить отслеживание доступности ключевых страниц (корректный ответ веб-сервера и разумное время отклика), чтобы узнавать о проблемах в функционировании интернет-магазина до момента спада продаж. В упомянутом Zabbix такие возможности, конечно, предусмотрены.

2. Проверьте нагрузку


Логичное следствие внедрения системы мониторинга и статистики — её «проверка боем», т.е. нагрузочным тестированием. Для этих целей тоже существует множество Open Source-средств: от классического и простейшего ab до более сложных решений вроде Apache JMeter (вот краткий обзор некоторых из них). Для самого простого и быстрого тестирования можно прибегнуть к упомянутой утилите ab, а в качестве более универсального, комплексного инструмента посоветуем российскую разработку — Яндекс.Танк (см. пример её использования от создателей).

В идеале тестировать нужно всё, что действительно критично для бизнеса интернет-магазина: главную страницу, разделы каталога, просмотр товара, его заказ (с регистрацией пользователя) и т.п. Тестирование одной страницы (например, главной) поможет выявить глобальные проблемы (используемые повсеместно SQL-запросы или тот факт, что общая нагрузка так высока, что динамическая отдача страниц перестаёт работать в принципе), однако скроет более частные проблемы, связанные с SQL-запросами к конкретным таблицам (или просто с конкретными запросами), использованием в коде дополнительных механизмов и служб (например, отправка электронной почты пользователю или получение данных из какого-то стороннего сервиса/кэша) и т.п.

В проведении нагрузочного тестирования поможет и настроенная на предыдущем этапе система статистики/мониторинга, т.к. вы сможете посмотреть на графики потребления ресурсов (и на основе этих данных оценить потенциал инфраструктуры по максимальному трафику), а также убедиться в срабатывании мониторинга, когда ситуация становится критичной.

Актуальность следующих шагов во многом будет зависеть от результатов нагрузочного тестирования.

3. Разберитесь со статикой


Если вдруг у вас все запросы обслуживает условный Apache (а практика показывает, что такое действительно бывает), добавьте перед ним (в качестве фронтенда) легковесный веб-сервер — например, конечно, nginx — для более быстрой отдачи статического контента (изображений, JS, CSS, файлов шрифтов, видео и т.п.).

Даже если вы думаете, что у вас уже всё настроено, убедитесь в этом, просмотрев access-логи веб-сервера приложения (Apache). Как правило, достаточно несложных конструкций с grep или egrep, отбирающих GET-запросы с успешным статусом (не 404) и отсечением принятых у вас URL'ов для динамического контента (например, оканчивающихся на / или .html, или не имеющих в своём конце чего-нибудь вроде \.[^.]{2,4}). Очень часто выясняется, что далеко не всю статику отдаёт nginx, а это больше постоянно запущенных процессов Apache (приближение к лимиту MaxClients) и дополнительно потребляемые ресурсы.

Другой нюанс со статикой — её объёмы в масштабах интернет-канала сайта. Показатель проверяется с помощью данных из вашей статистики (графики с сетевым трафиком). Даже если при предполагаемой (высокой) нагрузке имеющегося канала должно хватить на отдачу всей статики, хорошим тоном будет оптимизировать графические файлы и минимизировать JS/CSS-файлы хотя бы на ключевых страницах интернет-магазина. Обнаружить/проверить эти и другие «клиентские» проблемы поможет Google PageSpeed Insights.

Если же трафика будет много и расширить канал невозможно (или слишком трудоёмко/дорого), стоит рассмотреть вариант внедрения CDN.

4. Найдите узкие места


Время разобраться с проблемными местами в работе самого веб-приложения. Короткий путь здесь — это регистрация trial-аккаунта в New Relic (выдаётся бесплатно на 2 недели), установка его агентской части на стороне веб-сервера, первичный сбор данных о нагрузке (можно ускорить этот процесс запуском нагрузочного тестирования, возможно, менее «агрессивного») и их анализ.

Обратите внимание на самые медленные транзакции (особенно самые часто встречающиеся).



Что конкретно в них тормозит (выполняется заметно дольше остального)? Если это конкретные части кода, то как их можно оптимизировать? Если SQL-запросы, то поработайте с СУБД (см. пункт 6). Если внешние сервисы — сведите их использование к минимуму и/или по возможности закэшируйте получаемые результаты (прямо при пользовательских запросах в коде приложения или же вызовом по cron дополнительных скриптов, периодически получающих нужные данные из сервисов и сохраняющих их локально).

5. Оптимизируйте веб-сервер


Общую производительность кода могут повысить вспомогательные средства, такие как акселераторы для PHP, принцип работы которых заключается в кэшировании скомпилированного кода (opcode/bytecode) исполняемых файлов приложения. Для старых PHP (5.x) сохраняет актуальность APC (пусть и официально признанный «мёртвым»), а для более свежих версий (7.x) — встроенный Zend OPcache, который тоже ещё следует настроить.

Другая область глобальной оптимизации в случае PHP — это сессии. По-прежнему многие используют стандартную настройку session.save_handler = files, то есть сохранение сессий в файлы. При большом количестве посетителей сайта (а значит — и сессий, и файлов) это приводит к необоснованной нагрузке на диск, которая чудесным образом снимается переходом на NoSQL-хранилище вроде memcached, помещающее все сессии в оперативную память.

Самый же радикальный путь оптимизации работы веб-сервера — кэширование страниц, единожды в n минут сгенерированных интерпретатором языка программирования. Эта возможность актуальна для тех страниц, которые: 1) могут быть неизменными хотя бы какое-то время (например, допускается, чтобы блок «случайных товаров» сгенерировался движком один раз на 1-2 минуты для всех пользователей), 2) одинаковы для всех пользователей. Второе условие не так критично в том смысле, что кэширование можно настроить так, чтобы попросту исключить аутентифицированных пользователей. В таком случае готовые (закэшированные) страницы будут отображаться всем посетителям-«гостям» (а их всегда большинство, причём обычно — подавляющее), а для аутентифицированных будут «честно» генерироваться динамические страницы. Кэш можно настроить в nginx, но есть для этих нужд и более специализированное Open Source-решение — Varnish HTTP Cache.

Грамотная настройка кэширования основных групп страниц веб-сайта часто приносит просто фантастический результат, потому что ответом на основную часть запросов становится выдача статики — готовых данных из оперативной памяти без необходимости задействовать для этого веб-сервер (с интерпретатором языка программирования) и СУБД. Убедитесь в этом с помощью нагрузочного тестирования после настройки кэширующего сервера, но уделите особое внимание составлению условий кэширования, чтобы никакие критичные функции интернет-магазина (добавление продукта в корзину, его последующий заказ, регистрация пользователей…) не сломались.

6. Оптимизируйте СУБД


Оптимизацию производительности СУБД можно условно разделить на две части: 1) общая конфигурация на стороне самой СУБД, 2) работа над своими схемами и запросами.

Первый пункт подразумевает настройку буферов и других глобальных параметров в СУБД. Самые важные из них для MySQL можно увидеть на этом замечательном слайде из презентации Петра Зайцева (CEO Percona):

image

С более детальным анализом этих системных переменных и некоторых других (критичных для вопросов производительности MySQL/MariaDB) можно ознакомиться, например, в этой заметке от MariaDB.

Вторая область — схема БД и используемые SQL-запросы — обычно начинается с анализа медленных запросов (slow query log в MySQL) или же специализированных инструментов для обнаружения проблем производительности, таких как упомянутый New Relic. Львиная часть проблем, как правило, решается добавлением правильных индексов и вдумчивым разбором SQL-запросов (с последующей их оптимизацией или изменениями в схеме).

Дополнительным решением для быстрого получения данных, которые всё равно долго доставать из СУБД, может быть кэширование результатов частых/сложных операций, например, в NoSQL-хранилище. Как и в других случаях, надо не забывать при этом следить за актуальностью данных в кэше.

7. Оптимизируйте движок


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

Если в движке используется второстепенная функциональность, которая создаёт заметную нагрузку на сервер или СУБД, отключите её на время пиков: пусть лучше пользователь не получит некоторых функций, чем не получит вообще ничего.

8. Обновите железо


В зависимости от данных из системы статистики, результатов нагрузочного тестирования и проводимых оптимизаций (например, по итогам анализа проблем в СУБД выяснилось, что нужны огромные буферы) — добавьте ресурсов: увеличьте число ядер CPU и количество оперативной памяти, переведите требовательные к диску разделы (СУБД) на SSD, вынесите отдельные компоненты на отдельные серверы/виртуальные машины, расширьте канал (если CDN получается дорог или не помогает, т.к. проблема не только в статике).

9. Сделайте грамотную заглушку


Когда ничего уже не помогает (не все оптимизации были завершены или их оказалось недостаточно, возможностей по апгрейду железа не хватает, объём трафика превысил разумные ожидания) — пусть пользователи увидят информативную заглушку, в которой будет какой-то минимум полезных сведений: основные товары, контактные данные и т.п. Пропишите её в настройки веб-сервера и, возможно, предусмотрите её отдачу в приложении (для случаев срабатывания таймаутов и других проблем).

Конечно, это гораздо хуже закэшированных страниц для неаутентифицированных пользователей, которые тоже могут отдаваться как статика, но всё же значительно лучше, чем сообщение браузера о невозможности подключиться или печально привычная многим ошибка вроде:



10. Позовите профессионалов


Если всё это не получается качественно реализовать по каким-либо причинам (или не помогает так, как хотелось бы), не забывайте о возможности выйти за рамки технического аспекта стоящей проблемы — обратитесь к тем, кто более компетентен в нужных вопросах: друзьям/знакомым или компаниям, специализирующимся в соответствующей области. Иногда это может оказаться более эффективным для бизнеса решением.

Резюме


Несмотря на наличие некоторых конкретных технических указаний, статья носит скорее обзорный характер, стремясь указать на те важные моменты, которыми стоит озадачиться при решении непростой задачи подготовки интернет-магазина (или другого веб-сайта) к высоким нагрузкам и всплескам посещаемости, вызванным Black Friday и подобными событиями. О некоторых из обозначенных проблем, кстати, мы рассказывали подробнее на конференции Highload Junior 2017, проходившей в рамках РИТ++ 2017 (доклад «ТОП ошибок в инфраструктуре, мешающих высоким нагрузкам» Андрея Половова и Андрея Колаштова).

Надеемся, что хотя бы часть из приведенных советов поможет вам выдержать предстоящее испытание. Как гласит народная мудрость, «предупрежден — значит вооружён». А другая мудрость рекомендует «готовить сани летом»: если уж не к этой пятнице, то к периоду новогодних и рождественских распродаж точно можно успеть как следует подготовиться — не затягивайте!

Вы держитесь завтра! Вам всего доброго, хороших продаж!

Комментарии (2)


  1. Dev0ps
    23.11.2017 13:09

    CyberMonday начинается в BlackFriday


  1. firk
    23.11.2017 17:25

    Очень важный этап подготовки инет-магазина к "чёрной пятнице" — заранее увеличить "старые" цены в несколько раз, чтобы было от чего делать большие скидки.