Jump to content
View in the app

A better way to browse. Learn more.

T.M.I IThub

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Введение: Linux в продакшене — это не просто установить и забыть

Поставить Ubuntu, запустить приложение и назвать это "продакшеном" — рецепт будущей катастрофы. Настоящий production Linux — это слоёный пирог из правильного выбора дистрибутива, hardening-а, тюнинга ядра, настроенного мониторинга и чётких процедур обслуживания.

Статья написана для тех, кто уже умеет работать с Linux и хочет понять почему нужно делать именно так, а не просто скопировать команды.


Часть 1: Выбор дистрибутива

Критерии для production-выбора

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

  • Стабильность vs свежесть: старые проверенные пакеты или последние версии?

  • Коммерческая поддержка: нужен SLA от вендора или достаточно community?

  • Цикл жизни: как долго дистрибутив будет получать security-патчи?

  • Экосистема: есть ли готовые пакеты для вашего стека?

  • Команда: что знает ваша команда? Переучивание дорого стоит.


Ubuntu LTS Server

Цикл поддержки: 5 лет standard, 10 лет ESM (Extended Security Maintenance).
Текущий LTS: 24.04 (Noble Numbat), следующий — 26.04.

Ubuntu — безусловный лидер по распространённости в cloud-среде. AWS, GCP, Azure — везде Ubuntu является первым выбором по умолчанию. Огромная документация, большинство DevOps-инструментов тестируются на Ubuntu в первую очередь.

Подходит для:

  • Startups и компании без выделенного Linux-администратора

  • Kubernetes workers и cloud-native окружения

  • Быстрое развёртывание новых стеков

Осторожно:

  • Canonical меняет политики (snap-пакеты вместо deb без предупреждения)

  • Обновления между LTS требуют тщательного тестирования

  • ESM платный для > 5 машин в организации

# Проверка версии и EOL даты
lsb_release -a
ubuntu-advantage status  # Статус ESM подписки

# Отключить snap если не нужен (спорно, но часто делают)
sudo systemctl disable snapd --now
sudo apt purge snapd

Debian Stable

Цикл поддержки: ~3 года основная поддержка + 2 года LTS.
Текущий: Debian 12 "Bookworm" (до 2028).

Debian — "бабушка" большинства дистрибутивов. Её репутация: консервативная, предсказуемая, надёжная. Пакеты в Stable могут быть на 1–2 года старше апстрима, но зато они досконально протестированы. Никаких сюрпризов в 3 часа ночи.

Подходит для:

  • Серверы с длинным жизненным циклом (БД, хранилища)

  • Инфраструктура, где стабильность важнее новых фич

  • Встраиваемые и промышленные серверы

Осторожно:

  • Старые пакеты могут не поддерживать новые возможности (TLS 1.3 и т.п. уже везде есть, но крайние версии — нет)

  • community-поддержка без коммерческого SLA

# 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

RHEL / Rocky Linux / AlmaLinux

RHEL (Red Hat Enterprise Linux) — корпоративный стандарт в Enterprise-сегменте, особенно там где есть compliance требования (PCI DSS, HIPAA, FedRAMP). Платная подписка, но включает полный enterprise support от Red Hat.

Rocky Linux и AlmaLinux — бинарно-совместимые клоны RHEL, бесплатные. После смерти CentOS 8 (2021) это лучшая замена для тех, кто хочет RHEL-совместимость без платы.

Цикл поддержки: RHEL 9 → до 2032 (10 лет!). Rocky/Alma — аналогично.

Подходит для:

  • Enterprise-окружения с compliance требованиями

  • Компании с контрактами Red Hat (получают support + Satellite)

  • Там где нужен SELinux из коробки в полном объёме

  • Серверы баз данных Oracle (Oracle Linux — тоже RHEL-клон)

# 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

Alpine Linux

Цикл: rolling release (stable branches с ~2 годами поддержки).

Alpine — минималистичный дистрибутив: musl libc вместо glibc, busybox вместо GNU coreutils, базовый образ 5 МБ. Создан для контейнеров и встраиваемых систем.

Подходит для:

  • Docker-образы (но есть нюансы с musl!)

  • Edge-узлы и шлюзы с ограниченными ресурсами

  • Безопасные "голые" серверы с минимальной поверхностью атаки

Осторожно:

  • musl libc != glibc: некоторые Go/C программы ведут себя иначе

  • Не для тех, кто не знает что делает: меньше инструментов отладки

  • Не для продакшена с незнакомым стеком


Итоговая таблица выбора

Ubuntu LTS

Debian Stable

Rocky/Alma

Alpine

Cloud-native

★★★★★

★★★★

★★★

★★★★

Enterprise

★★★

★★★

★★★★★

★★

Стабильность пакетов

★★★★

★★★★★

★★★★★

★★★

Свежесть пакетов

★★★★

★★★

★★★

★★★★★

Безопасность (OOB)

★★★★

★★★★

★★★★★

★★★★

Поддержка сообщества

★★★★★

★★★★★

★★★★

★★★

Простота обслуживания

★★★★★

★★★★

★★★

★★

Рекомендация: для большинства команд без специфических требований — Ubuntu 24.04 LTS или Debian 12. Для enterprise с compliance — Rocky Linux 9. Для контейнеров — Alpine или Distroless.


Часть 2: Первичная настройка надёжности

Базовое обновление и автоматические security-патчи

# === Ubuntu / Debian ===
sudo apt update && 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 > /dev/null <<'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

Правильная настройка NTP

Время — фундамент для логов, TLS-сертификатов, Kerberos, баз данных. Неправильное время = необъяснимые баги.

# Современный стандарт: systemd-timesyncd (простой) или chrony (сложные сети)
# chronyd рекомендован для серверов — точнее и поддерживает аппаратные часы

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

# /etc/chrony.conf
sudo tee /etc/chrony.conf > /dev/null <<'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  # Источники времени

systemd: сделать сервисы по-настоящему надёжными

Большинство используют systemd только для start/stop/enable. Но в нём есть мощные механизмы для production-надёжности.

# /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    # Только если нужен порт < 1024

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

[Install]
WantedBy=multi-user.target
# Анализ конфигурации 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"

Настройка ulimits и системных лимитов

# /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

Часть 3: Тюнинг ядра для продакшена

Параметры sysctl: сеть

Это самое важное для highload-серверов. По умолчанию Linux оптимизирован для десктопа, не для сервера.

# /etc/sysctl.d/99-production.conf
sudo tee /etc/sysctl.d/99-production.conf > /dev/null <<'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

Hugepages: критично для баз данных

HugePages уменьшают нагрузку на TLB (Translation Lookaside Buffer) при работе с большими объёмами памяти. PostgreSQL, Oracle, MySQL InnoDB, Elasticsearch, Redis — все выигрывают от HugePages.

# Проверить текущее состояние
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 > /dev/null <<'EOF'
[Unit]
Description=Disable Transparent Huge Pages
DefaultDependencies=false
After=sysinit.target local-fs.target

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

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

Планировщик I/O: под задачу

# Посмотреть текущий планировщик для диска
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 > /dev/null <<'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 байт)

OOM Killer: кого убивать в последнюю очередь

# 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>/dev/null)
    score=$(cat $p/oom_score 2>/dev/null)
    adj=$(cat $p/oom_score_adj 2>/dev/null)
    [ -n "$score" ] && echo "$score $adj $pid $comm"
done | sort -rn | head -20

Часть 4: Файловые системы и хранилище

Выбор файловой системы

ext4 — проверенный стандарт, но без поддержки checksums данных (только метаданных).

XFS — отличная производительность на больших файлах и параллельном I/O. Default в RHEL. Нельзя уменьшить (только увеличить).

Btrfs — copy-on-write, встроенные checksums, снапшоты. Хорош для систем где нужны снапшоты без LVM. В продакшене требует RAID-конфигурации для надёжности.

ZFS — самая надёжная ФС с end-to-end checksums, встроенным RAID-Z, дедупликацией. Требует отдельной установки на Linux. Рекомендуется для хранилищ данных.

# Монтирование 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 без исправления

LVM: гибкое управление томами

LVM (Logical Volume Manager) — обязателен для production-серверов. Позволяет расширять разделы без downtime, создавать снапшоты для бэкапов.

# Создание 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

Часть 5: Мониторинг и observability

Встроенные инструменты диагностики

# ── Производительность системы ───────────────────────────────────────────────

# 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>&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

Настройка централизованного логирования

# journald — настройка хранения логов
sudo tee /etc/systemd/journald.conf.d/99-production.conf > /dev/null <<'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 > /dev/null <<'EOF'
# Высокопроизводительный режим
$ActionQueueType LinkedList
$ActionQueueSize 10000
$ActionResumeRetryCount -1    # Бесконечный retry при недоступности цели

# Отправка в centralized syslog (Loki, Graylog, Splunk)
*.* @@syslog.internal.example.com:514  # @@ = TCP (надёжнее UDP)
EOF

Prometheus Node Exporter: метрики для Grafana

# Установка 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 > /dev/null <<'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

Ключевые метрики для алертов (Prometheus rules):

# /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) > 85
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "CPU > 85% на {{ $labels.instance }} в течение 10 мин"

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

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

      - alert: HighDiskIOUtilization
        expr: rate(node_disk_io_time_seconds_total[5m]) * 100 > 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]) > 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"

Часть 6: Безопасность в продакшене

SSH: только так и никак иначе

# /etc/ssh/sshd_config.d/99-hardening.conf

sudo tee /etc/ssh/sshd_config.d/99-hardening.conf > /dev/null <<'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

UFW / nftables: файрвол

# 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

fail2ban: автоматическая блокировка атак

sudo apt install fail2ban

# /etc/fail2ban/jail.local
sudo tee /etc/fail2ban/jail.local > /dev/null <<'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

auditd: аудит действий на сервере

sudo apt install auditd

# /etc/audit/rules.d/99-production.rules
sudo tee /etc/audit/rules.d/99-production.rules > /dev/null <<'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       # Сводный отчёт

Часть 7: Процедуры и автоматизация обслуживания

Скрипт проверки здоровья сервера

#!/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 "

⚠️

WARN: $*"; ((ISSUES++)); } err() { log " ERROR: $*"; ((ISSUES++)); } ok() { log " 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 ] && err "Диск $mount заполнен на $usage%" && continue [ "$usage" -ge 80 ] && warn "Диск $mount заполнен на $usage%" && continue ok "Диск $mount: $usage%" done < <(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 ] && err "RAM: использовано $used_pct%" [ "$used_pct" -ge 80 ] && warn "RAM: использовано $used_pct%" ok "RAM: $used_pct% использовано ($available_mb МБ свободно)" [ "$swap_used" -gt 100 ] && 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))" ] && err "Load average $load_avg при $cpu_cores ядрах" [ "$load_int" -ge "$cpu_cores" ] && 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>/dev/null \ | cut -d= -f2) expiry_epoch=$(date -d "$expiry" +%s 2>/dev/null || continue) days_left=$(( (expiry_epoch - $(date +%s)) / 86400 )) [ "$days_left" -le 7 ] && err "Сертификат $cert_file истекает через $days_left дней!" [ "$days_left" -le 30 ] && warn "Сертификат $cert_file истекает через $days_left дней" [ "$days_left" -gt 30 ] && ok "Сертификат $cert_file: осталось $days_left дней" done # ── Итог ───────────────────────────────────────────────────────────────────── log "" log "=== Итог: $ISSUES проблем(а) ===" if [ "$ISSUES" -gt 0 ]; then mail -s "⚠️ Health Check FAILED: $(hostname) — $ISSUES проблем" \ "$ALERT_EMAIL" < "$REPORT_FILE" fi exit $((ISSUES > 0 ? 1 : 0))


Автоматизированный бэкап с проверкой целостности

#!/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>&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 {} \; > "/var/backup/checksums-$DATE.txt" 2>/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>/dev/null; echo done"

log "=== Бэкап завершён успешно ==="

Быстрая шпаргалка: что сделать на каждом новом сервере

#!/bin/bash
# Минимальный чеклист для нового production-сервера

echo "=== 1. Обновить систему ==="
apt update && 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 "

Базовая настройка завершена.

Не забыть:" echo " - Настроить бэкап"

echo " - Подключить к Prometheus/Grafana"

echo " - Добавить в систему конфигурационного управления (Ansible/Salt)"

echo " - Задокументировать сервер в CMDB"


Заключение

Production Linux — это не дистрибутив, это дисциплина. Правильный выбор дистрибутива даёт фундамент. Тюнинг ядра и sysctl — производительность. systemd с правильными политиками — надёжность. Мониторинг — видимость. Безопасность — защиту. А автоматизация обслуживания — предсказуемость.

Самые частые провалы в продакшене:

  1. Не настроены лимиты (ulimit, systemd LimitNOFILE) — приложение падает при нагрузке

  2. Нет мониторинга диска — о заполнении узнают по жалобам пользователей

  3. SSH доступен по паролям — вопрос не "взломают ли", а "когда"

  4. Не тестируются бэкапы — они есть, но не работают когда нужны

  5. Нет процедуры обновления — серверы не обновляются годами

Используйте Ansible или Terraform для воспроизводимости: каждая настройка из этой статьи должна быть в коде, а не только в голове у одного администратора. Сервер должен разворачиваться автоматически — это единственная гарантия того, что в 3 часа ночи вы сможете его поднять заново.

User Feedback

Create an account or sign in to leave a review

There are no reviews to display.

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.