В предыдущей статье мы рассказали, как построили работу серверов и оптимизировали изображения в нашем агрегаторе презентаций. Это помогло. Дискам стало легче, страницы сайта начали загружаться быстрее. После того, как контент разлетелся по разным дискам, мы снова запустили конвейер обработки презентаций. Контент попёр. Мы радовались, даже не замечая, что целимся в собственную ногу.

Больше контента — больше возможностей для монетизации.
Больше контента — больше возможностей для монетизации.

В какой‑то момент изображения стали отдаваться медленно. Точнее ОЧЕНЬ медленно. И не только отдаваться. Иногда не получалось установить соединение с nginx»ом. Iotop снова показывал нагрузку в 99,99%, только теперь на двух дисках. Как? Откуда такая нагрузка? Наверное, мы стали слишком популярными. И диски медленные.

За такие мысли моя прошлая версия заслуженно получила бы по голове.

Проблемы со свободным местом на дисках

Конвейер с обработкой презентаций остановился. Раз — и новых презентаций больше не было. В логах висела грустная надпись, что не удалось сохранить файл на диск — место кончилось. Мы съели восемь терабайт.

При этом вывод df показывал, что место еще есть. И не какие‑то зарезервированные в ext4 проценты, а вполне адекватные значения. Но ничего на диске сделать не получалось.

Проблема в том, что люди бывают не только глупыми, но и невнимательными.

Маркетологи предложили удалить непосещаемые презентации, чтобы освободить место. За неимением других вариантов приняли это решение. Собрали презентации, которые приносили мало трафика, и спокойно закинули на удаление.

Массовое удаление страниц из проекта.
Массовое удаление страниц из проекта.

Удаление шло долго. Дискам и без того было тяжело, а тут требовалось удалить несколько миллионов мелких файлов, раскинутых по глубоким путям. Наконец, оно закончилось. Мы радостно запустили конвейер, увидели, что процесс пошёл, и мирно оставили его. Зря.

Поиск и решение проблемы

Через не очень продолжительное время контейнер с обработкой остановился. Снова. Ровно с той же проблемой — нет места на диске. А dfснова показывает, что место есть и его много.

Небольшое техническое отступление, чтобы пояснить, что произошло. В ext4 всё — файл. И папка — это файл. Для организации файловой таблицы используются inode — специальные структуры данных, которые хранят различные метаданные о хранимых файлах. Количество этих самых inode конечно и задаётся при первоначальном создании файловой системы. Например, на нашем сервере со статикой, на разделе в 4 терабайта будет где-то 243 миллиона этих самых inode. Каждый файл при сохранении использует 1 inode.

Места не было. Я буквально ничего не мог сделать. Почистил всё, что можно, но существенно это ситуацию не поменяло. Тут я вспомнил про inode ы. Пишу df ‑i и… впадаю в ступор. IUse% — 100. На двух дисках. Как? Что могло съесть такое количество inode? И тут до меня начинает ме‑е‑едленно доходить.

Сколько всего inode? Примерно 243 миллиона. Насколько глубокий путь я делал для статики? 8 папок. Что там с распределением? Начиная где-то с третьей папки количество папок в подпапках будет в районе единицы. Используя очень грубую оценку, можно получить, что при сохранении одного файла на диск мы занимаем 9 inode — 1 для файла, 8 для папок.

Каждая картинка сохраняется как две — ещё в WebP. Отдельно лежат слайды, отдельно лежат изображения со слайдов. Ещё отдельно лежат сами презентации, но их количество на пару порядков меньше, поэтому пока что пренебрегаем ими. Используя методы для статистики, я получил что-то в районе 13­–14 миллионов файлов в сервисе статики. Умножив это число на 9, а затем на 2, получил те самые 243 миллиона inode. Загадка разгадана.

Делать-то что? Конвейер с обработкой презентаций остановлен. Его нет смысла запускать, мы снова займем все inode’ы. На ум приходит хорошее и более разумное решение. Делаем, скажем, всего два уровня папок по тому же правилу с хэшом.

Об этом моя прошлая версия не догадалась, а решила броситься в крайность. Сначала мы все храним в одной папке — это плохо, поэтому будем хранить все в отдельных и максимально дробных папках.

Иногда лучшее — враг хорошего.
Иногда лучшее — враг хорошего.

Идем по проторенной дорожке. Вносим изменения в сервис для статики, чтобы все новое раскладывалось с учётом исправлений. Делаем небольшое приложение, которое будет рекурсивно поднимать всю старую статику наверх на несколько уровней. Настраиваем nginx для корректной обработки старых ссылок.

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

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

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


  1. sanchezzzhak
    31.01.2023 16:34
    +1

    Еще рекомендую сделать в кроне после команды

    `/dev/null 2>&1`

    А то не ожидано будет удивить что место засрало в логах крона, я так за год крона сьел 800гб свободного места и не заметил.

    Вот вам идеи, что если вам хранить презентацию в архиве?
    1)
    JS может распаковать архив ZIP на клиенте и в память браузера, картинки отображать через blob
    Мы экономим ноды без лишней вложенности.
    2)
    При обращении на урл первого слайда проверяем файл слайда
    По такому пути /slides/<id>/хеш1/файл.
    Если нет файла, то распаковать архив в некую временную папку tml/<id презентации>/вложенность из архива.

    перинаправление на реальный файл можно сделать через X-Accel-Redirect

    header(sprintf('X-Accel-Redirect: /tmp/<id>/%s', $path));

    Очищать временную папку можно по таймеру, скажем никто из посетителей не смотрел 2 дня презентацию.


    1. alxal
      31.01.2023 16:41
      +1

      https://github.com/google/ngx_brotli
      Если я не перепутал, он умеет отдавать сразу сжатые файлы, от этого увеличится скорость загрузки презентаций, и так же умеет динамически их сжимать. Понятно что не nginx'ом единым, но референс
      дальше продолжать копать


  1. aborouhin
    31.01.2023 19:31
    +4

    По-моему, эта история с исчерпанием inodes (и затыки с iops'ами) Вам сделала прозрачный такой намёк, что пора уже "повзрослеть" и в плане железа, и в плане софта... Я правильно понял по прошлому посту, что у Вас там просто 2 HDD по 4 Тб, даже не в RAID? У меня в домашней файлопомойке такие четырёхтерабайтники и то в RAID-6 (ну точнее, в программном RAIDZ2 средствами ZFS), а у Вас коммерческий проект... С постоянной нагрузкой шансы, что всё неожиданно загнётся, приближаются к 100%. И как это всё бэкапится, к слову?

    Ну т.е. хотя бы сделать нормальный RAID из SSD с запасом места под развитие уже очевидно пора. И завести на нём ту же ZFS, например. А возможно и перепрыгнуть сразу на следующую ступеньку и подумать про отказоустойчивость, кластер, распределённые ФС и вот это всё. Или венруться к облачным решениям и разместить эти Ваши 8 Тб в каком-нибудь объектном хранилище.


  1. numb
    31.01.2023 20:12
    +4

    Я вам открою еще одну, возможно, будущую проблему. В ext4 есть так называемое индексирование на уровне папки. Для каждой папки существует хеш-таблица имен вложенных в нее файлов. Максимальное кол-во записей в этой таблице фиксированно. Если место в таблице закончится, Вы получите ошибку о недостатке свободного места на диске, хотя и место на диске будет и inod'ы свободные будут.

    Лечится данная проблема через отключение индексирования на уровне раздела диска. Отключается утилитами для тюнинга ext4

    Я ловил эту проблему, когда разработчики положили в одну папку несколько десятков миллионов файлов.


  1. S-trace
    01.02.2023 15:47

    Почему бы не перейти например на BTRFS если такие проблемы с EXT4?

    Там и сжатие данных, и дедупликация (которую можно запланировать на наименее нагруженные часы, или же задать load average), и проверка целостности (scrub), и иноды выделяются динамически.


  1. IgorVoskresenskiy
    02.02.2023 05:44

    Как вариант, использовать xfs, btrfs, zfs - файловые системы, не зависимые от inodes


  1. Sorceress
    02.02.2023 05:44

    Два с половиной месяца простоя? Круто, а взять диски в аренду и сократить это время, ну скажем раз в 5? И вообще, 8 терабайт, сколько сейчас стоит 8 терабайтный диск(Хорошо 2, уговорили), в смысле просто купить... Я думаю это возможно. Еще стоит подумать о другой файловой системе, в общем предложений масса.