Сообщество - Лига Сисадминов

Лига Сисадминов

2 764 поста 19 206 подписчиков

Популярные теги в сообществе:

А чего добился ты?

Серия Всякое вне тем

Модераторам: не нашел в в текущей редакции правил никаких уточнений, можно ли ссылкаться на живущего в РФ и и присутствующего на половину конференций по ИБ Лукацкого, как на источник, если это его канал в телеграмме.

Источник: Пост Лукацкого

В своих презентациях я часто показываю реальных киберпреступников, которые разительно отличаются от экранного образа, нарисованного голливудскими блокбастерами "Хакеры", "Пароль Рыба-Меч", "Сеть" или поисковиками (попробуйте поискать по ключевому слову "хакер"). И вот в моей "коллекции" персонажей новое пополнение. Знакомьтесь – Елизавета Ивахненко, кличка "Муза", 24 года. Была арестована Высшим антикоррупционным судом Украины за крышевание мошеннических call-центров.

Но самое интересное другое (мало ли симпатичных девушек и неприглядных стариков по ту сторону баррикад). Ивахненко в 22 года стала заместителем декана факультета кибербезопасности и информационных технологий Одесской юридической академии. В 22 (!) года! Так и хочется добавить: "А чего добился ты?!" 🫵

А чего добился ты?
Показать полностью 1

Комплексное тестирование обновлений Linux и смежных продуктов

Серия Кудахтеры: безопасность и боль

Для ЛЛ: долго, дорого, не тиражируемо

У продуктов Microsoft есть WSUS (развитие прекращено, но про это я писал
Чего лишаемся в 2025 году внутри Microsoft

С продуктами (вторичными. Кто сдает продукт вторичный, тот снабжается отлично) в экосистеме Linux все гораздо хуже.
Причин несколько.
Первая, это сотрудники, имитирующие информационную безопасность.
Периодически такие эксперты показываются в диалогах, но в РФ (да и в мире) нет понятия «репутация», а должность нужно кем-то закрывать. Еще до 2022 года вменяемых специалистов по ИБ в РФ было «поштучно», и были они там, где речь шла о серьезном бизнесе серьезных парней. В этот бизнес, как показала практика, не входили МТС, Аэрофлот, СДЭК, и так далее по списку организаций, которых взломали. В том числе не входят фирмы по предоставлению ПО для информационной безопасности, которых взламывали для атаки на цепь поставок. И в РФ, и в мире.

Вторая, это суммарная сложность систем и совокупная стоимость владения.
Декларируется, но не подтверждается цифрами, что «экосистема Linux» это дешево. Практически это так же дорого, или дороже, чем в экосистеме Windows, но со своими нюансами.
Совокупно «так на так» и выходит, но есть и отличия.

Для того, чтобы понимать сложность, надо понимать устройство какого-то сервиса, сложнее сервиса печати.

Усредненный, сравнительно простой, продукт (программный комплекс) под Linux состоит из:
Ядра операционной системы и его окружения. С этим компонентом вопросов нет, хотите – убунта, хотите – дебиан, хотите – русифицированный дебиан, хотите – русифицированный RHEL \ CentOS.

Java поверх него. И это первая и очень большая неприятность. Под названием «несколько веток Java» - начиная с Oracle Java, OpenJDK, Microsoft Build of OpenJDK, и для РФ Axiom JDK

Они все не одинаковые. Конечно, на любой вы можете сделать
System.out.println("Hello, World");

Но чуть более сложные вещи могут работать не всегда. Или работать, только 2+ДВА у вас станет не 4, а ЧЕТЫРЕ.

Поверх операционной системы стоит сервер приложений. На выбор - Apache Tomcat, WildFly (JBoss), GlassFish и так далее.
Рядом с сервером приложений стоит сервер API . Или не стоит, а все крутится в контейнерах, и сервера приложений тоже нет.
Для Apache Tomcat - Spring Boot или Apache TomEE
Для WildFly не помню. Документация говорит «из коробки все самое лучшее». Почему-то вспоминается старый советский фильм с фразой "У барона Врангеля всё английское!".

Внутри сервера стоят драйвера для всего, в первую очередь для баз данных. И хорошо, если они работают, потому что под Java периодически не работает вообще ничего.

Над сервером стоит надстройка. Хорошо если это уже само приложение.  Не так хорошо, если это очередная надстройка, чтобы делать надстройки, внутри которой надстройка для ИИ, в котором агентами накручено приложение на но-код платформе.

Из рассмотрения выкинута кластеризация и виртуализация аппаратной части (или кубер с талосом, если вы взрослая, зрелая организация), сетевой кластер с l3-L7 фильтрацией, кластер баз данных, со своим зоопарком и к нему зоокипером, кластер очередей, кластер логов, кластер мониторинга, прочие взаимодействия с внешними системами, и так далее. Выкинута вся цепочка CI-CD.

Проблемы видите?
Нет никаких проблем, если этот копролит работает, согласно названию, одним окаменевшим куском, а вы занимаетесь только ручным обновлением браузера на рабочих станциях.
Ну как, занимаетесь -  в крон поставили apt update. Первой группе по 10м числам месяца, вторым по 20м. Назвали это все «разделением на группы обновлений», а тестирование представляет собой запуск пользователями своих ПК в виде «экран ввода логина появился, значит - все работает».
Или занимаетесь не автоматизацией тестирования, а сообщаете, что вы специалист и точно знаете, что автоматизация – зло, поэтому обновления проводятся вручную, специально обученным команде apt update сотрудником.
И все, кто говорит про автоматизацию - недоадминчики в рогах и копытах. Удобно.
Как только в организации приходят специалисты по имитации бурной деятельности (включая  меня), они сразу пытаются обновить один компонент где-то в середине цепочки, отчего вся система подпорок и костылей падает с грохотом.

Что делать?

У вас или есть контур автоматизированного тестирования, то есть CI\CD на стероидах и ИИ, или его нет, и вы просто льете в прод, и молитесь. А если не молитесь, то вам бы  следовало этим заняться

Комплексное тестирование обновлений Linux и смежных продуктов

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

Отдельные индивидуумы делают снапшоты СХД, подключают снапшоты к тестовым средам, и делают предварительное обновление прода и на нем тоже. Достаточно лишь простой американской (или китайской) системы хранения данных, которая может сделать снапшот данных, и подключить его к другой системе.

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

Заключение

Существует проблема ограниченного кругозора. Ряд персонажей, в том числе отвечающих за ИБ, воспринимают обновления безопасности как индивидульные обновления отдельных приложений. Когда приложение «всего лишь меняет цифру». Отсюда растет неприятие системы автоматических обновлений, поскольку система «просто ставит обновления, и ничего не делает».
Если у таких участников дискусии система автоматических обновлений, которую они, зачастую, не могут даже назвать, просто делает apt update,
- всем
- не выполняя никаких предварительных действий,
- делает это на пользовательском сегменте,
то, конечно, от такой «автоматизации» будут проблемы.

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

Литература

Top 10 Java Application Servers for 2025 https://mkyong.com/java/top-10-java-application-servers/

Top Java REST API Frameworks: A Comprehensive Guide https://www.digitalapi.ai/blogs/top-java-rest-api-frameworks-a-comprehensive-guide

10 best no-code AI app builders: At a glance https://www.zite.com/blog/no-code-ai-app-builder

Для тех, кто дочитал.
Лукацкий выдал интересный пост:

Один из самых крупных и влиятельных венчурных фондов из Кремниевой долины, Andreessen Horowitz (он же a16z) показал тут интересные графики по кибербезу. Понятно, что это не их данные, а собранные из разных источников, но тут интересно, что такую тему поднял венчурный фонд в рассылке для своих клиентов:

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

Рост числа уязвимостей, эксплуатируемых в день раскрытия или даже до этого дня. За последний год рост составил около 60%. Термин 0-Day уходит в небытие и ему на смену приходит N-Hours. Не за горами X-Minutes.

Медианное время для эксплуатации уязвимостей сегодня составляет около одних суток. В следующем году эта цифра сократится до... 1 минуты. Насколько вы готовы к такой скорости использования дыр в вашей инфраструктуре?

Число CVE, которое остается непроэксплутированными в течение 1,5-3 месяцев, снижается до... нуля уже в этом году. То есть все, что находится, будет использовано плохими парнями. В 2022-м году число таких уязвимостей составляло 50%. То есть сейчас, если не вы нашли и устранили уязвимости, это обязательно сделает кто-то другой. И хорошо, если это будет не белый хакер в рамках кибериспытаний.

Последние два графика показывают интерес инвесторов к этой тематике, который превосходит такие темы, как облака, инфраструктура, ПО и т.п. Тут интерес a16z понятен – они зарабатывают на этом. Но все цифры проверяемы и на уровне ощущений понятны.

Показать полностью 1
5

Ваш процессор спит на работе. Показываем, как это прекратить

Пакет приезжает в сервер. Миллисекунды на счету. А ядро процессора в этот момент — в глубоком сне, и его сначала надо разбудить.

Кто виноват? Энергосбережение.

Linux из коробки умеет экономить энергию и не держит процессор постоянно в режиме максимальной производительности.

За производительность отвечает CPUFreq: governor задаёт политику, а драйвер и сам процессор выбирают подходящий P-state. В результате частота может гулять вверх-вниз.

За сон — отдельная история: простаивающее ядро уходит в C-state.

Для обычного веб-хостинга это разумно. Для высокочастотного трейдинга, VoIP и других систем, чувствительных к задержкам, это уже может стать проблемой. Выход из глубокого C-state занимает микросекунды. Микросекунды складываются в джиттер, лаги и потерянные тики.

Сначала — как устроен сон процессора. Есть несколько состояний, и это почти как на работе:

  • C0 — работает. Исполняет инструкции, кофе, всё такое.

  • C1/C1E — задремал на стуле. Исполнение остановлено, но просыпается быстро.

  • C3/C6 и глубже — уехал домой. Всё больше блоков процессора переводится в энергосберегающий режим.

Экономия растёт с глубиной сна. Но вместе с ней обычно растёт и время выхода из idle.

Правило простое: чем глубже уснул, тем дороже просыпаться.

Ядро без работы, тихо сваливающее в C6.

Ядро без работы, тихо сваливающее в C6.

Теперь лечение. Понадобятся две утилиты: cpupower — смотреть и крутить параметры CPU, и tuned — применять готовые профили настройки системы.

Ubuntu/Debian:

sudo apt update
sudo apt install linux-tools-common linux-tools-generic tuned -y

CentOS/RHEL:

sudo dnf install kernel-tools tuned -y

И запускаем демон, чтобы настройки переживали перезагрузку:

sudo systemctl enable --now tuned

Шаг второй — смотрим, что сейчас

Сначала — политика управления частотой:

cpupower frequency-info

В выводе ищем блок current policy: минимальную и максимальную частоты, активный governor и используемый scaling driver.

Для наглядности можно посмотреть и на частоты из /proc/cpuinfo:

watch -n 1 "grep 'cpu MHz' /proc/cpuinfo"

Но воспринимать это как точный осциллограф не стоит: на современных CPU реальная частота может меняться быстрее, чем успевает обновляться такой вывод.

Теперь смотрим idle-состояния:

cpupower idle-info

А если хочется увидеть, сколько времени CPU действительно проводит в разных состояниях:

sudo cpupower monitor -i 1

Вот это уже интереснее. Мы можем не только что-то настроить, но и потом проверить, действительно ли процессор перестал проводить время в глубоких C-states.

Смотрим, как частоты и idle-состояния живут своей жизнью.

Смотрим, как частоты и idle-состояния живут своей жизнью.

Дальше — профиль. Вместо ручной крутилки десятков параметров у TuneD есть готовые профили. Для систем, чувствительных к задержкам, нас интересует latency-performance.

tuned-adm list
sudo tuned-adm profile latency-performance
tuned-adm active

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

Процессор перестаёт экономить на каждом удобном случае.

Но физическая частота всё ещё может меняться. Причины: Turbo, аппаратное управление производительностью, ограничения по температуре и мощности. Поэтому правильнее говорить не «частота прибита гвоздями», а «система больше не пытается агрессивно её занижать».

Для сетевых задач у TuneD есть родственный профиль network-latency, но сегодня мы говорим именно про latency-performance.

Но есть нюанс: частота — это P-states. Глубокий сон — C-states.

Сам Governor запретить ядрам уезжать домой не может.

Нельзя просто взять и запретить процессору спать через Governor.

Нельзя просто взять и запретить процессору спать через Governor.

Но мы включили не просто Governor, а профиль TuneD.

latency-performance знает про эту проблему и ограничивает допустимую задержку выхода из idle через PM QoS. В результате CPUIdle перестаёт выбирать слишком глубокие C-states с большой exit latency.

То есть в большинстве случаев на этом уже можно остановиться.

Проверяем:

tuned-adm verify
sudo cpupower monitor -i 1

Если профиль применён корректно, в мониторинге должно быть видно, что процессор перестал надолго проваливаться в глубокие idle-состояния.

А если хочется жёстче?

Иногда хочется не попросить CPUIdle избегать глубоких состояний, а вообще не давать драйверу их использовать.

Тогда можно ограничить C-states через параметры ядра.

Но сначала нужно понять, какой CPUIdle-драйвер работает:

cat /sys/devices/system/cpu/cpuidle/current_driver

Если используется intel_idle, можно ограничить его, например:

intel_idle.max_cstate=1

Если используется acpi_idle:

processor.max_cstate=1

Ориентироваться только на логотип Intel или AMD здесь не стоит. Важен именно активный CPUIdle-драйвер.

На Ubuntu/Debian параметры обычно добавляют в /etc/default/grub в строку:

GRUB_CMDLINE_LINUX_DEFAULT="..."

После чего:

sudo update-grub

И перезагрузка.

На RHEL/CentOS удобнее использовать grubby, например:

sudo grubby --update-kernel=ALL --args="intel_idle.max_cstate=1"

Для acpi_idle соответственно:

sudo grubby --update-kernel=ALL --args="processor.max_cstate=1"

После перезагрузки проверяем ещё раз:

cpupower idle-info
sudo cpupower monitor -i 1

Теперь глубокие idle-состояния должны быть недоступны или практически не использоваться — в зависимости от выбранного способа настройки.

Послесловие

Итог: сервер стал прожорливее и горячее (а счета за электричество весомее), зато ядрам больше не дают проваливаться в глубокий сон.

Пакет приехал — разбудить процессор можно гораздо быстрее.

Если процессор не уходит в глубокий сон, из латентности исчезает ещё один источник непредсказуемости.

Для трейдинга, VoIP и других систем, чувствительных к задержкам, это как раз тот случай, когда микросекунды действительно имеют значение.

Показать полностью 3
2

Очередное удаление поддержки старого оборудования в Linux

Серия Кудахтеры

Для ЛЛ. Кто-то в комментариях рассказывал, что "линукс поддерживает старое".Вовсе нет.

В текущей версии ядра Linux (7.3) многие устаревшие 32-битные платформы ARM переведены в разряд не поддерживаемых (deprecated), в результате чего сотни драйверов, предназначенных исключительно для этих платформ, остались без сопровождения. Удаление этого устаревшего кода запланировано на ближайшие один-два цикла разработки ядра. Значительный объем старого кода для 32-битных платформ ARM оказался под угрозой удаления после того, как поддержка этих платформ была прекращена. Это связано с тем, что устаревший код ядра стал обузой для основных разработчиков (upstream), мешая дальнейшей очистке кодовой базы и внедрению новых функций. При этом вероятность того, что кто-либо будет использовать сильно устаревшую 32-битную платформу ARM с актуальной версией ядра Linux (mainline) в 2026 году или позже, крайне мала. Сегодня Арнд Бергманн (Arnd Bergmann) представил серию из 13 патчей, удаляющих поддержку устаревших платформ ARM, что позволит исключить из проекта более 55 тысяч строк кода и файлов Device Tree. В дальнейшем это даст возможность удалить и другие драйверы, оставшиеся без сопровождения, обеспечив еще более существенное сокращение объема кода

Оригинал:

For the current Linux 7.3 kernel many older 32-bit ARM platforms are deprecated and in turn orphaning hundreds of drivers only relevant to those out-of-date ARM platforms. The removal of that now deprecated code is planned for the next kernel cycle or two.

A lot of old 32-bit ARM code is on the chopping block after being deprecated. This is happening since a lot of this old kernel code has become a burden to upstream kernel developers in getting in the way of further code clean-ups and delivering new kernel features. While the likelihood of anyone still running a very dated 32-bit ARM platform and running a mainline, upstream Linux kernel release in 2026+ is likely very miniscule.

Arnd Bergmann sent out today a set of 13 patches dropping the deprecated ARM platforms and in turn freeing up more than 55k lines of code and Device Tree files. After that more of those orphaned drivers can be removed for even greater code savings.

Полный текст желающие найдут на phoronix

Показать полностью
14

Сервера: распределяем нагрузку

Представим некого разработчика, который по выходным делает собственный пет-проект. Сначала всё живёт на одной виртуальной машине. Проект небольшой, поэтому вся инфраструктура находится там же. API, база данных, обработка запросов и фоновые задачи. На этом этапе всё просто, а нагрузки почти нет.

После завершения разработки MVP версии, этот разработчик решил показать проект своим друзьям и знакомым.

Проект многим понравился, да настолько, что пользователи начали неконтролируемо расти, только за счёт рекомендаций других пользователей.

Инфраструктура та же. Одни пользователи загружают файлы, другие активно работают с сайтом, третьи ждут ответа, чтобы получить результат.

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

Первая мысль, это улучшить код. В основном помогает, но не спасает от слабого сервера, на котором запущен проект.

Логично подумать: «Просто арендую сервер помощнее».

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

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


Написано специально для ▶️Timeweb Cloud и читателей Pikabu.


❯ Когда одного сервера становится мало

Нагрузка бывает разной

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

А генерация отчёта, изменение размера изображения или конвертация большого файла могут занимать секунды и минуты.

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

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

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

Поэтому фраза «сервер тормозит» бесполезна без уточнения.

Сначала нужно найти ресурс, который не успевает обслуживать работу:

  • CPU или память;

  • база данных и её пул соединений;

  • диск или сетевой канал;

  • внешний сервис с медленными ответами или лимитами.

Масштабировать систему можно по-разному

Есть 2 подхода по увеличению производительности:

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

  2. Вертикальное масштабирование — это взять более мощную машину. Добавить CPU, память, быстрый диск или GPU. Приложение почти не меняется, поэтому для небольшого проекта это хороший первый шаг. Ограничения тоже понятны: у машины есть предел, а при её отказе приложение остановится целиком.

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

❯ Что насчёт асинхронности?

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

Примеры выше, я приводил на основе того что код проекта является синхронным.

В синхронной модели обработчик ждёт завершения операции.
Если он читает медленный диск или ждёт внешний HTTP-ответ, конкретный поток занят ожиданием. В асинхронной модели приложение может отдать управление event loop циклу событий, который переключается между операциями, пока одна из них ждёт сеть или диск, и принять другой запрос.

Например, API принимает запрос на построение отчёта, сохраняет описание работы и сразу возвращает идентификатор. Отдельный процесс забирает фоновые задачи из очереди.

Пользователь не держит HTTP-соединение открытым несколько минут, а API продолжает отвечать на короткие запросы.

Асинхронность не ускоряет вычисление само по себе. Если внутри event loop запустить длительный расчёт на CPU, этот поток всё равно будет занят вычислениями. То же касается и GPU. Асинхронная оболочка не создаёт второй GPU и не увеличивает объём его памяти. Для такой работы нужен отдельный процесс или worker, а число одновременно запущенных задач приходится ограничивать.


источник

источник

❯ YouTube: как Google экономит магистральные каналы

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

Когда вы открываете видео на YouTube, файл не обязательно летит через полмира из центрального дата-центра Google. Значительная часть контента хранится на серверах, которые физически находятся внутри сети вашего провайдера или рядом с ней. Это и есть Google Global Cache (GGC).

Google Global Cache

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

Два режима кэширования:

  • Реактивный: контент попадает в кеш после нескольких запросов от пользователей. Например, после 5-10 просмотров одного ролика в сети провайдера он закрепляется на локальном сервере, и следующие зрители получают его оттуда.

  • Превентивный: самые популярные видео заранее загружаются на кеш-серверы, независимо от того, сколько раз их уже смотрели в конкретной сети. Это позволяет отдавать видео без задержек.

Что именно он кэширует?

GGC обслуживает не только YouTube, но и другие статические сервисы.
Google карты, Chrome, картинки из поиска и т.д.

Для YouTube в первую очередь кэшируются сами видеопотоки и превью.

Типичная оценка Google: 70–90% кэшируемого трафика можно отдать с узла, hit rate зависит от того, что смотрят абоненты.

Если GGC отключить, запросы на видео пойдут напрямую к удалённым серверам Google. Это увеличит задержки, ухудшит качество воспроизведения и повысит нагрузку на магистральные каналы.

Внутри YouTube нет «одной программы». Это набор микросервисов: рекомендации, поиск, метаданные роликов, комментарии, реклама и т.д. Когда вы открываете главную страницу, один сервис делает десятки внутренних RPC-вызовов к другим, чтобы собрать всё воедино. Каждый из этих сервисов запущен в сотнях реплик, поэтому перед каждым вызовом нужно решить, на какую реплику отправить запрос.

Для этого компания Google разработала Prequal (Probing to Reduce Queuing and Latency). Её цель не «уравнять загрузку CPU», а минимизировать реальную задержку запросов и избежать очередей, особенно в хвосте распределения.

Prequal

Вместо того, чтобы балансировать по средней загрузке процессора, Prequal выбирает реплику по двум сигналам.

  • RIF (Requests In Flight): сколько запросов прямо сейчас обрабатывается на реплике. Это мгновенный показатель, который хорошо предсказывает будущую нагрузку и потребление памяти.

  • Оценка задержки: медианная латентность недавно завершенных запросов при похожем уровне RIF.

Реплика выбирается по определенным правилам.

Сначала, клиент оценивает распределение RIF по всем репликам и помечает зонды:

  • «горячие» (hot): RIF выше выбранного квантиля (например, выше 80–90% реплик).

  • «холодные» (cold): остальные.

Если в пуле есть хотя бы один «холодный» зонд, выбирается холодный с наименьшей задержкой. Если же все зонды «горячие», то выбирается тот зонд, где RIF имеет меньшее значение.

Эти правила отражают приоритеты. Главное, не допустить, чтобы реплика ушла в перегруз по памяти и числу активных запросов. А второе, это минимизировать задержку.

Зонды Prequal отправляются асинхронно. Текущий запрос использует данные от зондов, запущенных предыдущими запросами, чтобы не добавлять задержку на критический путь. Каждый зонд переиспользуется несколько раз, но не бесконечно. Старые записи удаляются по возрасту, по лимиту переиспользования и через периодическую чистку «худших» зондов, чтобы пул не смещался в сторону перегруженных реплик.

Почему отказались от WRR

Почему prequal лучше старой реализации балансировки по CPU (WRR) ?

До внедрения Prequal, YouTube (и многие другие сервисы Google) долгое время использовался балансировку по CPU на основе Weighted Round Robin (WRR).

Её базовый принцип: каждая реплика получает «вес» пропорционально своей средней утилизации процессора, и запросы распределяются по репликам циклически с учётом этих весов, чтобы в среднем нагрузка по CPU была примерно одинаковой.

WRR хорошо выравнивал среднюю загрузку CPU, но плохо реагировал на короткие всплески и «несчастливые» реплики. Это приводило к росту очередей, увеличению хвостовых задержек и периодическим таймаутам на чувствительных к латентности сервисах.

Даже при «нормальной» средней утилизации CPU отдельные реплики регулярно уходили в перегруз из-за конкуренции с другими процессами на той же машине. Балансировка по CPU не успевала это фиксировать и продолжала направлять запросы на уже перегруженные узлы.

Prequal сместил фокус с «средней утилизации CPU» на реальную задержку и число активных запросов. Это позволило быстрее обходить проблемные реплики, сократить хвостовые задержки на 40–50% и практически устранить ошибки из-за дисбаланса нагрузки, что и стало причиной перехода.


источник

источник

❯ Яндекс Диск: как избежать двойной нагрузки на сеть

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

При загрузке файлов на Яндекс Диск трафик идёт через промежуточное звено, которое не хранит данные, но вынуждено пропускать через себя гигабайты. Это создаёт узкое место. Балансировщик тратит ресурсы на передачу чужих данных, а сеть нагружается вдвое сильнее, чем нужно.

Разделение Control- и Data-запросов

В Яндекс Диске этот путь разделили. Сначала клиент отправляет маленький управляющий запрос и спрашивает, куда загружать файл. В ответ получает ссылку на конкретный сервер и передаёт данные сразу туда, минуя промежуточный балансировщик.

Компонент, который выбирает сервер, в Яндексе назвали Балансерун. Сервер-приёмник получил имя Кладун. Балансерун знает список доступных Кладунов, следит за их состоянием и возвращает пользователю ссылку на один из них.

Основная суть:

  • Control-запросы: лёгкие HTTP-запросы для получения адреса загрузки. Они проходят через стандартный балансировщик, так как почти не создают сетевой нагрузки.

  • Data-запросы: сами файлы, которые идут напрямую от клиента к выбранному Кладуну, минуя балансировщик.

Это снимает двойную нагрузку на сеть. Данные больше не проходят через промежуточное звено.

Почему не подходят Random и Round Robin

Оба алгоритма распределяют запросы, но не учитывают реальную скорость загрузки:

  • Random: случайный выбор узла. При высокой дисперсии скоростей пользователей (от 10 Мбит/с до 1 Гбит/с) некоторые узлы получают несколько «тяжёлых» клиентов подряд и перегружаются, пока другие простаивают.

  • Round Robin: чередование узлов. Даёт равное число запросов, но не равный трафик. Один узел может получить трёх быстрых клиентов, другой, трёх медленных.

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

Решение проблемы: HOBA

Для решения этой проблемы каждый Балансерун получает собственную группу серверов. Выдав ссылку пользователю, он сразу увеличивает расчётную нагрузку выбранного Кладуна на ожидаемую скорость загрузки. Ждать следующего отчёта от сервера не нужно. Периодические общие показатели лишь исправляют накопившуюся ошибку прогноза. В Яндексе этот алгоритм назвали HOBA (Hierarchical Optimized Balancing Algorithm).

Как это работает:

  • Локальные пулы. Каждый Балансерун получает непересекающийся пул Кладунов. При назначении загрузки он мгновенно обновляет виртуальную нагрузку выбранного узла на ожидаемую скорость клиента, не дожидаясь отчётов. Это даёт актуальную «картину мира» в ту же миллисекунду.

  • Учёт скорости. Балансировщик хранит данные о скорости пользователя (текущей, средней по истории или медианной по системе) и оперирует прогнозируемой нагрузкой на канал, а не числом запросов.

  • Глобальная статистика. Периодические отчёты от Кладунов (раз в K секунд) теперь используются не для мгновенных решений, а для коррекции накопленной погрешности локальных расчётов. Это позволило увеличить интервал K и разгрузить шину управления.

Rendezvous Hashing: стабильное распределение пулов

Остаётся понять, какой Балансерун отвечает за какие серверы. Для каждого Кладуна все балансировщики независимо вычисляют одного и того же владельца. Если Балансерун исчезает, переназначаются только его серверы. Этот метод называется Rendezvous Hashing.

Принцип работы:

1. Для каждой пары «Кладун + Балансерун» вычисляется весовая функция.
2. Кладун назначается в пул того балансировщика, у которого вес максимален.
3. Если один Балансерун падает, пересчитываются веса только для его Кладунов — остальные пары сохраняют свои веса и остаются в прежних пулах.

Это минимизирует миграции и сохраняет локальную статистику на живых балансировщиках.

Ограничение размера пулов

Чтобы случайность не отдала одному Балансеруну слишком много Кладунов, размер каждой группы дополнительно ограничивается:

  • Вычисляется «идеальный» размер пула: uploaders_num / balancers_num.

  • При назначении Кладуна проверяется, не достиг ли пул балансировщика лимита. Если да, выбирается следующий по весу балансировщик.

  • Дополнительно можно ограничивать пул снизу, чтобы разница между наиболее и наименее заполненными пулами не превышала единицы.

После внедрения HOBA распределение загрузки сети стало более равномерным. Стандартное отклонение снизилось на 22%, количество перегруженных «хвостов» уменьшилось, а утилизация сети выровнялась. Алгоритм особенно эффективен при высокой дисперсии скоростей пользователей.


источник

источник

❯ Timeweb Cloud: не запустить всё сразу

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

Внутренний сервис Timeweb Cloud под названием vapi-server сначала измеряет доступную скорость между исходным и целевым сервером. Затем он решает, сколько виртуальных машин можно переносить одновременно, и делит между ними канал. Новая миграция начинается, когда освобождается место.

Принцип:

1. Если запустить все миграции сразу, они «забьют» сеть, и каждая будет идти медленно, мешая остальным.

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

Решение: агенты на гипервизорах

При создании виртуальных машин используется другой подход. Физический сервер, на котором запускаются виртуальные машины, называется гипервизором. Раньше команды для всех гипервизоров проходили через центральный API и образовывали длинную общую цепочку.

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

Как это работает:

  • Центральный API: принимает запросы от пользователей и распределяет задачи между гипервизорами.

  • Агент на гипервизоре: получает команду, выполняет её локально (создание ВМ, старт, остановка) и возвращает результат.

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

Как взять этот принцип и применить у себя

Похожий подход можно использовать и при настройке пользовательской инфраструктуры.

Например, проект можно разместить на нескольких облачных серверах Timeweb Cloud, а входящий трафик распределить между ними с помощью балансировщика нагрузки.

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

Такой сценарий позволяет постепенно расширять инфраструктуру. Начать с двух серверов, а затем добавлять новые ресурсы по мере роста нагрузки. Серверы создаются через панель Timeweb Cloud.

Здесь используется тот же общий принцип, что и во внутренней инфраструктуре Timeweb Cloud. Не отправлять все операции или запросы одному исполнителю, а распределять нагрузку между несколькими доступными ресурсами.

Что дала новая архитектура

В июльском дайджесте Timeweb Cloud указывает результат этой перестройки. Что от нажатия кнопки до готовой виртуальной машины проходит около 30 секунд.

Такой подход работает не только внутри платформы. Когда вы разворачиваете проект на облачных серверах Timeweb Cloud, аналогичные принципы применяются и к вашей инфраструктуре. балансировщик нагрузки распределяет трафик между несколькими VPS, а новые виртуальные машины создаются за секунды через API или панель управления.

В одном случае компания распределяет команды между локальными исполнителями. В другом ограничивает число одновременных переносов реальной скоростью сети. Это внутренняя инфраструктура Timeweb Cloud, а не описание балансировщика, который предлагается пользователям.

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


❯ .sound: пример из жизни

Музыкальная платформа с загрузкой треков, поиском, рекомендациями и автоматической синхронизацией текста с аудио

Музыкальная платформа с загрузкой треков, поиском, рекомендациями и автоматической синхронизацией текста с аудио

Одна из самых тяжёлых операций выглядит так:
1. Получить аудиофайл.
2. При необходимости отделить вокал через Demucs.
3. Распознать слова с помощью faster-whisper.
4. Сопоставить строки с моментами в аудио.
5. Вернуть текст и тайм-коды в основную серверную часть, или Backend.

Сначала такая обработка выполнялась рядом с основным приложением. В итоге веб-запросы и распознавание музыки конкурировали за процессор, память и диск. Масштабировать весь Backend ради одной тяжёлой операции было бессмысленно.

Я вынес вычисления в отдельный репозиторий.

При его запуске создаётся отдельный worker. Он сам подключается к Backend и просит следующую задачу.
Такой порядок называют pull-моделью. Не сервер ищет свободного исполнителя, а исполнитель приходит за работой.

Благодаря этому, на домашнем компьютере или сервере с видеокартой не нужно открывать входящий порт. Да и нагрузка значительно снижается, ведь распределена между разными серверами.

Разные задачи, разные способы обработки

В «.sound» разные типы задач не складываются в одну общую очередь.

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

Это позволяет задавать для них разные ограничения, правила запуска и повторной обработки.

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

Как исполнители делят задачи между собой

При подключении исполнитель указывает профиль:
cpu_light (обычный процессор) или gpu_full (видеокарта) и какие типы задач умеет брать. Распознавание песен (ASR) обычно ждёт машину с видеокартой.

Сервер (Backend) выбирает работу по типу, приоритету, профилю и времени, когда можно пробовать снова. Если несколько исполнителей тянут одну задачу сразу, PG на короткое время блокирует эту запись. Остальные не ждут, а берут следующую.

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

Это не гарантия, что задача физически запустится ровно один раз. После сбоя её могут выполнить снова. Поэтому повторная постановка не должна создавать копии, а повторное сохранение результата, портить данные.

Как Backend понимает, что исполнитель жив

Каждые 15 секунд исполнитель шлёт запрос «я жив» на сервер, и обновляет время последней связи.

И в том же ответе может сказать: не бери новые задачи или отмени текущую задачу.

Почему аудио и остальные задачи выполняются по-разному?

Сложные GPU задачи работают строго по одой задаче. Пока текущая не закончена, следующую не берут.

Остальные вычисления можно делать параллельно. Есть общий потолок и отдельные лимиты по типам.

Что даёт отдельный исполнитель

Тяжёлые задачи больше не живут в том же процессе, который отвечает пользователям. База и выдача музыки отделены от вычислений.

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

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

Про «в десять раз быстрее» писать не буду. Задачи стали обрабатываться быстрее, но траты на аренду тоже выросли. Воркеры живут на отдельных серверах.


❯ Общие принципы

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

  1. Балансируй конкретный ресурс, а не «запросы вообще».
    В примерах выше узким местом оказывался не CPU в целом, а что-то одно: магистральный канал (YouTube), сеть на балансировщике при загрузке файлов (Яндекс Диск), очередь RPC-реплик с разной латентностью (Prequal), канал между гипервизорами (Timeweb), GPU/CPU под тяжёлыми вычислениями (.sound). Прежде чем выбирать алгоритм, нужно определить ресурс который будете оптимизировать.

  2. Разделяй короткие HTTP-запросы и тяжёлую работу.
    В .sound тяжёлые задачи вынесены в отдельных воркеров, которые сами забирают задачи. В YouTube тяжёлый трафик вынесен на кэш-узлы у провайдера, в Яндексе data-запросы идут напрямую на узел хранения. Если тяжёлая операция живёт в том же процессе, что и веб-ответы, она будет воровать CPU, память и диск у обычных запросов.

  3. Метрика важнее названия алгоритма.
    Google ушёл от WRR (балансировка по среднему CPU) к Prequal, потому что «средний CPU за минуту» врал. Реплики уходили в перегруз по памяти и числу активных запросов, а хвостовые задержки росли. В Яндексе HOBA оперирует не числом запросов, а прогнозируемой нагрузкой на канал с учётом скорости клиента. Выбор метрики (RIF + латентность, нагрузка на сеть, число активных миграций) определяет, будет ли алгоритм работать.

❯ Заключение

Балансировать нужно не абстрактные запросы, а ресурс, который заканчивается.

Масштабирование не начинается с покупки самого мощного сервера или подключения случайного балансировщика. Сначала нужно понять, что именно не справляется: CPU, GPU, сеть, диск, база данных или очередь задач.

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

Для небольшого проекта не нужно копировать инфраструктуру YouTube или Яндекс Диска. На первых этапах достаточно вынести долгие операции в отдельные воркеры, ограничить число параллельных задач и при необходимости добавить несколько серверов за балансировщиком. Инфраструктура должна расти вместе с продуктом, а не опережать его.


Ссылки:

GitHub: https://github.com/network-user

Источники:

YouTube и Google

1. Load Is Not What You Should Balance: Introducing Prequal, NSDI '24.
2. Google Global Cache: overview.
3. Google Global Cache: Cache Fill.
4. Google Global Cache: Content Served.

Яндекс Диск

1. Как Яндекс Диск выдерживает сотни гигабит входящего трафика.

Timeweb Cloud

1. Июльский дайджест: сервер за 30 секунд, новые модели и токены на агента.
2. Transfer 2.0, или Как я перестал бояться и полюбил миграции облачных серверов.

Больше интересных статей и новостей в нашем блоге на Хабре и телеграм-канале.

Реклама. ООО «ТАЙМВЭБ.КЛАУД», ИНН: 7810945525
Показать полностью 13
46

FUSE простым языком: как обычные программы притворяются файловыми системами

Вы наверняка сталкивались с этим эффектом: монтируете sshfs, rclone mount или gocryptfs — и после этого файловый менеджер, редактор, архиватор и бэкапилка работают с появившимся каталогом так, будто перед ними самая обычная файловая система. Хотя байты могут жить на сервере, в S3, внутри шифрованного каталога или вообще рождаться на лету.

Как программа из userspace умудряется встать между open() и данными так, чтобы остальная система ничего особенного не заметила? Сегодня разберёмся. Заваривайте чай покрепче (ядро любит крепкий), устраивайтесь поудобнее — поговорим про FUSE.

Сразу одна оговорка, чтобы не заложить мину в фундамент статьи: не всякая «сетевая папка» или подключённый телефон в файловом менеджере — это FUSE. GNOME GVfs, KDE KIO и MTP умеют показывать похожую картину на уровне приложений. FUSE идёт глубже: он подключается к файловому стеку ядра, поэтому смонтированное дерево видят обычные программы через привычные open(2), read(2), stat(2) и компанию.

Зачем вообще было огород городить

Обычные Linux-файловые системы вроде ext4, XFS и btrfs работают в ядре. Но причина не совсем в том, что «только ядро умеет трогать диск». Процесс с достаточными правами вполне может открыть блочное устройство и читать его напрямую.

Главное другое.

Главное — не доступ к блочному устройству, а единый слой, через который приложения вообще видят файлы.

В Linux эта задача была решена задолго до появления FUSE. Между программой и конкретной файловой системой находится VFS — Virtual File System: общий слой ядра, который позволяет ext4, XFS, NFS, tmpfs и десяткам других файловых систем выглядеть для приложений одинаково.

Программа говорит open("/home/user/file"), а VFS разбирается, какой mount находится под этим путём и какой конкретной файловой системе передать операцию.

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

А теперь представьте, что вы придумали супер-файловую систему. Например, хотите показывать почтовый ящик как каталог: письмо — файл, папка IMAP — директория, вложения — ещё файлы. Или хотите сделать файловый интерфейс к архивам, облаку, базе данных, удалённому серверу — да хоть к API кофеварки.

Путь первый — модуль ядра.

А это уже kernel-space: C, никаких привычных libc-шных удобств, аккуратная работа с памятью и блокировками, внутренние API ядра, которые для внешних модулей не обязаны вечно оставаться стабильными, и возможность превратить обычную ошибку в kernel oops или panic.

Знаете, как называется человек, который добровольно так делает?

Правильно: разработчик файловых систем ядра.

Их мало, и теперь вы возможно понимаете почему.

Обычная рабочая обстановка разработчика kernel space

Обычная рабочая обстановка разработчика kernel space

Но хотелось другого: чтобы файловую логику можно было написать обычной программой в userspace, отлаживать как обычную программу и при этом подключить к VFS так, чтобы остальная система видела нормальный mount.

Вот для этого и появился FUSE.

Однажды в Будапеште

История FUSE связана с венгерским разработчиком Миклошем Середи (Miklos Szeredi) и проектом AVFS — Virtual File System, который позволял обращаться к архивам и другим необычным источникам данных как к каталогам.

AVFS сначала экспериментировал с трюками вроде LD_PRELOAD, затем использовал интерфейс Coda. Но у обоих подходов были ограничения. В какой-то момент Середи решил, что нужен отдельный универсальный мост между VFS ядра и файловой системой в пользовательском процессе.

Так появился FUSE — Filesystem in Userspace.

Публичное объявление FUSE датируется 12 ноября 2001 года. Стабильный FUSE 1.0 вышел 20 февраля 2003-го. А 27 октября 2005 года FUSE вошёл в основное ядро Linux 2.6.14.

Это важная последовательность: FUSE не возник внезапно в момент попадания в mainline. К тому времени проект уже несколько лет жил отдельно, развивался и доказывал, что идея вообще работает. Ну знаете, как та история как когда bcachefs выпиздили из mainline, но только наоборот.

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

То есть не «вытащить файловые системы из ядра вообще», а провести новую границу ответственности.

С этого момента экзотическую файловую систему стало возможно разрабатывать без собственного kernel-модуля. А обычный пользователь при подходящей конфигурации системы мог монтировать FUSE-файловые системы без полноценного root-доступа.

И вот тут начинается самое интересное.

Как это устроено

Упростим путь одного чтения файла.

Допустим, программа делает:

open("/mnt/cloud/photo.jpg", ...)
read(fd, buf, 4096)

Если /mnt/cloud — FUSE-mount, схема примерно такая:

Происходит следующее.

  1. Программа вызывает обычный системный вызов. Никакого специального FUSE API она не знает.

  2. VFS определяет, к какому mount относится путь. Часть работы ядро вообще может выполнить без обращения к демону — например, если нужные данные или метаданные уже есть в кэше.

  3. Если для операции нужен userspace, FUSE-клиент в ядре формирует запрос. Он помещается в очередь соединения FUSE и становится доступен через специальное символьное устройство /dev/fuse.

  4. FUSE-демон читает запрос из /dev/fuse. Например: «прочитай такой-то файл с такого-то смещения». Дальше ядру всё равно, откуда демон возьмёт результат. Он может прочитать локальный файл, сходить по SSH, запросить объект из S3, расшифровать блок или сгенерировать содержимое на лету.

  5. Демон записывает ответ обратно в /dev/fuse. Ядро принимает его, обновляет своё состояние и завершает системный вызов приложения.

Получается почти МФЦ файловых систем.

Приложение приносит заявление ядру. Ядро понимает, что вопрос не по его ведомству, выдаёт талончик FUSE-демону, тот бегает по нужным инстанциям и возвращает официальный ответ. А заявитель на выходе получает обычные байты и вообще может не знать, что половина учреждения сидела в userspace.

Важно только не воспринимать метафору слишком буквально: не каждый read() обязательно превращается в отдельный поход через /dev/fuse. VFS, page cache, attribute cache и собственные механизмы FUSE умеют часть обращений обслуживать без нового round trip.

Именно поэтому точнее говорить не «FUSE пересылает в userspace каждый файловый вызов», а «FUSE пересылает туда те операции, для которых ядру нужен ответ файлового сервера».

А если демон упадёт?

Вот здесь у userspace есть огромный практический плюс.

Если ошибка происходит в файловой системе внутри ядра, радиус поражения потенциально велик: повреждение памяти, oops, deadlock, panic — спектр развлечений широкий.

Если падает FUSE-демон, ядро обычно продолжает жить.

Но есть важное «но»: сам mount после этого здоровее не становится. Процессы, которые обращаются к нему, могут получать ошибки, а некоторые незавершённые запросы способны зависнуть до разрыва/abort соединения.

То есть FUSE не превращает плохой код в безопасный. Он прежде всего изолирует значительную часть файловой логики от kernel-space. Ошибка теперь чаще ломает конкретную файловую систему и её клиентов, а не всю ОС.

Это всё ещё намного приятнее, чем отлаживать свежий kernel-модуль на машине, на которой вы параллельно пишете статью о преимуществах userspace.

А как пользователь вообще делает mount без root?

Сам системный вызов mount(2) — привилегированная операция. Поэтому в классической схеме libfuse есть небольшой помощник fusermount, а в современных версиях — fusermount3.

Обычно он устанавливается setuid-root и выполняет строго ограниченную привилегированную часть операции: проверяет условия, открывает нужное FUSE-соединение и помогает создать mount от имени пользователя. Сама файловая система после этого работает обычным процессом с обычными пользовательскими правами.

Отсюда же ещё одна деталь безопасности.

По умолчанию FUSE-mount пользователя не должен внезапно становиться ловушкой для всех соседей по машине. Чтобы разрешить доступ другим пользователям, файловую систему монтируют с опцией:

-o allow_other

А чтобы не-root пользователь вообще имел право запросить allow_other, администратор должен разрешить это в /etc/fuse.conf:

user_allow_other
То есть user_allow_other сам по себе никому mount не «расшаривает». Он только разрешает обычным пользователям применять опцию allow_other.

Маленькая деталь, но именно на таких деталях обычно и живёт безопасность многопользовательской системы.

Чем это закончилось

А дальше случилось то, что обычно случается с хорошей абстракцией: люди начали запихивать в неё всё подряд.

SSHFS появился в 2004 году и сделал почти неприлично простой следующую вещь: берём SFTP и показываем удалённый сервер как локальный каталог. Никакого отдельного сетевого файлового протокола для приложения — оно просто читает файлы.

NTFS-3G в июле 2006-го вышел как beta, а 21 февраля 2007 года получил стабильный релиз 1.0. Для Linux-пользователей эпохи dual boot это было почти бытовой магией: полноценная запись на NTFS без внедрения самой реализации NTFS-3G в ядро. Сегодня в Linux есть и отдельный in-kernel драйвер ntfs3, поэтому NTFS-3G интересен здесь прежде всего как исторически важный пример возможностей FUSE.

Есть EncFS и gocryptfs, которые показывают расшифрованное представление данных поверх зашифрованного каталога.

Есть s3fs и rclone mount, превращающие объектные и облачные хранилища в дерево файлов — иногда с теми семантическими компромиссами, которые неизбежны, когда API вида «положить объект по ключу» заставляют притворяться POSIX-файловой системой.

Есть GlusterFS. У CephFS есть и kernel-клиент, и userspace-вариант ceph-fuse, так что говорить «CephFS работает через FUSE» целиком было бы неверно — FUSE там один из способов подключения.

А в 2009 году появился ещё один родственник — CUSE, Character device in Userspace. Он вошёл в Linux 2.6.31 и применил ту же идею уже к символьным устройствам: kernel-side прокладка, а логика устройства — в userspace. Один из ранних показательных примеров, OSS Proxy, создавал через CUSE привычные /dev/dsp, /dev/adsp и /dev/mixer, а дальше пересылал звук в userspace-аудиостек.

То есть кто-то посмотрел на FUSE и сказал:

— А файловыми системами ограничиваться обязательно?

Конечно нет.

Android — тоже FUSE, но история там хитрее

С Android легко написать эффектную фразу и почти гарантированно соврать.

История shared/external storage там несколько раз менялась. В старых версиях Android использовался FUSE-слой, который в том числе помогал реализовывать нужную модель доступа к общему хранилищу. В Android 8 ради производительности появился SDCardFS — реализация в ядре. Затем архитектура снова изменилась: в Android 11 SDCardFS для современных устройств был выведен из игры, а эмуляция shared storage вернулась к обновлённому FUSE-подходу вместе с MediaProvider и scoped storage.

В Android 12 появился ещё и Android-специфичный FUSE passthrough для некоторых веток ядер 5.4/5.10.

И это отдельная причина не писать, что «FUSE passthrough появился в Linux 5.15». В Android похожая технология жила в своих kernel-ветках раньше, чем её вариант попал в mainline Linux.

А если вы просто открыли телефон по MTP в Nautilus или Dolphin — это вообще может быть GVfs/KIO, а не FUSE-mount. Внешне похоже, этаж абстракции другой.

И даже виртуалки

В конце 2010-х появился virtiofs — файловая система для быстрого совместного доступа виртуальной машины к дереву каталогов на хосте. Поддержка virtiofs есть в mainline начиная с Linux 5.4 (2019).

Тут особенно интересно, что virtiofs использует протокол FUSE, но классическая схема меняется: вместо обычного обмена guest-демона с ядром через /dev/fuse запросы идут между гостем и хостом через virtio/virtqueues.

То есть FUSE к этому моменту оказался уже не только удобным способом написать userspace-файловуху. Его протокол стал строительным блоком для других архитектур.

Неплохо для идеи, выросшей из желания нормально лазить по архивам.

А что со скоростью?

А вот здесь наступает момент, когда за удобную абстракцию приходит счёт.

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

Отсюда многолетняя репутация FUSE как «удобно, но медленно».

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

Passthrough: иногда демон можно вообще убрать с data path

В Linux 6.9, вышедшем в мае 2024 года, в mainline появился FUSE passthrough для обычного файлового I/O.

Идея такая: иногда userspace-демон нужен, чтобы решить, какому реальному файлу соответствует FUSE-файл, проверить политику или подготовить отображение. Но после этого нет особого смысла гонять через демон каждый read() и write(), если данные в итоге всё равно лежат в обычном backing file на нижележащей файловой системе.

Демон регистрирует backing file и при открытии говорит ядру примерно: «вот этому FUSE-файлу соответствует вот этот нижний файл». После этого поддерживаемые операции чтения/записи и mmap ядро может направлять непосредственно в backing filesystem.

Это очень мощная оптимизация, но не волшебная кнопка «ускорить любой FUSE».

Если ваши данные на самом деле синтезируются демоном, лежат в S3 или прилетают с другого конца SSH-соединения, реального локального backing file может просто не быть. Пропускать демон тогда некуда.

Кроме того, нынешняя upstream-реализация накладывает ограничения на создание passthrough-отображений: это не механизм, который произвольный непривилегированный FUSE-сервер автоматически получает бесплатно.

Так что правильнее думать о passthrough как об ускоренном маршруте для определённого класса FUSE-файловых систем.

io_uring: демон остаётся, но общаться с ним можно дешевле

В Linux 6.14, вышедшем 24 марта 2025 года, появилась другая оптимизация: FUSE over io_uring. Основную серию патчей для нового транспорта вёл Бернд Шуберт (Bernd Schubert).

Здесь никто не пытается убрать userspace-демон из схемы. Вместо этого меняется транспорт между ядром и демоном.

Классический /dev/fuse-интерфейс исторически означает много отдельных операций чтения и записи запросов. io_uring позволяет организовать очереди эффективнее: обрабатывать несколько запросов с меньшим syscall overhead, совмещать возврат ответа на старый запрос с получением нового и лучше сохранять CPU/NUMA locality.

Условно:

Classic:
kernel -> request -> daemon
kernel <- reply <- daemon
kernel -> request -> daemon
kernel <- reply <- daemon

io_uring:
kernel <==== queues / batches / commit+fetch ====> daemon

На микробенчмарках ранних patch series результаты местами были очень впечатляющими: отдельные сценарии показывали ускорения в два-три раза, а некоторые direct-I/O тесты — ещё больше. Но разброс между workload'ами огромный: другие тесты были близки к прежней скорости, а в одном из более поздних наборов измерений выигрыш составлял около 25%.

Поэтому фраза «в Linux 6.14 FUSE стал в несколько раз быстрее» была бы отличным заголовком и плохим техническим утверждением.

Корректнее так: FUSE-over-io_uring заметно снижает накладные расходы связи kernel -userspace и на подходящих нагрузках способен дать большой выигрыш, но величина ускорения сильно зависит от конкретной нагрузки.

И есть ещё одна важная деталь: поддержка io_uring-пути развивалась постепенно, и не все типы FUSE-запросов обязаны проходить через него — часть служебного обмена по-прежнему может использовать /dev/fuse.

Вот это различие стоит запомнить:

  • passthrough пытается не ходить к демону там, где данные можно отдать напрямую из backing file;

  • io_uring оставляет демон в цепочке, но делает сам обмен с ним дешевле.

Две оптимизации, две разные причины тормозов.

Так что же в FUSE было такого важного?

FUSE не доказал, что «файловые системы не нужны в ядре», и в целом не преследовал такую цель.

В ядре по-прежнему остаются VFS, mount namespace, кэши, проверки доступа, FUSE-клиент и куча другой работы. Из kernel-space вынесли то, что особенно удобно выносить: специфическую логику конкретной файловой системы.

И в этом, пожалуй, главное достижение FUSE.

Без него все перечисленные вещи не были бы физически невозможны — существовали и существуют kernel-модули, сетевые файловые протоколы, Coda, NFS, GVfs/KIO и другие способы решать похожие задачи.

Но FUSE сделал одну конкретную штуку почти рутинной:
обычный процесс может выставить произвольный источник данных в общий файловый namespace так, чтобы обычные программы работали с ним через привычные файловые системные вызовы.

Хотите файловую систему поверх SSH — пожалуйста.

Поверх шифрованного каталога — пожалуйста.

Поверх S3 — с оговорками, но пожалуйста.

Хотите протокол FUSE между виртуалкой и хостом — и до этого дошли.

А началось всё с вопроса: обязательно ли вся специфическая логика файловой системы должна жить в ядре?

Оказалось — нет.

И ядро с этим вполне ужилось: оставило себе контроль над границей, а userspace выдало окошко для приёма заявлений — почти как в МФЦ.

Показать полностью 5
6

Зачем выносить Linux-консоль из ядра: разбираемся с kmscon

Серия Эксперименты в homelab

Нажимаем Ctrl+Alt+F3, получаем чёрный экран с приглашением login: и обычно называем всё это «консолью» или «TTY».

Кто рисует буквы? Кто читает клавиатуру? Где здесь getty, а где terminal emulator? Почему kitty и Alacritty живут в userspace, а системная Linux-консоль исторически устроена иначе?

В статье я разбираю этот стек на работающем kmscon (преимущественно конечно потому, что мне очень интересно было посмотреть, как организован перенос консоли в userspace). Программа здесь нужна как стенд: её можно запустить на одном VT, посмотреть файловые дескрипторы и журнал, и увидеть границу между kernel VT, PTY, эмулятором терминала и DRM.

Что обычно называют консолью

Упрощённо классический путь выглядит так:

/dev/tty3 здесь не bash и не эмулятор. Это виртуальная консоль ядра. agetty печатает /etc/issue, выставляет скорость линии (на VT это формальность) и запускает login. Shell читает и пишет байты. Escape-последовательности вроде ESC[31m кто-то должен интерпретировать: в классической Linux console значительная часть этой работы исторически сидит в ядре. fbcon рисует в framebuffer; на современных GPU этот framebuffer часто даёт fbdev-эмуляция DRM-драйвера, но сама kernel console не ходит в KMS так, как это делает kmscon.

Короткий словарь, без которого дальше легко запутаться:

Что kmscon уносит в userspace

kmscon остаётся системной консолью: на обычном seat0 он привязан к конкретному kernel VT, например tty3, чтобы ядро переключало его вместе с остальными консолями. Эмуляция терминала, шрифты, клавиатура и отрисовка идут уже в userspace; на монитор картинка уходит через DRM/KMS или fbdev.

Это не схема исходников один в один, но она совпадает с устройством объекта terminal в v10.0.3:

struct kmscon_terminal {
struct tsm_screen *console;
struct tsm_vte *vte;
struct kmscon_pty *pty;
struct kmscon_font *font;
/* ... */
};

В одном процессе собраны state machine терминала (libtsm), PTY, шрифт и вывод. Видеоподсистема в этой сборке умеет drm2d, drm3d и fbdev; среди renderer'ов есть software bbulk и OpenGL ES gltex.

Оговорка, без которой легко написать лишнее: kmscon на seat0 не «удаляет kernel VT». Он занимает один VT как слот переключения и рисует уже своим renderer'ом. Соседние консоли могут по-прежнему быть обычным fbcon.

Запуск на tty

Первый запуск лучше делать не на tty1 (как минимум потому что некоторые дистрибутивы любят занимать его для запуска графического сервера) . Unit kmsconvt@.service занимает конкретный VT и конфликтует с getty на том же номере:

Conflicts=getty@%i.service
OnFailure=getty@%i.service
ExecStart=kmscon --vt=%I --no-switchvt

--no-switchvt не перехватывает активную консоль: процесс садится на tty3 и ждёт, пока этот VT станет передним. Conflicts при старте остановит getty@tty3, поэтому перед командой стоит глянуть, что там никого нет.

who
fgconsole
systemctl is-active getty@tty3.service
sudo systemctl start kmsconvt@tty3.service

Сразу после старта в журнале есть VT и шрифт, но ещё нет DRM:

NOTICE: using tty /dev/tty3
NOTICE: font_freetype: Using font Hack Regular
NOTICE: font_freetype: Using font Hack Bold

В /proc/$pid/fd то же самое: открыт /dev/tty3, /dev/dri/card0 нет. kmscon не забирает GPU, пока его VT неактивен.

После chvt 3 появляется строка, которая уже описывает реальный путь отрисовки:

NOTICE: terminal: Display [...] with backend [drm2d] text renderer
[bbulk] font engine [freetype]

Software-путь: DRM без 3D (drm2d), renderer bbulk, глифы через FreeType. chvt 1 вернул графический сеанс на место.

На рабочей машине я бы несколько дней пожил с kmscon только на дополнительном VT и только потом решал, нужен ли он на всех консолях. Это кусок recovery path.

Что видно снаружи процесса

Пока VT активен:

kmscon --vt=tty3 --no-switchvt
`- login -p

agetty в дереве нет. С 10.0.2 kmscon сам разбирает /etc/issue и запускает login.

Дескрипторы процесса kmscon после активации VT:

/dev/tty3
/dev/dri/card0
/dev/ptmx
/dev/input/event* # клавиатура и мышь

У login stdin/stdout/stderr смотрят в /dev/pts/2. Внутри сессии то же устройство:

/dev/pts/2
COLORTERM=truecolor

Shell сидит на PTY slave. /dev/tty3 держит kmscon как слот kernel VT. Байты от shell идут в PTY master, libtsm разбирает escape-последовательности, FreeType рисует глифы, bbulk кладёт их в DRM.

--reset-env по умолчанию включён, поэтому дочернему процессу kmscon отдаёт короткое окружение. COLORTERM=truecolor он выставляет сам.

printf 'English: Hello world\n'
printf 'Русский: Привет, мир\n'
printf 'CJK: 日本語 中文\n'
printf '\e[31mRED\e[0m \e[32mGREEN\e[0m \e[34mBLUE\e[0m\n'

Байты ушли в PTY. Корректная обработка UTF-8 и наличие глифа в выбранном шрифте это разные вещи.

С --hwaccel тот же VT идёт другим путём:

Display [...] with backend [drm3d] text renderer [gltex] font engine [freetype]

Консоль поднялась. Насколько gltex быстрее bbulk, без своего бенчмарка я цифр не ставлю. Man page обещает заметный выигрыш на новом железе.

setfont меняет шрифт kernel console. kmscon рисует сам, поэтому setfont ему безразличен. loadkeys тоже: раскладка идёт через libxkbcommon. Оба отличия прямо описаны в README.

Zoom (Ctrl++ / Ctrl+-) и scrollback (Shift+PageUp) в man заданы как штатные grab'ы: это уже поведение userspace-renderer'а, а не fbcon.

Что происходит по SSH без terminfo для kmscon

По умолчанию kmscon выставляет TERM=kmscon, а проект поставляет собственное описание terminfo.
$TERM едет на удалённую машину вместе с SSH PTY. Описание возможностей нужно уже там.

Если записи нет, приложения это видят сразу:

REMOTE_TERM=kmscon
infocmp: error: no match in terminfo database for terminal type
"kmscon"
tput: unknown terminal "kmscon" # exit 3

Тот же SSH с TERM=xterm-256color:

REMOTE_TERM=xterm-256color
tput colors
256

$TERM не имя бинарника, в котором запущена shell. Это идентификатор набора terminal capabilities. Приложение смотрит его через terminfo. Лечится установкой описания kmscon на удалённой стороне. Подменять тип терминала вручную на каждой SSH-сессии хуже: приложениям сообщают чужой набор возможностей.

То же чинят в разделе troubleshooting ArchWiki.

Кто выдаёт GPU и клавиатуру

Доступ к modesetting через primary DRM node и к сырым input devices нужно координировать: кто-то должен решить, какой сеанс сейчас активен, кому разрешено работать с GPU и клавиатурой и когда эти устройства нужно отдать другому сеансу.

Нужно понимать, какой процесс сейчас на переднем плане seat, когда он должен отпустить карту, и когда может взять её обратно.

kmscon умеет ходить в libseat (systemd-logind, elogind или seatd). Сборка с libseat опциональна, meson default false. Без неё остаётся собственная работа с VT. На практике это выглядит так:

  1. старт с --no-switchvt на неактивном tty3: процесс есть, /dev/dri/card0 ещё не открыт;

  2. chvt 3: у kmscon появляются /dev/dri/card0 и /dev/input/event*;

  3. chvt 1: графический сеанс снова на месте.

Открытый /dev/dri/card0 показывает, что на активном VT kmscon получает доступ к DRM-устройству. Сам по себе этот fd ещё не доказывает статус DRM master. Но переключение обратно на vt1 показывает, что управление display path корректно возвращается графической сессии.

Fallback и где kmscon не нужен

OnFailure=getty@%i.service срабатывает. Если instance kmscon падает, systemd поднимает обычный getty на том же VT:

/sbin/agetty --noreset --noclear --issue-file=... - linux

systemctl enable kmsconvt@ делает autovt@.service alias на этот шаблон: автоматически создаваемые VT пойдут в kmscon. Сервисы, которые явно зависят от getty@.service, не изменятся. На машине, где tty1 это recovery, я бы так не делал с первого дня.

Где от kmscon мало прока:

  • headless, только SSH;

  • serial console;

  • облачная VM без GPU, на которую вы всё равно не смотрите.

Пока его VT активен, kmscon использует DRM/KMS display path и должен координировать доступ к primary DRM device с графической сессией. README предупреждает: второй display server/compositor не сможет просто независимо забрать KMS-управление той же картой. Для передачи карты есть kmscon-launch-gui; связка с современным Wayland/libseat это уже отдельная история.

Показать полностью 3
Отличная работа, все прочитано!

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества

Недвижимость и ремонт

Теги

Популярные авторы

Сообщества