{"id":14276,"url":"\/distributions\/14276\/click?bit=1&hash=721b78297d313f451e61a17537482715c74771bae8c8ce438ed30c5ac3bb4196","title":"\u0418\u043d\u0432\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432 \u043b\u044e\u0431\u043e\u0439 \u0442\u043e\u0432\u0430\u0440 \u0438\u043b\u0438 \u0443\u0441\u043b\u0443\u0433\u0443 \u0431\u0435\u0437 \u0431\u0438\u0440\u0436\u0438","buttonText":"","imageUuid":""}

Как потопить сайт с 600k MAU из-за плохой серверной оптимизации

С предыдущей статьи про наш агрегатор презентаций прошло несколько лет, но Slide-Share живёт. Не всегда здравствует, но живёт, что не может не радовать. В 2022 году получили более 6 млн посетителей на сайт.

Динамика трафика на сайте в 2022 году.

Та статья закончилась на моменте, как мы спасли данные и контент из лап Microsoft, взяли нормальный сервер и радовались. Радовались так сильно, что сервер с ресурсом стал выполнять дополнительные функции: на нём развернули тестовый полигон для других проектов. Иногда не самый лёгких.

В какой-то момент на одном сервере крутились: сам сайт, конвейер обработки презентаций, Jenkins для сборки проектов, пачка тестовых бэкендов и фронтендов. Постепенно сервер перестал вывозить. Посещаемость ресурса росла, количество контента — тоже, но мощности были конечными. Google и Яндекс отреагировали на это предупреждениями и ухудшением позиций.

Разделяем сервера

Можно было бы заморочиться с кэшем, попытаться что-то оптимизировать, но волей определенного (пусть и не самого приятного) случая мы приняли решение перевезти всю инфраструктуру из юрисдикции Российской Федерации.

Так у нас освободился еще один сервер на территории РФ, который решили использовать для организации «фронтовой» части проекта. Обработку запланировали вынести на отдельный сервер, чтоб она не нагружала контентные сервера.

Сервера уезжают из РФ.

Таким образом, пришли к схеме с тремя серверами:

  1. Сервер с «сайтом». На нем не предполагается хранить что-то тяжелое. База, кэш, Elasticsearch для полнотекстового поиска, какие-то другие не очень тяжелые проекты.
  2. Сервер с конвейером для обработки. Всё, что требует постоянных нагрузок, уезжает туда и жрет все возможные мощности.
  3. Сервер со статикой. Его задача — принимать в себя картинки и файлы презентаций, а потом отдавать посетителям и роботам. Именно этой задачей стал заниматься сервер, на котором изначально было все развернуто. В том числе потому, что у него самые большие диски, а поток контента и не думал уменьшаться.

Переезд прошёл гладко и без даун-тайма, так как продумали все заранее. Остановили обработку, чтоб ничего нового в базу не писалось. Сдампили базу и развернули её на новом сервере, запустили сайт. Старый не отключали сразу. Ждали, пока пройдёт TTL для обновления A-записей. Даже когда они обновились, оставили на какое-то время проксирование на новый сервер со старого через nginx.

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

Оптимизируем работу с изображениями

Google любит оптимизированные форматы для изображений. Хлебом его кормить не стоит, а вот WebP — хорошее лакомство. Впрочем, как и для Яндекса сейчас.

Google любит WebP, а пользователи — нет.

Наш конвейер обработки был достаточно прямолинейным. Последним шагом после конвертации было сохранение исходного документа, слайдов, изображений со слайдов. Все раскладывалось в отдельные папки на диск. На один диск из двух. В одни и те же папки.

Вы когда-нибудь пытались выполнить `ls` в папке, где, скажем, десять миллионов мелких JPEG’ов? И у вас не шустрый SSD, а обычный HDD на 7200 RPM. Я — пробовал. И не дожидался результата, а только подвешивал дисковую подсистему попытками.

Итак, у нас есть несколько миллионов файлов на одном диске. Ext4, которую мы используем на серверах, работать с таким умеет, но это не очень приятно стреляет в производительность. Количество файлов продолжает расти, место постепенно заканчивается, статика отдается все медленнее. В iotop мы видели нагрузку в 99.99% на один диск. Все сжирал nginx, который героически пытался справиться с этим.

Нужно было придумать решение, которое бы позволило:

  • задействовать оба диска сервера со статикой для равномерной загрузки,
  • преобразовывать входящие jpeg в WebP,
  • не складывать все файлы в одну папку никогда,
  • раскидать весь текущий контент с учетом новой системы.

С разработкой решения для статики проблем не возникло (думала прошлая версия меня). Создал небольшое приложение на Python с Flask, которое умеет:

  1. Принимать в себя файл.
  2. Сохранять на диск.
  3. Если JPEG — преобразовывать в WebP.
  4. Вести статистику по использованному месту, чтобы было проще распределять нагрузку.

Идея, по которой файлы раскладывались на диск, показалась мне гениальной. В качестве имен для загружаемых файлов будем генерировать UUID’ы.

От UUID’ов посчитаем какой-нибудь хэш, но не очень длинный, например, MD5. Переводим MD5 в hex-строку, разбиваем по два символа и получаем путь из папок. Гениальное решение, да? Теперь в каждой папке у нас может быть не больше 256 папок, а файлов будет и того меньше.

Это было ужасное решение, которое выстрелило нам в ногу позже, но рассказ об этом будет в следующей статье.

Итак, мы сделали решение, которое позволило нам организовать статику. Поправили компонент в конвейере, который сохранял файлы. Теперь он раскидывал всё на два отдельных диска. Сделали небольшое приложение, которое должно было раскидать старую статику под новый, настроили редиректы для изображений. Запустили процесс, оценили, сколько потребуется времени на переносы и затаились в ожидании…

0
Комментарии
-3 комментариев
Раскрывать всегда