<?xml version="1.0"?>
<rss version="2.0"><channel><title>Articles: Linux (GNU) OS Articles</title><link>https://ithub.uno/statiarticles/3_linux-gnu-os-articles/?d=1</link><description>Articles: Linux (GNU) OS Articles</description><language>en</language><item><title>&#x41A;&#x430;&#x43A; &#x437;&#x430;&#x433;&#x440;&#x443;&#x436;&#x430;&#x435;&#x442;&#x441;&#x44F; Linux: &#x43F;&#x43E;&#x43B;&#x43D;&#x44B;&#x439; &#x440;&#x430;&#x437;&#x431;&#x43E;&#x440; &#x43F;&#x440;&#x43E;&#x446;&#x435;&#x441;&#x441;&#x430; &#x43E;&#x442; BIOS &#x434;&#x43E; &#x43F;&#x43E;&#x43B;&#x44C;&#x437;&#x43E;&#x432;&#x430;&#x442;&#x435;&#x43B;&#x44C;&#x441;&#x43A;&#x43E;&#x433;&#x43E; &#x441;&#x435;&#x430;&#x43D;&#x441;&#x430;</title><link>https://ithub.uno/statiarticles/3_linux-gnu-os-articles/%D0%BA%D0%B0%D0%BA-%D0%B7%D0%B0%D0%B3%D1%80%D1%83%D0%B6%D0%B0%D0%B5%D1%82%D1%81%D1%8F-linux-%D0%BF%D0%BE%D0%BB%D0%BD%D1%8B%D0%B9-%D1%80%D0%B0%D0%B7%D0%B1%D0%BE%D1%80-%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81%D0%B0-%D0%BE%D1%82-bios-%D0%B4%D0%BE-%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D1%81%D0%BA%D0%BE%D0%B3%D0%BE-%D1%81%D0%B5%D0%B0%D0%BD%D1%81%D0%B0-r9/</link><description><![CDATA[
<p><img src="https://ithub.uno/uploads/tmi_files_2026_01/Kak-izmenit-parametry-zagruzki-GRUB.png.c644c6dd5aa26dac74ed0e706477bef5.png" /></p>
<h1>Процесс загрузки Linux: подробный разбор по этапам</h1><p>Загрузка операционной системы Linux — это многоэтапный процесс, в ходе которого система проходит путь от включения питания до запуска пользовательской среды. Каждый этап выполняет строго определённую функцию и критически важен для корректной и стабильной работы ОС. Ниже подробно рассмотрены все ключевые стадии загрузки Linux в правильной последовательности.</p><hr><h2>1. Включение питания и инициализация BIOS / UEFI</h2><p>Процесс загрузки начинается в момент подачи питания на компьютер. Управление получает микропрограмма материнской платы — <strong>BIOS</strong> (Basic Input/Output System) или его современный аналог <strong>UEFI</strong> (Unified Extensible Firmware Interface).</p><p>На этом этапе выполняются следующие задачи:</p><ul><li><p>Инициализация базовых аппаратных компонентов: процессора, оперативной памяти, контроллеров ввода-вывода.</p></li><li><p>Проведение <strong>POST (Power-On Self Test)</strong> — самотестирования оборудования для выявления критических ошибок.</p></li><li><p>Определение доступных загрузочных устройств в соответствии с заданным приоритетом (SSD, HDD, USB-накопители, сетевой загрузчик).</p></li><li><p>Передача управления загрузчику операционной системы, расположенному на выбранном устройстве.</p></li></ul><p>UEFI отличается от классического BIOS поддержкой графического интерфейса, GPT-разметки, Secure Boot и более гибкой архитектурой загрузки.</p><hr><h2>2. Загрузчик операционной системы</h2><p>После завершения инициализации BIOS/UEFI управление передаётся <strong>загрузчику</strong>. В экосистеме Linux наиболее распространённым является <strong>GRUB2 (Grand Unified Bootloader)</strong>, однако также могут использоваться LILO, Syslinux, systemd-boot и другие решения.</p><p>Основные функции загрузчика:</p><ul><li><p>Загрузка ядра Linux (<code>vmlinuz</code>) в оперативную память.</p></li><li><p>Загрузка и передача ядру параметров командной строки.</p></li><li><p>(Опционально) Отображение меню выбора операционной системы, версии ядра или режима загрузки (обычный, recovery, single-user).</p></li><li><p>Загрузка начального RAM-диска (<code>initramfs</code> или <code>initrd</code>).</p></li></ul><p>В системах с UEFI загрузчик запускается через EFI-механизм и представляет собой <code>.efi</code>-приложение.</p><hr><h2>3. Инициализация ядра Linux</h2><p>После загрузки ядра в память управление полностью переходит к нему. Ядро Linux является центральным компонентом операционной системы и отвечает за взаимодействие между программным обеспечением и аппаратной частью.</p><p>На этапе инициализации ядра выполняются:</p><ul><li><p>Настройка процессора, планировщика задач и управления памятью.</p></li><li><p>Обнаружение и инициализация доступного оборудования.</p></li><li><p>Запуск драйверов, встроенных в ядро или загружаемых динамически.</p></li><li><p>Монтирование временной корневой файловой системы (<code>initramfs</code>).</p></li><li><p>Поиск и монтирование основной корневой файловой системы.</p></li><li><p>Запуск первого пользовательского процесса с PID 1 — <code>init</code>.</p></li></ul><p>Если ядро не может найти корневую файловую систему или необходимые драйверы, загрузка завершается ошибкой (kernel panic).</p><hr><h2>4. init-система и запуск пользовательского пространства</h2><p>Процесс с идентификатором <strong>PID 1</strong> — это основа пользовательского пространства Linux. Исторически это был <code>init</code> (SysVinit), однако в современных дистрибутивах его роль чаще всего выполняет <strong>systemd</strong>.</p><p>Функции init-системы:</p><ul><li><p>Чтение конфигурации загрузки (runlevels или targets).</p></li><li><p>Монтирование файловых систем.</p></li><li><p>Запуск и управление системными сервисами и демонами (udev, networking, logging).</p></li><li><p>Обеспечение корректного порядка запуска служб с учётом зависимостей.</p></li><li><p>Перевод системы в целевое состояние (например, <code>multi-user.target</code> или <code>graphical.target</code>).</p></li></ul><p>Systemd также отвечает за логирование (journald), управление устройствами и мониторинг состояния сервисов.</p><hr><h2>5. Запуск графической среды (при наличии)</h2><p>Если система предназначена для работы в графическом режиме, следующим этапом становится запуск графической подсистемы.</p><p>На этом шаге происходит:</p><ul><li><p>Запуск дисплейного сервера (<strong>Xorg</strong> или <strong>Wayland</strong>).</p></li><li><p>Инициализация менеджера входа в систему (GDM, SDDM, LightDM и др.).</p></li><li><p>Подготовка графической оболочки (GNOME, KDE Plasma, XFCE, Cinnamon и т. д.).</p></li></ul><p>Для серверных систем этот этап обычно отсутствует — загрузка завершается на уровне текстовой консоли.</p><hr><h2>6. Пользовательский сеанс</h2><p>Заключительный этап загрузки начинается после аутентификации пользователя. В этот момент система полностью готова к работе.</p><p>Запускаются:</p><ul><li><p>Пользовательские конфигурационные файлы и настройки среды.</p></li><li><p>Фоновые процессы и пользовательские службы.</p></li><li><p>Графические или консольные приложения, необходимые для работы.</p></li><li><p>Рабочее окружение с доступом к файловой системе, сети и периферии.</p></li></ul><p>На этом этапе Linux переходит в стабильное рабочее состояние.</p><hr><h2>FAQ — Часто задаваемые вопросы</h2><h3>Что делать, если Linux не загружается?</h3><p>Проверьте настройки BIOS/UEFI, порядок загрузки устройств и попробуйте восстановить загрузчик с помощью Live USB (например, через <code>grub-install</code>).</p><h3>Чем GRUB отличается от других загрузчиков?</h3><p>GRUB поддерживает большое количество файловых систем, гибкую конфигурацию, меню выбора ОС и сложные сценарии мультизагрузки.</p><h3>Можно ли обойтись без initramfs?</h3><p>Да, но только при статической сборке драйверов в ядро. Это усложняет обновление и снижает универсальность системы.</p><h3>Что делать, если загрузка зависает на этапе init/systemd?</h3><p>Используйте режим восстановления, проанализируйте логи командой <code>journalctl -xb</code> и проверьте конфигурацию сервисов.</p><h3>Как ускорить загрузку Linux?</h3><p>Отключите ненужные сервисы (<code>systemctl disable</code>), оптимизируйте targets, используйте SSD и файловые системы с быстрой инициализацией.</p><hr><h2>Заключение</h2><p>Процесс загрузки Linux представляет собой строго последовательную цепочку этапов, начиная с инициализации оборудования и заканчивая запуском пользовательской среды. BIOS или UEFI подготавливают аппаратную платформу и передают управление загрузчику, который загружает ядро. Ядро, в свою очередь, инициализирует систему и запускает init-процесс, управляющий всеми остальными компонентами. После старта системных служб и (при необходимости) графической среды пользователь получает полностью готовую к работе операционную систему.</p><p>Понимание этих этапов критически важно для диагностики проблем, оптимизации загрузки и администрирования Linux-систем.</p>]]></description><guid isPermaLink="false">9</guid><pubDate>Sun, 25 Jan 2026 21:28:29 +0000</pubDate></item><item><title>&#x41E;&#x441;&#x43D;&#x43E;&#x432;&#x44B; &#x430;&#x434;&#x43C;&#x438;&#x43D;&#x438;&#x441;&#x442;&#x440;&#x438;&#x440;&#x43E;&#x432;&#x430;&#x43D;&#x438;&#x44F; Linux: &#x43A;&#x43E;&#x43C;&#x430;&#x43D;&#x434;&#x44B;, &#x43D;&#x430;&#x441;&#x442;&#x440;&#x43E;&#x439;&#x43A;&#x430; &#x438; &#x443;&#x43F;&#x440;&#x430;&#x432;&#x43B;&#x435;&#x43D;&#x438;&#x435; &#x441;&#x438;&#x441;&#x442;&#x435;&#x43C;&#x43E;&#x439;</title><link>https://ithub.uno/statiarticles/3_linux-gnu-os-articles/%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D1%8B-%D0%B0%D0%B4%D0%BC%D0%B8%D0%BD%D0%B8%D1%81%D1%82%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F-linux-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D1%8B-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-%D0%B8-%D1%83%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%BE%D0%B9-r15/</link><description><![CDATA[
<p><img src="https://ithub.uno/uploads/tmi_files_2026_02/276b95773f8c925929d28e76e9ad8f84.webp.6123a597666e93b771434c249b485d11.webp" /></p>
<h2>Введение</h2><p>Администратор Linux-систем отвечает за поддержание стабильной работы программного обеспечения, мониторинг состояния системы, управление аппаратными ресурсами и резервное копирование. В этой статье мы рассмотрим основные команды и принципы работы системного администратора, а также приведём практические примеры, которые помогут новичкам освоить базовое администрирование Linux.</p><hr><h2>Базовые настройки Linux</h2><h3>Установка имени хоста</h3><p>Чтобы изменить имя компьютера (hostname), откройте терминал и выполните команду:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>sudo hostnamectl set-hostname your_hostname
</code></pre><p><strong>Примечание:</strong></p><ul><li><p>Замените <code>your_hostname</code> на желаемое имя.</p></li><li><p>Команда <code>hostnamectl</code> изменяет имя хоста постоянно. В отличие от <code>hostname</code>, которая действует только в текущей сессии.</p></li></ul><h3>Настройка часового пояса</h3><p>Чтобы установить часовой пояс, создайте символическую ссылку на нужную зону:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>sudo ln -sf /usr/share/zoneinfo/Asia/Kolkata /etc/localtime
</code></pre><p><strong>Примечание:</strong></p><ul><li><p>Путь <code>/usr/share/zoneinfo/Asia/Kolkata</code> замените на ваш регион.</p></li><li><p>Эта команда обновляет системное время в соответствии с выбранной зоной.</p></li></ul><hr><h2>Управление файлами в Linux</h2><p>В Linux <strong>всё является файлом</strong>: устройства, каталоги, конфигурации и программы. Владение базовыми командами управления файлами критически важно.</p><div class="tmiRichText__table-wrapper"><table style="min-width: 40px;"><colgroup><col style="min-width:20px;"><col style="min-width:20px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Команда</p></th><th colspan="1" rowspan="1"><p>Назначение</p></th></tr><tr><td colspan="1" rowspan="1"><p><code>cd</code></p></td><td colspan="1" rowspan="1"><p>Переход в другой каталог</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>ls</code></p></td><td colspan="1" rowspan="1"><p>Просмотр списка файлов и папок</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>vi</code></p></td><td colspan="1" rowspan="1"><p>Консольный редактор для конфигурационных файлов</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>nano</code></p></td><td colspan="1" rowspan="1"><p>Удобный редактор с подсказками</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>touch</code></p></td><td colspan="1" rowspan="1"><p>Создание нового файла или обновление временной метки</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>cp</code></p></td><td colspan="1" rowspan="1"><p>Копирование файлов и каталогов</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>mv</code></p></td><td colspan="1" rowspan="1"><p>Перемещение и переименование файлов</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>rm</code></p></td><td colspan="1" rowspan="1"><p>Удаление файлов и папок</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>fdisk</code></p></td><td colspan="1" rowspan="1"><p>Управление таблицами разделов диска</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>mount</code></p></td><td colspan="1" rowspan="1"><p>Монтирование файловой системы</p></td></tr></tbody></table></div><p><strong>Примеры:</strong></p><p>Создание нового файла:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>touch example.txt
</code></pre><p>Копирование файла:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>cp example.txt backup.txt
</code></pre><p>Переименование файла:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>mv backup.txt old_backup.txt
</code></pre><p>Удаление файла:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>rm old_backup.txt
</code></pre><hr><h2>Сетевые команды</h2><p>Сетевые инструменты позволяют администратору проверять соединение, настраивать интерфейсы и удалённо управлять системой.</p><div class="tmiRichText__table-wrapper"><table style="min-width: 40px;"><colgroup><col style="min-width:20px;"><col style="min-width:20px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Команда</p></th><th colspan="1" rowspan="1"><p>Назначение</p></th></tr><tr><td colspan="1" rowspan="1"><p><code>ping</code></p></td><td colspan="1" rowspan="1"><p>Проверка доступности сервера</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>traceroute</code></p></td><td colspan="1" rowspan="1"><p>Отслеживание маршрута пакетов</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>nslookup</code></p></td><td colspan="1" rowspan="1"><p>Проверка DNS-записей</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>ifconfig</code> / <code>ip</code></p></td><td colspan="1" rowspan="1"><p>Настройка сетевых интерфейсов</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>ssh</code></p></td><td colspan="1" rowspan="1"><p>Удалённый доступ и туннелирование</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>scp</code></p></td><td colspan="1" rowspan="1"><p>Копирование файлов по SSH</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>curl</code></p></td><td colspan="1" rowspan="1"><p>Передача данных на сервер или с него</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>nmap</code></p></td><td colspan="1" rowspan="1"><p>Сканирование сети и аудит безопасности</p></td></tr></tbody></table></div><p><strong>Пример проверки соединения:</strong></p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>ping google.com
</code></pre><p><strong>Пример копирования файла на удалённый сервер:</strong></p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>scp example.txt user@remote:/home/user/
</code></pre><hr><h2>Управление пользователями и группами</h2><p>В Linux есть три типа пользователей:</p><ol><li><p><strong>Root</strong> — суперпользователь с UID 0.</p></li><li><p><strong>Обычные пользователи</strong> — повседневные пользователи с уникальными UID.</p></li><li><p><strong>Системные пользователи</strong> — для работы служб, например <code>mysql</code> или <code>www-data</code>.</p></li></ol><h3>Основные команды управления пользователями</h3><div class="tmiRichText__table-wrapper"><table style="min-width: 40px;"><colgroup><col style="min-width:20px;"><col style="min-width:20px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Команда</p></th><th colspan="1" rowspan="1"><p>Назначение</p></th></tr><tr><td colspan="1" rowspan="1"><p><code>adduser</code></p></td><td colspan="1" rowspan="1"><p>Создание нового пользователя</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>useradd</code></p></td><td colspan="1" rowspan="1"><p>Низкоуровневая команда для создания пользователя</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>passwd</code></p></td><td colspan="1" rowspan="1"><p>Установка или изменение пароля</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>deluser</code> / <code>userdel</code></p></td><td colspan="1" rowspan="1"><p>Удаление пользователя</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>usermod</code></p></td><td colspan="1" rowspan="1"><p>Изменение свойств пользователя</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>groups</code></p></td><td colspan="1" rowspan="1"><p>Просмотр групп пользователя</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>groupadd</code> / <code>groupdel</code></p></td><td colspan="1" rowspan="1"><p>Создание и удаление групп</p></td></tr></tbody></table></div><p><strong>Пример:</strong></p><p>Создание нового пользователя с паролем:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>sudo adduser newuser
</code></pre><p>Добавление пользователя в группу <code>sudo</code>:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>sudo usermod -aG sudo newuser
</code></pre><hr><h2>Диагностика и мониторинг системы</h2><p>Эффективный администратор следит за состоянием системы и производительностью.</p><div class="tmiRichText__table-wrapper"><table style="min-width: 40px;"><colgroup><col style="min-width:20px;"><col style="min-width:20px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Команда</p></th><th colspan="1" rowspan="1"><p>Назначение</p></th></tr><tr><td colspan="1" rowspan="1"><p><code>top</code></p></td><td colspan="1" rowspan="1"><p>Просмотр процессов в реальном времени</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>htop</code></p></td><td colspan="1" rowspan="1"><p>Улучшенная версия <code>top</code> с цветным интерфейсом</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>vmstat</code></p></td><td colspan="1" rowspan="1"><p>Статистика процессов, памяти и CPU</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>iostat</code></p></td><td colspan="1" rowspan="1"><p>Мониторинг загрузки дисков</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>lsof</code></p></td><td colspan="1" rowspan="1"><p>Список открытых файлов</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>nmon</code></p></td><td colspan="1" rowspan="1"><p>Комплексный мониторинг системы</p></td></tr></tbody></table></div><hr><h2>Работа с логами</h2><p>Логи позволяют быстро находить ошибки и анализировать работу системы.</p><div class="tmiRichText__table-wrapper"><table style="min-width: 40px;"><colgroup><col style="min-width:20px;"><col style="min-width:20px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Команда</p></th><th colspan="1" rowspan="1"><p>Назначение</p></th></tr><tr><td colspan="1" rowspan="1"><p><code>dmesg</code></p></td><td colspan="1" rowspan="1"><p>Сообщения ядра</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>tail</code></p></td><td colspan="1" rowspan="1"><p>Последние строки файла</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>journalctl</code></p></td><td colspan="1" rowspan="1"><p>Управление логами systemd</p></td></tr></tbody></table></div><p><strong>Пример просмотра последних 20 строк системного журнала:</strong></p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>journalctl -n 20
</code></pre><hr><h2>Заключение</h2><p>Администрирование Linux требует понимания работы с файлами, пользователями, сетью, логами и системными ресурсами. Освоение приведённых команд позволяет:</p><ul><li><p>Настраивать и поддерживать систему;</p></li><li><p>Контролировать пользователей и права доступа;</p></li><li><p>Проводить диагностику и мониторинг;</p></li><li><p>Обеспечивать безопасность и стабильность работы системы.</p></li></ul><p>Практическое использование этих команд формирует фундаментальные навыки системного администратора, необходимые для работы в реальной среде Linux.</p>]]></description><guid isPermaLink="false">15</guid><pubDate>Fri, 06 Feb 2026 17:48:16 +0000</pubDate></item><item><title>Linux Server Hardening: &#x437;&#x430;&#x449;&#x438;&#x449;&#x430;&#x435;&#x43C; &#x441;&#x435;&#x440;&#x432;&#x435;&#x440; &#x441; &#x43D;&#x443;&#x43B;&#x44F; &#x434;&#x43E; &#x43F;&#x440;&#x43E;&#x434;&#x430;&#x43A;&#x448;&#x43D;&#x430;</title><link>https://ithub.uno/statiarticles/3_linux-gnu-os-articles/linux-server-hardening-%D0%B7%D0%B0%D1%89%D0%B8%D1%89%D0%B0%D0%B5%D0%BC-%D1%81%D0%B5%D1%80%D0%B2%D0%B5%D1%80-%D1%81-%D0%BD%D1%83%D0%BB%D1%8F-%D0%B4%D0%BE-%D0%BF%D1%80%D0%BE%D0%B4%D0%B0%D0%BA%D1%88%D0%BD%D0%B0-r31/</link><description><![CDATA[
<p><img src="https://ithub.uno/uploads/tmi_files_2026_02/linux-security-750x422.jpeg.0d290c1f22e37bb214d9cf01ffaf18b2.jpeg" /></p>
<p>Когда вы разворачиваете новый Linux-сервер, он похож на только что купленный смартфон — всё работает из коробки, но настройки безопасности далеки от идеала. SSH открыт для всего мира, root логинится напрямую, лишние сервисы весело крутятся в фоне. Эта статья — пошаговый гайд по hardening'у: от первого входа до полноценно защищённого продакшн-сервера.</p><hr><h2>Шаг 1. Первый вход и базовая гигиена</h2><h3>Обновление системы — это не опционально</h3><p>Первое, что делаем после подключения:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>apt update &amp;&amp; apt upgrade -y   # Debian/Ubuntu
dnf update -y                  # RHEL/CentOS/Fedora
</code></pre><p>Звучит банально, но большинство взломов происходит через известные уязвимости, для которых патчи уже давно вышли. Настройте автоматические обновления безопасности:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Ubuntu/Debian
apt install unattended-upgrades -y
dpkg-reconfigure --priority=low unattended-upgrades
</code></pre><p>Файл конфигурации <code>/etc/apt/apt.conf.d/50unattended-upgrades</code> — проверьте, что там включены только security-обновления, а не всё подряд. Автоапгрейд всего и сразу на продакшне — риск, обновления безопасности — необходимость.</p><h3>Создание непривилегированного пользователя</h3><p>Работать под root — это как ходить с заряженным пистолетом в кармане без предохранителя. Создаём обычного пользователя и даём ему sudo:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>adduser deploy
usermod -aG sudo deploy

# Или на RHEL-системах
useradd -m -G wheel deploy
passwd deploy
</code></pre><p>Проверяем, что sudo работает:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>su - deploy
sudo whoami  # должен вернуть root
</code></pre><hr><h2>Шаг 2. Настройка SSH — главные ворота на сервер</h2><p>SSH-демон — самое атакуемое место на любом публичном сервере. Посмотрите на логи нового сервера через сутки после запуска:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>grep "Failed password" /var/log/auth.log | wc -l
</code></pre><p>Тысячи попыток — это норма. Потому что боты сканируют интернет 24/7.</p><h3>Ключи вместо паролей</h3><p>Генерируем ключ на локальной машине (если ещё нет):</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>ssh-keygen -t ed25519 -C "your_email@example.com"
# ed25519 современнее и быстрее RSA-4096, при той же безопасности
</code></pre><p>Копируем публичный ключ на сервер:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@your-server-ip
</code></pre><p>Или вручную:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "ваш_публичный_ключ" &gt;&gt; ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
</code></pre><h3>Конфигурация sshd</h3><p>Редактируем <code>/etc/ssh/sshd_config</code>:</p><pre spellcheck="" class="tmiCode language-plaintext" data-language="Простой текст"><code># Порт — меняем с дефолтного 22
# Это не security через obscurity, а просто уменьшение шума в логах
Port 2222

# Запрещаем root-логин категорически
PermitRootLogin no

# Только ключи, никаких паролей
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no

# Только наш пользователь
AllowUsers deploy

# Отключаем X11 forwarding если не нужен
X11Forwarding no

# Ограничиваем попытки аутентификации
MaxAuthTries 3
MaxSessions 5

# Таймаут для неактивных сессий (секунды)
ClientAliveInterval 300
ClientAliveCountMax 2

# Более современные алгоритмы
KexAlgorithms curve25519-sha256,diffie-hellman-group14-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512,hmac-sha2-256
</code></pre><p>Перезапускаем sshd — но сначала проверяем конфиг:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>sshd -t  # тест конфига без перезапуска
systemctl restart sshd
</code></pre><p><strong>Важно:</strong> не закрывайте текущую сессию, пока не убедитесь, что можете подключиться с новыми настройками в отдельном окне терминала.</p><h3>Двухфакторная аутентификация через Google Authenticator</h3><p>Для особо важных серверов добавляем TOTP:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>apt install libpam-google-authenticator -y
# Запускаем от имени пользователя deploy
su - deploy
google-authenticator
</code></pre><p>Отвечаем на вопросы (рекомендую: yes, yes, no, yes), сохраняем QR-код и backup-коды.</p><p>В <code>/etc/pam.d/sshd</code> добавляем:</p><pre spellcheck="" class="tmiCode language-plaintext" data-language="Простой текст"><code>auth required pam_google_authenticator.so
</code></pre><p>В <code>sshd_config</code>:</p><pre spellcheck="" class="tmiCode language-plaintext" data-language="Простой текст"><code>ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
</code></pre><hr><h2>Шаг 3. Firewall — iptables vs nftables vs ufw</h2><h3>UFW — простой вариант для Ubuntu</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>apt install ufw -y

# Политика по умолчанию — всё запрещено входящее
ufw default deny incoming
ufw default allow outgoing

# Разрешаем только то, что нужно
ufw allow 2222/tcp  # наш SSH порт
ufw allow 80/tcp    # HTTP
ufw allow 443/tcp   # HTTPS

# Включаем
ufw enable
ufw status verbose
</code></pre><h3>nftables — современный и мощный подход</h3><p>nftables — замена iptables, доступная в современных дистрибутивах. Конфиг в <code>/etc/nftables.conf</code>:</p><pre spellcheck="" class="tmiCode language-plaintext" data-language="Простой текст"><code>#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;
        
        # Разрешаем established соединения
        ct state established,related accept
        
        # Loopback
        iif "lo" accept
        
        # ICMP (ping) — ограниченно
        ip protocol icmp icmp type echo-request limit rate 5/second accept
        
        # SSH на нашем порту только с определённых IP
        ip saddr 1.2.3.4 tcp dport 2222 accept
        
        # HTTP/HTTPS
        tcp dport { 80, 443 } accept
        
        # Логируем всё остальное перед дропом
        log prefix "nftables-drop: " level warn
        drop
    }
    
    chain forward {
        type filter hook forward priority 0; policy drop;
    }
    
    chain output {
        type filter hook output priority 0; policy accept;
    }
}
</code></pre><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>systemctl enable nftables
systemctl start nftables
nft list ruleset  # проверяем
</code></pre><h3>Защита от брутфорса — fail2ban</h3><p>fail2ban анализирует логи и банит IP-адреса, которые ломятся брутфорсом:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>apt install fail2ban -y
</code></pre><p>Создаём <code>/etc/fail2ban/jail.local</code>:</p><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code>[DEFAULT]
bantime  = 3600    ; 1 час бана
findtime  = 600    ; окно в 10 минут
maxretry = 3       ; 3 неудачных попытки

# Белый список
ignoreip = 127.0.0.1/8 ::1 ваш_офисный_ip

[sshd]
enabled = true
port    = 2222
filter  = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 86400  ; сутки для особо настойчивых

[nginx-http-auth]
enabled = true
port    = http,https
filter  = nginx-http-auth
logpath = /var/log/nginx/error.log

[nginx-limit-req]
enabled = true
port    = http,https
filter  = nginx-limit-req
logpath = /var/log/nginx/error.log
maxretry = 10
</code></pre><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>systemctl enable fail2ban
systemctl start fail2ban
fail2ban-client status  # проверяем статус
fail2ban-client status sshd  # статус конкретного jail
</code></pre><hr><h2>Шаг 4. Минимизация поверхности атаки</h2><h3>Отключаем ненужные сервисы</h3><p>Смотрим, что запущено:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>systemctl list-units --type=service --state=running
</code></pre><p>Типичные кандидаты на отключение на серверах без GUI:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>systemctl disable --now avahi-daemon    # mDNS — нужен только в локалке
systemctl disable --now cups            # принтеры — зачем на сервере?
systemctl disable --now bluetooth       # очевидно
systemctl disable --now ModemManager    # модемы на сервере?
</code></pre><h3>Удаляем ненужные пакеты</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>apt autoremove --purge
apt purge telnet rsh-client rsh-redone-client  # старые небезопасные протоколы
</code></pre><h3>Ограничиваем права на критичные файлы</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Проверяем SUID/SGID файлы — потенциальные векторы privilege escalation
find / -perm /4000 -o -perm /2000 2&gt;/dev/null | grep -v proc

# Устанавливаем строгие права на критичные файлы
chmod 600 /etc/shadow
chmod 644 /etc/passwd
chmod 644 /etc/group
chmod 600 /boot/grub/grub.cfg
</code></pre><h3>Настройка sysctl — параметры ядра</h3><p>Редактируем <code>/etc/sysctl.d/99-security.conf</code>:</p><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code># Защита от IP spoofing
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Отключаем принятие source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# Игнорируем ICMP redirect
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Защита от SYN flood
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2

# Логируем подозрительные пакеты
net.ipv4.conf.all.log_martians = 1

# Отключаем IPv6 если не используется
# net.ipv6.conf.all.disable_ipv6 = 1

# ASLR включён по умолчанию, но проверяем
kernel.randomize_va_space = 2

# Ограничиваем дамп core только root
fs.suid_dumpable = 0

# Защита от переполнения буфера
kernel.exec-shield = 1  # если поддерживается
</code></pre><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>sysctl -p /etc/sysctl.d/99-security.conf
</code></pre><hr><h2>Шаг 5. Аудит и мониторинг безопасности</h2><h3>auditd — аудит системных вызовов</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>apt install auditd audispd-plugins -y

# Пример правил в /etc/audit/rules.d/audit.rules
-w /etc/passwd -p wa -k user-modify
-w /etc/shadow -p wa -k user-modify
-w /etc/sudoers -p wa -k sudo-modify
-w /var/log/auth.log -p wa -k auth-log
-a always,exit -F arch=b64 -S execve -k command-exec
</code></pre><h3>AIDE — контроль целостности файлов</h3><p>AIDE создаёт базу данных хешей критичных файлов и сигнализирует об изменениях:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>apt install aide -y
aideinit  # инициализируем базу (занимает время)
cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db

# Проверка (запускаем по крону ежедневно)
aide --check
</code></pre><p>Добавляем в cron:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>0 4 * * * root /usr/bin/aide --check | mail -s "AIDE check $(hostname)" admin@example.com
</code></pre><h3>Проверяем открытые порты и соединения</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Смотрим что слушает
ss -tlnp  # лучше чем netstat
# или
netstat -tlnp

# Открытые соединения
ss -tnp state established

# Проверяем на rootkits
apt install rkhunter chkrootkit
rkhunter --update
rkhunter --check
chkrootkit
</code></pre><hr><h2>Шаг 6. AppArmor и SELinux — мандатный контроль доступа</h2><h3>AppArmor (Ubuntu/Debian)</h3><p>AppArmor ограничивает приложения набором разрешённых действий:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>apt install apparmor apparmor-utils -y
systemctl enable apparmor
aa-status  # проверяем статус профилей

# Переводим профили в enforce mode
aa-enforce /etc/apparmor.d/*

# Создаём профиль для приложения
aa-genprof /usr/sbin/nginx
</code></pre><h3>SELinux (RHEL/CentOS)</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Проверяем статус
getenforce  # Enforcing/Permissive/Disabled

# Включаем если выключен
setenforce 1
# В /etc/selinux/config: SELINUX=enforcing

# Просматриваем нарушения
audit2why &lt; /var/log/audit/audit.log

# Создаём разрешающий модуль из нарушений (осторожно!)
audit2allow -a -M mypolicy
semodule -i mypolicy.pp
</code></pre><hr><h2>Шаг 7. Шифрование и безопасность данных</h2><h3>Шифрование диска — LUKS</h3><p>Лучше делать при установке, но можно и на существующей системе (с резервным копированием!):</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Создаём зашифрованный контейнер
cryptsetup luksFormat /dev/sdb1

# Открываем
cryptsetup luksOpen /dev/sdb1 encrypted_data

# Монтируем
mkfs.ext4 /dev/mapper/encrypted_data
mount /dev/mapper/encrypted_data /mnt/secure
</code></pre><h3>Безопасное хранение секретов</h3><p>Никогда не кладите пароли и ключи в переменные окружения напрямую или в конфиги в git. Варианты:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># HashiCorp Vault
vault kv put secret/myapp db_password="supersecret"
vault kv get secret/myapp

# Systemd credentials (systemd 250+)
systemd-creds encrypt --name=db-password - &gt; /etc/credstore/db-password.cred
# В unit-файле:
# LoadCredential=db-password:/etc/credstore/db-password.cred
</code></pre><hr><h2>Чеклист: что проверить перед деплоем</h2><p>Финальный чеклист для продакшн-сервера:</p><ul><li><p>[ ] Система обновлена, настроены автоматические security-патчи</p></li><li><p>[ ] Создан непривилегированный пользователь, root-вход запрещён</p></li><li><p>[ ] SSH: только ключи, нестандартный порт, AllowUsers</p></li><li><p>[ ] Firewall настроен, открыты только нужные порты</p></li><li><p>[ ] fail2ban запущен и настроен</p></li><li><p>[ ] Отключены ненужные сервисы</p></li><li><p>[ ] sysctl параметры безопасности установлены</p></li><li><p>[ ] auditd запущен, правила аудита настроены</p></li><li><p>[ ] AIDE инициализирован, проверка по расписанию</p></li><li><p>[ ] AppArmor/SELinux в enforcing mode</p></li><li><p>[ ] Секреты не хранятся в открытом виде</p></li><li><p>[ ] Настроено оповещение о нарушениях безопасности</p></li></ul><hr><h2>Инструменты автоматической проверки</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Lynis — комплексный аудит безопасности
apt install lynis
lynis audit system

# OpenSCAP — проверка соответствия стандартам (CIS Benchmarks)
apt install libopenscap8 ssg-base
oscap xccdf eval --profile xccdf_org.ssgproject.content_profile_cis \
  /usr/share/xml/scap/ssg/content/ssg-ubuntu2004-ds.xml

# Nmap — сканируем себя снаружи
nmap -sV -p- --script vuln your-server-ip
</code></pre><p>Безопасность — это не состояние, это процесс. Регулярно проверяйте логи, обновляйте систему, пересматривайте правила по мере изменения инфраструктуры. И помните: самое уязвимое звено всегда находится между монитором и креслом.</p>]]></description><guid isPermaLink="false">31</guid><pubDate>Sun, 22 Feb 2026 13:07:13 +0000</pubDate></item><item><title>Linux Performance Tuning: &#x432;&#x44B;&#x436;&#x438;&#x43C;&#x430;&#x435;&#x43C; &#x43C;&#x430;&#x43A;&#x441;&#x438;&#x43C;&#x443;&#x43C; &#x438;&#x437; &#x441;&#x435;&#x440;&#x432;&#x435;&#x440;&#x430;</title><link>https://ithub.uno/statiarticles/3_linux-gnu-os-articles/linux-performance-tuning-%D0%B2%D1%8B%D0%B6%D0%B8%D0%BC%D0%B0%D0%B5%D0%BC-%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D1%83%D0%BC-%D0%B8%D0%B7-%D1%81%D0%B5%D1%80%D0%B2%D0%B5%D1%80%D0%B0-r34/</link><description><![CDATA[
<p><img src="https://ithub.uno/uploads/tmi_files_2026_02/c5f6d304a7c50b8_1400x689.jpg.be13fe4b524ae46afba298bcd4f0f947.jpg" /></p>
<p>Сервер работает, но медленно. Страницы грузятся с задержкой, база данных тормозит, под нагрузкой всё падает. Прежде чем покупать новое железо или мигрировать на более дорогой тариф — убедитесь, что вы используете текущее на 100%. Эта статья о том, как профилировать, диагностировать и оптимизировать Linux-сервер под реальную нагрузку.</p><hr><h2>Методология: сначала измеряем, потом меняем</h2><p>Главная ошибка при оптимизации — начинать с изменений без понимания проблемы. «Давайте увеличим swap» или «добавим RAM» — это стрельба вслепую. Правильный подход: <strong>измерить → найти узкое место → устранить → снова измерить</strong>.</p><p>Инструментарий делится на уровни детализации:</p><pre spellcheck="" class="tmiCode language-plaintext" data-language="Простой текст"><code>системный уровень   → top, htop, vmstat, iostat, sar
процессный уровень  → perf, strace, ltrace, lsof
сетевой уровень     → ss, netstat, iftop, nethogs, tcpdump
дисковый уровень    → iotop, blktrace, fio
профилировщики      → perf, bpftrace, flame graphs
</code></pre><hr><h2>CPU: диагностика и оптимизация</h2><h3>Что жрёт процессор</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Базовый top с сортировкой по CPU
top -o %CPU

# htop — интерактивный, красивый
htop

# Детальная картина по ядрам
mpstat -P ALL 1 5  # 5 измерений каждую секунду

# pidstat — статистика по процессам
pidstat -u 1 10   # каждую секунду, 10 раз
</code></pre><p>Смотрите на <code>us</code> (userspace), <code>sy</code> (kernel), <code>wa</code> (iowait), <code>st</code> (steal — для виртуалок). Высокий <code>wa</code> — проблема с диском или сетью, а не с CPU.</p><h3>Приоритеты процессов — nice и ionice</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Запустить процесс с низким приоритетом
nice -n 19 ./heavy_backup.sh

# Изменить приоритет запущенного процесса
renice -n 10 -p 1234

# Приоритет I/O (для компилятора в фоне)
ionice -c 3 -p 1234   # класс idle — работает только когда диск свободен

# Совместно
ionice -c 2 -n 7 nice -n 19 make -j$(nproc)
</code></pre><h3>Привязка процессов к ядрам — CPU affinity</h3><p>Для highload-сервисов критично избегать перепрыгивания между ядрами (cache miss):</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Запустить nginx worker на ядрах 0-3
taskset -c 0-3 nginx

# Изменить для запущенного процесса
taskset -pc 0-3 $(pgrep nginx | head -1)

# Посмотреть текущую привязку
taskset -p 1234
</code></pre><h3>perf — профилировщик ядра</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Установка
apt install linux-tools-generic linux-tools-$(uname -r)

# Общая статистика производительности
perf stat ./your_program

# Найти горячие функции (10 секунд)
perf top -p $(pgrep php-fpm | head -1)

# Запись для flame graph
perf record -F 99 -g -p $(pgrep nginx) -- sleep 10
perf script &gt; perf.out

# Flame graph (нужен Brendan Gregg's flamegraph)
git clone https://github.com/brendangregg/FlameGraph
./FlameGraph/stackcollapse-perf.pl perf.out | ./FlameGraph/flamegraph.pl &gt; flame.svg
</code></pre><p>Flame graph — это визуализация стека вызовов. Широкие «языки пламени» = много времени в этой функции.</p><hr><h2>Память: диагностика и управление</h2><h3>Понимаем использование памяти</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Читаем правильно — free показывает не совсем то, что думаем
free -h
# "available" — реально доступная память, не "free"

# Детальная информация
cat /proc/meminfo

# Кто сколько жрёт
ps aux --sort=-%mem | head -20

# Smem — более точные данные (учитывает shared memory)
apt install smem
smem -tk -s uss | tail -5
</code></pre><h3>Virtual Memory: tuning через sysctl</h3><p><code>/etc/sysctl.d/99-vm.conf</code>:</p><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code># vm.swappiness — насколько агрессивно использовать swap
# 0 = почти не свопировать (но не отключить совсем)
# 10 = для серверов с достаточной RAM — хорошее значение
# 60 = дефолт, нормально для десктопа
vm.swappiness = 10

# vm.dirty_ratio — процент RAM, при котором начинается синхронный flush на диск
# Для серверов с SSD можно увеличить
vm.dirty_ratio = 20
vm.dirty_background_ratio = 5

# Размер таблицы vnodes
vm.vfs_cache_pressure = 50  # меньше = кешируем дольше

# Отключаем OOM killer для критичных процессов
# vm.overcommit_memory = 2  # осторожно, лучше знать что делаете
</code></pre><h3>Huge Pages для баз данных</h3><p>PostgreSQL, MySQL и Java-приложения хорошо работают с huge pages (2MB вместо 4KB):</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Проверяем текущее состояние
grep -i huge /proc/meminfo

# Transparent Huge Pages (THP) — автоматически
cat /sys/kernel/mm/transparent_hugepage/enabled
# Для баз данных THP лучше отключить!
echo never &gt; /sys/kernel/mm/transparent_hugepage/enabled
echo never &gt; /sys/kernel/mm/transparent_hugepage/defrag

# Статические huge pages для PostgreSQL
# В /etc/sysctl.conf:
# vm.nr_hugepages = 1024  # 1024 * 2MB = 2GB

# В PostgreSQL
# huge_pages = on  # в postgresql.conf
</code></pre><hr><h2>Диски: I/O оптимизация</h2><h3>Диагностика дисковой подсистемы</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Общая нагрузка на диски в реальном времени
iostat -xz 1

# Ключевые метрики:
# %util — насколько занят диск (&gt;80% = проблема)
# await — среднее время ожидания запроса (мс)
# r/s, w/s — операции чтения/записи в секунду

# Кто именно читает/пишет
iotop -o  # только активные процессы

# Детальная трассировка
blktrace -d /dev/sda -o - | blkparse -i -
</code></pre><h3>Планировщик I/O</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Посмотреть текущий scheduler
cat /sys/block/sda/queue/scheduler

# Выбор scheduler:
# none/mq-deadline — для NVMe SSD (hardware очередь хороша)
# mq-deadline — для SATA SSD и HDD
# bfq — для десктопа, справедливое распределение

echo mq-deadline &gt; /sys/block/sda/queue/scheduler

# Закрепить через udev /etc/udev/rules.d/60-scheduler.rules:
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", \
  ATTR{queue/scheduler}="none"
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", \
  ATTR{queue/scheduler}="mq-deadline"
</code></pre><h3>Параметры очереди</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Размер очереди запросов (для NVMe можно больше)
cat /sys/block/nvme0n1/queue/nr_requests
echo 1024 &gt; /sys/block/nvme0n1/queue/nr_requests

# Read-ahead: сколько данных читать заранее
# Для баз данных — уменьшаем, для стриминга файлов — увеличиваем
blockdev --setra 256 /dev/sda  # 256 * 512 = 128KB
</code></pre><h3>Тестирование производительности дисков</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># fio — промышленный стандарт для тестирования I/O
apt install fio

# Тест случайного чтения 4K (как у базы данных)
fio --name=randread --ioengine=libaio --iodepth=32 \
    --rw=randread --bs=4k --direct=1 --size=1G \
    --numjobs=4 --runtime=60 --group_reporting

# Последовательная запись (как у лога)
fio --name=seqwrite --ioengine=libaio --iodepth=8 \
    --rw=write --bs=1M --direct=1 --size=10G \
    --numjobs=1 --runtime=60 --group_reporting
</code></pre><hr><h2>Сеть: тюнинг стека TCP/IP</h2><h3>Диагностика сетевой подсистемы</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Текущие соединения и их состояния
ss -s  # краткая статистика
ss -tnp  # все TCP соединения с процессами

# Ширина полосы в реальном времени
iftop -i eth0
nethogs eth0  # по процессам

# Статистика сетевого интерфейса
ip -s link show eth0

# Ошибки и дропы
ethtool -S eth0 | grep -i drop
cat /proc/net/dev
</code></pre><h3>Тюнинг TCP стека</h3><p><code>/etc/sysctl.d/99-network.conf</code>:</p><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code># Размеры буферов сокетов
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.core.rmem_default = 65536
net.core.wmem_default = 65536

# TCP буферы (min, default, max в байтах)
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728

# BBR — современный алгоритм контроля перегрузки Google
# Значительно улучшает пропускную способность при потерях
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

# TIME_WAIT sockets
net.ipv4.tcp_tw_reuse = 1      # переиспользуем TIME_WAIT сокеты для новых соединений
net.ipv4.tcp_fin_timeout = 15  # уменьшаем время ожидания FIN

# Очередь соединений
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535

# Keep-alive для обнаружения мёртвых соединений
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5

# Диапазон портов для исходящих соединений
net.ipv4.ip_local_port_range = 1024 65535
</code></pre><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Проверяем что BBR загружен
sysctl net.ipv4.tcp_congestion_control
lsmod | grep bbr
</code></pre><h3>Настройка сетевого интерфейса</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Проверяем настройки карты
ethtool eth0

# Включаем offloading (перекладываем работу на NIC)
ethtool -K eth0 tso on gso on gro on  # TCP/Generic segmentation offload

# Ring buffers — увеличиваем для highload
ethtool -G eth0 rx 4096 tx 4096

# IRQ affinity — привязываем прерывания к ядрам
cat /proc/interrupts | grep eth0
# Пишем скрипт привязки через /proc/irq/N/smp_affinity
</code></pre><hr><h2>Профилирование с bpftrace и eBPF</h2><p>Современный инструментарий для глубокого анализа без влияния на производительность:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Установка
apt install bpftrace

# Топ системных вызовов
bpftrace -e 'tracepoint:syscalls:sys_enter_* { @[probe] = count(); }'

# Медленные запросы к диску (&gt;10мс)
bpftrace -e 'tracepoint:block:block_rq_complete { 
    if (args-&gt;nr_sector &gt; 0) {
        $latency = (nsecs - @start[args-&gt;sector]) / 1000000;
        if ($latency &gt; 10) { printf("Slow I/O: %d ms\n", $latency); }
    }
}'

# Трассировка TCP retransmits
bpftrace -e 'tracepoint:tcp:tcp_retransmit_skb { @[comm] = count(); }'
</code></pre><hr><h2>Профилирование PHP-FPM специфически</h2><p>Раз уж мы работаем с PHP:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Статус PHP-FPM (если включён status page)
curl http://localhost/fpm-status?full

# Медленные запросы через slow log
# В php-fpm pool config:
# slowlog = /var/log/php-fpm/slow.log
# request_slowlog_timeout = 5s

# Анализ slow log
sort -t$'\t' -k1 -rn /var/log/php-fpm/slow.log | head -20

# strace на php-fpm worker
strace -c -p $(pgrep php-fpm | head -1) 2&gt;&amp;1 | head -50
</code></pre><hr><h2>Система мониторинга производительности</h2><h3>sar — System Activity Reporter</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>apt install sysstat
systemctl enable sysstat

# Статистика за сегодня
sar -u  # CPU
sar -r  # память
sar -b  # I/O
sar -n DEV  # сеть

# За конкретное время
sar -u -s 10:00:00 -e 11:00:00

# Сохранить историю в файл
sar -A &gt; /tmp/system_report_$(date +%Y%m%d).txt
</code></pre><h3>vmstat — обзор системы одной командой</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Запускаем каждую секунду
vmstat 1

# Колонки:
# procs: r(run queue), b(blocked)
# memory: swpd, free, buff, cache
# io: bi(blocks in), bo(blocks out)
# cpu: us, sy, id, wa, st
</code></pre><p>Если <code>r</code> &gt; числа CPU — CPU перегружен. Если <code>wa</code> &gt; 20% — проблема с диском.</p><hr><h2>Тюнинг NUMA-систем</h2><p>Для серверов с несколькими CPU-сокетами:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Проверяем топологию NUMA
numactl --hardware
numastat

# Запуск на конкретном NUMA узле
numactl --cpunodebind=0 --membind=0 ./your_app

# Для Redis на NUMA-системе
numactl --interleave=all redis-server

# Автоматический балансировщик NUMA
echo 1 &gt; /proc/sys/kernel/numa_balancing
</code></pre><hr><h2>Чеклист оптимизации</h2><p>Порядок важен — сначала находим, потом чиним:</p><ol><li><p>Измерить базовые метрики (<code>top</code>, <code>iostat</code>, <code>free</code>, <code>ss</code>)</p></li><li><p>Определить узкое место (CPU / RAM / Disk I/O / Network)</p></li><li><p>Детальное профилирование проблемной подсистемы</p></li><li><p>Применить изменение</p></li><li><p>Снова измерить — убедиться в улучшении</p></li><li><p>Применить к продакшну через конфиги (sysctl, udev, systemd)</p></li></ol><p>Помните: преждевременная оптимизация — корень всех зол. Оптимизируйте то, что реально тормозит, с данными в руках.</p>]]></description><guid isPermaLink="false">34</guid><pubDate>Sun, 22 Feb 2026 13:10:14 +0000</pubDate></item><item><title>Systemd: &#x43F;&#x43E;&#x43B;&#x43D;&#x43E;&#x435; &#x440;&#x443;&#x43A;&#x43E;&#x432;&#x43E;&#x434;&#x441;&#x442;&#x432;&#x43E; &#x434;&#x43B;&#x44F; &#x441;&#x438;&#x441;&#x442;&#x435;&#x43C;&#x43D;&#x44B;&#x445; &#x430;&#x434;&#x43C;&#x438;&#x43D;&#x438;&#x441;&#x442;&#x440;&#x430;&#x442;&#x43E;&#x440;&#x43E;&#x432;</title><link>https://ithub.uno/statiarticles/3_linux-gnu-os-articles/systemd-%D0%BF%D0%BE%D0%BB%D0%BD%D0%BE%D0%B5-%D1%80%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE-%D0%B4%D0%BB%D1%8F-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%BD%D1%8B%D1%85-%D0%B0%D0%B4%D0%BC%D0%B8%D0%BD%D0%B8%D1%81%D1%82%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BE%D0%B2-r40/</link><description><![CDATA[
<p><img src="https://ithub.uno/uploads/tmi_files_2026_02/systemd-dark.png.00b8b03e77ddbe31f6f3b3360a790ffa.png" /></p>
<p>Systemd — это уже не просто init-система. Это целая экосистема для управления сервисами, логами, сетью, таймерами, точками монтирования и многим другим. Большинство администраторов использует 20% его возможностей, не подозревая об остальных 80%. Эта статья закроет пробелы.</p><hr><h2>Архитектура systemd: что за чем стоит</h2><pre spellcheck="" class="tmiCode language-plaintext" data-language="Простой текст"><code>systemd
├── systemd-journald   — централизованное логирование
├── systemd-networkd   — управление сетью
├── systemd-resolved   — DNS resolver
├── systemd-timesyncd  — синхронизация времени
├── systemd-logind     — управление сессиями
├── systemd-udevd      — управление устройствами
└── systemd-tmpfilesd  — управление временными файлами
</code></pre><p>Всё взаимодействует через D-Bus и сокеты. Это важно понимать для отладки.</p><hr><h2>Unit-файлы: анатомия сервиса</h2><h3>Базовая структура</h3><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code>[Unit]
Description=MyApp Web Service
Documentation=https://myapp.example.com/docs
After=network.target mysql.service redis.service
Requires=mysql.service
Wants=redis.service

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/php /var/www/myapp/server.php
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -TERM $MAINPID
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
</code></pre><h3>Секция [Unit]: зависимости и порядок</h3><p>Разница между <code>After/Before</code> и <code>Requires/Wants</code> критически важна:</p><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code># ПОРЯДОК ЗАПУСКА (не зависимость!)
After=network.target    # запускаемся после network.target
Before=nginx.service    # запускаемся до nginx

# ЗАВИСИМОСТИ (и порядок)
Requires=mysql.service  # жёсткая: если mysql упал — мы тоже падаем
Wants=redis.service     # мягкая: пробуем запустить redis, но не критично
BindsTo=device.mount    # как Requires, но реагирует на остановку устройства
PartOf=app.service      # stop/restart parent = stop/restart нас

# Конфликты
Conflicts=apache2.service  # не запускаться вместе с apache2
</code></pre><h3>Типы сервисов — Type=</h3><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code>Type=simple     # процесс запущен = сервис готов (дефолт)
Type=exec       # как simple, но ждёт exec() (systemd 240+)
Type=forking    # главный процесс форкает дочерний и завершается (nginx, apache)
Type=oneshot    # выполняется и завершается (для скриптов)
Type=notify     # процесс сигнализирует systemd о готовности через sd_notify()
Type=dbus       # готовность через D-Bus
Type=idle       # запускается после других, когда нет других задач
</code></pre><p>Для PHP-FPM используем <code>Type=notify</code> — он поддерживает sd_notify:</p><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code>[Service]
Type=notify
ExecStart=/usr/sbin/php-fpm8.2 --nodaemonize
</code></pre><hr><h2>Продвинутые настройки сервиса</h2><h3>Переменные окружения и секреты</h3><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code>[Service]
# Прямо в unit-файле (не для секретов!)
Environment=APP_ENV=production
Environment=LOG_LEVEL=info

# Из файла (безопаснее)
EnvironmentFile=/etc/myapp/environment
EnvironmentFile=-/etc/myapp/local.env  # дефис = не ошибка если нет файла

# Systemd credentials (systemd 250+ — самый безопасный способ)
LoadCredential=db-password:/etc/credstore/db-password.cred
# Доступно в: /run/credentials/myapp.service/db-password
</code></pre><p>Файл <code>/etc/myapp/environment</code>:</p><pre spellcheck="" class="tmiCode language-plaintext" data-language="Простой текст"><code>DB_HOST=localhost
DB_PORT=3306
DB_NAME=myapp
DB_USER=myapp
DB_PASSWORD=supersecret
REDIS_HOST=127.0.0.1
</code></pre><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>chmod 600 /etc/myapp/environment
chown root:www-data /etc/myapp/environment
</code></pre><h3>Изоляция и безопасность сервиса</h3><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code>[Service]
# Запуск от непривилегированного пользователя
User=www-data
Group=www-data

# Динамический пользователь (создаётся на время жизни сервиса)
DynamicUser=yes

# Файловая система
PrivateTmp=yes              # отдельный /tmp
PrivateDevices=yes          # нет доступа к /dev (кроме базовых)
ProtectHome=yes             # нет доступа к /home /root /run/user
ProtectSystem=strict        # / только для чтения
ReadWritePaths=/var/www/myapp/storage /var/log/myapp

# Сеть
PrivateNetwork=no           # нужна для веб-сервиса
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX

# Процессы
NoNewPrivileges=yes         # нет setuid/setgid после старта
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes

# Системные вызовы (белый список)
SystemCallFilter=@system-service  # разрешённые для сервисов
SystemCallFilter=~@privileged     # запрещаем привилегированные
SystemCallErrorNumber=EPERM

# Capabilities
CapabilityBoundingSet=CAP_NET_BIND_SERVICE  # только для порта &lt;1024
AmbientCapabilities=CAP_NET_BIND_SERVICE

# Ограничение ресурсов
LimitNOFILE=65535       # открытые файловые дескрипторы
LimitNPROC=512          # процессы
MemoryLimit=2G          # максимум памяти
CPUQuota=50%            # не более 50% CPU
</code></pre><h3>Политики перезапуска</h3><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code>[Service]
Restart=on-failure      # только при ненулевом коде возврата
Restart=always          # всегда (кроме systemctl stop)
Restart=unless-stopped  # всегда, даже при ненулевом коде

RestartSec=5s           # пауза перед перезапуском

# Ограничение перезапусков (не более 3 за 30 секунд)
StartLimitIntervalSec=30s
StartLimitBurst=3
StartLimitAction=none   # что делать при достижении лимита: none|reboot|poweroff

# Watchdog — если нет heartbeat за N секунд — перезапуск
WatchdogSec=30s
</code></pre><hr><h2>systemctl: продвинутое использование</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Основные команды
systemctl start myapp
systemctl stop myapp
systemctl restart myapp
systemctl reload myapp       # отправить SIGHUP (graceful reload)
systemctl status myapp

# Включить/выключить автозапуск
systemctl enable myapp
systemctl disable myapp
systemctl enable --now myapp  # включить и сразу запустить

# Маскировка (нельзя запустить даже вручную)
systemctl mask cups
systemctl unmask cups

# Информация о юните
systemctl cat myapp           # показать содержимое unit-файла
systemctl show myapp          # все свойства (машиночитаемо)
systemctl show myapp -p Restart,RestartSec  # конкретные свойства

# Зависимости
systemctl list-dependencies myapp
systemctl list-dependencies --reverse myapp  # кто зависит от нас

# Все юниты
systemctl list-units --type=service
systemctl list-units --state=failed
systemctl list-unit-files

# Применение изменений без потери сервиса (systemd 230+)
systemctl daemon-reload
systemctl try-reload-or-restart myapp
</code></pre><hr><h2>systemd Timers: замена cron</h2><p>Таймеры systemd мощнее cron: есть логирование, управление зависимостями, можно запустить вручную.</p><h3>Создаём пару service + timer</h3><p><code>/etc/systemd/system/backup.service</code>:</p><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code>[Unit]
Description=Daily Database Backup
After=network.target mysql.service

[Service]
Type=oneshot
User=backup
ExecStart=/usr/local/bin/backup-database.sh
StandardOutput=journal
StandardError=journal
</code></pre><p><code>/etc/systemd/system/backup.timer</code>:</p><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code>[Unit]
Description=Daily Database Backup Timer
Requires=backup.service

[Timer]
# Каждый день в 2:30
OnCalendar=*-*-* 02:30:00

# После загрузки системы (если пропустили)
Persistent=true

# Случайная задержка до 5 минут (чтобы не все запускались одновременно)
RandomizedDelaySec=5min

[Install]
WantedBy=timers.target
</code></pre><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>systemctl enable --now backup.timer
systemctl list-timers  # все таймеры и когда запустятся
systemctl start backup.service  # запустить вручную без ожидания таймера
</code></pre><h3>Синтаксис OnCalendar</h3><pre spellcheck="" class="tmiCode language-plaintext" data-language="Простой текст"><code>OnCalendar=Mon-Fri *-*-* 09:00:00   # каждый будний день в 9:00
OnCalendar=weekly                    # каждую неделю (понедельник 0:00)
OnCalendar=monthly                   # первый день месяца
OnCalendar=*-*-* *:00/15:00          # каждые 15 минут
OnCalendar=2024-03-*                 # каждый день в марте 2024
OnCalendar=Sat,Sun 12:00:00          # субботу и воскресенье в полдень
</code></pre><p>Проверка синтаксиса:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>systemd-analyze calendar "Mon-Fri *-*-* 09:00:00"
</code></pre><hr><h2>journald: работа с логами</h2><h3>Базовые команды</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Логи конкретного сервиса
journalctl -u nginx
journalctl -u nginx -n 100  # последние 100 строк
journalctl -u nginx -f      # следим в реальном времени (как tail -f)

# По времени
journalctl --since "2024-01-15 10:00" --until "2024-01-15 11:00"
journalctl --since "1 hour ago"
journalctl --since yesterday

# По приоритету
journalctl -p err          # только ошибки
journalctl -p warning..err # от warning до err
# 0=emerg 1=alert 2=crit 3=err 4=warning 5=notice 6=info 7=debug

# Форматы вывода
journalctl -u myapp -o json     # JSON
journalctl -u myapp -o json-pretty  # читаемый JSON
journalctl -u myapp -o short-precise  # с микросекундами

# Загрузки системы
journalctl --list-boots
journalctl -b 0    # текущая загрузка
journalctl -b -1   # предыдущая загрузка
journalctl -b -1 -p err  # ошибки при предыдущей загрузке
</code></pre><h3>Настройка journald</h3><p><code>/etc/systemd/journald.conf</code>:</p><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code>[Journal]
# Максимальный размер хранилища
SystemMaxUse=2G
SystemKeepFree=500M

# Максимальный размер одного файла журнала
SystemMaxFileSize=200M

# Хранить журналы N дней
MaxRetentionSec=1month

# Сжатие
Compress=yes

# Пересылка в syslog (если нужен rsyslog)
ForwardToSyslog=no
ForwardToWall=no

# Максимальный размер сообщения
LineMax=48K
</code></pre><h3>Структурированное логирование из приложения</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Systemd-cat
echo "Application started" | systemd-cat -t myapp -p info

# С полями для фильтрации
systemd-cat -t myapp &lt;&lt;&lt; "MESSAGE=User login SUCCESSFUL
USER_ID=42
REMOTE_IP=192.168.1.100"

# Фильтрация по custom полям
journalctl SYSLOG_IDENTIFIER=myapp
journalctl _SYSTEMD_UNIT=myapp.service USER_ID=42
</code></pre><hr><h2>Drop-in конфиги: расширяем без изменения оригинала</h2><p>Никогда не редактируйте файлы в <code>/lib/systemd/system/</code> — они перезапишутся при обновлении. Используйте drop-in:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Создаём директорию
mkdir -p /etc/systemd/system/nginx.service.d/

# Создаём override
cat &gt; /etc/systemd/system/nginx.service.d/override.conf &lt;&lt;EOF
[Service]
# Добавляем переменные окружения к nginx
Environment=APP_ENV=production

# Увеличиваем лимит файловых дескрипторов
LimitNOFILE=65535

# Рестарт при падении (дефолт nginx — no)
Restart=on-failure
RestartSec=5s
EOF

# Или через systemctl edit (открывает редактор автоматически)
systemctl edit nginx

systemctl daemon-reload
</code></pre><hr><h2>cgroups v2 через systemd</h2><p>Современные версии systemd используют cgroups v2 для изоляции ресурсов:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Статистика ресурсов для сервиса
systemctl status myapp
# или детальнее
systemd-cgtop

# Запустить с ограничениями на лету (transient unit)
systemd-run --unit=my-temp-task --slice=user.slice \
    -p MemoryMax=512M -p CPUQuota=25% \
    /usr/bin/python3 heavy_script.py

# Ограничить группу пользователя
systemctl set-property user-1000.slice MemoryMax=4G
</code></pre><hr><h2>Анализ времени загрузки</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Общее время загрузки
systemd-analyze

# Детально по сервисам
systemd-analyze blame

# Визуализация в SVG
systemd-analyze plot &gt; boot.svg

# Критический путь загрузки
systemd-analyze critical-chain

# Проверка unit-файла на ошибки
systemd-analyze verify /etc/systemd/system/myapp.service
</code></pre><hr><h2>Полезные паттерны для PHP-приложений</h2><h3>Сервис PHP-FPM с worker isolation</h3><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code>[Service]
# Мягкий рестарт (дождаться завершения текущих запросов)
ExecReload=/bin/kill -USR2 $MAINPID

# Watchdog через sd_notify
WatchdogSec=60s
NotifyAccess=main

# Логи в journald напрямую
StandardOutput=journal
StandardError=journal
SyslogIdentifier=php-fpm

# Автоматический сбор core dump
LimitCORE=infinity
Slice=web.slice  # группировка в slice для общих лимитов
</code></pre><h3>Slice для группировки сервисов</h3><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code># /etc/systemd/system/web.slice
[Unit]
Description=Web Services Slice
Before=slices.target

[Slice]
MemoryMax=8G
CPUQuota=200%
</code></pre><p>Все сервисы в <code>web.slice</code> суммарно не превысят 8GB RAM и 200% CPU (2 ядра).</p><hr><h2>Отладка: когда что-то пошло не так</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Быстрая диагностика
systemctl --failed              # упавшие сервисы
journalctl -p err -b            # ошибки с последней загрузки
journalctl -xe                  # последние записи с расшифровкой

# Подробный запуск в debug-режиме
SYSTEMD_LOG_LEVEL=debug systemd-analyze verify myapp.service

# Проверка прав и доступов
systemd-run --unit=debug-shell -p PrivateTmp=yes bash
# Запускает оболочку с теми же ограничениями, что и сервис

# Strace сервиса через systemd
systemctl set-environment STRACE_OPTS="-f -e trace=network"
systemctl restart myapp
</code></pre><p>Systemd — мощный инструмент, который при правильном использовании превращает управление сервисами из искусства в инженерию. Используйте drop-in конфиги, настраивайте изоляцию, пишите таймеры вместо cron — и ваша система станет предсказуемой и управляемой.</p>]]></description><guid isPermaLink="false">40</guid><pubDate>Sun, 22 Feb 2026 13:16:59 +0000</pubDate></item><item><title>&#x41F;&#x43E;&#x434;&#x440;&#x43E;&#x431;&#x43D;&#x43E;&#x435; &#x440;&#x443;&#x43A;&#x43E;&#x432;&#x43E;&#x434;&#x441;&#x442;&#x432;&#x43E; &#x43F;&#x43E; &#x430;&#x434;&#x43C;&#x438;&#x43D;&#x438;&#x441;&#x442;&#x440;&#x438;&#x440;&#x43E;&#x432;&#x430;&#x43D;&#x438;&#x44E; Linux (&#x417;&#x430;&#x445;&#x432;&#x430;&#x442;&#x438;&#x43C; &#x441;&#x440;&#x430;&#x437;&#x443; &#x43C;&#x43D;&#x43E;&#x433;&#x43E; &#x43E;&#x431;&#x43B;&#x430;&#x441;&#x442;&#x435;&#x439;)</title><link>https://ithub.uno/statiarticles/3_linux-gnu-os-articles/%D0%BF%D0%BE%D0%B4%D1%80%D0%BE%D0%B1%D0%BD%D0%BE%D0%B5-%D1%80%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE-%D0%BF%D0%BE-%D0%B0%D0%B4%D0%BC%D0%B8%D0%BD%D0%B8%D1%81%D1%82%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8E-linux-%D0%B7%D0%B0%D1%85%D0%B2%D0%B0%D1%82%D0%B8%D0%BC-%D1%81%D1%80%D0%B0%D0%B7%D1%83-%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE-%D0%BE%D0%B1%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D0%B9-r46/</link><description><![CDATA[
<p><img src="https://ithub.uno/uploads/tmi_files_2026_02/maxresdefault.jpg.e1dfbfafdec467b8c5b6c096af239023.jpg" /></p>
<h1>Сети в Linux: от основ до продвинутой диагностики</h1><h2>ip и iproute2: современная работа с сетью</h2><p>Команда <code>ifconfig</code> устарела. Всё что нужно — в пакете <code>iproute2</code>:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Интерфейсы и адреса
ip addr show
ip addr show eth0
ip -4 addr  # только IPv4
ip -6 addr  # только IPv6

# Добавить/удалить IP
ip addr add 192.168.1.100/24 dev eth0
ip addr del 192.168.1.100/24 dev eth0

# Маршруты
ip route show
ip route add default via 192.168.1.1
ip route add 10.0.0.0/8 via 10.100.0.1 dev eth1
ip route del 10.0.0.0/8

# ARP таблица
ip neigh show
ip neigh flush all

# Статистика интерфейсов
ip -s link show eth0

# Состояние соединений
ss -tnp               # TCP с процессами
ss -tlnp              # только слушающие
ss -u                 # UDP
ss -x                 # Unix sockets
ss -tnp state established  # установленные
ss -tnp state time-wait    # TIME_WAIT
ss -s                 # сводная статистика
</code></pre><h2>Диагностика сетевых проблем</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Трассировка маршрута
traceroute -n google.com     # числовые адреса, быстрее
mtr --report google.com      # интерактивный traceroute

# DNS диагностика
dig google.com
dig @8.8.8.8 google.com      # конкретный DNS
dig google.com MX            # MX записи
dig -x 8.8.8.8               # reverse lookup
nslookup google.com
host google.com

# Захват трафика
tcpdump -i eth0 port 80
tcpdump -i eth0 host 10.0.0.1
tcpdump -i eth0 -w capture.pcap  # запись в файл
tcpdump -r capture.pcap          # чтение из файла

# Тест пропускной способности
iperf3 -s                        # сервер
iperf3 -c server-ip              # клиент
iperf3 -c server-ip -P 4         # 4 параллельных потока

# Latency
ping -c 10 -i 0.2 server-ip
hping3 -S -p 80 -c 10 server-ip  # TCP ping
</code></pre><h2>Виртуальные сети: bridge, vlan, veth</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># VLAN
ip link add link eth0 name eth0.100 type vlan id 100
ip addr add 192.168.100.1/24 dev eth0.100
ip link set dev eth0.100 up

# Bridge (для виртуальных машин)
ip link add br0 type bridge
ip link set eth0 master br0
ip link set br0 up
ip addr add 192.168.1.10/24 dev br0

# veth пара (для контейнеров)
ip link add veth0 type veth peer name veth1
ip link set veth1 netns container-ns

# Bonding (агрегация каналов)
ip link add bond0 type bond mode active-backup
ip link set eth0 master bond0
ip link set eth1 master bond0
ip link set bond0 up
</code></pre><h2>nftables: полный конфиг для веб-сервера</h2><pre spellcheck="" class="tmiCode language-plaintext" data-language="Простой текст"><code>#!/usr/sbin/nft -f

flush ruleset

define WEB_PORTS = { 80, 443 }
define SSH_PORT = 2222
define TRUSTED_IPS = { 10.0.0.0/8, 192.168.0.0/16 }

table inet filter {
    # Счётчики для статистики
    counter ssh_allowed {}
    counter web_traffic {}
    counter dropped {}

    set banned_ips {
        type ipv4_addr
        flags timeout
        timeout 1d
    }

    chain input {
        type filter hook input priority 0; policy drop;
        
        # Заблокированные IP
        ip saddr @banned_ips drop
        
        # Established/related
        ct state established,related accept
        ct state invalid drop
        
        # Loopback
        iif "lo" accept
        
        # ICMP (ограниченно)
        ip protocol icmp icmp type { echo-request, echo-reply, destination-unreachable, time-exceeded } limit rate 10/second accept
        ip6 nexthdr icmpv6 accept
        
        # SSH с ограничением скорости (защита от brute-force)
        tcp dport $SSH_PORT ct state new limit rate 3/minute counter name ssh_allowed accept
        tcp dport $SSH_PORT ct state new counter name dropped drop
        
        # HTTP/HTTPS
        tcp dport $WEB_PORTS counter name web_traffic accept
        
        # Внутренние сервисы только с доверенных IP
        ip saddr $TRUSTED_IPS tcp dport { 3306, 6379, 9100 } accept
        
        log prefix "nft-drop: " flags all
    }
    
    chain forward {
        type filter hook forward priority 0; policy drop;
    }
    
    chain output {
        type filter hook output priority 0; policy accept;
    }
}

# Rate limiting для защиты от DDoS
table inet mangle {
    chain prerouting {
        type filter hook prerouting priority -150;
        
        # SYN flood protection
        tcp flags syn tcp option maxseg size 1-500 drop
        
        # Новые TCP соединения: не более 100 в секунду с одного IP
        tcp flags syn ct state new \
            meter syn_flood { ip saddr limit rate 100/second } \
            accept
        tcp flags syn ct state new drop
    }
}
</code></pre><hr><h1>Файловые системы Linux: выбор, настройка, оптимизация</h1><h2>Обзор файловых систем</h2><div class="tmiRichText__table-wrapper"><table style="min-width: 80px;"><colgroup><col style="min-width:20px;"><col style="min-width:20px;"><col style="min-width:20px;"><col style="min-width:20px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>ФС</p></th><th colspan="1" rowspan="1"><p>Сильные стороны</p></th><th colspan="1" rowspan="1"><p>Слабые стороны</p></th><th colspan="1" rowspan="1"><p>Применение</p></th></tr><tr><td colspan="1" rowspan="1"><p>ext4</p></td><td colspan="1" rowspan="1"><p>Стабильность, скорость</p></td><td colspan="1" rowspan="1"><p>Нет встроенного RAID</p></td><td colspan="1" rowspan="1"><p>Общее назначение</p></td></tr><tr><td colspan="1" rowspan="1"><p>XFS</p></td><td colspan="1" rowspan="1"><p>Большие файлы, параллелизм</p></td><td colspan="1" rowspan="1"><p>Нельзя уменьшить</p></td><td colspan="1" rowspan="1"><p>Медиа, базы данных</p></td></tr><tr><td colspan="1" rowspan="1"><p>Btrfs</p></td><td colspan="1" rowspan="1"><p>Снэпшоты, сжатие, RAID</p></td><td colspan="1" rowspan="1"><p>Сложность</p></td><td colspan="1" rowspan="1"><p>Разработка, NAS</p></td></tr><tr><td colspan="1" rowspan="1"><p>ZFS</p></td><td colspan="1" rowspan="1"><p>Надёжность, снэпшоты</p></td><td colspan="1" rowspan="1"><p>Память, лицензия</p></td><td colspan="1" rowspan="1"><p>Продакшн NAS</p></td></tr><tr><td colspan="1" rowspan="1"><p>tmpfs</p></td><td colspan="1" rowspan="1"><p>Скорость (RAM)</p></td><td colspan="1" rowspan="1"><p>Нет персистентности</p></td><td colspan="1" rowspan="1"><p>/tmp, кэш</p></td></tr></tbody></table></div><h2>Монтирование и /etc/fstab</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Посмотреть текущие точки монтирования
df -hT
mount | column -t
findmnt  # дерево монтирований

# Временное монтирование
mount -t ext4 /dev/sdb1 /mnt/data
mount -o remount,ro /  # перемонтировать только для чтения

# /etc/fstab — правильный формат:
# UUID=xxx   /data   ext4   defaults,noatime,nodiratime   0 2
#
# Важные опции:
# noatime    — не обновлять время доступа (производительность!)
# nodiratime — то же для директорий
# relatime   — компромисс: обновляем только если mtime новее atime
# nodev      — нет устройств (безопасность для /home)
# nosuid     — нет suid бит (безопасность)
# noexec     — нет выполнения (для /var/tmp)

# Получить UUID
blkid /dev/sdb1

# Монтирование tmpfs (RAM-диск)
# tmpfs /tmp tmpfs defaults,noatime,nosuid,size=2G 0 0
</code></pre><h2>Производительность ext4</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Опции при создании (важно для SSD!)
mkfs.ext4 -b 4096 -E stride=128,stripe-width=128 /dev/sdb1
# stride = chunk_size / block_size (для RAID)

# Настройка журналирования
# data=writeback  — самый быстрый, менее безопасный
# data=ordered    — дефолт, баланс
# data=journal    — самый безопасный, самый медленный
tune2fs -o journal_data_writeback /dev/sdb1

# Отключение fsck при загрузке (для SSD)
tune2fs -i 0 -c 0 /dev/sdb1

# Дефрагментация ext4 (редко нужно)
e4defrag -c /dev/sdb1  # только анализ
e4defrag /dev/sdb1     # дефрагментация

# Resize2fs — изменение размера
resize2fs /dev/sdb1 20G  # уменьшить (только для ext2/3/4)
resize2fs /dev/sdb1      # расширить до максимума
</code></pre><h2>Btrfs: продвинутые функции</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Создание с несколькими устройствами
mkfs.btrfs -d raid1 /dev/sdb /dev/sdc  # RAID1 для данных

# Создание снэпшота
btrfs subvolume create /data/subvol
btrfs subvolume snapshot /data/subvol /data/subvol-snap-$(date +%Y%m%d)

# Только для чтения (для бэкапов)
btrfs subvolume snapshot -r /data/subvol /data/backups/snap-$(date +%Y%m%d)

# Отправка снэпшота на другой сервер
btrfs send /data/backups/snap-20240101 | ssh backup-server "btrfs receive /backups/"

# Инкрементальный бэкап
btrfs send -p /data/backups/snap-20240101 /data/backups/snap-20240102 | \
    ssh backup-server "btrfs receive /backups/"

# Сжатие
btrfs filesystem defragment -r -czstd /data  # сжать существующее
# В fstab: compress=zstd:3

# Статистика
btrfs filesystem show
btrfs filesystem df /data
btrfs scrub start /data  # проверка целостности
</code></pre><h2>LVM: гибкое управление томами</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Структура: Physical Volumes → Volume Groups → Logical Volumes

# Создание PV
pvcreate /dev/sdb /dev/sdc
pvdisplay
pvs

# Volume Group
vgcreate vg_data /dev/sdb /dev/sdc
vgdisplay
vgs

# Logical Volume
lvcreate -L 50G -n lv_mysql vg_data
lvcreate -l 100%FREE -n lv_data vg_data  # всё свободное место
lvdisplay
lvs

# Форматирование и монтирование
mkfs.ext4 /dev/vg_data/lv_mysql

# Расширение онлайн!
lvextend -L +20G /dev/vg_data/lv_mysql
resize2fs /dev/vg_data/lv_mysql  # для ext4

# Снэпшоты LVM (для backup без даунтайма)
lvcreate -L 5G -s -n lv_mysql_snap /dev/vg_data/lv_mysql
mount /dev/vg_data/lv_mysql_snap /mnt/backup
# Делаем бэкап из /mnt/backup
lvremove /dev/vg_data/lv_mysql_snap

# Добавить новый диск в VG
pvcreate /dev/sdd
vgextend vg_data /dev/sdd
</code></pre><hr><h1>Docker и контейнеризация в Linux</h1><h2>Пространства имён: основа изоляции</h2><p>Контейнеры — это не виртуальные машины, это изоляция через Linux namespaces:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Типы namespaces
# pid    — изоляция процессов
# net    — изоляция сети
# mnt    — изоляция точек монтирования
# uts    — hostname и domainname
# ipc    — IPC (очереди сообщений, семафоры)
# user   — пользователи и группы
# cgroup — изоляция cgroups

# Создать отдельное сетевое пространство (вручную, как делает Docker)
ip netns add myns
ip netns exec myns ip link list
ip netns exec myns bash  # shell внутри namespace
</code></pre><h2>Docker: производительность и безопасность</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Просмотр ресурсов контейнеров
docker stats --no-stream
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}"

# Ограничение ресурсов
docker run -d \
    --name myapp \
    --memory="512m" \
    --memory-swap="1g" \
    --cpus="0.5" \
    --pids-limit=100 \
    myapp:latest

# Security options
docker run -d \
    --security-opt no-new-privileges:true \
    --security-opt seccomp=/etc/docker/seccomp.json \
    --cap-drop ALL \
    --cap-add NET_BIND_SERVICE \
    --read-only \
    --tmpfs /tmp:size=100m \
    myapp:latest
</code></pre><h2>Docker daemon оптимизация</h2><p><code>/etc/docker/daemon.json</code>:</p><pre spellcheck="" class="tmiCode language-json" data-language="JSON"><code>{
    "log-driver": "json-file",
    "log-opts": {
        "max-size": "100m",
        "max-file": "5"
    },
    "storage-driver": "overlay2",
    "storage-opts": ["overlay2.override_kernel_check=true"],
    "default-ulimits": {
        "nofile": {
            "Name": "nofile",
            "Hard": 65536,
            "Soft": 65536
        }
    },
    "live-restore": true,
    "userland-proxy": false,
    "experimental": false,
    "metrics-addr": "127.0.0.1:9323",
    "max-concurrent-downloads": 5
}
</code></pre><h2>Многоэтапные Dockerfile для PHP</h2><pre spellcheck="" class="tmiCode language-dockerfile" data-language="Dockerfile"><code># Stage 1: Зависимости
FROM composer:2 AS deps
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-scripts

# Stage 2: Production image
FROM php:8.2-fpm-alpine

# Устанавливаем только нужные расширения
RUN apk add --no-cache \
        libpng-dev \
        libjpeg-turbo-dev \
        libwebp-dev \
    &amp;&amp; docker-php-ext-configure gd --with-jpeg --with-webp \
    &amp;&amp; docker-php-ext-install -j$(nproc) \
        pdo_mysql \
        redis \
        gd \
        opcache \
        intl \
    &amp;&amp; rm -rf /var/cache/apk/*

# Копируем зависимости из предыдущего этапа
COPY --from=deps /app/vendor /var/www/html/vendor

# Копируем только нужные файлы
COPY --chown=www-data:www-data app/ /var/www/html/app/
COPY --chown=www-data:www-data public/ /var/www/html/public/

# PHP конфиг для продакшна
COPY docker/php.ini /usr/local/etc/php/conf.d/99-production.ini
COPY docker/www.conf /usr/local/etc/php-fpm.d/www.conf

# Непривилегированный пользователь
USER www-data

EXPOSE 9000
CMD ["php-fpm"]
</code></pre><h2>Docker Compose для dev-среды</h2><pre spellcheck="" class="tmiCode language-yaml" data-language="YAML"><code>version: '3.9'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: deps  # используем только dev зависимости
    volumes:
      - .:/var/www/html:cached  # cached ускоряет macOS
      - /var/www/html/vendor    # исключаем vendor из маппинга
    environment:
      APP_ENV: local
      DB_HOST: db
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

  nginx:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - .:/var/www/html:cached
    depends_on:
      - app

  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: myapp
    volumes:
      - mysql_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      timeout: 3s
      retries: 10

  redis:
    image: redis:7-alpine
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru

volumes:
  mysql_data:
</code></pre><hr><h1>SSH: продвинутые возможности</h1><h2>SSH туннели: три типа</h2><h3>Local forwarding — пробрасываем удалённый порт локально</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Доступ к MySQL на сервере через локальный порт
ssh -L 3307:localhost:3306 user@remote-server

# Теперь подключаемся локально
mysql -h 127.0.0.1 -P 3307 -u myapp -p

# В фоне, без терминала
ssh -fNL 3307:localhost:3306 user@remote-server
</code></pre><h3>Remote forwarding — пробрасываем локальный порт на сервер</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Даём серверу доступ к нашему локальному сервису (для демо, CI/CD)
ssh -R 8080:localhost:3000 user@remote-server
# На сервере: curl http://localhost:8080 → ваш локальный порт 3000
</code></pre><h3>Dynamic forwarding — SOCKS5 прокси</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Весь трафик через сервер
ssh -D 1080 user@remote-server
# Настраиваем браузер на SOCKS5 proxy: localhost:1080

# В автоматическом режиме
ssh -fND 1080 user@remote-server
</code></pre><h2>~/.ssh/config: удобная конфигурация</h2><pre spellcheck="" class="tmiCode language-plaintext" data-language="Простой текст"><code># Глобальные настройки
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    ControlMaster auto
    ControlPath ~/.ssh/sockets/%r@%h-%p
    ControlPersist 10m  # переиспользовать соединение 10 минут
    AddKeysToAgent yes
    IdentityFile ~/.ssh/id_ed25519

# Продакшн
Host prod-web01
    HostName 203.0.113.10
    User deploy
    Port 2222
    IdentityFile ~/.ssh/id_ed25519_prod

Host prod-db01
    HostName 10.0.0.5  # внутренний IP
    User dbadmin
    ProxyJump prod-web01  # прыгаем через web01

# Staging
Host staging
    HostName staging.example.com
    User deploy
    LocalForward 3307 localhost:3306  # автоматический туннель к MySQL

# Wildcard для dev-серверов
Host dev-*
    User vagrant
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null  # не сохраняем fingerprint для dev
</code></pre><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Мультиплексирование — одно TCP соединение для нескольких сессий
mkdir -p ~/.ssh/sockets

# Теперь второй ssh к тому же хосту использует уже открытое соединение
ssh prod-web01  # открывает новое соединение
ssh prod-web01  # переиспользует существующее (мгновенно!)
ssh prod-web01 ls /var/log  # команда через существующее соединение
</code></pre><h2>SSH-агент и управление ключами</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Запуск агента
eval $(ssh-agent -s)

# Добавляем ключ с таймаутом
ssh-add -t 3600 ~/.ssh/id_ed25519  # 1 час

# Просмотр ключей в агенте
ssh-add -l

# Переброска агента на удалённый сервер
ssh -A user@server  # ForwardAgent yes в конфиге

# Или через конфиг:
# Host prod-*
#     ForwardAgent yes

# Hardware token (YubiKey)
# ssh-keygen -t ecdsa-sk  # создать ключ на YubiKey
</code></pre><h2>Копирование файлов: rsync через SSH</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Основной синтаксис
rsync -avz --progress source/ user@server:/destination/

# Полезные опции
rsync -avz \
    --delete \           # удалять на приёмнике то, чего нет в источнике
    --exclude='*.log' \
    --exclude='.git' \
    --exclude='node_modules' \
    --checksum \         # сравнивать по контрольной сумме, не по времени
    --backup \           # бэкап изменённых файлов
    --backup-dir=/backup/$(date +%Y%m%d) \
    /var/www/myapp/ user@backup-server:/backups/myapp/

# Синхронизация через нестандартный порт
rsync -avz -e "ssh -p 2222" source/ user@server:/dest/

# Dry run — что будет сделано без фактических изменений
rsync -avzn source/ user@server:/dest/
</code></pre><hr><h1>Ядро Linux: мониторинг и тюнинг</h1><h2>/proc и /sys: окна в ядро</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Информация о системе
cat /proc/version
cat /proc/cpuinfo | grep -E "processor|model name|cpu MHz" | head -20
cat /proc/meminfo
cat /proc/loadavg
cat /proc/uptime

# Текущие сетевые соединения ядра
cat /proc/net/tcp   # hex, неудобно, лучше ss

# Статистика прерываний
cat /proc/interrupts
watch -n1 cat /proc/interrupts

# I/O статистика
cat /proc/diskstats

# Параметры ядра (sysctl)
sysctl -a | grep vm
sysctl -a | grep net.ipv4.tcp

# /sys — дерево устройств
ls /sys/class/net/       # сетевые интерфейсы
ls /sys/block/           # блочные устройства
cat /sys/block/sda/queue/scheduler
echo 512 &gt; /sys/block/sda/queue/nr_requests  # изменяем параметр
</code></pre><h2>Настройка планировщика задач CPU</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Текущий планировщик
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

# Доступные governors
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
# conservative ondemand userspace powersave performance schedutil

# Для сервера: performance или schedutil
echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

# Закрепляем через /etc/default/grub:
# GRUB_CMDLINE_LINUX="cpufreq.default_governor=performance"

# NUMA балансировка
cat /proc/sys/kernel/numa_balancing
echo 1 &gt; /proc/sys/kernel/numa_balancing

# Transparent Huge Pages
cat /sys/kernel/mm/transparent_hugepage/enabled
echo madvise &gt; /sys/kernel/mm/transparent_hugepage/enabled

# Watchdog (отключаем на виртуалках для экономии)
echo 0 &gt; /proc/sys/kernel/nmi_watchdog
</code></pre><h2>Параметры ядра для highload (полный sysctl)</h2><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code># /etc/sysctl.d/99-highload.conf

##########################
# VM
##########################
vm.swappiness = 10
vm.dirty_ratio = 20
vm.dirty_background_ratio = 5
vm.dirty_writeback_centisecs = 500
vm.dirty_expire_centisecs = 3000
vm.overcommit_memory = 1      # для Redis: разрешаем overcommit
vm.max_map_count = 262144     # для Elasticsearch

##########################
# NET: буферы
##########################
net.core.rmem_default = 262144
net.core.wmem_default = 262144
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.core.netdev_max_backlog = 65535
net.core.somaxconn = 65535

##########################
# NET: TCP
##########################
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
net.ipv4.tcp_mem = 786432 1048576 26777216

net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq

net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_max_tw_buckets = 1440000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5

net.ipv4.ip_local_port_range = 1024 65535

# ECN — явное уведомление о перегрузке
net.ipv4.tcp_ecn = 1

# Fast Open (повторное использование рукопожатия)
net.ipv4.tcp_fastopen = 3

##########################
# Безопасность
##########################
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.log_martians = 1
kernel.randomize_va_space = 2
</code></pre><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>sysctl -p /etc/sysctl.d/99-highload.conf
# Проверяем что применилось
sysctl net.ipv4.tcp_congestion_control
sysctl vm.swappiness
</code></pre><h2>Анализ паник и OOM</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Последняя паника ядра
journalctl -k -b -1 | grep -A 50 "Oops:"

# OOM killer
journalctl -k | grep -i "oom\|out of memory"
dmesg | grep -i oom

# Kernel panic dump (kdump)
apt install kdump-tools
# После паники дамп будет в /var/crash/

# Анализ crash dump
apt install crash
crash /usr/lib/debug/boot/vmlinux-$(uname -r) /var/crash/vmcore
# Команды внутри crash:
# bt        — backtrace
# log       — kernel log buffer
# ps        — процессы в момент паники
</code></pre><p>Linux — это айсберг. Большинство из нас видит только надводную часть: файлы, процессы, сеть. Но под водой — целый мир механизмов, которые делают его надёжной основой для миллиардов устройств. Понимание этих механизмов — отличие хорошего администратора от великого.</p>]]></description><guid isPermaLink="false">46</guid><pubDate>Sun, 22 Feb 2026 13:24:18 +0000</pubDate></item><item><title>&#x411;&#x430;&#x442;&#x44F; &#x432;&#x441;&#x435;&#x445; &#x43C;&#x435;&#x442;&#x440;&#x438;&#x43A; - &#x427;&#x442;&#x43E; &#x442;&#x430;&#x43A;&#x43E;&#x435; Load Average (LA) &#x432; Linux: &#x43A;&#x430;&#x43A; &#x441;&#x447;&#x438;&#x442;&#x430;&#x435;&#x442;&#x441;&#x44F; &#x438; &#x447;&#x442;&#x43E; &#x43D;&#x430; &#x441;&#x430;&#x43C;&#x43E;&#x43C; &#x434;&#x435;&#x43B;&#x435; &#x43F;&#x43E;&#x43A;&#x430;&#x437;&#x44B;&#x432;&#x430;&#x435;&#x442;</title><link>https://ithub.uno/statiarticles/3_linux-gnu-os-articles/%D0%B1%D0%B0%D1%82%D1%8F-%D0%B2%D1%81%D0%B5%D1%85-%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D0%BA-%D1%87%D1%82%D0%BE-%D1%82%D0%B0%D0%BA%D0%BE%D0%B5-load-average-la-%D0%B2-linux-%D0%BA%D0%B0%D0%BA-%D1%81%D1%87%D0%B8%D1%82%D0%B0%D0%B5%D1%82%D1%81%D1%8F-%D0%B8-%D1%87%D1%82%D0%BE-%D0%BD%D0%B0-%D1%81%D0%B0%D0%BC%D0%BE%D0%BC-%D0%B4%D0%B5%D0%BB%D0%B5-%D0%BF%D0%BE%D0%BA%D0%B0%D0%B7%D1%8B%D0%B2%D0%B0%D0%B5%D1%82-r61/</link><description><![CDATA[
<p><img src="https://ithub.uno/uploads/tmi_files_2026_02/f619bf09-a60b-4e06-b204-fe0535e5be5a.webp.52869e33eff77ea02c4a97637097c59f.webp" /></p>
<p>Во время технического собеседования в крупную компанию мне задали простой вопрос: <strong>что такое Load Average?</strong></p><p>Формально ответить несложно — это «средняя загрузка системы за 1, 5 и 15 минут». Но если копнуть глубже, возникает ряд неудобных вопросов:</p><ul><li><p>Что именно усредняется?</p></li><li><p>С какой частотой происходит измерение?</p></li><li><p>Какие процессы считаются «ожидающими ресурсы»?</p></li><li><p>Почему при кратковременных пиках мы не видим резких скачков?</p></li><li><p>Почему Load Average = 1 соответствует 100% загрузке одноядерной системы?</p></li></ul><p>Если вас интересует не бытовое, а <strong>точное техническое понимание</strong>, разберёмся детально — с опорой на исходный код ядра Linux.</p><hr><h2>Что такое Load Average (LA)</h2><p>В системах Linux и UNIX Load Average — это показатель среднего количества процессов:</p><ul><li><p>находящихся в состоянии <strong>RUNNING</strong> (исполняются или готовы к выполнению),</p></li><li><p>находящихся в состоянии <strong>UNINTERRUPTIBLE</strong> (обычно ожидание I/O).</p></li></ul><p>Три значения, которые показывает команда <code>uptime</code>, соответствуют окнам:</p><ul><li><p>1 минута</p></li><li><p>5 минут</p></li><li><p>15 минут</p></li></ul><p>Важно: <strong>Load Average — это не процент загрузки CPU</strong>.<br>Это среднее количество активных (или ожидающих) задач.</p><p>Для одноядерной системы:</p><ul><li><p>LA = 1 → процессор полностью занят</p></li><li><p>LA &lt; 1 → процессор простаивает часть времени</p></li><li><p>LA &gt; 1 → есть очередь процессов</p></li></ul><hr><h2>Где «подвох» в стандартном объяснении</h2><h3>1. Это не арифметическое среднее</h3><p>Если бы LA считался как обычное среднее арифметическое, возникал бы вопрос о частоте дискретизации:</p><ul><li><p>считаем каждую секунду?</p></li><li><p>каждые 10 мс?</p></li><li><p>раз в минуту?</p></li></ul><p>Чем выше частота измерения — тем меньше получилось бы среднее значение.</p><p>Но в Linux используется <strong>экспоненциальное сглаживание</strong>, а не классическое среднее.</p><hr><h3>2. Кто такие «ожидающие ресурсы»?</h3><p>Согласно исходному коду ядра Linux, учитываются процессы в состояниях:</p><pre spellcheck="" class="tmiCode language-plaintext" data-language="Простой текст"><code>TASK_RUNNING
TASK_UNINTERRUPTIBLE</code></pre><p>То есть:</p><ul><li><p>задачи, выполняющиеся на CPU;</p></li><li><p>задачи, ожидающие завершения операций ввода-вывода (например, медленный диск или NFS).</p></li></ul><p>Именно поэтому высокий Load Average может быть при низкой загрузке CPU — если система «застряла» на I/O.</p><hr><h2>Как именно считается Load Average в Linux</h2><p>Реализация находится в ядре Linux (например, в версии 2.4 — <code>timer.c</code> и <code>sched.h</code>).</p><h3>Ключевые факты:</h3><ul><li><p>Измерение происходит <strong>каждые 5 секунд</strong></p></li><li><p>Используется фиксированная точка (fixed-point arithmetic)</p></li><li><p>Применяется формула экспоненциального затухания</p></li></ul><p>Константы:</p><pre spellcheck="" class="tmiCode language-plaintext" data-language="Простой текст"><code>#define LOAD_FREQ (5*HZ)    /* интервал 5 секунд */
#define EXP_1     1884      /* коэффициент для 1 минуты */
#define EXP_5     2014
#define EXP_15    2037</code></pre><h3>Формула расчёта</h3><p>В упрощённом виде:</p><p>Lnew=Lold⋅e−Δt/T+n⋅(1−e−Δt/T)L_{new} = L_{old} \cdot e^{-Δt/T} + n \cdot (1 - e^{-Δt/T})Lnew=Lold⋅e−Δt/T+n⋅(1−e−Δt/T)</p><p>где:</p><ul><li><p>LLL — текущее значение Load Average</p></li><li><p>nnn — число активных задач</p></li><li><p>TTT — окно усреднения (1, 5, 15 минут)</p></li><li><p>Δt=5Δt = 5Δt=5 секунд</p></li></ul><p>Это <strong>дискретная форма экспоненциального сглаживания</strong>.</p><hr><h2>Почему используется экспонента</h2><p>Формула основана на законе экспоненциального распада:</p><p>dLdt=−1T(L−n)\frac{dL}{dt} = -\frac{1}{T}(L - n)dtdL=−T1(L−n)</p><p>Смысл:</p><ul><li><p>если процессов больше текущего LA → показатель растёт</p></li><li><p>если меньше → показатель экспоненциально уменьшается</p></li><li><p>чем больше окно (15 минут), тем медленнее реакция</p></li></ul><p>Это обеспечивает:</p><ul><li><p>сглаживание кратковременных пиков</p></li><li><p>устойчивость к «шуму»</p></li><li><p>предсказуемую динамику</p></li></ul><hr><h2>Почему не видно резких скачков?</h2><p>Представим, что вы запустили 100 коротких процессов.</p><p>Логично ожидать, что LA резко взлетит.<br>Но этого не происходит, потому что:</p><ul><li><p>измерение идёт раз в 5 секунд</p></li><li><p>используется экспоненциальное сглаживание</p></li><li><p>старые значения затухают постепенно</p></li></ul><p>Экспонента выполняет роль фильтра низких частот.</p><hr><h2>Почему LA = 1 означает 100% загрузку одноядерной системы</h2><p>При постоянном числе процессов nnn:</p><ul><li><p>если n&gt;Ln &gt; Ln&gt;L → LA растёт к n</p></li><li><p>если n&lt;Ln &lt; Ln&lt;L → LA уменьшается к n</p></li></ul><p>Если на одноядерной системе:</p><ul><li><p>в каждый момент времени активен ровно 1 процесс,</p></li><li><p>очереди нет,</p></li></ul><p>то система полностью загружена — и LA стабилизируется на 1.</p><p>Если LA &gt; 1 — появляется очередь.</p><hr><h2>Важные нюансы</h2><h3>1. Load Average учитывает I/O</h3><p>Если процессы ждут диск или NFS, LA растёт, даже если CPU простаивает.</p><h3>2. На многоядерных системах</h3><p>Если у вас 8 ядер:</p><ul><li><p>LA = 8 → система полностью загружена</p></li><li><p>LA &gt; 8 → есть очередь</p></li></ul><h3>3. Это не мгновенная метрика</h3><p>LA показывает <strong>тренд</strong>, а не текущую загрузку.</p><hr><h2>Ограничения модели</h2><p>Экспоненциальная модель предполагает:</p><ul><li><p>плавное изменение нагрузки</p></li><li><p>отсутствие «жёстких» ограничений пропускной способности</p></li></ul><p>В реальности же:</p><ul><li><p>CPU имеет конечную пропускную способность</p></li><li><p>I/O может быть узким местом</p></li><li><p>высокие значения LA не всегда означают CPU-bound систему</p></li></ul><p>Поэтому интерпретировать Load Average нужно вместе с:</p><ul><li><p><code>top</code></p></li><li><p><code>htop</code></p></li><li><p><code>iostat</code></p></li><li><p><code>vmstat</code></p></li></ul><hr><h2>Выводы</h2><ol><li><p>Load Average — это <strong>экспоненциально сглаженное среднее количества активных процессов</strong>.</p></li><li><p>Измерение происходит каждые 5 секунд.</p></li><li><p>Учитываются процессы в состояниях RUNNING и UNINTERRUPTIBLE.</p></li><li><p>Это не процент загрузки CPU.</p></li><li><p>Значение &gt; числа ядер означает наличие очереди.</p></li></ol><p>Главное:<br><strong>Load Average — это математическая модель сглаживания нагрузки, а не прямой счётчик занятости процессора.</strong></p>]]></description><guid isPermaLink="false">61</guid><pubDate>Sun, 22 Feb 2026 15:50:00 +0000</pubDate></item><item><title>Linux &#x434;&#x43B;&#x44F; &#x432;&#x441;&#x442;&#x440;&#x430;&#x438;&#x432;&#x430;&#x435;&#x43C;&#x44B;&#x445; &#x441;&#x438;&#x441;&#x442;&#x435;&#x43C;: &#x43E;&#x442; Raspberry Pi &#x434;&#x43E; &#x43F;&#x440;&#x43E;&#x43C;&#x44B;&#x448;&#x43B;&#x435;&#x43D;&#x43D;&#x43E;&#x433;&#x43E; &#x448;&#x43B;&#x44E;&#x437;&#x430;</title><link>https://ithub.uno/statiarticles/3_linux-gnu-os-articles/linux-%D0%B4%D0%BB%D1%8F-%D0%B2%D1%81%D1%82%D1%80%D0%B0%D0%B8%D0%B2%D0%B0%D0%B5%D0%BC%D1%8B%D1%85-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC-%D0%BE%D1%82-raspberry-pi-%D0%B4%D0%BE-%D0%BF%D1%80%D0%BE%D0%BC%D1%8B%D1%88%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE-%D1%88%D0%BB%D1%8E%D0%B7%D0%B0-r113/</link><description><![CDATA[
<p><img src="https://ithub.uno/uploads/tmi_files_2026_03/raspberry-pi4-1.jpg.78e110788a1f74097fe26efed82f7d03.jpg" /></p>
<h2>Когда Linux вместо микроконтроллера</h2><p>Выбор между голым МК (Arduino/STM32) и Linux-системой (Raspberry Pi/промышленный ПК) — одно из ключевых архитектурных решений.</p><p><strong>Linux выигрывает когда нужно:</strong></p><ul><li><p>Сложные сетевые протоколы (TCP/IP стек, TLS, MQTT, OPC UA)</p></li><li><p>Работа с файлами: логирование, конфигурация, обновления ПО</p></li><li><p>Высокоуровневые вычисления: Python/NumPy, ML-инференс</p></li><li><p>Несколько параллельных задач с разной логикой</p></li><li><p>Веб-интерфейс или REST API</p></li><li><p>Большой объём RAM/Flash (база данных, historian)</p></li></ul><p><strong>Микроконтроллер выигрывает когда нужно:</strong></p><ul><li><p>Детерминированное реальное время (&lt; 1 мс)</p></li><li><p>Мгновенный старт (Linux загружается 10–30 секунд)</p></li><li><p>Минимальное энергопотребление</p></li><li><p>Дешёвое серийное производство</p></li></ul><p><strong>Золотое правило:</strong> Linux для "мозга" и коммуникаций, микроконтроллер для "мышц" и реального времени. Оба — в одной системе, связанные UART или SPI.</p><hr><h2>Raspberry Pi в промышленности: что учесть</h2><p>Raspberry Pi не проектировался для промышленного применения, но активно используется. Ключевые ограничения и решения:</p><p><strong>Ограничение 1: SD-карта умирает</strong> SD-карты не рассчитаны на постоянную запись. В промышленном применении — выход из строя за 3–12 месяцев.</p><p>Решения:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># 1. Read-only файловая система (overlayfs)
# В /boot/cmdline.txt добавить: overlayroot=tmpfs
# Данные писать только на специальный раздел с journaling

# 2. Переместить tmpfs для логов в RAM
# /etc/fstab:
tmpfs /tmp      tmpfs defaults,noatime,size=100m 0 0
tmpfs /var/log  tmpfs defaults,noatime,size=50m  0 0
tmpfs /var/tmp  tmpfs defaults,noatime,size=20m  0 0

# 3. Использовать SSD через USB3 или eMMC-модуль (CM4)
</code></pre><p><strong>Ограничение 2: Нет RTC (часов реального времени)</strong> При потере питания время сбивается.</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Установить модуль DS3231 через I2C
# /boot/config.txt:
dtoverlay=i2c-rtc,ds3231

# Синхронизация при загрузке:
sudo hwclock --hctosys  # Hardware clock → System clock
</code></pre><p><strong>Ограничение 3: Нет аппаратного watchdog "из коробки"</strong></p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Включить встроенный watchdog BCM2835
# /boot/config.txt:
dtparam=watchdog=on

# /etc/systemd/system.conf:
RuntimeWatchdogSec=10   # Сброс если systemd не пингует 10 секунд
RebootWatchdogSec=60

# Проверка:
ls /dev/watchdog  # Должен существовать
</code></pre><hr><h2>GPIO: управление пинами из userspace</h2><h3>sysfs (устаревший, но всё ещё работает):</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Экспортируем GPIO 17
echo "17" &gt; /sys/class/gpio/export

# Устанавливаем направление
echo "out" &gt; /sys/class/gpio/gpio17/direction

# Устанавливаем значение
echo "1" &gt; /sys/class/gpio/gpio17/value

# Читаем состояние входа
cat /sys/class/gpio/gpio18/value
</code></pre><h3>libgpiod (современный стандарт):</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Установка
sudo apt install gpiod libgpiod-dev

# Командная строка
gpioget gpiochip0 17         # Прочитать GPIO 17
gpioset gpiochip0 17=1       # Установить в HIGH
gpioset gpiochip0 17=0 18=1  # Установить несколько
gpioinfo gpiochip0           # Информация о всех пинах
</code></pre><pre spellcheck="" class="tmiCode language-python" data-language="Python"><code># Python + gpiod
# pip install gpiod
import gpiod
import time

# Открываем чип
chip = gpiod.Chip('gpiochip0')

# Настраиваем пины
led_line    = chip.get_line(17)
button_line = chip.get_line(18)

led_config = gpiod.LineRequest()
led_config.consumer = "myapp"
led_config.request_type = gpiod.LineRequest.DIRECTION_OUTPUT
led_line.request(led_config)

btn_config = gpiod.LineRequest()
btn_config.consumer = "myapp"
btn_config.request_type = gpiod.LineRequest.EVENT_BOTH_EDGES  # Прерывания!
button_line.request(btn_config)

try:
    while True:
        # Ожидание события с таймаутом 100 мс
        event_happened = button_line.event_wait(nsec=100_000_000)
        
        if event_happened:
            event = button_line.event_read()
            if event.type == gpiod.LineEvent.RISING_EDGE:
                print("Кнопка нажата")
                led_line.set_value(1)
            else:
                print("Кнопка отпущена")
                led_line.set_value(0)
finally:
    led_line.release()
    button_line.release()
</code></pre><hr><h2>SPI и I2C из userspace</h2><h3>I2C (smbus2):</h3><pre spellcheck="" class="tmiCode language-python" data-language="Python"><code># pip install smbus2
import smbus2
import time

class BME280_Linux:
    """Работа с датчиком BME280 через Linux I2C"""
    
    ADDR = 0x76
    REG_ID     = 0xD0
    REG_CTRL   = 0xF4
    REG_DATA   = 0xF7
    
    def __init__(self, bus_num: int = 1):
        self.bus = smbus2.SMBus(bus_num)
    
    def read_reg(self, reg: int) -&gt; int:
        return self.bus.read_byte_data(self.ADDR, reg)
    
    def write_reg(self, reg: int, value: int):
        self.bus.write_byte_data(self.ADDR, reg, value)
    
    def read_burst(self, reg: int, length: int) -&gt; bytes:
        return bytes(self.bus.read_i2c_block_data(self.ADDR, reg, length))
    
    def init(self):
        chip_id = self.read_reg(self.REG_ID)
        if chip_id != 0x60:
            raise RuntimeError(f"BME280 не найден, ID={chip_id:#x}")
        
        # Нормальный режим, oversampling ×4
        self.write_reg(0xF4, 0x97)  # ctrl_meas
        self.write_reg(0xF5, 0xA0)  # config: IIR filter 16
        time.sleep(0.1)
    
    def read(self) -&gt; dict:
        data = self.read_burst(self.REG_DATA, 6)
        
        raw_press = (data[0] &lt;&lt; 12) | (data[1] &lt;&lt; 4) | (data[2] &gt;&gt; 4)
        raw_temp  = (data[3] &lt;&lt; 12) | (data[4] &lt;&lt; 4) | (data[5] &gt;&gt; 4)
        
        # Упрощённое преобразование (без компенсации)
        # В реальности нужно читать калибровочные коэффициенты!
        temp     = raw_temp / 5120.0
        pressure = raw_press / 25600.0 / 100.0  # гПа
        
        return {'temperature': round(temp, 1), 'pressure': round(pressure, 1)}

# Использование:
sensor = BME280_Linux(bus_num=1)  # /dev/i2c-1
sensor.init()

while True:
    data = sensor.read()
    print(f"T={data['temperature']}°C, P={data['pressure']}гПа")
    time.sleep(1)
</code></pre><h3>SPI (spidev):</h3><pre spellcheck="" class="tmiCode language-python" data-language="Python"><code>import spidev
import time

class MCP3208_ADC:
    """12-битный АЦП MCP3208 через SPI"""
    
    CHANNELS = 8
    
    def __init__(self, bus: int = 0, device: int = 0, speed_hz: int = 1_000_000):
        self.spi = spidev.SpiDev()
        self.spi.open(bus, device)  # /dev/spidev0.0
        self.spi.max_speed_hz = speed_hz
        self.spi.mode = 0
    
    def read_channel(self, channel: int) -&gt; int:
        """Чтение канала 0-7, возвращает 0-4095"""
        if not 0 &lt;= channel &lt; self.CHANNELS:
            raise ValueError(f"Канал {channel} вне диапазона 0-7")
        
        # MCP3208: 3 байта транзакции
        # Байт 1: старт-бит + single/diff + D2
        # Байт 2: D1, D0, X, X, X, X, X, X
        # Байт 3: X, X, X, X, X, X, X, X
        cmd = [0x06 | (channel &gt;&gt; 2), (channel &amp; 0x03) &lt;&lt; 6, 0x00]
        
        response = self.spi.xfer2(cmd)
        
        # Из ответа: байт 1 биты 1-0 + байт 2 все 8 бит = 12 бит
        result = ((response[1] &amp; 0x0F) &lt;&lt; 8) | response[2]
        return result
    
    def read_voltage(self, channel: int, vref: float = 3.3) -&gt; float:
        """Чтение в вольтах"""
        raw = self.read_channel(channel)
        return raw * vref / 4095.0
    
    def read_all(self, vref: float = 3.3) -&gt; list:
        """Чтение всех 8 каналов"""
        return [self.read_voltage(ch, vref) for ch in range(self.CHANNELS)]
    
    def close(self):
        self.spi.close()

# Использование:
adc = MCP3208_ADC()

while True:
    voltages = adc.read_all()
    for ch, v in enumerate(voltages):
        print(f"CH{ch}: {v:.3f}В", end="  ")
    print()
    time.sleep(0.5)
</code></pre><hr><h2>UART и последовательный порт</h2><pre spellcheck="" class="tmiCode language-python" data-language="Python"><code>import serial
import serial.tools.list_ports

# Найти все доступные порты
def list_serial_ports():
    ports = serial.tools.list_ports.comports()
    for port in ports:
        print(f"{port.device}: {port.description} ({port.hwid})")

# На Raspberry Pi:
# /dev/ttyAMA0 или /dev/serial0 — встроенный UART (GPIO 14/15)
# /dev/ttyUSB0 — USB-UART адаптер
# /dev/ttyACM0 — USB CDC (Arduino)

# Важно для RPi: отключить console на /dev/serial0
# sudo raspi-config → Interface Options → Serial Port
# "Would you like a login shell to be accessible over serial?" → No
# "Would you like the serial port hardware to be enabled?" → Yes

class SerialDevice:
    """Надёжная работа с последовательным портом"""
    
    def __init__(self, port: str, baudrate: int = 9600, timeout: float = 1.0):
        self.port_name = port
        self.baudrate  = baudrate
        self.timeout   = timeout
        self.ser       = None
    
    def connect(self):
        try:
            self.ser = serial.Serial(
                port     = self.port_name,
                baudrate = self.baudrate,
                bytesize = serial.EIGHTBITS,
                parity   = serial.PARITY_NONE,
                stopbits = serial.STOPBITS_ONE,
                timeout  = self.timeout
            )
            print(f"Подключено: {self.port_name}")
            return True
        except serial.SerialException as e:
            print(f"Ошибка подключения: {e}")
            return False
    
    def send(self, data: bytes) -&gt; bool:
        try:
            self.ser.write(data)
            return True
        except serial.SerialException:
            return False
    
    def send_line(self, text: str) -&gt; bool:
        return self.send((text + '\n').encode('utf-8'))
    
    def read_line(self) -&gt; str | None:
        try:
            line = self.ser.readline()
            if line:
                return line.decode('utf-8', errors='replace').strip()
            return None
        except serial.SerialException:
            return None
    
    def close(self):
        if self.ser and self.ser.is_open:
            self.ser.close()
</code></pre><hr><h2>systemd: управление сервисами приложения</h2><p>Правильное промышленное приложение на Linux должно запускаться как системный сервис — автостарт при загрузке, перезапуск при сбое, логирование.</p><h3>Создание systemd unit:</h3><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code># /etc/systemd/system/industrial-gateway.service
[Unit]
Description=Industrial IoT Gateway
After=network.target
Wants=network-online.target
After=network-online.target

# Зависимость от другого сервиса (например, MQTT-брокера)
# Requires=mosquitto.service
# After=mosquitto.service

[Service]
Type=simple
User=pi
Group=pi
WorkingDirectory=/opt/gateway

# Переменные окружения из файла
EnvironmentFile=/etc/gateway/config.env

# Команда запуска
ExecStart=/opt/gateway/venv/bin/python /opt/gateway/main.py

# Перезапуск при сбое
Restart=on-failure
RestartSec=10s
StartLimitIntervalSec=60s
StartLimitBurst=3       # Максимум 3 попытки за 60 секунд

# Watchdog интеграция с systemd
# Приложение должно вызывать sd_notify WATCHDOG=1 каждые N секунд
WatchdogSec=30s

# Логирование
StandardOutput=journal
StandardError=journal
SyslogIdentifier=gateway

# Безопасность (опционально, но рекомендуется)
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target
</code></pre><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Установка и управление:
sudo systemctl daemon-reload
sudo systemctl enable industrial-gateway
sudo systemctl start industrial-gateway
sudo systemctl status industrial-gateway

# Логи:
journalctl -u industrial-gateway -f          # В реальном времени
journalctl -u industrial-gateway --since today
journalctl -u industrial-gateway -n 100      # Последние 100 строк
</code></pre><h3>Watchdog из Python (sd-notify):</h3><pre spellcheck="" class="tmiCode language-python" data-language="Python"><code># pip install sdnotify
import sdnotify
import time
import threading

notifier = sdnotify.SystemdNotifier()

def watchdog_thread():
    """Пингуем systemd watchdog каждые 10 секунд"""
    while True:
        notifier.notify("WATCHDOG=1")
        time.sleep(10)

def main():
    # Сообщаем systemd что мы готовы
    notifier.notify("READY=1")
    notifier.notify("STATUS=Инициализация...")
    
    # Запускаем watchdog в фоне
    wdg = threading.Thread(target=watchdog_thread, daemon=True)
    wdg.start()
    
    try:
        # Основной цикл приложения
        while True:
            notifier.notify("STATUS=Работает нормально")
            # ... бизнес-логика
            time.sleep(1)
    except Exception as e:
        notifier.notify(f"STATUS=ОШИБКА: {e}")
        raise
</code></pre><hr><h2>Сетевая конфигурация для промышленного шлюза</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># /etc/dhcpcd.conf — статический IP для промышленной сети
interface eth0
static ip_address=192.168.1.200/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1 8.8.8.8

# Или через NetworkManager (современный способ):
sudo nmcli con add type ethernet ifname eth0 con-name industrial \
    ipv4.method manual ipv4.addresses 192.168.1.200/24 \
    ipv4.gateway 192.168.1.1 ipv4.dns "192.168.1.1 8.8.8.8"
sudo nmcli con up industrial
</code></pre><h3>Bonding (резервирование сети):</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Два сетевых интерфейса — один основной, второй резервный
# /etc/network/interfaces:
auto bond0
iface bond0 inet static
    address 192.168.1.200
    netmask 255.255.255.0
    gateway 192.168.1.1
    bond-slaves eth0 eth1
    bond-mode active-backup    # При отказе eth0 — переключаемся на eth1
    bond-miimon 100            # Проверка связи каждые 100 мс
    bond-primary eth0
</code></pre><hr><h2>Buildroot: минимальный Linux-образ</h2><p>Для серийного производства не нужен полный Raspberry Pi OS с Python IDE и LibreOffice. Нужен минимальный образ с только нужными компонентами.</p><p>Buildroot — система сборки кастомных Linux-образов:</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Клонируем Buildroot
git clone https://git.buildroot.net/buildroot
cd buildroot

# Начинаем с дефолтной конфигурации для Raspberry Pi
make raspberrypi4_64_defconfig

# Настраиваем через menuconfig
make menuconfig
# Target packages → Networking applications → mosquitto (MQTT-брокер)
# Target packages → Libraries → python3
# Target packages → Libraries → python-paho-mqtt
# System configuration → Root password

# Собираем (первый раз ~1-2 часа)
make -j4

# Результат: output/images/sdcard.img
# Записываем на SD:
sudo dd if=output/images/sdcard.img of=/dev/sdX bs=4M status=progress
</code></pre><p>Преимущества кастомного образа:</p><ul><li><p>Размер: 50–200 МБ вместо 4–8 ГБ</p></li><li><p>Быстрый старт: 5–8 секунд вместо 30+</p></li><li><p>Безопасность: минимальная поверхность атаки</p></li><li><p>Reproducible builds: одинаковый образ на всех устройствах</p></li></ul><hr><h2>Python-приложение как надёжный шлюз</h2><pre spellcheck="" class="tmiCode language-python" data-language="Python"><code>#!/opt/gateway/venv/bin/python3
"""
Промышленный IoT-шлюз: Modbus RTU → MQTT
"""

import logging
import signal
import sys
import time
import json
import threading
from pathlib import Path
from typing import Optional

import sdnotify
import paho.mqtt.client as mqtt
from pymodbus.client import ModbusSerialClient

# Настройка логирования (в journald через stderr)
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(name)s: %(message)s',
    stream=sys.stderr
)
log = logging.getLogger('gateway')


class IndustrialGateway:
    
    def __init__(self):
        self.running    = False
        self.notifier   = sdnotify.SystemdNotifier()
        self.mqtt_client: Optional[mqtt.Client] = None
        self.modbus_client: Optional[ModbusSerialClient] = None
        
        # Конфигурация из переменных окружения
        import os
        self.mqtt_host    = os.getenv('MQTT_HOST', 'localhost')
        self.mqtt_port    = int(os.getenv('MQTT_PORT', '1883'))
        self.modbus_port  = os.getenv('MODBUS_PORT', '/dev/serial0')
        self.modbus_baud  = int(os.getenv('MODBUS_BAUD', '9600'))
        self.poll_interval = float(os.getenv('POLL_INTERVAL', '1.0'))
    
    def setup(self):
        """Инициализация подключений"""
        log.info("Инициализация шлюза...")
        
        # MQTT
        self.mqtt_client = mqtt.Client(client_id="industrial-gateway")
        self.mqtt_client.on_connect    = self._on_mqtt_connect
        self.mqtt_client.on_disconnect = self._on_mqtt_disconnect
        self.mqtt_client.will_set("gateway/status", 
                                   '{"online": false}', retain=True)
        
        self.mqtt_client.connect_async(self.mqtt_host, self.mqtt_port)
        self.mqtt_client.loop_start()
        
        # Modbus
        self.modbus_client = ModbusSerialClient(
            port=self.modbus_port,
            baudrate=self.modbus_baud,
            timeout=1.0
        )
        
        if not self.modbus_client.connect():
            log.error(f"Не удалось подключиться к Modbus: {self.modbus_port}")
    
    def _on_mqtt_connect(self, client, userdata, flags, rc):
        if rc == 0:
            log.info(f"MQTT подключён: {self.mqtt_host}")
            client.publish("gateway/status", '{"online": true}', retain=True)
        else:
            log.error(f"MQTT ошибка подключения: {rc}")
    
    def _on_mqtt_disconnect(self, client, userdata, rc):
        log.warning(f"MQTT отключён (rc={rc}), переподключение...")
    
    def poll_device(self, device_addr: int, reg_start: int,
                    reg_count: int) -&gt; Optional[list]:
        """Опрос устройства Modbus"""
        try:
            result = self.modbus_client.read_input_registers(
                address=reg_start, count=reg_count, slave=device_addr
            )
            if result.isError():
                log.warning(f"Modbus ошибка: устройство {device_addr}")
                return None
            return result.registers
        except Exception as e:
            log.error(f"Исключение при опросе {device_addr}: {e}")
            return None
    
    def publish(self, topic: str, data: dict):
        """Публикация в MQTT"""
        try:
            payload = json.dumps(data, ensure_ascii=False)
            self.mqtt_client.publish(topic, payload)
        except Exception as e:
            log.error(f"Ошибка публикации: {e}")
    
    def run(self):
        """Главный цикл"""
        self.running = True
        
        # Сигналы завершения
        signal.signal(signal.SIGTERM, self._shutdown)
        signal.signal(signal.SIGINT,  self._shutdown)
        
        self.setup()
        
        # Сообщаем systemd о готовности
        self.notifier.notify("READY=1")
        log.info("Шлюз запущен и готов к работе")
        
        poll_count = 0
        
        while self.running:
            loop_start = time.monotonic()
            
            # Опрашиваем частотник (адрес 1, регистры 0-5)
            vfd_data = self.poll_device(1, 0, 6)
            if vfd_data:
                self.publish("factory/line1/vfd1/telemetry", {
                    'status_word': vfd_data[0],
                    'freq_hz':     vfd_data[1] / 100.0,
                    'current_a':   vfd_data[2] / 10.0,
                    'voltage_v':   vfd_data[3],
                    'power_kw':    vfd_data[4] / 10.0,
                    'fault_code':  vfd_data[5],
                    'running':     bool(vfd_data[0] &amp; 0x0001),
                    'fault':       bool(vfd_data[0] &amp; 0x0008),
                })
            
            # Опрашиваем датчик давления (адрес 5, регистры 0-1)
            pressure_data = self.poll_device(5, 0, 2)
            if pressure_data:
                import struct
                raw = struct.pack('&gt;HH', pressure_data[0], pressure_data[1])
                pressure = struct.unpack('&gt;f', raw)[0]
                self.publish("factory/line1/pressure1/telemetry", {
                    'pressure_bar': round(pressure, 2)
                })
            
            poll_count += 1
            
            # Watchdog
            if poll_count % 5 == 0:
                self.notifier.notify("WATCHDOG=1")
                self.notifier.notify(f"STATUS=Опросов: {poll_count}")
            
            # Точный таймер интервала опроса
            elapsed = time.monotonic() - loop_start
            sleep_time = self.poll_interval - elapsed
            if sleep_time &gt; 0:
                time.sleep(sleep_time)
            elif sleep_time &lt; -0.1:
                log.warning(f"Опрос занял {elapsed:.3f}с (лимит {self.poll_interval}с)")
    
    def _shutdown(self, signum, frame):
        log.info(f"Получен сигнал {signum}, завершаем работу...")
        self.running = False
        
        if self.mqtt_client:
            self.mqtt_client.publish("gateway/status", 
                                     '{"online": false}', retain=True)
            self.mqtt_client.loop_stop()
        
        if self.modbus_client:
            self.modbus_client.close()
        
        sys.exit(0)


if __name__ == "__main__":
    gateway = IndustrialGateway()
    gateway.run()
</code></pre><hr><h2>Безопасность Linux-устройства в сети</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Минимальный hardening для промышленного устройства

# 1. Обновление системы (автоматически)
sudo apt install unattended-upgrades
sudo dpkg-reconfigure unattended-upgrades

# 2. UFW файрвол
sudo apt install ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 1883   # MQTT (только если нужен снаружи)
sudo ufw enable

# 3. Отключить неиспользуемые сервисы
sudo systemctl disable bluetooth
sudo systemctl disable avahi-daemon

# 4. Изменить стандартный пароль!
passwd pi  # ОБЯЗАТЕЛЬНО

# 5. SSH ключи вместо паролей
# Скопировать ключ: ssh-copy-id pi@192.168.1.200
# /etc/ssh/sshd_config:
# PasswordAuthentication no
# PermitRootLogin no

# 6. Логирование попыток взлома
sudo apt install fail2ban
</code></pre><hr><h2>Заключение</h2><p>Linux на встраиваемых системах — мощнейший инструмент. Raspberry Pi, как платформа, имеет ограничения для промышленного применения, но при правильной настройке (read-only FS, watchdog, systemd-сервисы, резервирование) служит надёжно годами.</p><p>Для новых промышленных проектов рассмотрите специализированные платформы: Raspberry Pi CM4 (eMMC, нет SD-карты), BeagleBone Black (PRU для реального времени), Toradex Colibri/Apalis (промышленный температурный диапазон, многолетняя поддержка), IEI, Advantech (сертифицированные промышленные платформы).</p><p>Главный принцип для промышленного Linux: <strong>устройство должно работать без участия человека годами</strong>. Watchdog, автоперезапуск сервисов, защита файловой системы от записи, автоматические обновления безопасности — это не опции, это базовые требования.</p>]]></description><guid isPermaLink="false">113</guid><pubDate>Sat, 21 Mar 2026 17:27:02 +0000</pubDate></item><item><title>Linux &#x432; &#x43F;&#x440;&#x43E;&#x434;&#x430;&#x43A;&#x448;&#x435;&#x43D;&#x435;: &#x43A;&#x430;&#x43A;&#x43E;&#x439; &#x434;&#x438;&#x441;&#x442;&#x440;&#x438;&#x431;&#x443;&#x442;&#x438;&#x432; &#x432;&#x44B;&#x431;&#x440;&#x430;&#x442;&#x44C;, &#x43A;&#x430;&#x43A; &#x43D;&#x430;&#x441;&#x442;&#x440;&#x43E;&#x438;&#x442;&#x44C; &#x43D;&#x430;&#x434;&#x451;&#x436;&#x43D;&#x43E;&#x441;&#x442;&#x44C; &#x438; &#x432;&#x44B;&#x436;&#x430;&#x442;&#x44C; &#x43C;&#x430;&#x43A;&#x441;&#x438;&#x43C;&#x443;&#x43C;</title><link>https://ithub.uno/statiarticles/3_linux-gnu-os-articles/linux-%D0%B2-%D0%BF%D1%80%D0%BE%D0%B4%D0%B0%D0%BA%D1%88%D0%B5%D0%BD%D0%B5-%D0%BA%D0%B0%D0%BA%D0%BE%D0%B9-%D0%B4%D0%B8%D1%81%D1%82%D1%80%D0%B8%D0%B1%D1%83%D1%82%D0%B8%D0%B2-%D0%B2%D1%8B%D0%B1%D1%80%D0%B0%D1%82%D1%8C-%D0%BA%D0%B0%D0%BA-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B8%D1%82%D1%8C-%D0%BD%D0%B0%D0%B4%D1%91%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C-%D0%B8-%D0%B2%D1%8B%D0%B6%D0%B0%D1%82%D1%8C-%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D1%83%D0%BC-r161/</link><description><![CDATA[
<p><img src="https://ithub.uno/uploads/tmi_files_2026_03/8dd25082-ce50-41fe-bbce-6c4716802c08.webp.dc9a6ca49b7c282f9e6a8009508480bf.webp" /></p>
<h2>Введение: Linux в продакшене — это не просто установить и забыть</h2><p>Поставить Ubuntu, запустить приложение и назвать это "продакшеном" — рецепт будущей катастрофы. Настоящий production Linux — это слоёный пирог из правильного выбора дистрибутива, hardening-а, тюнинга ядра, настроенного мониторинга и чётких процедур обслуживания.</p><p>Статья написана для тех, кто уже умеет работать с Linux и хочет понять <strong>почему</strong> нужно делать именно так, а не просто скопировать команды.</p><hr><h2>Часть 1: Выбор дистрибутива</h2><h3>Критерии для production-выбора</h3><p>Прежде чем смотреть на дистрибутивы, определитесь с приоритетами:</p><ul><li><p><strong>Стабильность vs свежесть</strong>: старые проверенные пакеты или последние версии?</p></li><li><p><strong>Коммерческая поддержка</strong>: нужен SLA от вендора или достаточно community?</p></li><li><p><strong>Цикл жизни</strong>: как долго дистрибутив будет получать security-патчи?</p></li><li><p><strong>Экосистема</strong>: есть ли готовые пакеты для вашего стека?</p></li><li><p><strong>Команда</strong>: что знает ваша команда? Переучивание дорого стоит.</p></li></ul><hr><h3>Ubuntu LTS Server</h3><p><strong>Цикл поддержки:</strong> 5 лет standard, 10 лет ESM (Extended Security Maintenance).<br><strong>Текущий LTS:</strong> 24.04 (Noble Numbat), следующий — 26.04.</p><p>Ubuntu — безусловный лидер по распространённости в cloud-среде. AWS, GCP, Azure — везде Ubuntu является первым выбором по умолчанию. Огромная документация, большинство DevOps-инструментов тестируются на Ubuntu в первую очередь.</p><p><strong>Подходит для:</strong></p><ul><li><p>Startups и компании без выделенного Linux-администратора</p></li><li><p>Kubernetes workers и cloud-native окружения</p></li><li><p>Быстрое развёртывание новых стеков</p></li></ul><p><strong>Осторожно:</strong></p><ul><li><p>Canonical меняет политики (snap-пакеты вместо deb без предупреждения)</p></li><li><p>Обновления между LTS требуют тщательного тестирования</p></li><li><p>ESM платный для &gt; 5 машин в организации</p></li></ul><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Проверка версии и EOL даты
lsb_release -a
ubuntu-advantage status  # Статус ESM подписки

# Отключить snap если не нужен (спорно, но часто делают)
sudo systemctl disable snapd --now
sudo apt purge snapd
</code></pre><hr><h3>Debian Stable</h3><p><strong>Цикл поддержки:</strong> ~3 года основная поддержка + 2 года LTS.<br><strong>Текущий:</strong> Debian 12 "Bookworm" (до 2028).</p><p>Debian — "бабушка" большинства дистрибутивов. Её репутация: <strong>консервативная, предсказуемая, надёжная</strong>. Пакеты в Stable могут быть на 1–2 года старше апстрима, но зато они досконально протестированы. Никаких сюрпризов в 3 часа ночи.</p><p><strong>Подходит для:</strong></p><ul><li><p>Серверы с длинным жизненным циклом (БД, хранилища)</p></li><li><p>Инфраструктура, где стабильность важнее новых фич</p></li><li><p>Встраиваемые и промышленные серверы</p></li></ul><p><strong>Осторожно:</strong></p><ul><li><p>Старые пакеты могут не поддерживать новые возможности (TLS 1.3 и т.п. уже везде есть, но крайние версии — нет)</p></li><li><p>community-поддержка без коммерческого SLA</p></li></ul><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Debian: правильные sources.list для продакшена
# Только stable, никакого testing/sid!
cat /etc/apt/sources.list
# deb http://deb.debian.org/debian bookworm main contrib non-free-firmware
# deb http://security.debian.org/debian-security bookworm-security main
# deb http://deb.debian.org/debian bookworm-updates main

# Backports только для конкретных пакетов, не массово
# deb http://deb.debian.org/debian bookworm-backports main
</code></pre><hr><h3>RHEL / Rocky Linux / AlmaLinux</h3><p><strong>RHEL</strong> (Red Hat Enterprise Linux) — корпоративный стандарт в Enterprise-сегменте, особенно там где есть compliance требования (PCI DSS, HIPAA, FedRAMP). Платная подписка, но включает полный enterprise support от Red Hat.</p><p><strong>Rocky Linux и AlmaLinux</strong> — бинарно-совместимые клоны RHEL, бесплатные. После смерти CentOS 8 (2021) это лучшая замена для тех, кто хочет RHEL-совместимость без платы.</p><p><strong>Цикл поддержки:</strong> RHEL 9 → до 2032 (10 лет!). Rocky/Alma — аналогично.</p><p><strong>Подходит для:</strong></p><ul><li><p>Enterprise-окружения с compliance требованиями</p></li><li><p>Компании с контрактами Red Hat (получают support + Satellite)</p></li><li><p>Там где нужен SELinux из коробки в полном объёме</p></li><li><p>Серверы баз данных Oracle (Oracle Linux — тоже RHEL-клон)</p></li></ul><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Rocky Linux: подписка на обновления безопасности (бесплатно)
sudo dnf install epel-release
sudo dnf update

# Проверка SELinux статуса
getenforce  # Enforcing / Permissive / Disabled
sestatus

# RHEL-специфика: subscription-manager
sudo subscription-manager status
sudo subscription-manager repos --list-enabled
</code></pre><hr><h3>Alpine Linux</h3><p><strong>Цикл:</strong> rolling release (stable branches с ~2 годами поддержки).</p><p>Alpine — минималистичный дистрибутив: musl libc вместо glibc, busybox вместо GNU coreutils, базовый образ 5 МБ. Создан для контейнеров и встраиваемых систем.</p><p><strong>Подходит для:</strong></p><ul><li><p>Docker-образы (но есть нюансы с musl!)</p></li><li><p>Edge-узлы и шлюзы с ограниченными ресурсами</p></li><li><p>Безопасные "голые" серверы с минимальной поверхностью атаки</p></li></ul><p><strong>Осторожно:</strong></p><ul><li><p>musl libc != glibc: некоторые Go/C программы ведут себя иначе</p></li><li><p>Не для тех, кто не знает что делает: меньше инструментов отладки</p></li><li><p>Не для продакшена с незнакомым стеком</p></li></ul><hr><h3>Итоговая таблица выбора</h3><div class="tmiRichText__table-wrapper"><table style="min-width: 100px;"><colgroup><col style="min-width:20px;"><col style="min-width:20px;"><col style="min-width:20px;"><col style="min-width:20px;"><col style="min-width:20px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p></p></th><th colspan="1" rowspan="1"><p>Ubuntu LTS</p></th><th colspan="1" rowspan="1"><p>Debian Stable</p></th><th colspan="1" rowspan="1"><p>Rocky/Alma</p></th><th colspan="1" rowspan="1"><p>Alpine</p></th></tr><tr><td colspan="1" rowspan="1"><p>Cloud-native</p></td><td colspan="1" rowspan="1"><p>★★★★★</p></td><td colspan="1" rowspan="1"><p>★★★★</p></td><td colspan="1" rowspan="1"><p>★★★</p></td><td colspan="1" rowspan="1"><p>★★★★</p></td></tr><tr><td colspan="1" rowspan="1"><p>Enterprise</p></td><td colspan="1" rowspan="1"><p>★★★</p></td><td colspan="1" rowspan="1"><p>★★★</p></td><td colspan="1" rowspan="1"><p>★★★★★</p></td><td colspan="1" rowspan="1"><p>★★</p></td></tr><tr><td colspan="1" rowspan="1"><p>Стабильность пакетов</p></td><td colspan="1" rowspan="1"><p>★★★★</p></td><td colspan="1" rowspan="1"><p>★★★★★</p></td><td colspan="1" rowspan="1"><p>★★★★★</p></td><td colspan="1" rowspan="1"><p>★★★</p></td></tr><tr><td colspan="1" rowspan="1"><p>Свежесть пакетов</p></td><td colspan="1" rowspan="1"><p>★★★★</p></td><td colspan="1" rowspan="1"><p>★★★</p></td><td colspan="1" rowspan="1"><p>★★★</p></td><td colspan="1" rowspan="1"><p>★★★★★</p></td></tr><tr><td colspan="1" rowspan="1"><p>Безопасность (OOB)</p></td><td colspan="1" rowspan="1"><p>★★★★</p></td><td colspan="1" rowspan="1"><p>★★★★</p></td><td colspan="1" rowspan="1"><p>★★★★★</p></td><td colspan="1" rowspan="1"><p>★★★★</p></td></tr><tr><td colspan="1" rowspan="1"><p>Поддержка сообщества</p></td><td colspan="1" rowspan="1"><p>★★★★★</p></td><td colspan="1" rowspan="1"><p>★★★★★</p></td><td colspan="1" rowspan="1"><p>★★★★</p></td><td colspan="1" rowspan="1"><p>★★★</p></td></tr><tr><td colspan="1" rowspan="1"><p>Простота обслуживания</p></td><td colspan="1" rowspan="1"><p>★★★★★</p></td><td colspan="1" rowspan="1"><p>★★★★</p></td><td colspan="1" rowspan="1"><p>★★★</p></td><td colspan="1" rowspan="1"><p>★★</p></td></tr></tbody></table></div><p><strong>Рекомендация:</strong> для большинства команд без специфических требований — <strong>Ubuntu 24.04 LTS</strong> или <strong>Debian 12</strong>. Для enterprise с compliance — <strong>Rocky Linux 9</strong>. Для контейнеров — <strong>Alpine</strong> или <strong>Distroless</strong>.</p><hr><h2>Часть 2: Первичная настройка надёжности</h2><h3>Базовое обновление и автоматические security-патчи</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># === Ubuntu / Debian ===
sudo apt update &amp;&amp; sudo apt upgrade -y

# Автоматические security-обновления (unattended-upgrades)
sudo apt install unattended-upgrades apt-listchanges
sudo dpkg-reconfigure unattended-upgrades

# Конфиг /etc/apt/apt.conf.d/50unattended-upgrades
# Оставляем только security, НЕ автоустанавливаем всё подряд
sudo tee /etc/apt/apt.conf.d/50unattended-upgrades &gt; /dev/null &lt;&lt;'EOF'
Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}-security";
    "${distro_id}ESMApps:${distro_codename}-apps-security";
    "${distro_id}ESM:${distro_codename}-infra-security";
};
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::MinimalSteps "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false";  // НЕ перезагружаем автоматически!
Unattended-Upgrade::Mail "ops@yourcompany.com";
EOF

# === Rocky / AlmaLinux ===
sudo dnf install dnf-automatic
sudo systemctl enable --now dnf-automatic-install.timer
# /etc/dnf/automatic.conf: apply_updates = yes, upgrade_type = security
</code></pre><hr><h3>Правильная настройка NTP</h3><p>Время — фундамент для логов, TLS-сертификатов, Kerberos, баз данных. Неправильное время = необъяснимые баги.</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Современный стандарт: systemd-timesyncd (простой) или chrony (сложные сети)
# chronyd рекомендован для серверов — точнее и поддерживает аппаратные часы

sudo apt install chrony   # Ubuntu/Debian
# или: sudo dnf install chrony  # RHEL-based

# /etc/chrony.conf
sudo tee /etc/chrony.conf &gt; /dev/null &lt;&lt;'EOF'
# Используем несколько источников из разных пулов
pool 0.ru.pool.ntp.org iburst
pool 1.ru.pool.ntp.org iburst
pool 2.europe.pool.ntp.org iburst
pool 3.pool.ntp.org iburst

# Внутренний NTP-сервер (если есть) — даём максимальный приоритет
# server 192.168.1.1 prefer iburst

# Разрешаем step при большом расхождении (только при старте)
makestep 1.0 3

# Дрейф файла
driftfile /var/lib/chrony/drift

# Логирование
logdir /var/log/chrony

# Синхронизация RTC (hardware clock)
rtcsync
EOF

sudo systemctl restart chrony
chronyc tracking    # Статус синхронизации
chronyc sources -v  # Источники времени
</code></pre><hr><h3>systemd: сделать сервисы по-настоящему надёжными</h3><p>Большинство используют systemd только для <code>start/stop/enable</code>. Но в нём есть мощные механизмы для production-надёжности.</p><pre spellcheck="" class="tmiCode language-ini" data-language="ini"><code># /etc/systemd/system/myapp.service — production-grade unit

[Unit]
Description=My Production Application
Documentation=https://docs.mycompany.com/myapp

# Зависимости — стартуем ПОСЛЕ того как сеть полностью готова
After=network-online.target postgresql.service redis.service
Wants=network-online.target
Requires=postgresql.service

# Условие запуска — только если файл конфига существует
ConditionPathExists=/etc/myapp/config.yaml

[Service]
Type=notify           # Приложение сигнализирует о готовности через sd_notify
NotifyAccess=main

User=myapp
Group=myapp
WorkingDirectory=/opt/myapp

# Переменные окружения из защищённого файла (права 600, владелец root)
EnvironmentFile=/etc/myapp/environment

ExecStartPre=/opt/myapp/bin/validate-config  # Валидация конфига перед стартом
ExecStart=/opt/myapp/bin/myapp --config /etc/myapp/config.yaml
ExecReload=/bin/kill -HUP $MAINPID          # Graceful reload по сигналу HUP

# ── Restart политика ──────────────────────────────────────────────────────────
Restart=on-failure          # Перезапускаем только при сбое (не при systemctl stop)
RestartSec=5s               # Ждём 5 секунд перед перезапуском
StartLimitIntervalSec=120s  # Окно для подсчёта неудачных стартов
StartLimitBurst=5           # Максимум 5 перезапусков за 120 секунд
# После исчерпания — юнит в Failed. Оповестить алертинг!

# ── Watchdog ─────────────────────────────────────────────────────────────────
# Приложение ДОЛЖНО вызывать sd_notify("WATCHDOG=1") каждые WatchdogSec/2
WatchdogSec=30s

# ── Таймауты ─────────────────────────────────────────────────────────────────
TimeoutStartSec=60s    # Максимальное время на инициализацию
TimeoutStopSec=30s     # Максимальное время на graceful shutdown
KillMode=mixed         # Сначала SIGTERM главному процессу, потом SIGKILL всей группе
KillSignal=SIGTERM

# ── Лимиты ресурсов ──────────────────────────────────────────────────────────
LimitNOFILE=65536      # Максимум открытых файлов
LimitNPROC=4096        # Максимум процессов/потоков
LimitMEMLOCK=infinity  # Для приложений с mlock (JVM, некоторые БД)

# ── Безопасность (sandboxing) ─────────────────────────────────────────────────
NoNewPrivileges=true         # Запрет повышения привилегий
PrivateTmp=true              # Изолированный /tmp
PrivateDevices=true          # Запрет доступа к /dev (кроме basic)
ProtectSystem=strict         # /usr, /boot, /etc только на чтение
ProtectHome=true             # Нет доступа к /home, /root
ReadWritePaths=/var/lib/myapp /var/log/myapp  # Разрешаем запись только сюда
CapabilityBoundingSet=CAP_NET_BIND_SERVICE    # Только если нужен порт &lt; 1024

# ── Логирование ──────────────────────────────────────────────────────────────
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp

[Install]
WantedBy=multi-user.target
</code></pre><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Анализ конфигурации unit (покажет ошибки и предупреждения)
systemd-analyze verify /etc/systemd/system/myapp.service

# Просмотр "дерева" зависимостей
systemd-analyze critical-chain myapp.service

# Лимиты запущенного сервиса
cat /proc/$(systemctl show -p MainPID --value myapp)/limits

# История перезапусков
journalctl -u myapp --since "7 days ago" | grep -E "Started|Stopped|Failed"
</code></pre><hr><h3>Настройка ulimits и системных лимитов</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># /etc/security/limits.conf — лимиты для пользователей

# Для высоконагруженного приложения (например, веб-сервер, БД)
myapp soft nofile 65536
myapp hard nofile 65536
myapp soft nproc  65536
myapp hard nproc  65536

# Для Elasticsearch / Kafka / других Java-приложений
elasticsearch soft memlock unlimited
elasticsearch hard memlock unlimited
elasticsearch soft nofile 65536
elasticsearch hard nofile 65536

# /etc/systemd/system.conf — глобальные лимиты для systemd-процессов
# DefaultLimitNOFILE=65536
# DefaultLimitNPROC=65536

# Проверка текущих лимитов процесса
cat /proc/$(pgrep myapp | head -1)/limits
</code></pre><hr><h2>Часть 3: Тюнинг ядра для продакшена</h2><h3>Параметры sysctl: сеть</h3><p>Это самое важное для highload-серверов. По умолчанию Linux оптимизирован для десктопа, не для сервера.</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># /etc/sysctl.d/99-production.conf
sudo tee /etc/sysctl.d/99-production.conf &gt; /dev/null &lt;&lt;'EOF'

# ════════════════════════════════════════════════════════════════════
#  СЕТЬ — TCP/IP стек
# ════════════════════════════════════════════════════════════════════

# Буферы сокетов (receive/send)
# Для highload: 128 МБ (default ~212 КБ — катастрофически мало!)
net.core.rmem_max          = 134217728   # 128 МБ
net.core.wmem_max          = 134217728
net.core.rmem_default      = 31457280    # 30 МБ
net.core.wmem_default      = 31457280

# TCP-специфичные буферы (min, default, max)
net.ipv4.tcp_rmem          = 4096 87380 134217728
net.ipv4.tcp_wmem          = 4096 65536 134217728

# Очередь подключений (backlog)
# Должна совпадать с параметром backlog в listen() вашего приложения
net.core.somaxconn         = 65535
net.core.netdev_max_backlog = 65535

# SYN-очередь (защита от SYN flood + highload accept)
net.ipv4.tcp_max_syn_backlog = 65535

# TIME_WAIT: ускоряем переиспользование сокетов
net.ipv4.tcp_tw_reuse      = 1    # Переиспользовать TIME_WAIT сокеты (безопасно)
net.ipv4.tcp_fin_timeout   = 15   # Сократить FIN_WAIT таймаут (default 60 сек)

# Диапазон эфемерных портов (для outbound соединений)
net.ipv4.ip_local_port_range = 1024 65535

# Keepalive — обнаружение мёртвых соединений
net.ipv4.tcp_keepalive_time    = 120   # Начинаем проверку после 120 сек простоя
net.ipv4.tcp_keepalive_intvl   = 10    # Интервал между пробами
net.ipv4.tcp_keepalive_probes  = 6     # Количество проб

# Алгоритм управления перегрузкой (congestion control)
# BBR — современный алгоритм Google, значительно лучше для WAN и highload
net.core.default_qdisc         = fq
net.ipv4.tcp_congestion_control = bbr

# Быстрый открытый TCP (TFO) — уменьшает latency на 1 RTT
# (убедитесь что клиенты поддерживают)
net.ipv4.tcp_fastopen          = 3  # 1=клиент, 2=сервер, 3=оба

# Защита от мусорных RST пакетов
net.ipv4.tcp_rfc1337           = 1

# ════════════════════════════════════════════════════════════════════
#  ПАМЯТЬ
# ════════════════════════════════════════════════════════════════════

# vm.swappiness: насколько агрессивно использовать swap
# 0  = swap только при крайней необходимости (для БД и latency-критичных)
# 10 = рекомендуется для production-серверов
# 60 = default (слишком агрессивный для сервера)
vm.swappiness = 10

# Dirty pages: когда сбрасывать на диск
# Уменьшаем для предсказуемой latency (не даём накопиться большому flush)
vm.dirty_ratio           = 10   # Начинаем sync при 10% RAM dirty (default 20%)
vm.dirty_background_ratio = 3   # Фоновый flush начинается при 3% (default 10%)
vm.dirty_writeback_centisecs = 500  # Интервал фонового flush (5 сек, default 5 сек)
vm.dirty_expire_centisecs    = 3000 # Страницы считаются "старыми" через 30 сек

# Overcommit (для Java/JVM и других приложений с большими heap)
# 0 = эвристика ядра (default)
# 1 = всегда overcommit (опасно, но нужно для некоторых БД)
# 2 = строгий лимит (CommitLimit = swap + vm.overcommit_ratio% от RAM)
vm.overcommit_memory = 0

# OOM Killer: штраф для критических процессов
# Устанавливается через /proc или systemd OOMPolicy
# (ниже — пример для PostgreSQL)

# ════════════════════════════════════════════════════════════════════
#  ФАЙЛОВАЯ СИСТЕМА
# ════════════════════════════════════════════════════════════════════

# Максимум открытых файлов в системе (не путать с per-process ulimit)
fs.file-max = 2097152

# Inotify: для приложений, следящих за файлами (Docker, K8s, IDE)
fs.inotify.max_user_watches   = 524288
fs.inotify.max_user_instances = 512

# AIO: асинхронный ввод-вывод (PostgreSQL, некоторые БД)
fs.aio-max-nr = 1048576

# ════════════════════════════════════════════════════════════════════
#  БЕЗОПАСНОСТЬ
# ════════════════════════════════════════════════════════════════════

# Запрет разыменования символических ссылок в /tmp (защита от атак)
fs.protected_symlinks = 1
fs.protected_hardlinks = 1

# Запрет записи в память выполняемых файлов
fs.protected_regular = 2
fs.protected_fifos   = 2

# ASLR (Address Space Layout Randomization)
kernel.randomize_va_space = 2  # Полная рандомизация

# Запрет SysRq (кроме sync/reboot — оставляем для экстренного случая)
kernel.sysrq = 16  # 16 = только sync

# Защита от Spectre/Meltdown через ptrace
kernel.yama.ptrace_scope = 1

# Core dumps только в определённую директорию
kernel.core_pattern = /var/crash/core.%e.%p.%t
kernel.core_uses_pid = 1

EOF

# Применить без перезагрузки
sudo sysctl --system

# Проверить конкретный параметр
sysctl net.ipv4.tcp_congestion_control
</code></pre><hr><h3>Hugepages: критично для баз данных</h3><p>HugePages уменьшают нагрузку на TLB (Translation Lookaside Buffer) при работе с большими объёмами памяти. PostgreSQL, Oracle, MySQL InnoDB, Elasticsearch, Redis — все выигрывают от HugePages.</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Проверить текущее состояние
cat /proc/meminfo | grep -i huge
# HugePages_Total:    0    ← не настроены
# Hugepagesize:    2048 kB  ← 2 МБ каждая страница

# Рассчитать сколько нужно:
# Для PostgreSQL: shared_buffers + прочая SHM / 2 МБ
# Например: 16 ГБ shared_buffers → 16384 МБ / 2 МБ = 8192 страниц

# /etc/sysctl.d/99-hugepages.conf
echo "vm.nr_hugepages = 8192" | sudo tee /etc/sysctl.d/99-hugepages.conf

# Или прозрачные HugePages (THP) — автоматически, но с latency spikes!
# Для БД (особенно Redis) — ОТКЛЮЧИТЬ THP:
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/defrag

# Сделать постоянным через rc.local или systemd service:
sudo tee /etc/systemd/system/disable-thp.service &gt; /dev/null &lt;&lt;'EOF'
[Unit]
Description=Disable Transparent Huge Pages
DefaultDependencies=false
After=sysinit.target local-fs.target

[Service]
Type=oneshot
ExecStart=/bin/sh -c 'echo never &gt; /sys/kernel/mm/transparent_hugepage/enabled'
ExecStart=/bin/sh -c 'echo never &gt; /sys/kernel/mm/transparent_hugepage/defrag'
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now disable-thp
</code></pre><hr><h3>Планировщик I/O: под задачу</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Посмотреть текущий планировщик для диска
cat /sys/block/sda/queue/scheduler
# [mq-deadline] kyber bfq none

# Выбор планировщика:
# none (noop)  — для NVMe SSD и виртуальных дисков (SSD сами управляют очередью)
# mq-deadline  — универсальный, хорош для смешанных нагрузок
# bfq          — для десктопов и интерактивных задач (не для продакшена)
# kyber        — для NVMe с очень низкой latency

# Для NVMe SSD — none:
echo none | sudo tee /sys/block/nvme0n1/queue/scheduler

# Для SATA SSD — mq-deadline:
echo mq-deadline | sudo tee /sys/block/sda/queue/scheduler

# Через udev (постоянно):
sudo tee /etc/udev/rules.d/60-ioschedulers.rules &gt; /dev/null &lt;&lt;'EOF'
# NVMe
ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/scheduler}="none"
# SATA/SAS SSD (по ROTATIONAL=0)
ACTION=="add|change", KERNEL=="sd[a-z]|xvd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline"
# HDD (по ROTATIONAL=1)
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq"
EOF

# Дополнительные параметры очереди диска:
# Глубина очереди запросов (для NVMe можно больше)
cat /sys/block/nvme0n1/queue/nr_requests  # default 64
echo 1024 | sudo tee /sys/block/nvme0n1/queue/nr_requests

# Read-ahead (предварительное чтение)
# Для последовательного чтения (логи, backup): увеличить
# Для random I/O (OLTP БД): уменьшить
blockdev --setra 256 /dev/sda  # 128 КБ (256 × 512 байт)
</code></pre><hr><h3>OOM Killer: кого убивать в последнюю очередь</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># OOM killer убивает процессы когда RAM исчерпана.
# Значение oom_score_adj: от -1000 (никогда не убивать) до +1000 (убить первым)

# Защитить критические процессы (PostgreSQL, основное приложение):
echo -1000 | sudo tee /proc/$(pgrep postgres | head -1)/oom_score_adj

# В systemd unit — правильный способ:
# [Service]
# OOMScoreAdjust=-900   # -1000 зарезервирован для ядра

# Для throaway-процессов (worker, task runner) — пусть убивают первыми:
# OOMScoreAdjust=500

# Включить расширенное логирование OOM kill:
echo 1 | sudo tee /proc/sys/vm/oom_dump_tasks

# Посмотреть oom_score всех процессов
for p in /proc/[0-9]*/; do
    pid=$(basename $p)
    comm=$(cat $p/comm 2&gt;/dev/null)
    score=$(cat $p/oom_score 2&gt;/dev/null)
    adj=$(cat $p/oom_score_adj 2&gt;/dev/null)
    [ -n "$score" ] &amp;&amp; echo "$score $adj $pid $comm"
done | sort -rn | head -20
</code></pre><hr><h2>Часть 4: Файловые системы и хранилище</h2><h3>Выбор файловой системы</h3><p><strong>ext4</strong> — проверенный стандарт, но без поддержки checksums данных (только метаданных).</p><p><strong>XFS</strong> — отличная производительность на больших файлах и параллельном I/O. Default в RHEL. Нельзя уменьшить (только увеличить).</p><p><strong>Btrfs</strong> — copy-on-write, встроенные checksums, снапшоты. Хорош для систем где нужны снапшоты без LVM. В продакшене требует RAID-конфигурации для надёжности.</p><p><strong>ZFS</strong> — самая надёжная ФС с end-to-end checksums, встроенным RAID-Z, дедупликацией. Требует отдельной установки на Linux. Рекомендуется для хранилищ данных.</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Монтирование ext4 с оптимальными опциями для продакшена:
# /etc/fstab:
# /dev/sdb1  /data  ext4  defaults,noatime,lazytime,errors=remount-ro  0 2

# noatime    — не обновлять atime при каждом чтении (значительно снижает I/O)
# lazytime   — обновлять временны́е метки только при flush (компромисс)
# errors=remount-ro — при ошибке ФС переходит в read-only вместо паники

# XFS с journalling только на metadata (для производительности):
# /dev/sdb1  /data  xfs   defaults,noatime,logbsize=256k  0 2

# Проверка здоровья файловой системы:
sudo tune2fs -l /dev/sdb1 | grep -E "Last checked|Mount count|Max mount count"
sudo xfs_info /data
sudo xfs_repair -n /data  # Проверка XFS без исправления
</code></pre><hr><h3>LVM: гибкое управление томами</h3><p>LVM (Logical Volume Manager) — обязателен для production-серверов. Позволяет расширять разделы без downtime, создавать снапшоты для бэкапов.</p><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Создание LVM структуры:
# 1. Physical Volumes (PV)
sudo pvcreate /dev/sdb /dev/sdc

# 2. Volume Group (VG)
sudo vgcreate data_vg /dev/sdb /dev/sdc
sudo vgs  # Проверка

# 3. Logical Volumes (LV)
sudo lvcreate -L 100G -n postgres_lv data_vg   # 100 ГБ для PostgreSQL
sudo lvcreate -L 50G  -n logs_lv    data_vg    # 50 ГБ для логов
sudo lvcreate -l 100%FREE -n backup_lv data_vg # Остаток под бэкапы

# Форматирование
sudo mkfs.xfs /dev/data_vg/postgres_lv
sudo mkfs.ext4 /dev/data_vg/logs_lv

# Расширение без downtime (XFS умеет расти онлайн):
sudo lvextend -L +50G /dev/data_vg/postgres_lv  # Добавить 50 ГБ к LV
sudo xfs_growfs /var/lib/postgresql              # Расширить ФС

# Снапшот для онлайн-бэкапа:
sudo lvcreate -L 10G -s -n postgres_snap /dev/data_vg/postgres_lv
sudo mount -o ro /dev/data_vg/postgres_snap /mnt/backup_snap
# rsync с /mnt/backup_snap → бэкап без остановки БД
sudo umount /mnt/backup_snap
sudo lvremove -f /dev/data_vg/postgres_snap
</code></pre><hr><h2>Часть 5: Мониторинг и observability</h2><h3>Встроенные инструменты диагностики</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># ── Производительность системы ───────────────────────────────────────────────

# top на стероидах: htop
sudo apt install htop

# Статистика I/O по процессам
sudo iotop -o    # -o = только активные процессы

# Сетевые соединения (замена netstat)
ss -tunap        # TCP/UDP, номера, приложения, pid
ss -s            # Сводная статистика
ss 'state established' # Только ESTABLISHED

# Дисковый I/O в реальном времени
iostat -xz 1     # Расширенная статистика, без нулей, обновление 1с

# Нагрузка на сеть по интерфейсам
sar -n DEV 1     # Из пакета sysstat

# Сколько памяти реально свободно
free -h
cat /proc/meminfo | grep -E "MemAvailable|MemFree|Cached|SwapUsed"

# ── Анализ производительности ────────────────────────────────────────────────

# perf: профилировщик ядра (какие системные вызовы тормозят)
sudo perf top -g              # Онлайн profiling
sudo perf stat -p $(pgrep myapp) sleep 10  # Статистика за 10 секунд

# strace: что делает процесс (дорого, только для диагностики)
sudo strace -p $(pgrep myapp) -e trace=network,file -T 2&gt;&amp;1 | head -50

# lsof: открытые файлы и сокеты
sudo lsof -p $(pgrep myapp) | wc -l  # Сколько открыто
sudo lsof -i :8080            # Кто слушает порт 8080

# ── Диагностика сети ─────────────────────────────────────────────────────────

# Статистика TCP ошибок (ретрансмиты, dropped пакеты)
netstat -s | grep -E "retransmit|failed|overflow|listen"
# Или через nstat (более детально):
nstat -az | grep -i -E "retrans|drop|overflow|fail"

# Потери пакетов на интерфейсе
ip -s link show eth0
# RX errors/dropped/overrun — проблемы приёма
# TX errors/dropped         — проблемы передачи

# Traceroute с временами (для диагностики latency)
mtr --report --report-cycles 20 8.8.8.8

# TCP-дамп для анализа (осторожно с нагрузкой!)
sudo tcpdump -i eth0 -n port 5432 -c 1000 -w /tmp/postgres.pcap
# Анализ в Wireshark
</code></pre><hr><h3>Настройка централизованного логирования</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># journald — настройка хранения логов
sudo tee /etc/systemd/journald.conf.d/99-production.conf &gt; /dev/null &lt;&lt;'EOF'
[Journal]
Storage=persistent        # Хранить на диске (не только в RAM)
Compress=yes              # Сжатие
SystemMaxUse=2G           # Максимум 2 ГБ для системных логов
SystemKeepFree=500M       # Оставлять 500 МБ свободными
MaxRetentionSec=30day     # Хранить не дольше 30 дней
MaxFileSec=1day           # Ротация ежедневно
ForwardToSyslog=no        # Не дублировать в rsyslog (если не нужно)
RateLimitBurst=1000       # Лимит: 1000 сообщений
RateLimitIntervalSec=30s  # за 30 секунд на единицу
EOF
sudo systemctl restart systemd-journald

# Полезные запросы journalctl:
journalctl --since "1 hour ago" -p err  # Ошибки за последний час
journalctl -u myapp -f --output=json    # Поток логов в JSON
journalctl --disk-usage                 # Сколько занимают логи
journalctl --vacuum-time=7d             # Удалить старше 7 дней

# rsyslog → файлы (для совместимости с legacy-инструментами):
# /etc/rsyslog.d/99-production.conf
sudo tee /etc/rsyslog.d/99-production.conf &gt; /dev/null &lt;&lt;'EOF'
# Высокопроизводительный режим
$ActionQueueType LinkedList
$ActionQueueSize 10000
$ActionResumeRetryCount -1    # Бесконечный retry при недоступности цели

# Отправка в centralized syslog (Loki, Graylog, Splunk)
*.* @@syslog.internal.example.com:514  # @@ = TCP (надёжнее UDP)
EOF
</code></pre><hr><h3>Prometheus Node Exporter: метрики для Grafana</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># Установка Node Exporter (собирает 1000+ метрик системы)
wget https://github.com/prometheus/node_exporter/releases/latest/download/node_exporter-*.linux-amd64.tar.gz
tar xf node_exporter-*.tar.gz
sudo mv node_exporter-*/node_exporter /usr/local/bin/
sudo chmod +x /usr/local/bin/node_exporter

# systemd unit для Node Exporter
sudo tee /etc/systemd/system/node_exporter.service &gt; /dev/null &lt;&lt;'EOF'
[Unit]
Description=Prometheus Node Exporter
After=network.target

[Service]
Type=simple
User=nobody
ExecStart=/usr/local/bin/node_exporter \
    --collector.systemd \
    --collector.processes \
    --collector.interrupts \
    --collector.tcpstat \
    --collector.diskstats \
    --web.listen-address=:9100 \
    --web.telemetry-path=/metrics
Restart=on-failure
RestartSec=5s
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now node_exporter

# Проверка
curl -s http://localhost:9100/metrics | head -20
</code></pre><p>Ключевые метрики для алертов (Prometheus rules):</p><pre spellcheck="" class="tmiCode language-yaml" data-language="YAML"><code># /etc/prometheus/rules/node-alerts.yml
groups:
  - name: node-alerts
    rules:

      - alert: HighCPULoad
        expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) &gt; 85
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "CPU &gt; 85% на {{ $labels.instance }} в течение 10 мин"

      - alert: LowMemory
        expr: node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100 &lt; 10
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Доступно &lt; 10% RAM на {{ $labels.instance }}"

      - alert: DiskSpaceCritical
        expr: node_filesystem_avail_bytes{fstype!~"tmpfs|fuse.lxcfs"} / node_filesystem_size_bytes * 100 &lt; 10
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Диск {{ $labels.mountpoint }} заполнен на 90%+"

      - alert: HighDiskIOUtilization
        expr: rate(node_disk_io_time_seconds_total[5m]) * 100 &gt; 80
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Диск {{ $labels.device }} загружен на 80%+"

      - alert: HighNetworkErrorRate
        expr: rate(node_network_receive_errs_total[5m]) + rate(node_network_transmit_errs_total[5m]) &gt; 10
        for: 5m
        labels:
          severity: warning

      - alert: SystemdServiceFailed
        expr: node_systemd_unit_state{state="failed"} == 1
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Сервис {{ $labels.name }} в состоянии Failed"
</code></pre><hr><h2>Часть 6: Безопасность в продакшене</h2><h3>SSH: только так и никак иначе</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># /etc/ssh/sshd_config.d/99-hardening.conf

sudo tee /etc/ssh/sshd_config.d/99-hardening.conf &gt; /dev/null &lt;&lt;'EOF'
# Только ключи, никаких паролей
PasswordAuthentication no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no

# Запрет root-логина
PermitRootLogin no

# Только конкретные пользователи или группы
AllowGroups sshusers sudo

# Современные алгоритмы (убираем слабые)
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256
Ciphers aes256-gcm@openssh.com,aes128-gcm@openssh.com,chacha20-poly1305@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com

# Таймаут неактивной сессии (30 минут)
ClientAliveInterval 300
ClientAliveCountMax 6

# Защита от брутфорса: максимум 3 попытки аутентификации
MaxAuthTries 3
MaxSessions 10
LoginGraceTime 30s

# Отключить X11 forwarding и агент forwarding на серверах
X11Forwarding no
AllowAgentForwarding no

# Логировать все подключения
LogLevel VERBOSE
EOF

sudo sshd -t  # Проверка синтаксиса
sudo systemctl reload sshd
</code></pre><h3>UFW / nftables: файрвол</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code># UFW — простой и читаемый интерфейс к iptables/nftables
sudo apt install ufw

# Политика по умолчанию: всё запрещено входящее, всё разрешено исходящее
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Разрешаем только нужное
sudo ufw allow from 10.0.0.0/8 to any port 22    # SSH только из внутренней сети
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow from 10.0.0.0/8 to any port 9100  # Node Exporter только изнутри

# Rate limiting для SSH (защита от брутфорса)
sudo ufw limit ssh

sudo ufw enable
sudo ufw status verbose

# Логирование отклонённых пакетов
sudo ufw logging on
</code></pre><h3>fail2ban: автоматическая блокировка атак</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>sudo apt install fail2ban

# /etc/fail2ban/jail.local
sudo tee /etc/fail2ban/jail.local &gt; /dev/null &lt;&lt;'EOF'
[DEFAULT]
bantime  = 3600       # Блокировка на 1 час
findtime = 600        # Окно анализа: 10 минут
maxretry = 5          # Максимум попыток

# Уведомление по email
destemail = ops@yourcompany.com
sender    = fail2ban@yourserver.example.com
action    = %(action_mwl)s  # Ban + email с логами

# Белый список (никогда не блокировать)
ignoreip  = 127.0.0.1/8 ::1 10.0.0.0/8 192.168.0.0/16

[sshd]
enabled  = true
port     = ssh
logpath  = %(sshd_log)s
maxretry = 3          # SSH: только 3 попытки

[nginx-http-auth]
enabled  = true
maxretry = 5

[nginx-limit-req]
enabled  = true
maxretry = 10

[nginx-botsearch]
enabled  = true
maxretry = 2
EOF

sudo systemctl enable --now fail2ban
sudo fail2ban-client status       # Общий статус
sudo fail2ban-client status sshd  # Статус SSH jail
</code></pre><hr><h3>auditd: аудит действий на сервере</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>sudo apt install auditd

# /etc/audit/rules.d/99-production.rules
sudo tee /etc/audit/rules.d/99-production.rules &gt; /dev/null &lt;&lt;'EOF'
# Удаляем все текущие правила
-D

# Максимальный буфер (для highload)
-b 8192

# Отказоустойчивость: при ошибке — продолжать (не паниковать)
-f 1

# ── Критические файлы ────────────────────────────────────────────────────────
-w /etc/passwd  -p wa -k identity
-w /etc/shadow  -p wa -k identity
-w /etc/sudoers -p wa -k sudo_changes
-w /etc/ssh/    -p wa -k ssh_config

# Системные бинарники
-w /usr/bin/sudo  -p x -k privileged
-w /usr/bin/su    -p x -k privileged
-w /bin/chmod     -p x -k privileged
-w /bin/chown     -p x -k privileged

# Изменения конфигурации cron
-w /etc/cron.d/       -p wa -k cron
-w /var/spool/cron/   -p wa -k cron

# Загрузка/выгрузка модулей ядра
-w /sbin/insmod  -p x -k kernel_modules
-w /sbin/rmmod   -p x -k kernel_modules
-a always,exit -F arch=b64 -S init_module -k kernel_modules

# Сетевые изменения
-a always,exit -F arch=b64 -S sethostname -S setdomainname -k system-locale
-w /etc/hosts             -p wa -k hosts
-w /etc/network/          -p wa -k network

# Неудачные системные вызовы (попытки эскалации привилегий)
-a always,exit -F arch=b64 -S open -F exit=-EACCES -k access_denied
-a always,exit -F arch=b64 -S open -F exit=-EPERM  -k access_denied

# Мониторинг директории приложения
-w /opt/myapp/bin/ -p x -k app_exec
EOF

sudo augenrules --load   # Загрузить правила
sudo auditctl -l         # Проверить активные правила
ausearch -k sudo_changes --start today  # Посмотреть события sudo
aureport --summary       # Сводный отчёт
</code></pre><hr><h2>Часть 7: Процедуры и автоматизация обслуживания</h2><h3>Скрипт проверки здоровья сервера</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>#!/bin/bash
# /usr/local/bin/health-check.sh — ежедневный health check
# Запускать через cron: 0 7 * * * /usr/local/bin/health-check.sh

set -euo pipefail
REPORT_FILE="/var/log/health-check-$(date +%Y%m%d).log"
ALERT_EMAIL="ops@yourcompany.com"
ISSUES=0

log() { echo "[$(date '+%H:%M:%S')] $*" | tee -a "$REPORT_FILE"; }
warn() { log "</code></pre><p><code><span class="tmiEmoji" title="">⚠️</span></code></p><p><code>  WARN: $*"; ((ISSUES++)); } err()  { log "<span class="tmiEmoji" title="">❌</span> ERROR: $*"; ((ISSUES++)); } ok()   { log "<span class="tmiEmoji" title="">✅</span> OK: $*"; }  log "=== Health Check: $(hostname) === $(date) ==="  # ── Свободное место на дисках ──────────────────────────────────────────────── log "--- Дисковое пространство ---" while IFS= read -r line; do     usage=$(echo "$line" | awk '{print $5}' | tr -d '%')     mount=$(echo "$line" | awk '{print $6}')     [ "$usage" -ge 90 ] &amp;&amp; err "Диск $mount заполнен на $usage%" &amp;&amp; continue     [ "$usage" -ge 80 ] &amp;&amp; warn "Диск $mount заполнен на $usage%" &amp;&amp; continue     ok "Диск $mount: $usage%" done &lt; &lt;(df -h --output=pcent,target -x tmpfs -x devtmpfs | tail -n +2)  # ── Память ─────────────────────────────────────────────────────────────────── log "--- Память ---" available_mb=$(awk '/MemAvailable/ {printf "%.0f", $2/1024}' /proc/meminfo) total_mb=$(awk '/MemTotal/ {printf "%.0f", $2/1024}' /proc/meminfo) used_pct=$(( (total_mb - available_mb) * 100 / total_mb )) swap_used=$(awk '/SwapTotal/{t=$2} /SwapFree/{f=$2} END{printf "%.0f", (t-f)/1024}' /proc/meminfo)  [ "$used_pct" -ge 90 ] &amp;&amp; err "RAM: использовано $used_pct%" [ "$used_pct" -ge 80 ] &amp;&amp; warn "RAM: использовано $used_pct%" ok "RAM: $used_pct% использовано ($available_mb МБ свободно)" [ "$swap_used" -gt 100 ] &amp;&amp; warn "Swap: используется ${swap_used} МБ"  # ── Failed сервисы ─────────────────────────────────────────────────────────── log "--- Systemd сервисы ---" failed=$(systemctl list-units --state=failed --no-legend --no-pager | awk '{print $1}') if [ -n "$failed" ]; then     for svc in $failed; do         err "Сервис в Failed: $svc"     done else     ok "Все сервисы работают" fi  # ── Нагрузка ───────────────────────────────────────────────────────────────── log "--- CPU загрузка ---" cpu_cores=$(nproc) load_avg=$(cut -d' ' -f1 /proc/loadavg) load_int=$(echo "$load_avg" | cut -d. -f1) [ "$load_int" -ge "$((cpu_cores * 2))" ] &amp;&amp; err "Load average $load_avg при $cpu_cores ядрах" [ "$load_int" -ge "$cpu_cores" ]         &amp;&amp; warn "Load average $load_avg при $cpu_cores ядрах" ok "Load average: $load_avg (ядер: $cpu_cores)"  # ── SSL-сертификаты ────────────────────────────────────────────────────────── log "--- SSL сертификаты ---" for cert_file in /etc/letsencrypt/live/*/cert.pem /etc/ssl/certs/*.pem; do     [ -f "$cert_file" ] || continue     expiry=$(openssl x509 -in "$cert_file" -noout -enddate 2&gt;/dev/null \              | cut -d= -f2)     expiry_epoch=$(date -d "$expiry" +%s 2&gt;/dev/null || continue)     days_left=$(( (expiry_epoch - $(date +%s)) / 86400 ))          [ "$days_left" -le 7  ] &amp;&amp; err "Сертификат $cert_file истекает через $days_left дней!"     [ "$days_left" -le 30 ] &amp;&amp; warn "Сертификат $cert_file истекает через $days_left дней"     [ "$days_left" -gt 30 ] &amp;&amp; ok "Сертификат $cert_file: осталось $days_left дней" done  # ── Итог ───────────────────────────────────────────────────────────────────── log "" log "=== Итог: $ISSUES проблем(а) ==="  if [ "$ISSUES" -gt 0 ]; then     mail -s "<span class="tmiEmoji" title="">⚠️</span> Health Check FAILED: $(hostname) — $ISSUES проблем" \          "$ALERT_EMAIL" &lt; "$REPORT_FILE" fi  exit $((ISSUES &gt; 0 ? 1 : 0)) </code></p><hr><h3>Автоматизированный бэкап с проверкой целостности</h3><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>#!/bin/bash
# /usr/local/bin/backup.sh — ежедневный инкрементальный бэкап

set -euo pipefail

BACKUP_DIR="/var/backup"
REMOTE="backup-user@backup-server.example.com:/backups/$(hostname)"
DATE=$(date +%Y%m%d-%H%M)
LOG="/var/log/backup.log"
RETENTION_DAYS=30

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG"; }

log "=== Бэкап начат: $DATE ==="

# ── Конфигурация ─────────────────────────────────────────────────────────────
rsync -avz --delete \
    --exclude='*.tmp' \
    --exclude='*.log' \
    --exclude='/proc' \
    --exclude='/sys' \
    --exclude='/dev' \
    --exclude='/run' \
    --exclude='/tmp' \
    --link-dest="$REMOTE/latest" \
    / \
    "$REMOTE/$DATE/" \
    --log-file="$LOG" \
    2&gt;&amp;1

# Обновляем симлинк на последний бэкап
ssh backup-user@backup-server.example.com \
    "ln -sfn /backups/$(hostname)/$DATE /backups/$(hostname)/latest"

# ── Проверка целостности (checksums) ─────────────────────────────────────────
log "Генерация checksums..."
find /etc /opt/myapp /var/lib -type f -newer /var/backup/.last_backup \
    -exec sha256sum {} \; &gt; "/var/backup/checksums-$DATE.txt" 2&gt;/dev/null || true

rsync -avz "/var/backup/checksums-$DATE.txt" \
    "$REMOTE/checksums/$DATE.txt"

touch /var/backup/.last_backup

# ── Очистка старых бэкапов ───────────────────────────────────────────────────
log "Очистка бэкапов старше $RETENTION_DAYS дней..."
ssh backup-user@backup-server.example.com \
    "find /backups/$(hostname) -maxdepth 1 -type d -mtime +$RETENTION_DAYS \
     ! -name 'latest' -exec rm -rf {} + 2&gt;/dev/null; echo done"

log "=== Бэкап завершён успешно ==="
</code></pre><hr><h2>Быстрая шпаргалка: что сделать на каждом новом сервере</h2><pre spellcheck="" class="tmiCode language-bash" data-language="Bash"><code>#!/bin/bash
# Минимальный чеклист для нового production-сервера

echo "=== 1. Обновить систему ==="
apt update &amp;&amp; apt upgrade -y  # или dnf update -y

echo "=== 2. Создать admin-пользователя ==="
adduser deploy
usermod -aG sudo deploy
mkdir -p /home/deploy/.ssh
cp ~/.ssh/authorized_keys /home/deploy/.ssh/
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys

echo "=== 3. Настроить SSH ==="
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/PermitRootLogin yes/PermitRootLogin no/'                /etc/ssh/sshd_config
systemctl reload sshd

echo "=== 4. Файрвол ==="
ufw --force enable
ufw default deny incoming
ufw allow from 10.0.0.0/8 to any port 22
ufw allow 80 443

echo "=== 5. NTP ==="
apt install -y chrony
systemctl enable --now chrony

echo "=== 6. Применить sysctl тюнинг ==="
# (скопировать /etc/sysctl.d/99-production.conf из шаблона)
sysctl --system

echo "=== 7. Node Exporter ==="
# (установить и включить — см. раздел выше)

echo "=== 8. fail2ban ==="
apt install -y fail2ban
systemctl enable --now fail2ban

echo "=== 9. Unattended upgrades ==="
apt install -y unattended-upgrades
dpkg-reconfigure -f noninteractive unattended-upgrades

echo "=== 10. Базовый мониторинг диска ==="
# Добавить в cron: 0 7 * * * /usr/local/bin/health-check.sh

echo "</code></pre><p><code><span class="tmiEmoji" title="">✅</span> Базовая настройка завершена. </code></p><p><code>Не забыть:" echo "   - Настроить бэкап" </code></p><p><code>echo "   - Подключить к Prometheus/Grafana" </code></p><p><code>echo "   - Добавить в систему конфигурационного управления (Ansible/Salt)" </code></p><p><code>echo "   - Задокументировать сервер в CMDB" </code></p><hr><h2>Заключение</h2><p>Production Linux — это не дистрибутив, это <strong>дисциплина</strong>. Правильный выбор дистрибутива даёт фундамент. Тюнинг ядра и sysctl — производительность. systemd с правильными политиками — надёжность. Мониторинг — видимость. Безопасность — защиту. А автоматизация обслуживания — предсказуемость.</p><p>Самые частые провалы в продакшене:</p><ol><li><p>Не настроены лимиты (<code>ulimit</code>, <code>systemd LimitNOFILE</code>) — приложение падает при нагрузке</p></li><li><p>Нет мониторинга диска — о заполнении узнают по жалобам пользователей</p></li><li><p>SSH доступен по паролям — вопрос не "взломают ли", а "когда"</p></li><li><p>Не тестируются бэкапы — они есть, но не работают когда нужны</p></li><li><p>Нет процедуры обновления — серверы не обновляются годами</p></li></ol><p>Используйте Ansible или Terraform для воспроизводимости: каждая настройка из этой статьи должна быть в коде, а не только в голове у одного администратора. Сервер должен разворачиваться автоматически — это единственная гарантия того, что в 3 часа ночи вы сможете его поднять заново.</p>]]></description><guid isPermaLink="false">161</guid><pubDate>Sat, 21 Mar 2026 22:19:11 +0000</pubDate></item></channel></rss>
