Всем привет! На связи ITSumma.За 15 лет, что мы делаем нагрузочное, у нас накопился список самых распространенных ошибок, которые совершают, когда строят и отлаживают инфраструктуру. Ну список и список скажете вы, но в этой статье мы хотим поделиться не только самими ошибками, но и инструкциями, как их найти. Так что не будем затягивать предисловие и сразу перейдем к делу. Суть этой проблемы — неправильно настроенный или отсутствующий мониторинг не способен отразить точный результат тестирования. Если не покрыть все необходимые метрики,то тестирование не покажет полную картину. Например, в одном из кейсов, мы на первом этапе мы провели 3 итерации, запустив относительно легкий скрипт с использованием Яндекс.Танка в качестве инструмента тестирования и мониторинга показателей. “Уронить” сайт удалось только после запуска шести экземпляров Я.Т с разных серверов. При этом сам Яндекс.Танк и мониторинг клиента показывали противоречивые результаты — цифры колебались в пределах 4000-6000 RPS. После настройки мониторинга удалось получить достоверные предельные показатели нагрузки в 8000 RPS.  Дальнейшие исследования выявили, что проблема находится в Docker-контейнерах, т. к. запуск тестирования в обход позволил преодолеть рубеж в 8000 RPS. Определить это было бы невозможно без настройки мониторинга на сервере клиента с использованием essm, благодаря чему мы получили точные показатели нагрузки и метрик Nginx. В том же кейсе мы столкнулись с другой распространенной ошибкой — неправильных настройках для процессора и операционки. Из-за некорректных настроек может не хватать ресурсов процессора на веб-сервере при большом объеме запросов. Что приводит к перегрузке сервера и снижению отзывчивости системы.  Эту ошибку можно обнаружить, когда вы проверяете обращения к бэкенду. У нас нагрузка постепенно увеличивалась с 1 RPS до 3000 RPS. После двух минут тестирования при уровне нагрузки в 315 RPS ресурсы CPU на веб-сервере закончились. Процессор стал обрабатывать запросы с задержкой, а PHP-FPM стала функционировать нестабильно — в логах появились ошибки server reached pm.max_children и upstream timed out. Спустя минуту после достижения пика нагрузки, все воркеры PHP оказались полностью заняты. В результате главная страница открывалась почти 17 секунд! Результаты показаны на рис. 1-3 Вторую итерацию мы провели по тому же сценарию — постепенный рост нагрузки с 1 до 3000 RPS. но с обращениями только к динамическому контенту. В результате тестирования была достигнута нагрузка в 715 RPS, и дальнейшее увеличение нагрузки привело к исчерпанию ресурсов процессора, а в логах Nginx и PHP-FPM появились ошибки вида: WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 32 children, there are 0 idle, and 804 total children WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 32 children, there are 0 idle, and 836 total children Устранить эту проблему, можно оптимизировав настройки производительности сервера, выделив достаточные ресурсы процессора и памяти для обработки запросов и размещения всех необходимых файлов. А также распределить нагрузку на несколько серверов или использовать балансировку нагрузки для равномерного распределения запросов. Это одна из классических проблем, которая попадается нам довольно часто. Статический контент, такой как изображения, CSS и JavaScript-файлы, ограничивает производительность сайта, занимая значительную часть пропускной способности сетевого канала.  Это увеличивает время загрузки страниц и снижает общую производительность системы. Часто так бывает, когда клиент работает в CMS Bitrix, т. к. в настройках по умолчанию, она хранит файлы и обращается к статике непосредственно на диске. В таких случаях рекомендуется оптимизировать доставку статического контента, например, использовать кэширование или CDN, чтобы снизить нагрузку на сетевой канал и улучшить время загрузки страниц. Найти эту проблему порой бывает довольно сложно. Иногда приходится провести 3 итерации тестирования по разным сценариям: Схема 1 сценария: Главная → Подбор мест на главной → Переход и подбор с изменением параметров.  Схема 2 сценария: Главная → Авторизация → История заявок. Схема 3 сценария: Главная → Подбор МКБ. В одном из проектов после  такого тестирования мы обнаружили, что при нагрузке ресурсы сервера быстро закончились, что привело к возникновению ошибок Nginx и PHP-FPM вида: 2023/08/09 07:44:12 [error] 10898#10898: *159968493 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 31.184.213.92, server: teststest.ru, request: "GET / HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "teststest.ru" 2023/08/09 07:44:12 [error] 10938#10938: *159968757 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 31.184.213.92, server: teststest.ru, request: "GET /booking/?sessid=de81df3d7428a9650b77deacabd6fc48&form=1&ym=24287&adult=2&children=0&skk=375&hotel=382&m204ajax=Y HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "teststest.ru", referrer: "http://teststest.ru/booking/?skk=375&hotel=0&ym=24287&adult=2&children=0&m204ajax=Y" При этом PHP-процессы многократно обращались к SVG-файлам, тем самым замедляя работу сайта. Происходило это из-за CMS, которая отправляла запросы к статическому контенту непосредственно на жесткий диск. Из-за недостатка воркеров запросы забивают канал на веб-сервере, что приводит к тому, что сайт медленно открывается.  Увидеть эту ошибку, можно проверив количество процессов PHP-fpm для обработки запросов. Для этого нужно провести базовую нагрузку от одного запроса в секунду до 1000 запросов в секунду.  Скорее всего, в PHP-fpm вы увидите ошибки выполнения запросов: 110: Operation timed out. При этом не будет ни нагрузки на процессор, ни на память: Самое вероятное решение такой проблемы — добавить воркеров для процессов PHP-fpm. Эта проблема является следствием ошибок логики кода, когда сайт взаимодействует с сервисом хранения логов, кэширования и гео-модулем или другим внешним сервисом. Как раз в одном из наших проектов клиент использовал внешний сервис GeoIP — базу данных для геолокации по IP-адресу. После первой итерации тестирования мы увидели, что максимальный уровень загрузки при обращении к динамическому контенту не поднимается выше 30 RPS. А днем и того хуже – 5 RPS. За основу нагрузочного тестирования взяли лог доступа к серверу, а именно динамические PHP-страницы из него. При этом уже при 31 RPS скорость работы сайта значительно снижалась, но не наблюдалось нагрузки ни на веб-сервере, ни на сервере с базой данных.  Одним из главных факторов, сдерживающих скорость работы сайта, являлось использование внешнего сервиса geoip: 05:09:07.227091 connect(23, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("208.95.112.1")}, 16) = -1 EINPROGRESS (Operation now in progress) 05:09:07.227236 poll([{fd=23, events=POLLIN|POLLOUT|POLLERR|POLLHUP}], 1, 5000) = 1 ([{fd=23, revents=POLLOUT}]) 05:09:07.270599 getsockopt(23, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 05:09:07.270726 fcntl(23, F_SETFL, O_RDWR) = 0 05:09:07.270899 getpeername(23, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("208.95.112.1")}, [16]) = 0 05:09:07.271123 access("/home/bitrix/www/bitrix/modules/main/lib/diag/logger.php", F_OK) = 0 05:09:07.271251 access("/home/bitrix/www/vendor/composer/../psr/log/Psr/Log/AbstractLogger.php", F_OK) = 0 05:09:07.271386 access("/home/bitrix/www/vendor/composer/../psr/log/Psr/Log/LoggerInterface.php", F_OK) = 0 05:09:07.271532 sendto(23, "GET /json/95.213.162.109?lang=ru HTTP/1.1\r\nHost: ip-api.com\r\nConnection: close\r\nAccept: */*\r\nAccept-Language: en\r\n\r\n", 116, MSG_DONTWAIT, NULL, 0) = 116 05:09:07.271652 poll([{fd=23, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout) 05:09:07.271760 poll([{fd=23, events=POLLIN|POLLERR|POLLHUP}], 1, 5000) = 0 (Timeout) 05:09:17.276580 close(23)               = 0 05:09:17.276878 select(21, [20], [20], NULL, {1, 0}) = 1 (out [20], left {0, 999997}) С точки зрения высоких нагрузок хорошо, если вы используете реализации geoip, не требующие внешних подключений. Также в этом случае поможет работа с логикой. В описанном кейсе мы сделали следующее: Настроили новый сервер как новую ноду для масштабирования веб-воркеров. Настроили конфигурационные файлы. На новом сервере протестировали перенос сессий. Подготовили конфигурационные файлы к подключению новых нод. В результате сайт стал выдерживать приблизительно 200 RPS:  Некоторые CMS и внешние сервисы склонны по умолчанию загружать файлы на HDD или хранить там кеш. Обращение к файлам, которые лежат на диске “подвешивает” текущие процессы и сильно загружает процессор. Есть и другой вариант этой проблемы — когда скорости чтения/записи на диск попросту не хватает для обслуживания большого количества запросов. Например, мы сталкивались с тем, что при открытии клиентского сайта каждый раз открывалась база SypexGeoIP. И при каждом открытии код загружал файл с настройками в память целых 8 секунд: начало обработки файла05:18:32.835075 open("/data/local/php_interface/generated_files/SxGeoCity.dat", O_RDONLY|O_LARGEFILE) = 8 конец обработки файла:05:18:40.530117 read(8 Статистика системных вызовов для одного fpm-процесса показывала, что более 90% времени процесс обрабатывал этот файл с настройками (вызовы munmap, read, mmap ). Из-за этого процессы fpm почти все висели в состоянии обработки базы и чрезмерно забивали канал на ЦПУ.  Проблему решило отключение перманентной загрузки файла в память. Это строку: /data/local/modules/citfact.sitecore/lib/geoip/sxgeo.php в контейнере с fpm поменяли вот так: define('SXGEO_MEMORY', 1); на define('SXGEO_MEMORY', 0); Решив сложности с geo-ip, мы ещё раз проанализировали системные вызовы под нагрузкой. Теперь большую часть времени сайт стал заниматься доступом к файлам. % time     seconds  usecs/call     calls    errors syscall------ ----------- ----------- --------- --------- ---------------- 37.18    0.155432           6     23256      7107 access 29.44    0.123042           4     25276      3119 stat  9.18    0.038387           5      7332       366 lstat  4.77    0.019952          12      1624           poll  4.72    0.019720          28       685           munmap Помимо доступа к файлам ядра, Битрикс регулярно обращался к файлам изображений одного и того же товара и проверял их в разных размерах: 2491803 06:21:26.756402 access("/data/upload/iblock/dc2/o9va4u3l2ni02gfp0kz7l390eyduvi2j/Massimo_mini_Premier_10_0003.jpg", F_OK) = 02491803 06:21:26.756467 access("/data/upload/resize_cache/iblock/dc2/o9va4u3l2ni02gfp0kz7l390eyduvi2j/1506_1506_1/Massimo_mini_Premier_10_0003.jpg", F_OK) = 02491803 06:21:26.756519 access("/data/upload/resize_cache/iblock/dc2/o9va4u3l2ni02gfp0kz7l390eyduvi2j/1506_1506_1/Massimo_mini_Premier_10_0003.webp", F_OK) = 02491803 06:21:26.756562 access("/data/upload/resize_cache/iblock/dc2/o9va4u3l2ni02gfp0kz7l390eyduvi2j/1506_1506_1/Massimo_mini_Premier_10_0003.jpg", F_OK) = 02491803 06:21:26.756603 access("/data/upload/resize_cache/iblock/dc2/o9va4u3l2ni02gfp0kz7l390eyduvi2j/753_753_1/Massimo_mini_Premier_10_0003.jpg", F_OK) = 02491803 06:21:26.756643 access("/data/upload/resize_cache/iblock/dc2/o9va4u3l2ni02gfp0kz7l390eyduvi2j/753_753_1/Massimo_mini_Premier_10_0003.webp", F_OK) = 0 Такое поведение было вызвано модулем остатков. Это занимало довольно много времени и сильно нагружало диск со статикой, где ещё и находился NFS с бэкапного сервера. Выхода в этом случае было 2:  Исправить логику работы кода,  чтобы при каждом открытии не проверялись сотни файлов.  Отказаться от NFS и перенести изображения на бэкенды — это снизит нагрузку на диски и ускорит время ответа сайта. Как видите, нагрузочное тестирование — это мощный инструмент, который помогает улучшить отказоустойчивость и стабильность любого сайта. Обычно мы рекомендуем проверить сервис, если вы ожидаете резкий всплеск трафика. Например, во время сезонных распродаж. В этот раз мы немного запоздали с этим советом, ведь «Черная пятница» идет своим ходом. Поэтому хотим пожелать всем удачи и стойкости под высокой нагрузкой, а если у вас возникли проблемы, которые не получается решить, вы знаете, куда обратиться. Больше материалов о нагрузочном тестировании, советов, как жить, если ты DevOps и шишках, которые набили наши клиенты при работе с высоконагруженными сайтами, читайте в нашем ТГ-канале: https://t.me/itsumma  Аккаунт компании