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.

IThub

Administrators
  • Joined

  • Last visited

Everything posted by IThub

  1. Байки из продакшна: истории DevOps-инженера, который всё ещё живМеня зовут Максим. Я DevOps с более чем десятилетним стажем, и за эти годы у меня накопилось столько историй, что хватило бы на отдельную книгу. Некоторые — смешные. Некоторые — страшные. Большинство — и то, и другое одновременно. Публикую их здесь, на ithub.uno, анонимизируя компании, но сохраняя каждую деталь, которая делала те моменты незабываемыми. Имена изменены. Боль — нет.
  2. Кластер работает. Теперь начинается настоящая работа: выжать из него максимум производительности, не потерять данные при апгрейде и знать, что делать когда (не «если») что-то сломается. Часть 1: Планирование железа — правильный стартПеред тем как тюнить — убедитесь что железо подобрано правильно. Никакой тюнинг не исправит плохую архитектуру. Сети: разделяйте публичную и кластернуюХудшее что можно сделать — смешать пользовательский трафик и репликацию в одну сеть. Публичная сеть (client network): клиенты → MON/OSD Кластерная сеть (cluster network): OSD → OSD (репликация) Рекомендация: - Публичная: 10 GbE minimum - Кластерная: 25 GbE или bond из двух 10 GbE Конфигурируем при bootstrap: cephadm bootstrap \ --mon-ip 192.168.10.11 \ --cluster-network 192.168.20.0/24 # или после: ceph config set global cluster_network 192.168.20.0/24 Размещение BlueStore компонентовBlueStore — три уровня данных с разными требованиями: Компонент Что хранит Требования Рекомендация DATA Тела объектов Ёмкость HDD или SSD DB (RocksDB) Метаданные объектов IOPS, latency NVMe SSD WAL Write-Ahead Log Высокие IOPS NVMe SSD Если все компоненты на одном диске — они конкурируют за I/O. Выносим DB и WAL на NVMe: # osd-spec-nvme.yaml service_type: osd service_id: nvme-optimized placement: host_pattern: 'ceph-node*' data_devices: paths: - /dev/sdb # HDD для данных - /dev/sdc db_devices: paths: - /dev/nvme0n1 # NVMe для DB (разделяется между несколькими OSD) wal_devices: paths: - /dev/nvme1n1 # отдельный NVMe для WAL Золотое правило: один NVMe может обслуживать WAL/DB для 4-6 HDD OSD. Расчёт оптимального числа OSD на серверБольше OSD — больше параллелизма, но больше RAM. Один OSD потребляет: ~1 GB RAM (HDD OSD, небольшие данные) ~2-4 GB RAM (SSD/NVMe OSD под нагрузкой) BlueStore cache: по умолчанию 1/4 RAM на все OSD # Проверяем потребление памяти OSD ceph daemon osd.0 dump_mempools ceph daemon osd.0 perf dump | grep -i mem Часть 2: Тюнинг производительностиBlueStore: кэш и компрессия# Размер BlueStore кэша — главный параметр # По умолчанию: 1/4 от общей RAM (авто) # Можно задать явно для SSD/NVMe (они меньше нуждаются в кэше) ceph config set osd bluestore_cache_size_ssd 1073741824 # 1 GB для SSD OSD # HDD нуждаются в бОльшем кэше ceph config set osd bluestore_cache_size_hdd 536870912 # 512 MB для HDD OSD # Компрессия — включаем для cold data ceph osd pool set mypool compression_mode aggressive # сжимать всегда # или ceph osd pool set mypool compression_mode passive # сжимать если выгодно ceph osd pool set mypool compression_algorithm zstd # лучший ratio # или snappy — быстрее, но меньше сжимает # или lz4 — самый быстрый, минимальное сжатие ceph osd pool set mypool compression_min_blob_size 8192 # мин. размер для сжатия Настройка очереди I/O — mclockС Ceph Pacific появился планировщик mclock, дающий QoS на уровне OSD: # Проверяем текущий планировщик ceph config get osd osd_op_queue # должен быть: mclock_scheduler # Приоритеты для workload'ов: # client — пользовательские операции # recovery — восстановление данных # scrub — фоновая проверка # Для HDD-кластера снижаем агрессивность recovery ceph config set osd osd_mclock_scheduler_client_res 1 ceph config set osd osd_mclock_scheduler_recovery_res 1 ceph config set osd osd_mclock_scheduler_scrub_res 1 В Tentacle добавили защиту от нереалистичных значений IOPS capacity для mclock — теперь если измеренное значение IOPS слишком низкое (< 50 для HDD, < 1000 для SSD), планировщик использует последнее валидное значение. Оптимизация для конкретных workload'овДля виртуальных машин (много случайных мелких I/O): # Увеличиваем число OSD threads ceph config set osd osd_op_num_shards 8 ceph config set osd osd_op_num_threads_per_shard 2 # Write pipeline ceph config set osd bluestore_throttle_bytes 67108864 # 64 MB ceph config set osd bluestore_throttle_deferred_bytes 134217728 # 128 MB # Для NVMe — отключаем оверхед на большие буферы ceph config set osd bluestore_max_blob_size_ssd 65536 Для больших последовательных записей (S3, медиа): # Увеличиваем объект для EC ceph config set osd osd_max_write_size 512 # MB # RGW chunk size ceph config set client rgw_obj_stripe_size 8388608 # 8 MB Для read-heavy workload'ов: # Увеличиваем BlueStore cache для чтения ceph config set osd bluestore_cache_meta_ratio 0.4 # 40% для метаданных ceph config set osd bluestore_cache_kv_ratio 0.4 # 40% для RocksDB # Readahead на уровне BlueStore ceph config set osd bluestore_default_buffered_read true Настройка RBD для Kubernetes / Proxmox# Включаем RBD кеширование на стороне клиента cat >> /etc/ceph/ceph.conf << 'EOF' [client] rbd cache = true rbd cache size = 134217728 # 128 MB rbd cache max dirty = 100663296 # 96 MB rbd cache target dirty = 67108864 # 64 MB rbd cache max dirty age = 5.0 rbd cache writethrough until flush = true EOF # Для diskless систем — через librbd rbd config image set vmpool/myvm-disk01 rbd_cache true rbd config image set vmpool/myvm-disk01 rbd_cache_size 134217728 Часть 3: FastEC — как правильно использоватьFastEC — главная фича Tentacle для EC пулов. Разберём как мигрировать и что получаем. Создаём новый EC пул с FastEC# Профиль с ISA-L (новый дефолт в Tentacle) ceph osd erasure-code-profile set fastec-profile \ k=4 m=2 \ plugin=isa \ technique=reed_sol_van \ crush-failure-domain=host # Создаём пул ceph osd pool create fastec-pool 64 64 erasure fastec-profile # Включаем FastEC оптимизации ceph osd pool set fastec-pool allow_ec_optimizations true # Проверяем что включилось ceph osd pool get fastec-pool allow_ec_optimizations Миграция существующего EC пулаЕсли у вас был EC пул с Jerasure — миграция возможна без пересоздания данных: # Обновляем OSD и MON до Tentacle (см. раздел Upgrade) # После апгрейда — включаем оптимизации ceph osd pool set oldpool allow_ec_optimizations true # Следим за состоянием пула во время активации watch ceph pg stat Бенчмарк: насколько быстрее FastEC?Сравниваем производительность: # Устанавливаем инструменты apt install ceph-common # Тест записи в EC пул с FastEC rados bench -p fastec-pool 60 write --no-cleanup # Тест чтения rados bench -p fastec-pool 60 seq # Сравниваем с репликацией rados bench -p vmpool 60 write --no-cleanup rados bench -p vmpool 60 seq # Очищаем после теста rados bench -p fastec-pool 60 cleanup rados bench -p vmpool 60 cleanup По данным разработчиков и независимым тестам (blog nuvotex.de, 42on.com): FastEC при workload'е с преобладанием чтения и объектами среднего размера (1-4 MB) может превысить производительность репликации 3x при вдвое меньшем расходе места. Часть 4: Апгрейд с Squid (19.x) до Tentacle (20.x)Подготовка к апгрейдуЭто самый важный раздел. Апгрейд Ceph — процедура, требующая внимания. # 1. Проверяем здоровье — ОБЯЗАТЕЛЬНО перед началом ceph status ceph health detail # Кластер ДОЛЖЕН быть в HEALTH_OK или HEALTH_WARN (без critical) # НЕ начинайте при: OSD down, degraded PGs, incomplete PGs # 2. Проверяем версии клиентов ceph features # показывает connected clients и их версии # 3. Делаем снэпшот всех RBD образов (опционально, но разумно) for pool in $(ceph osd pool ls); do for image in $(rbd ls $pool 2>/dev/null); do rbd snap create $pool/$image@pre-upgrade-$(date +%Y%m%d) done done # 4. Отключаем PG autoscaler на время апгрейда ceph osd pool set noautoscale # 5. Устанавливаем noout флаг (предотвращает rebalancing при рестарте OSD) ceph osd set noout Апгрейд через cephadm (рекомендуется)# Запускаем апгрейд — cephadm делает всё сам, rolling update ceph orch upgrade start --image quay.io/ceph/ceph:v20.2.0 # Мониторим прогресс ceph orch upgrade status # Детальный лог ceph -W cephadm # В реальном времени watch ceph versions Cephadm обновляет в правильном порядке: MGR (сначала standby, потом active) MON (по одному, ждёт quorum) OSD (по одному, ждёт чистых PG после каждого) MDS, RGW, другие сервисы Вы можете поставить на паузу и возобновить: ceph orch upgrade pause ceph orch upgrade resume Апгрейд вручную (для не-cephadm кластеров)# Порядок строго важен! # 1. MON for mon_host in ceph-node1 ceph-node2 ceph-node3; do echo "Upgrading MON on $mon_host" ssh root@$mon_host "apt update && apt install -y ceph-mon" ssh root@$mon_host "systemctl restart ceph-mon.target" # Ждём возврата quorum sleep 30 ceph mon stat done # Проверяем что все MON обновились ceph mon dump | grep min_mon_release # Должно показать: min_mon_release 20 (tentacle) # 2. MGR for mgr_host in ceph-node1 ceph-node2; do ssh root@$mgr_host "apt install -y ceph-mgr" ssh root@$mgr_host "systemctl restart ceph-mgr.target" sleep 10 done # 3. OSD (по одному за раз!) for osd_id in $(ceph osd ls); do osd_host=$(ceph osd find $osd_id | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['crush_location']['host'])") echo "Upgrading OSD.$osd_id on $osd_host" # Устанавливаем новый пакет ssh root@$osd_host "apt install -y ceph-osd" # Рестартуем OSD ssh root@$osd_host "systemctl restart ceph-osd@$osd_id" # Ждём пока OSD поднимется sleep 30 # Проверяем что OSD up и PGs чистые while ceph pg stat | grep -q "degraded\|recovering"; do echo "Waiting for PGs to recover..." sleep 30 done echo "OSD.$osd_id upgraded successfully" done # 4. После всех OSD — финализация ceph osd require-osd-release tentacle Финализация апгрейда# Снимаем noout ceph osd unset noout # Включаем PG autoscaler обратно ceph osd pool unset noautoscale # Проверяем что все демоны на новой версии ceph versions # Убеждаемся что все фичи Tentacle включены ceph osd dump | grep require_osd_release # Включаем новые возможности Tentacle ceph osd pool set mypool allow_ec_optimizations true # если EC пул Часть 5: Disaster Recovery — что делать когда всё плохоСценарий 1: OSD упал# Смотрим что произошло ceph health detail ceph osd tree | grep -i down # Оценка: сколько времени OSD уже down? ceph osd info osd.5 | grep "last_clean_epoch" # Быстрый рестарт (если проблема временная) systemctl restart ceph-osd@5 # или через cephadm: ceph orch daemon restart osd.5 # Если OSD не стартует — смотрим логи journalctl -u ceph-osd@5 -n 100 --no-pager # OSD сломан физически — нужно заменить # Помечаем как out (начнётся rebalancing) ceph osd out osd.5 # Ждём завершения rebalancing watch ceph pg stat # ждём active+clean # Удаляем из кластера ceph osd purge osd.5 --yes-i-really-mean-it # Меняем диск, зачищаем и добавляем обратно ceph orch daemon add osd ceph-node2:/dev/sdc Сценарий 2: Целый хост упал# Если хост не вернётся — убираем его OSD # Для примера: умер ceph-node2 с OSD 3,4,5 # Помечаем все OSD хоста как out ceph osd host-down-out ceph-node2 # если есть команда # или вручную: for osd in 3 4 5; do ceph osd out osd.$osd; done # После rebalancing — удаляем for osd in 3 4 5; do ceph osd purge osd.$osd --yes-i-really-mean-it done # Удаляем MON если он был на этом хосте ceph mon remove ceph-node2 # Удаляем хост из оркестратора ceph orch host drain ceph-node2 ceph orch host rm ceph-node2 --force # Проверяем здоровье после ceph status Сценарий 3: PG застряла в inconsistent/corrupt# Находим проблемные PG ceph pg dump | grep -v "active+clean" # Запускаем repair ceph pg repair 3.1a # Если repair не помогает — более агрессивно ceph osd set nodeep-scrub # временно отключаем deep-scrub # Смотрим детали PG ceph pg 3.1a query # OSD с повреждёнными данными ceph osd tree ceph pg 3.1a get # какие OSD участвуют # Принудительное восстановление из другой реплики # (осторожно! только если уверены что данные на primary повреждены) ceph pg force-recovery 3.1a Сценарий 4: MON потерял quorum# Проверяем статус MON ceph mon stat ceph mon dump # Если 1 из 3 MON не отвечает — quorum ещё есть (2 из 3) # Рестартуем проблемный systemctl restart ceph-mon@ceph-node2 # Если quorum потерян (0 из 3 доступны) — режим аварийного восстановления # Это серьёзная ситуация # На одном живом MON: ceph-mon -i ceph-node1 --extract-monmap /tmp/monmap monmaptool --print /tmp/monmap # Удаляем недостижимые MON из карты monmaptool --rm ceph-node2 /tmp/monmap monmaptool --rm ceph-node3 /tmp/monmap # Инжектируем исправленную monmap ceph-mon -i ceph-node1 --inject-monmap /tmp/monmap # Запускаем с одним MON ceph-mon -i ceph-node1 # Добавляем новые MON после стабилизации ceph orch apply mon ceph-node1,ceph-node2,ceph-node3 Сценарий 5: Восстановление удалённого RBD образа# Если образ удалён — проверяем trash rbd trash ls vmpool # Восстанавливаем из trash (образы там держатся delay_seconds) rbd trash restore vmpool/trash-id # Если включён rbd-mirror с журналированием — восстановление из журнала # В крайнем случае — восстановление из снэпшота rbd snap ls vmpool/myvm-disk01 rbd snap rollback vmpool/myvm-disk01@pre-upgrade-20241201 # Восстановление из бэкапа через export rbd export vmpool/myvm-disk01 /mnt/backup/myvm-disk01.raw # Восстановление: rbd import /mnt/backup/myvm-disk01.raw vmpool/myvm-disk01-restored Часть 6: Продвинутые возможности TentacleSMB shares из CephFS# Создаём SMB кластер (Active Directory интеграция) ceph smb cluster create mysmb \ active-directory \ --domain DC=corp,DC=example,DC=com \ --realm CORP.EXAMPLE.COM \ --dns-server 192.168.1.10 # Добавляем CephFS share ceph smb share create mysmb myshare \ --cephfs-volume myfs \ --cephfs-path /shares/myshare # Проверяем ceph smb cluster ls ceph smb share ls # Через Dashboard — аналогично с GUI RBD Live Migration — новинка TentacleМгновенный импорт образов из других кластеров без копирования данных: # Импорт из другого Ceph кластера (native format) rbd migration prepare \ --source-spec '{"type":"native","cluster_name":"src-cluster","pool_name":"vmpool","image_name":"myvm"}' \ dstpool/myvm-imported # Импорт через NBD (из любого источника) rbd migration prepare \ --source-spec '{"type":"nbd","uri":"nbd://192.168.1.100:10809/disk"}' \ dstpool/imported-disk # Запускаем миграцию (фоновая копия данных) rbd migration execute dstpool/myvm-imported # Когда завершится — фиксируем rbd migration commit dstpool/myvm-imported Магия в том, что образ доступен для чтения и записи немедленно — пока данные копируются в фоне, читаются напрямую с источника. Data Availability Score — новый инструмент мониторинга# Включаем tracking ceph config set global enable_availability_tracking true # Проверяем score для каждого пула ceph osd pool availability-status # Вывод: # POOL AVAILABLE SCORE # vmpool yes 1.00 # ecpool yes 0.99 ← одна PG в не-clean состоянии # Очищаем статус для пула после устранения проблемы ceph osd pool clear-availability-status vmpool Scrub: планирование глубоких проверок# Принудительный scrub для конкретной PG ceph pg scrub 1.a3 ceph pg deep-scrub 1.a3 # Scrub всего пула ceph osd pool scrub vmpool # Планирование — ограничиваем scrub нерабочим временем ceph config set osd osd_scrub_begin_hour 1 # с 1:00 ceph config set osd osd_scrub_end_hour 6 # до 6:00 ceph config set osd osd_scrub_min_interval 86400 # не чаще раза в день ceph config set osd osd_deep_scrub_interval 604800 # deep-scrub раз в неделю # Статус scrub ceph pg dump | awk '{print $1, $16, $17}' | head -30 # PG_ID | LAST_SCRUB | LAST_DEEP_SCRUB Часть 7: Capacity planning и масштабированиеДобавление нового хоста и OSD# Добавляем хост ceph orch host add ceph-node4 192.168.10.14 # Добавляем OSD ceph orch daemon add osd ceph-node4:/dev/sdb ceph orch daemon add osd ceph-node4:/dev/sdc # Автоматическая ребалансировка начнётся сразу # Следим за прогрессом ceph progress watch ceph df Расчёт сырого хранилищаПолезное место = (Общий объём) / overhead_factor Репликация 3x: overhead = 3.0 EC 4+2: overhead = 1.5 EC 6+3: overhead = 1.5 EC 8+3: overhead = 1.375 Для кластера с 9 × 4TB HDD и репликацией 3x: - Сырое: 36 TB - Полезное: 36 / 3 = 12 TB (минус ~10% overhead Ceph = ~10.8 TB) Правило большого пальца: не заполняйте более 80% полезного места! При 80%+ производительность падает из-за фрагментации и задержек recovery. Мониторинг через Prometheus# ceph exporter уже встроен, prometheus конечные точки: # http://ceph-node1:9283/metrics - MGR prometheus module # Ключевые метрики для алертов: # ceph_health_status != 0 — нездоровый кластер # ceph_osd_in == 0 — OSD out # ceph_pg_degraded > 0 — деградированные PG # ceph_osd_available_bytes < 20% — заканчивается место # ceph_osd_apply_latency_ms > 50 — высокая задержка записи # Пример alertmanager rule: cat >> /etc/prometheus/rules/ceph.yml << 'EOF' groups: - name: ceph rules: - alert: CephHealthError expr: ceph_health_status == 2 for: 1m labels: severity: critical annotations: summary: "Ceph cluster is in ERROR state" - alert: CephOSDDown expr: ceph_osd_up == 0 for: 2m labels: severity: warning - alert: CephDiskAlmostFull expr: (ceph_osd_stat_bytes_used / ceph_osd_stat_bytes) > 0.80 for: 5m labels: severity: warning annotations: summary: "Ceph OSD {{ $labels.ceph_daemon }} is {{ $value | humanizePercentage }} full" EOF Чеклист: Ceph в продакшнеДо разворачивания: [ ] Минимум 3 физических хоста (лучше 5+ для отказоустойчивости) [ ] Отдельные сети для публичного и кластерного трафика (10 GbE+) [ ] NTP синхронизирован на всех узлах [ ] SSD/NVMe для BlueStore DB и WAL [ ] Резервные диски наготове для горячей замены [ ] CRUSH map настроен с учётом физической топологии (стойки, ЦОД) Оперативный мониторинг: [ ] Prometheus + Grafana с Ceph дашбордами [ ] Алерты на HEALTH_ERR, OSD down, PG degraded, диск >80% [ ] mgmt-gateway настроен (Tentacle 20.x) [ ] certmgr управляет TLS сертификатами Регулярные процедуры: [ ] Ежедневная проверка ceph status [ ] Еженедельный deep-scrub (автоматически через cron) [ ] Тестирование восстановления из снэпшотов раз в квартал [ ] Обновления безопасности: следим за ceph-announce Перед апгрейдом: [ ] Кластер в HEALTH_OK [ ] Снэпшоты критичных RBD образов [ ] ceph osd set noout [ ] Тест в staging среде [ ] Откат-план: как вернуться на предыдущую версию (downgrade невозможен, нужен rollback через снэпшоты) Где учиться дальшеОфициальная документация: docs.ceph.com — эталонная документация, всегда актуальная ceph.io/en/news/blog — официальный блог с release notes и углублёнными техническими статьями Сообщество: ceph-users@ceph.io — рассылка для пользователей irc.oftc.net #ceph — IRC канал Cephalocon — ежегодная конференция сообщества Практика: Vagrant + VirtualBox: поднимите тестовый кластер на ноутбуке (cephadm работает в VM) Rook — Ceph оператор для Kubernetes, хороший способ изучить интеграцию Proxmox VE имеет встроенный Ceph — отличная песочница Ceph — это не инструмент «поставил и забыл». Это живая система, требующая понимания и регулярного внимания. Но когда вы научитесь с ней работать — получаете petabyte-scale хранилище корпоративного уровня на обычном commodity железе. Это стоит вложенных усилий.
  3. В прошлой статье мы разобрались с теорией — теперь руки в землю. Будем разворачивать минимальный продакшн-кластер Ceph Tentacle (20.2.x) через cephadm — официальный инструмент оркестровки, который умеет всё: установку, конфигурирование, обновление, добавление узлов. Что мы будем строитьМинимальная продакшн-конфигурация: ┌─────────────────────────────────────────────────────┐ │ ceph-node1 │ ceph-node2 │ ceph-node3 │ │ │ │ │ │ MON + MGR │ MON + MGR │ MON │ │ OSD.0 │ OSD.3 │ OSD.6 │ │ OSD.1 │ OSD.4 │ OSD.7 │ │ OSD.2 │ OSD.5 │ OSD.8 │ │ │ │ │ │ /dev/sdb │ /dev/sdb │ /dev/sdb │ │ /dev/sdc │ /dev/sdc │ /dev/sdc │ │ /dev/sdd │ /dev/sdd │ /dev/sdd │ └─────────────────────────────────────────────────────┘ Требования к каждому узлу: OS: Ubuntu 22.04 LTS или Debian 12 (рекомендуется), RHEL 9 тоже ок RAM: минимум 16 GB (рекомендуется 32+ GB для продакшна) CPU: 4+ ядра Сеть: минимум 1 GbE, лучше 10 GbE; отдельная сеть для репликации — хорошая идея Диски: минимум 1 диск для OSD (не системный!), лучше SSD или NVMe Важно: диски для OSD должны быть пустыми — без разделов, без файловых систем. BlueStore сам их форматирует. Шаг 1: Подготовка всех узловВыполняем на каждом из трёх узлов. Обновление системы и базовые пакетыapt update && apt upgrade -y apt install -y \ chrony \ curl \ python3 \ python3-pip \ lvm2 \ podman \ # или docker ntp Почему chrony важен: Ceph очень чувствителен к рассинхронизации времени. Разница > 5 секунд между узлами вызывает предупреждения и может дестабилизировать кластер. Убедитесь что NTP работает: timedatectl status chronyc tracking Настройка hostname и /etc/hosts# На ceph-node1: hostnamectl set-hostname ceph-node1 # На всех трёх узлах добавляем в /etc/hosts: cat >> /etc/hosts << 'EOF' 192.168.10.11 ceph-node1 192.168.10.12 ceph-node2 192.168.10.13 ceph-node3 EOF SSH ключи: cephadm общается через SSHГенерируем ключ на первом узле (bootstrap узел) и распространяем: # На ceph-node1: ssh-keygen -t ed25519 -N "" -f /root/.ssh/id_ed25519 # Копируем на все узлы (включая node1 самого себя): for node in ceph-node1 ceph-node2 ceph-node3; do ssh-copy-id -i /root/.ssh/id_ed25519.pub root@$node done # Проверяем: for node in ceph-node1 ceph-node2 ceph-node3; do echo "=== $node ===" ssh root@$node "hostname && uname -r" done Подготовка дисков: убеждаемся что они чистые# Проверяем состояние дисков lsblk fdisk -l /dev/sdb wipefs -a /dev/sdb # если нужно очистить # cephadm сам зачистит диски при добавлении — если они "чистые" # (без LVM, без партиций, без файловой системы) # Принудительно зачистить: ceph-volume lvm zap /dev/sdb --destroy # после установки ceph Шаг 2: Bootstrap первого узлаУстанавливаем cephadm# На ceph-node1: curl --silent --remote-name --location \ https://github.com/ceph/ceph/raw/reef/src/cephadm/cephadm chmod +x cephadm # Устанавливаем в систему ./cephadm install # Добавляем репозиторий Tentacle cephadm add-repo --release tentacle # Устанавливаем ceph-common (для команды ceph) cephadm install ceph-common Bootstrap кластераcephadm bootstrap \ --mon-ip 192.168.10.11 \ --cluster-network 192.168.20.0/24 \ --initial-dashboard-user admin \ --initial-dashboard-password 'YourStrongPassword!123' \ --allow-fqdn-hostname \ --skip-monitoring-stack # добавим мониторинг позже отдельно Что делает эта команда за кулисами: Создаёт директории конфигурации /etc/ceph/ Генерирует ключи аутентификации Поднимает первый MON в контейнере Поднимает MGR Активирует модуль Dashboard Пишет /etc/ceph/ceph.conf и /etc/ceph/ceph.client.admin.keyring После успешного выполнения вы увидите URL дашборда: Ceph Dashboard is now available at: URL: https://ceph-node1:8443/ User: admin Password: YourStrongPassword!123 Параметр --cluster-network: Это сеть для трафика репликации между OSD. Если у вас только одна сеть — уберите этот параметр. Но если есть выделенная сеть — обязательно используйте её, это критично для производительности публичной сети. Шаг 3: Добавляем узлы в кластерПроверяем первый узелceph status # Должны увидеть: mon: 1 mons at quorum... # health: HEALTH_WARN (это нормально на старте) ceph orch status # Оркестратор должен быть активен Добавляем ceph-node2 и ceph-node3# На ceph-node1 — добавляем public SSH ключ cephadm в авторизованные на узлах ceph cephadm get-pub-key > /tmp/ceph.pub ssh root@ceph-node2 "mkdir -p /root/.ssh && \ cat >> /root/.ssh/authorized_keys" < /tmp/ceph.pub ssh root@ceph-node3 "mkdir -p /root/.ssh && \ cat >> /root/.ssh/authorized_keys" < /tmp/ceph.pub # Добавляем хосты в кластер ceph orch host add ceph-node2 192.168.10.12 ceph orch host add ceph-node3 192.168.10.13 # Проверяем ceph orch host ls Шаг 4: Добавляем MON и MGR# По умолчанию cephadm хочет 5 MON — для нас 3 достаточно ceph orch apply mon 3 # Проверяем что MON есть на всех трёх узлах ceph orch ps --daemon-type mon # Добавляем второй MGR (для failover) ceph orch apply mgr 2 Ждём пока cephadm автоматически запустит MON на node2 и node3. Следим: watch ceph status # Ждём: mon: 3 mons at quorum ceph-node1,ceph-node2,ceph-node3 Шаг 5: Добавляем OSD — сердце кластераИнвентаризация доступных дисков# Смотрим что cephadm видит на всех узлах ceph orch device ls # Вывод покажет диски и их статус: # HOST PATH TYPE SIZE AVAILABLE REFRESHED # ceph-node1 /dev/sdb hdd 2TiB Yes 12s ago # ceph-node1 /dev/sdc hdd 2TiB Yes 12s ago # ... Диск помечен как AVAILABLE если он полностью пустой. Если нет — смотрим причину в колонке REJECT REASONS. Автоматическое добавление всех доступных дисков# Самый простой способ — использовать все доступные диски ceph orch apply osd --all-available-devices # Следим за прогрессом watch ceph osd tree Ручное добавление конкретных дисков (рекомендуется для продакшна)# Добавляем по одному — больше контроля ceph orch daemon add osd ceph-node1:/dev/sdb ceph orch daemon add osd ceph-node1:/dev/sdc ceph orch daemon add osd ceph-node1:/dev/sdd ceph orch daemon add osd ceph-node2:/dev/sdb ceph orch daemon add osd ceph-node2:/dev/sdc ceph orch daemon add osd ceph-node2:/dev/sdd ceph orch daemon add osd ceph-node3:/dev/sdb ceph orch daemon add osd ceph-node3:/dev/sdc ceph orch daemon add osd ceph-node3:/dev/sdd OSD Service Spec для воспроизводимой конфигурацииДля инфраструктуры-как-код создаём spec-файл: # osd-spec.yaml service_type: osd service_id: default placement: host_pattern: 'ceph-node*' data_devices: paths: - /dev/sdb - /dev/sdc - /dev/sdd # Если есть отдельные SSD для WAL/DB: # db_devices: # paths: # - /dev/nvme0n1 # wal_devices: # paths: # - /dev/nvme1n1 ceph orch apply -i osd-spec.yaml Шаг 6: Проверяем здоровье кластераПосле добавления OSD кластер начнёт балансировку данных (backfill). Это нормально и займёт время. Следим: # Общий статус ceph status # Подробный статус OSD ceph osd stat ceph osd df # использование дискового пространства # Статус PG ceph pg stat # Потребление ресурсов ceph df detail # Дерево OSD с весами ceph osd tree Хорошее состояние: cluster: id: a7f64266-0894-4f1e-a635-d0aeaca0e993 health: HEALTH_OK services: mon: 3 daemons, quorum ceph-node1,ceph-node2,ceph-node3 mgr: ceph-node1.xxx(active), ceph-node2.xxx(standby) osd: 9 osds: 9 up (since 5m), 9 in (since 5m) data: pools: 1 pools, 1 pgs objects: 0 objects, 0 B usage: 450 MiB used, 54 TiB / 54 TiB avail pgs: 1 active+clean Шаг 7: Создаём пулы храненияПул — логический контейнер для данных. Каждый пул имеет свою политику репликации/EC, количество PG и другие параметры. Пул с репликацией 3x (для VM, баз данных)# Создаём пул ceph osd pool create vmpool 32 32 # 32 PG # Настраиваем репликацию ceph osd pool set vmpool size 3 # 3 копии ceph osd pool set vmpool min_size 2 # минимум 2 для записи # Тип пула - для RBD ceph osd pool application enable vmpool rbd # Инициализируем для RBD rbd pool init vmpool # Проверяем ceph osd pool ls detail Сколько PG нужно?Формула: PG = (OSDs * 100) / pool_size Для нашего кластера (9 OSD, репликация 3): PG = (9 * 100) / 3 = 300 — но возьмём ближайшую степень 2 = 256 # Изменить количество PG (только увеличение) ceph osd pool set vmpool pg_num 64 ceph osd pool set vmpool pgp_num 64 С Ceph Luminous появился PG autoscaler — он сам подбирает оптимальное число PG: # Включаем автоскалер для пула ceph osd pool set vmpool pg_autoscale_mode on # Глобально включить автоскалер ceph mgr module enable pg_autoscaler ceph config set global osd_pool_default_pg_autoscale_mode on Пул с Erasure Coding (для S3/бэкапов)# Создаём EC-профиль # k=4 data chunks, m=2 parity chunks = 6 OSD минимум # overhead = 1.5x против 3x у репликации ceph osd erasure-code-profile set myec \ k=4 m=2 \ plugin=isa \ crush-failure-domain=host # Просматриваем профиль ceph osd erasure-code-profile get myec # Создаём пул с EC ceph osd pool create ecpool 32 32 erasure myec # Включаем FastEC оптимизации (Tentacle 20.x+) ceph osd pool set ecpool allow_ec_optimizations true # Для работы RGW с EC нужен overlay pool ceph osd pool create ecpool-index 16 # репликация для индексов ceph osd pool application enable ecpool rgw ceph osd pool application enable ecpool-index rgw Шаг 8: Подключаем RBD — блочное хранилищеСоздаём RBD-образ# Создаём образ диска 100 GB rbd create --size 102400 vmpool/myvm-disk01 # Смотрим информацию rbd info vmpool/myvm-disk01 # Листинг образов в пуле rbd ls vmpool # Размер всех образов rbd du vmpool Монтируем на Linux через kernel driver# Маппируем образ как блочное устройство rbd device map vmpool/myvm-disk01 --id admin \ --keyring /etc/ceph/ceph.client.admin.keyring # Видим устройство rbd device list # /dev/rbd0 → vmpool/myvm-disk01 # Форматируем и монтируем mkfs.xfs /dev/rbd0 mkdir /mnt/rbd-data mount /dev/rbd0 /mnt/rbd-data # Авто-монтирование через /etc/fstab через rbdmap # /etc/ceph/rbdmap: # vmpool/myvm-disk01 id=admin,keyring=/etc/ceph/ceph.client.admin.keyring systemctl enable rbdmap RBD для Proxmox VEProxmox имеет встроенную поддержку Ceph. Добавляем через GUI или: # На Proxmox хосте устанавливаем ceph-клиент apt install ceph-common # Копируем конфиг и ключ с Ceph кластера scp root@ceph-node1:/etc/ceph/ceph.conf /etc/pve/ceph.conf scp root@ceph-node1:/etc/ceph/ceph.client.admin.keyring \ /etc/pve/priv/ceph/ # Или создаём отдельного пользователя с ограниченными правами ceph auth get-or-create client.proxmox \ mon 'profile rbd' \ osd 'profile rbd pool=vmpool' \ mgr 'profile rbd pool=vmpool' \ > /tmp/ceph.client.proxmox.keyring # Добавляем Ceph storage в Proxmox pveceph pool create vmpool --pg_num 64 --pg_autoscale_mode on Шаг 9: CephFS — распределённая файловая система# Создаём CephFS (автоматически создаёт metadata и data пулы) ceph fs volume create myfs --placement="3" # Проверяем статус MDS ceph mds stat ceph fs status myfs # Монтируем через FUSE (для тестов) apt install ceph-fuse mkdir /mnt/cephfs ceph-fuse /mnt/cephfs -m ceph-node1:6789 # Или через kernel driver (лучше производительность) # Получаем ключ ceph auth get-key client.admin | base64 # Монтируем: mount -t ceph ceph-node1:6789:/ /mnt/cephfs \ -o name=admin,secret=<base64-key> # В /etc/fstab: # ceph-node1:6789,ceph-node2:6789:/ /mnt/cephfs ceph \ # name=admin,secretfile=/etc/ceph/admin.secret,noatime 0 0 Subvolumes для Kubernetes# Создаём subvolume group ceph fs subvolumegroup create myfs k8s # Создаём subvolume (persistent volume) ceph fs subvolume create myfs pvc-001 --group-name k8s --size 10G # Получаем путь ceph fs subvolume getpath myfs pvc-001 --group-name k8s # /volumes/k8s/pvc-001/... Шаг 10: RGW — S3-совместимое объектное хранилище# Разворачиваем RGW через cephadm ceph orch apply rgw myrgw --placement="2 ceph-node1 ceph-node2" \ --port=8080 # Проверяем статус ceph orch ps --daemon-type rgw # Создаём пользователя radosgw-admin user create \ --uid=s3user \ --display-name="S3 User" \ --email=s3user@example.com # Получаем ключи radosgw-admin user info --uid=s3user # access_key и secret_key для S3 клиентов # Тестируем через s3cmd или mc (MinIO client) apt install s3cmd s3cmd --configure # вводим access_key, secret_key, endpoint # Или через AWS CLI aws configure # вводим ключи aws --endpoint-url http://ceph-node1:8080 s3 mb s3://mybucket aws --endpoint-url http://ceph-node1:8080 s3 ls aws --endpoint-url http://ceph-node1:8080 s3 cp /tmp/test.txt s3://mybucket/ Шаг 11: Стек мониторингаCeph Tentacle поставляет готовый стек мониторинга через cephadm. В Tentacle появился новый mgmt-gateway — единая точка входа: # Разворачиваем полный стек мониторинга ceph orch apply prometheus ceph orch apply grafana ceph orch apply alertmanager ceph orch apply node-exporter # Новый в Tentacle: mgmt-gateway (nginx reverse proxy + TLS) cat > mgmt-gateway.yaml << 'EOF' service_type: mgmt-gateway placement: count: 2 # HA — два инстанса spec: port: 443 enable_auth: true # требовать аутентификацию EOF ceph orch apply -i mgmt-gateway.yaml # Проверяем ceph orch ps --daemon-type mgmt-gateway Теперь Dashboard, Grafana, Prometheus — всё доступно через один HTTPS endpoint на порту 443. Встроенные Grafana-дашборды Ceph показывают: OSD latency и throughput Pool utilization MON quorum status PG состояния Алерты Полезные команды для ежедневной работы# === КЛАСТЕР === ceph status # общий статус ceph health detail # детали о проблемах ceph df # использование пространства ceph versions # версии всех демонов # === OSD === ceph osd tree # топология ceph osd df # место по OSD ceph osd perf # latency метрики ceph osd dump # полный дамп карты OSD # Вывести из эксплуатации OSD (graceful) ceph osd out osd.5 ceph osd drain osd.5 # ждём пока PG переедут # === PG === ceph pg stat # статус всех PG ceph pg dump | grep -v active+clean # проблемные PG ceph pg repair 1.a3 # принудительный repair конкретной PG # === Логи === ceph log last 20 # последние записи кластерного лога journalctl -u ceph-osd@0 -f # лог конкретного OSD # === Оркестратор === ceph orch ls # список сервисов ceph orch ps # список демонов с состоянием ceph orch events # события оркестратора Типичные проблемы при развёртыванииHEALTH_WARN: too few PGsceph health detail # HEALTH_WARN too few PGs per OSD # Увеличиваем PG для затронутых пулов ceph osd pool set vmpool pg_num 64 ceph osd pool set vmpool pgp_num 64 OSD не добавляется: диск не определяется как доступный# Смотрим причины отказа ceph orch device ls --wide # Часто причина: старые сигнатуры на диске # Зачищаем через ceph-volume cephadm shell -- ceph-volume lvm zap /dev/sdb --destroy # Или более агрессивно wipefs -a /dev/sdb dd if=/dev/zero of=/dev/sdb bs=4M count=10 clock skew detected# Проверяем время на всех узлах for node in ceph-node1 ceph-node2 ceph-node3; do echo "$node: $(ssh root@$node date)" done # На проблемном узле — синхронизируем немедленно chronyc makestep timedatectl set-ntp true Кластер застрял в rebalancing надолго# Смотрим прогресс ceph progress # Ускоряем (только во время обслуживания, не в бою) ceph tell osd.* injectargs --osd-max-backfills 8 ceph tell osd.* injectargs --osd-recovery-max-active 8 # После — возвращаем в норму ceph tell osd.* injectargs --osd-max-backfills 3 В следующей, финальной статье: производительность и тюнинг кластера, стратегии апгрейда с предыдущих версий, disaster recovery и продвинутые сценарии использования. Далее читай - Часть #3
  4. Почему Ceph, а не просто NAS или SAN?Представьте ситуацию: у вас 50 серверов, каждый с несколькими терабайтами данных, виртуальные машины, S3-хранилище для бэкапов, общий файловый ресурс для кластера Kubernetes — и всё это нужно хранить надёжно, быстро и так, чтобы смерть одного (или нескольких) серверов не привела к потере данных и даунтайму. Традиционные решения здесь ломаются. NAS — единая точка отказа. SAN — дорого, сложно, проприетарно. RAID — не масштабируется за пределы одной машины. Ceph решает эту задачу радикально иначе: он распределяет данные по всем дискам всех серверов одновременно, и любой узел может умереть прямо сейчас, пока вы это читаете, — вы ничего не потеряете. Ceph используют CERN (те самые, что ищут бозон Хиггса), крупнейшие облачные провайдеры, Proxmox, OpenStack — в общем, люди, которым нельзя терять данные. Давайте разберёмся, как это устроено. Три уровня хранения в одном кластереCeph — это не одна технология, это три совершенно разных интерфейса хранения, построенных поверх одного движка: ┌─────────────────────────────────────────────────┐ │ Приложения и клиенты │ ├──────────────┬──────────────┬───────────────────┤ │ RBD │ CephFS │ RGW (S3/Swift) │ │ Блочное │ Файловая │ Объектное │ │ хранилище │ система │ хранилище │ ├──────────────┴──────────────┴───────────────────┤ │ RADOS │ │ (Reliable Autonomic Distributed │ │ Object Store) │ ├─────────────────────────────────────────────────┤ │ OSD OSD OSD OSD OSD OSD OSD │ │ (физические диски/SSD/NVMe) │ └─────────────────────────────────────────────────┘ RBD (RADOS Block Device) — виртуальный блочный диск. С точки зрения виртуальной машины или Kubernetes pod — это просто диск. Внутри он разбит на объекты по 4 МБ (по умолчанию) и размазан по всему кластеру. Размер — до 16 эксабайт. CephFS — POSIX-совместимая распределённая файловая система. Монтируется как обычная папка, понимает права доступа, символические ссылки, всё как у людей. Метаданные хранит отдельно от данных через специальный демон MDS. RGW (RADOS Gateway) — HTTP-интерфейс объектного хранилища, совместимый с Amazon S3 и OpenStack Swift. Загружаете файлы через API, получаете бакеты, версионирование, lifecycle-политики — всё как в S3. Самое красивое: всё три интерфейса используют один и тот же кластер RADOS. Вы можете одновременно монтировать CephFS на NFS-сервере, раздавать RBD-диски виртуалкам Proxmox и гонять бэкапы в RGW — и все они делят одни и те же физические диски. Архитектура: четыре типа демоновCeph-кластер — это набор демонов, каждый со своей ролью. Никаких монолитов, никакого единого «сервера хранилища». MON — Monitor (мозг кластера)MON1 MON2 MON3 \ | / \ | / кластерная карта (cluster map) Мониторы хранят карту кластера — полное описание топологии: какие OSD существуют, где они физически расположены, здоровы ли они. Это не данные, это метаданные. Мониторы работают по протоколу Paxos и требуют кворума: нужно нечётное число, минимум 3 в продакшне. Без кворума мониторов — нет записи (но чтение может работать). Мониторы не хранят пользовательские данные вообще — они лёгкие, их можно держать даже на небольших VM. OSD — Object Storage Daemon (мышцы кластера)Один OSD = один физический диск (или раздел). OSD хранит данные, обслуживает запросы чтения/записи, участвует в репликации, сам находит соседей для репликации по карте кластера. Типичный сервер в кластере: 12 дисков = 12 OSD-процессов + небольшой SSD для BlueStore WAL/DB. OSD общаются напрямую — без центрального сервера хранения. Если клиент пишет данные в pool с репликацией 3x, primary OSD сам синхронно реплицирует на двух соседей и только потом отвечает клиенту «записано». MDS — Metadata ServerНужен только для CephFS. Хранит иерархию директорий и метаданные файлов (права, размеры, время). Данные файлов хранятся в обычных RADOS-объектах — MDS только помогает по пути /my/dir/file.txt найти нужные объекты. Можно запустить несколько MDS для параллелизма — активный-активный режим (multi-MDS). MGR — ManagerМенеджер собирает статистику, запускает модули (dashboard, prometheus-экспортер, балансировщик), обрабатывает оркестровку через cephadm. Нужно минимум 2 для отказоустойчивости (один active, один standby). CRUSH: как Ceph решает, куда положить данныеВот где начинается самое интересное. В обычном RAID контроллер знает: «диск 1, 2, 3». В Ceph нет центрального индекса «где лежит файл» — это было бы узким местом в огромном кластере. Вместо этого используется алгоритм CRUSH (Controlled Replication Under Scalable Hashing). Зная только имя объекта и карту кластера, CRUSH детерминированно вычисляет, на каких OSD хранить данные — без запросов к какому-либо серверу метаданных. object "my_file_chunk_0042" │ ▼ pg_id = hash(object_name) % pg_count │ ▼ CRUSH(pg_id, crush_map) → [OSD.7, OSD.23, OSD.41] Когда приходит запрос «где лежит объект X» — любой клиент, зная карту кластера, сам вычисляет ответ и идёт напрямую к нужному OSD. Без промежуточных серверов. Это и есть причина масштабируемости. Placement Groups (PG): промежуточный уровеньОбъектов в кластере могут быть миллиарды. Если бы каждый объект CRUSH маппил напрямую на OSD — карта кластера была бы гигантской. Поэтому объекты сначала группируются в Placement Groups (PG), а уже PG маппятся на OSD. Объект → PG (группа объектов) → OSD Число PG на pool — важный параметр настройки. Слишком мало — неравномерное распределение, узкое место. Слишком много — накладные расходы. Золотое правило: ~100 PG на OSD в pool. CRUSH Map: физическая топологияCRUSH знает физику вашего датацентра: datacenter DC1 ├── rack Rack-A │ ├── host server-01 │ │ ├── osd.0 (weight 1.0) │ │ ├── osd.1 (weight 1.0) │ │ └── osd.2 (weight 1.0) │ └── host server-02 │ ├── osd.3 │ └── osd.4 └── rack Rack-B └── host server-03 ├── osd.5 └── osd.6 Правило репликации может звучать так: «три копии, каждая на отдельном rack'е». Тогда при смерти целого стойки ни одна PG не потеряет больше одной копии данных. BlueStore: почему Ceph не использует ext4 или XFSДо Ceph 12 OSD хранил данные на обычной файловой системе (FileStore). Это работало, но было медленно: каждая запись проходила через XFS/ext4 со всеми их накладными расходами, двойным кешированием, лишними syscall'ами. С Ceph 12 появился BlueStore — кастомный бэкенд хранения, который работает напрямую с блочным устройством, минуя файловую систему. FileStore официально удалён начиная с Reef (18.x). Архитектура BlueStoreOSD Process ├── BlueStore │ ├── RocksDB (метаданные объектов, omap) │ │ └── хранится на быстром SSD/NVMe (BlueFS) │ ├── WAL (write-ahead log) │ │ └── тоже лучше на SSD │ └── данные объектов │ └── на основном диске (HDD или SSD) └── BlueFS (микрофайловая система для RocksDB) В Tentacle (20.x) BlueStore получил улучшенное сжатие и новый, более быстрый WAL — это не маркетинг, а реальные измеримые улучшения для workload'ов с частой записью. Ключевые преимущества BlueStore: Полный контроль над I/O без лишних слоёв Атомарные транзакции без двойного буферирования Встроенное сжатие (zlib, snappy, zstd, lz4) Checksums для данных и метаданных (обнаружение битрот) Эффективный omap для небольших значений ключ-значение Репликация vs. Erasure Coding: выбираем стратегиюРепликация (Replication)Простейший вариант: каждый объект хранится в N копиях на N разных OSD. Запись "hello.txt": [OSD.5 — первичная копия] ├── реплицирует → [OSD.12 — копия 2] └── реплицирует → [OSD.31 — копия 3] Плюсы: простота, низкая latency, любой OSD может обслужить чтение. Минусы: 3x overhead по дисковому пространству. Для продакшна стандарт — size=3, min_size=2. Это значит: нормальный режим — 3 копии, деградированный (когда один OSD умер) — 2 копии, меньше 2 — запись заблокирована. Erasure Coding (EC)EC — это как RAID 5/6, но распределённый. Данные разбиваются на K кусков, добавляются M паритетных кусков. Всего K+M кусков на K+M OSD. Для восстановления нужно любые K из K+M кусков. Пример EC 4+2: chunk0 chunk1 chunk2 chunk3 | parity0 parity1 OSD.1 OSD.2 OSD.3 OSD.4 OSD.5 OSD.6 При смерти OSD.2 и OSD.5 — данные восстанавливаются из оставшихся 4 из 6. Плюсы: экономия места. EC 4+2 даёт overhead 1.5x против 3x для репликации. Минусы: сложнее, выше latency, CPU overhead на кодирование/декодирование. EC оптимально для холодного хранилища, S3-бэкапов, больших объектов. Для горячих IOPS-нагруженных данных (БД, VM) — репликация. FastEC в Tentacle: революция для Erasure CodingВ Ceph Tentacle (20.2.0) появилась долгожданная функция FastEC — принципиально новая реализация I/O для EC пулов с поддержкой partial reads и partial writes. До FastEC: запись небольшого объекта в EC-пул требовала читать все K кусков, обновлять данные, пересчитывать все паритеты и писать всё обратно. Это называется Read-Modify-Write (RMW) — катастрофа для производительности при мелких записях. FastEC оптимизирует именно этот случай. По словам разработчиков и независимым тестам, на определённых workload'ах FastEC обгоняет даже репликацию 3x по производительности — при вдвое меньшем расходе места. Важно: FastEC включается явно на уровне пула командой allow_ec_optimizations: ceph osd pool set mypool allow_ec_optimizations true Существующие пулы можно мигрировать без пересоздания данных — достаточно обновить OSD и MON до Tentacle. Что нового в Ceph Tentacle (20.2.0)Tentacle вышел 18 ноября 2025 года и является 20-м стабильным релизом Ceph. Это значительный релиз, не косметический. Вот главное: FastEC — новый движок Erasure CodingУже разобрали выше. Переключение плагина по умолчанию с устаревшего Jerasure на ISA-L (Intel ISA-L library) — более быстрый, активно поддерживаемый. Jerasure больше не обслуживается авторами. SMB-поддержка через CephCeph теперь умеет создавать SMB-шары прямо из кластера через новый модуль mgr. Технически это Samba поверх CephFS с автоматическим управлением через cephadm. Поддерживает Active Directory и standalone. Работает в кластерном режиме через CTDB. ceph smb cluster create mysmb active-directory DC=corp,DC=example,DC=com \ --domain-realm corp.example.com mgmt-gateway: единая точка входа для управленияНовый сервис mgmt-gateway — nginx reverse proxy с TLS, который объединяет Dashboard, Prometheus, Grafana, Alertmanager под одним адресом. Никаких «зайди на порт 8443 для дашборда, 9090 для Prometheus, 3000 для Grafana». Плюс интеграция с OAuth 2.0/OIDC для SSO. Настраивается через cephadm в пару команд. certmgr: автоматические TLS-сертификатыПодсистема управления сертификатами. Ceph теперь сам выступает корневым CA, выпускает сертификаты для своих сервисов, обновляет их автоматически, предупреждает об истечении. Никаких самоподписанных сертификатов вручную. Data Availability ScoreНовая команда для мониторинга доступности данных: ceph osd pool availability-status Показывает «score» для каждого пула — сколько данных доступно прямо сейчас. Пул считается недоступным если любая PG не в состоянии active или есть unfound объекты. Crimson OSD + SeaStore (Tech Preview)Crimson — полностью переписанный OSD на основе Seastar (асинхронный, без блокирующих операций). В Tentacle к нему добавили развёртывание SeaStore — нового бэкенда хранения рядом с Crimson. Это всё ещё tech preview, в продакшне не используем — но прогресс виден. Удаление устаревших модулейМодули mgr/restful и mgr/zabbix официально удалены. Они были deprecated с 2020 года и имели уязвимости в зависимостях (CVE-2023-46136). Переходите на Dashboard API и Prometheus. Когда Ceph — правильный выборCeph имеет смысл когда у вас: Минимум 3 физических сервера (иначе нет смысла в распределённости) Объём данных от нескольких терабайт Потребность в нескольких типах хранилища одновременно (block + object + file) Нужна горизонтальная масштабируемость: добавил серверы → ёмкость и производительность выросли Нужна отказоустойчивость без дорогого проприетарного железа Когда Ceph — не правильный выбор: Один сервер или только два — берите ZFS/BTRFS Небольшой проект: overhead на управление не окупится Нужна очень низкая latency (< 1ms) для транзакционной БД — NVMe All-Flash Array или local SSD в приоритете Итог: ключевые концепции для запоминанияКонцепция Коротко RADOS Нижний уровень — distributed object store CRUSH Алгоритм распределения данных без метасервера OSD 1 демон = 1 диск PG Группа объектов, единица репликации MON Кворумный регистр карты кластера BlueStore Нативный бэкенд OSD без ФС RBD Блочный диск поверх RADOS CephFS POSIX-ФС поверх RADOS + MDS RGW S3/Swift API поверх RADOS FastEC Быстрый Erasure Coding в Tentacle В следующей статье мы разворачиваем реальный кластер с нуля через cephadm, настраиваем пулы и подключаем RBD к Proxmox. Далее читай - Часть #2
  5. Резервное копирование в Linux: стратегии и инструментыПравило 3-2-1Любая стратегия резервного копирования должна начинаться с правила 3-2-1: 3 копии данных 2 разных типа носителей 1 копия вне офиса Rsync: умное инкрементальное копирование#!/usr/bin/env bash # Скрипт резервного копирования с ротацией BACKUP_SOURCE="/var/www" BACKUP_DEST="/mnt/backup" RETAIN_DAYS=30 DATE=$(date +%Y%m%d_%H%M%S) # Создаём снэпшот через hard links (не дублируем неизменённые файлы) rsync -avz --delete \ --link-dest="$BACKUP_DEST/latest" \ --exclude="*.log" \ --exclude="cache/" \ --exclude="tmp/" \ "$BACKUP_SOURCE/" \ "$BACKUP_DEST/$DATE/" # Обновляем симлинк на последний бэкап ln -sfn "$BACKUP_DEST/$DATE" "$BACKUP_DEST/latest" # Удаляем старые бэкапы find "$BACKUP_DEST" -maxdepth 1 -type d -mtime +$RETAIN_DAYS -exec rm -rf {} + echo "Backup completed: $BACKUP_DEST/$DATE" du -sh "$BACKUP_DEST/$DATE" Borg: дедупликация и шифрованиеBorg — продвинутый инструмент с дедупликацией (одинаковые блоки хранятся один раз): # Установка apt install borgbackup # Инициализация репозитория с шифрованием borg init --encryption=repokey-blake2 user@backup-server:/backups/myserver # Создание бэкапа borg create \ --verbose \ --filter AME \ --list \ --stats \ --show-rc \ --compression lz4 \ --exclude-caches \ --exclude '/home/*/.cache/*' \ --exclude '/var/cache/*' \ --exclude '/var/tmp/*' \ user@backup-server:/backups/myserver::myserver-$(date +%Y%m%d_%H%M) \ /etc \ /var/www \ /home \ /var/lib/mysql # осторожно с активной БД! # Список архивов borg list user@backup-server:/backups/myserver # Проверка целостности borg check user@backup-server:/backups/myserver # Восстановление cd /tmp/restore borg extract user@backup-server:/backups/myserver::myserver-20240115_0300 \ var/www/myapp/public # только конкретная директория # Ротация (хранить: 7 ежедневных, 4 недельных, 12 ежемесячных) borg prune \ --keep-daily=7 \ --keep-weekly=4 \ --keep-monthly=12 \ user@backup-server:/backups/myserver Бэкап MySQL без блокировок#!/usr/bin/env bash # Бэкап MySQL с минимальным влиянием на продакшн DB_USER="backup" DB_PASS="backup_password" BACKUP_DIR="/var/backups/mysql" DATE=$(date +%Y%m%d_%H%M) mkdir -p "$BACKUP_DIR" # Создаём пользователя для бэкапа (только необходимые права) # GRANT SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER, PROCESS ON *.* TO 'backup'@'localhost'; # Бэкап всех баз mysqldump \ --user="$DB_USER" \ --password="$DB_PASS" \ --single-transaction \ --routines \ --triggers \ --events \ --all-databases \ --master-data=2 \ | gzip > "$BACKUP_DIR/full-$DATE.sql.gz" # Проверяем что файл не пустой size=$(stat -c%s "$BACKUP_DIR/full-$DATE.sql.gz") if [[ $size -lt 1000 ]]; then echo "ERROR: Backup file too small ($size bytes)" >&2 rm "$BACKUP_DIR/full-$DATE.sql.gz" exit 1 fi echo "Backup created: $BACKUP_DIR/full-$DATE.sql.gz ($size bytes)" # Ротация — удаляем старше 7 дней find "$BACKUP_DIR" -name "full-*.sql.gz" -mtime +7 -delete # XtraBackup для горячего бэкапа InnoDB (без --single-transaction ограничений) # apt install percona-xtrabackup-80 # xtrabackup --backup --user="$DB_USER" --password="$DB_PASS" \ # --target-dir="$BACKUP_DIR/xtrabackup-$DATE" Проверка восстановления — самое важноеБэкап без проверки восстановления — не бэкап. Автоматизируйте: #!/usr/bin/env bash # Тест восстановления MySQL (запускать еженедельно) BACKUP_FILE=$(ls -t /var/backups/mysql/full-*.sql.gz | head -1) TEST_DB="restore_test_$(date +%s)" echo "Testing restore of $BACKUP_FILE" # Создаём тестовую базу mysql -e "CREATE DATABASE $TEST_DB" # Восстанавливаем zcat "$BACKUP_FILE" | mysql "$TEST_DB" # Проверяем количество таблиц table_count=$(mysql -sN -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$TEST_DB'") echo "Tables restored: $table_count" # Удаляем тестовую базу mysql -e "DROP DATABASE $TEST_DB" if [[ $table_count -gt 0 ]]; then echo "Restore test PASSED" else echo "Restore test FAILED!" >&2 exit 1 fi Nginx: тюнинг и продвинутая конфигурацияПроизводительность nginx# /etc/nginx/nginx.conf user www-data; # Одни worker per CPU core worker_processes auto; # Привязываем к ядрам (снижаем context switch) worker_cpu_affinity auto; # Максимум соединений = worker_processes * worker_connections events { worker_connections 4096; use epoll; # лучший I/O multiplexer для Linux multi_accept on; # принимаем все соединения за один раз } http { # Базовые оптимизации sendfile on; tcp_nopush on; # отправлять заголовки и начало файла вместе tcp_nodelay on; # отключить Nagle для активных соединений # Таймауты keepalive_timeout 65; keepalive_requests 1000; client_header_timeout 15; client_body_timeout 15; send_timeout 15; # Буферы client_body_buffer_size 128k; client_max_body_size 50M; client_header_buffer_size 1k; large_client_header_buffers 4 16k; # Сжатие gzip on; gzip_vary on; gzip_min_length 1024; gzip_comp_level 5; gzip_types text/plain text/css text/javascript application/javascript application/json application/xml image/svg+xml; # Кэширование статики open_file_cache max=10000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on; # Безопасность server_tokens off; more_clear_headers Server; # если установлен nginx-extras # Rate limiting limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m; limit_conn_zone $binary_remote_addr zone=perip:10m; } Virtual host для PHP-приложения# /etc/nginx/sites-available/myapp.conf # Upstream pool с health checks upstream php_fpm { least_conn; # балансировка по наименее загруженному server 127.0.0.1:9000 weight=5 max_fails=3 fail_timeout=30s; server 127.0.0.1:9001 weight=5 max_fails=3 fail_timeout=30s; keepalive 32; # постоянные соединения к FPM } # Кэш для FastCGI ответов fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=php_cache:100m max_size=2g inactive=60m use_temp_path=off; server { listen 80; server_name myapp.example.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name myapp.example.com; root /var/www/myapp/public; index index.php; # SSL ssl_certificate /etc/letsencrypt/live/myapp.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/myapp.example.com/privkey.pem; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; ssl_stapling on; ssl_stapling_verify on; # Безопасность add_header X-Frame-Options "SAMEORIGIN" always; add_header X-XSS-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; # Логи access_log /var/log/nginx/myapp-access.log combined buffer=512k flush=1m; error_log /var/log/nginx/myapp-error.log warn; # Ограничения limit_conn perip 20; # Статика с долгим кешированием location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; log_not_found off; access_log off; } # API с rate limiting location /api/ { limit_req zone=api burst=20 nodelay; try_files $uri $uri/ /index.php?$query_string; } location /api/auth { limit_req zone=login burst=5 nodelay; try_files $uri $uri/ /index.php?$query_string; } # PHP-FPM location ~ \.php$ { fastcgi_pass php_fpm; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; # Кеширование (осторожно — только для некэшируемого поставьте X-Cache-Bypass) fastcgi_cache php_cache; fastcgi_cache_key "$scheme$request_method$host$request_uri"; fastcgi_cache_valid 200 302 60m; fastcgi_cache_valid 404 1m; fastcgi_cache_bypass $http_pragma $http_authorization $cookie_PHPSESSID; fastcgi_no_cache $http_pragma $http_authorization; add_header X-Cache-Status $upstream_cache_status; # Буферизация fastcgi_buffer_size 128k; fastcgi_buffers 4 256k; fastcgi_busy_buffers_size 256k; # Таймаут для долгих запросов fastcgi_read_timeout 300; } location / { try_files $uri $uri/ /index.php?$query_string; } # Запрещаем служебные файлы location ~ /\.(ht|git|env) { deny all; return 404; } } Централизованное логирование: rsyslog, loki, ELKrsyslog: маршрутизация логов# /etc/rsyslog.conf — продвинутая конфигурация # Шаблоны template(name="FileFormat" type="string" string="%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n" ) # JSON формат для Logstash/Loki template(name="JSONFormat" type="list") { constant(value="{") constant(value="\"timestamp\":\"") property(name="timereported" dateFormat="rfc3339") constant(value="\",\"host\":\"") property(name="hostname") constant(value="\",\"severity\":\"") property(name="syslogseverity-text") constant(value="\",\"facility\":\"") property(name="syslogfacility-text") constant(value="\",\"program\":\"") property(name="programname") constant(value="\",\"pid\":\"") property(name="procid") constant(value="\",\"message\":\"") property(name="msg" format="json") constant(value="\"}\n") } # Маршрутизация по приоритету *.emerg :omusrmsg:* # все терминалы при критической ошибке auth,authpriv.* /var/log/auth.log mail.* -/var/log/mail.log # дефис = буферизованная запись cron.* /var/log/cron.log *.warn /var/log/warnings.log # Отдельный файл для nginx if $programname == 'nginx' then { action(type="omfile" file="/var/log/nginx/error.log" template="FileFormat") stop } # Пересылка на центральный сервер *.* action(type="omfwd" target="log-server.internal" port="514" protocol="tcp" template="JSONFormat" action.resumeRetryCount="-1" queue.type="linkedList" queue.size="50000" queue.filename="rsyslog_queue" queue.saveonshutdown="on" ) Loki + Promtail: современный стекLoki — это "Prometheus для логов", хранит логи как метрики с метками: # /etc/promtail/promtail-config.yaml server: http_listen_port: 9080 positions: filename: /var/log/positions.yaml clients: - url: http://loki:3100/loki/api/v1/push scrape_configs: - job_name: nginx static_configs: - targets: [localhost] labels: job: nginx env: production __path__: /var/log/nginx/access.log pipeline_stages: - regex: expression: '^(?P<remote_addr>\S+) - (?P<remote_user>\S+) \[(?P<time>[^\]]+)\] "(?P<method>\S+) (?P<path>[^\s"]+)[^"]*" (?P<status>\d+) (?P<body_bytes>\d+)' - labels: method: status: - metrics: http_requests_total: type: Counter description: "Total HTTP requests" source: status config: action: inc - job_name: php-app static_configs: - targets: [localhost] labels: job: php-app __path__: /var/www/myapp/storage/logs/*.log pipeline_stages: - multiline: firstline: '^\[\d{4}-\d{2}-\d{2}' max_wait_time: 3s - regex: expression: '^\[(?P<time>[^\]]+)\] (?P<env>\w+)\.(?P<level>[A-Z]+): (?P<message>.+)' - labels: level: env: Запросы LogQL (язык Loki)# Все ошибки nginx {job="nginx"} |= "error" # HTTP 500 ошибки за последний час {job="nginx"} | regex `status=(?P<status>\d+)` | status="500" # Медленные запросы (>1 секунды) {job="nginx"} | regex `request_time=(?P<rt>[0-9.]+)` | rt > 1.0 # Топ URL по количеству запросов topk(10, sum by (path) (rate({job="nginx"} | json [5m]))) # Уровень ошибок в приложении sum(rate({job="php-app", level="ERROR"}[5m])) by (level) Ansible: управление конфигурациями Linux-серверовСтруктура Ansible-проектаansible/ ├── ansible.cfg ├── inventory/ │ ├── production/ │ │ ├── hosts.yml │ │ └── group_vars/ │ │ ├── all.yml │ │ ├── web.yml │ │ └── db.yml │ └── staging/ │ └── hosts.yml ├── roles/ │ ├── common/ │ ├── nginx/ │ ├── php/ │ └── mysql/ └── playbooks/ ├── site.yml ├── deploy.yml └── update.yml ansible.cfg[defaults] inventory = inventory/production remote_user = deploy private_key_file = ~/.ssh/id_ed25519 host_key_checking = False retry_files_enabled = False stdout_callback = yaml callback_whitelist = timer, profile_tasks forks = 20 [ssh_connection] pipelining = True ssh_args = -o ControlMaster=auto -o ControlPersist=60s Роль для hardening# roles/common/tasks/main.yml --- - name: Update and upgrade apt packages apt: upgrade: dist update_cache: yes cache_valid_time: 3600 - name: Install required packages apt: name: - ufw - fail2ban - unattended-upgrades - logrotate - htop - curl - git state: present - name: Configure sysctl security settings sysctl: name: "{{ item.key }}" value: "{{ item.value }}" state: present reload: yes loop: "{{ sysctl_settings }}" - name: Configure UFW ufw: state: enabled policy: deny direction: incoming - name: Allow SSH ufw: rule: allow port: "{{ ssh_port }}" proto: tcp - name: Configure fail2ban template: src: jail.local.j2 dest: /etc/fail2ban/jail.local owner: root group: root mode: '0644' notify: restart fail2ban - name: Configure SSH template: src: sshd_config.j2 dest: /etc/ssh/sshd_config validate: 'sshd -t -f %s' owner: root group: root mode: '0600' notify: restart sshd Идемпотентность: делаем правильно# Создание пользователя (идемпотентно) - name: Create deploy user user: name: deploy groups: www-data shell: /bin/bash create_home: yes state: present # Копируем SSH ключ - name: Set authorized keys authorized_key: user: deploy state: present key: "{{ lookup('file', 'files/deploy_key.pub') }}" exclusive: yes # удалить другие ключи # Изменение конфига только если нужно - name: Configure PHP-FPM template: src: php-fpm-pool.conf.j2 dest: /etc/php/8.2/fpm/pool.d/www.conf owner: root group: root mode: '0644' notify: reload php-fpm # Handlers (выполняются только если что-то изменилось) # roles/php/handlers/main.yml - name: reload php-fpm service: name: php8.2-fpm state: reloaded - name: restart php-fpm service: name: php8.2-fpm state: restarted Деплой приложения через Ansible# playbooks/deploy.yml --- - name: Deploy MyApp hosts: web serial: "30%" # Rolling update: 30% серверов одновременно vars: app_dir: /var/www/myapp git_repo: git@github.com:company/myapp.git git_branch: "{{ branch | default('main') }}" tasks: - name: Pull latest code git: repo: "{{ git_repo }}" dest: "{{ app_dir }}" version: "{{ git_branch }}" force: yes - name: Install Composer dependencies composer: command: install working_dir: "{{ app_dir }}" no_dev: yes optimize_autoloader: yes - name: Run migrations command: php spark migrate --all args: chdir: "{{ app_dir }}" run_once: true # только на одном сервере - name: Clear application cache command: php spark cache:clear args: chdir: "{{ app_dir }}" - name: Reload PHP-FPM (graceful) service: name: php8.2-fpm state: reloaded - name: Warm up cache uri: url: "https://{{ inventory_hostname }}/health" status_code: 200 retries: 5 delay: 2 Диагностика Linux: алгоритм поиска проблемМетодология USEUSE Method (Brendan Gregg): для каждого ресурса проверяем: Utilization — использование (в %) Saturation — насыщение (очереди, ожидание) Errors — ошибки # CPU Utilization mpstat -P ALL 1 3 # CPU Saturation (очередь на выполнение) vmstat 1 | awk '{print $1}' # r - run queue # Memory Utilization free -h # Memory Saturation (swapping) vmstat 1 | awk '{print $7, $8}' # si/so - swap in/out # Disk Utilization iostat -xz 1 | grep -E "Device|sd|nvme" # Disk Saturation (await > service time) iostat -xz 1 | awk 'NR>3 {print $1, $16}' # %util # Network Utilization sar -n DEV 1 5 60-секундный анализ сервера# Быстрый обзор за 60 секунд (по Brendan Gregg) uptime # load average dmesg -T | tail -5 # ошибки ядра vmstat -SM 1 3 # VM, CPU, I/O обзор mpstat -P ALL 1 3 # CPU по ядрам pidstat 1 3 # процессы iostat -xz 1 3 # I/O дисков free -m # память sar -n DEV 1 3 # сеть sar -n TCP,ETCP 1 3 # TCP метрики top # интерактивно Диагностика "сервер завис"# 1. Можем ли мы что-то делать? # Если не отвечает по SSH - физический доступ или IPMI/iLO # 2. Что не отвечает? ping server-ip # сеть живая? nc -zv server-ip 22 # SSH порт открыт? nc -zv server-ip 80 # HTTP открыт? # 3. Загрузка uptime # load: 0.5 — норма # load: = CPU cores — занят # load: > CPU cores * 2 — перегружен # 4. Кто виноват? top -bn1 | head -20 ps auxwf | head -30 # 5. Есть ли OOM? dmesg | grep -i "oom\|killed process" journalctl -k --since "1 hour ago" | grep -i oom # 6. Диск переполнен? df -h du -sh /var /tmp /home # кто занял место # 7. Иноды кончились? df -i # 8. Что происходит с сетью? ss -s # статистика сокетов ss -tnp state time-wait | wc -l # TIME_WAIT netstat -i # ошибки на интерфейсах # 9. Дисковые проблемы dmesg | grep -i "error\|fail\|i/o" smartctl -H /dev/sda # здоровье диска # 10. Полная картина за последний час sar -A 1 10 # всё что собрал sar strace и ltrace: что делает процесс# Что делает процесс прямо сейчас strace -p $(pgrep nginx | head -1) # Только конкретные системные вызовы strace -e trace=open,read,write,network -p PID # Статистика системных вызовов за 5 секунд strace -c -p PID -e trace=all & sleep 5 kill %1 # ltrace — вызовы библиотечных функций ltrace -p PID # Запустить и трейсить strace -e trace=network curl google.com 2>&1 | grep connect # Дочерние процессы тоже strace -f -p PID -o /tmp/strace.log Анализ производительности с perf# Профиль за 10 секунд (нужен linux-tools-generic) perf record -F 99 -g -p $(pgrep php-fpm | head -1) -- sleep 10 perf report --stdio | head -50 # Hotspot функции perf top -K -p $(pgrep nginx | head -1) # Счётчики производительности perf stat -p PID -- sleep 5 # cache-misses, branch-misses, context-switches # Flame graph (установить FlameGraph от Brendan Gregg) perf record -F 99 -ag -- sleep 10 perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg Диагностика сетевых задержек# Измеряем задержки на разных уровнях # 1. ICMP (сеть) ping -c 100 server-ip | tail -3 # 2. TCP handshake (OS + сеть) hping3 -S -c 100 -p 80 server-ip | tail -5 # 3. HTTP time_to_first_byte (приложение) curl -o /dev/null -s -w " dns: %{time_namelookup}s connect: %{time_connect}s tls: %{time_appconnect}s ttfb: %{time_starttransfer}s total: %{time_total}s " https://myapp.example.com # 4. Детальная трассировка HTTP curl -v --trace-time https://myapp.example.com 2>&1 | head -50 # 5. Tcpdump для анализа конкретного запроса tcpdump -i eth0 -w request.pcap host client-ip and port 443 # Открываем в Wireshark для детального анализа # 6. Статистика задержки на уровне сокета ss -ti # socket timing info Инструменты для экстренной диагностики: шпаргалка# Процессы ps auxwf # дерево процессов pstree -pu # красивое дерево pgrep -a nginx # найти процессы lsof -p PID # файлы процесса lsof -i :80 # кто слушает порт 80 fuser -n tcp 80 # pid процесса на порту # Файловая система lsof +D /var/log # кто держит файлы в директории inotifywait -m /etc/passwd # слежка за изменениями find / -newer /tmp/stamp -type f 2>/dev/null # что изменилось с timestamp # Сеть tcpdump -i any port 80 -nn -q conntrack -L | wc -l # количество трекируемых соединений nmap -sV localhost # сканируем себя # История команд в случае инцидента history | grep -i "rm\|mv\|chmod\|dd" | tail -20 last | head -20 # последние логины lastb | head -10 # неудачные логины who # кто сейчас залогинен w # что они делают Диагностика — это смесь знаний, методологии и опыта. Самые ценные навыки: не паниковать, следовать методологии USE, измерять прежде чем делать выводы, и помнить что 90% проблем с производительностью — это диск, память или сеть, а не код.
  6. Сети в Linux: от основ до продвинутой диагностикиip и iproute2: современная работа с сетьюКоманда ifconfig устарела. Всё что нужно — в пакете iproute2: # Интерфейсы и адреса 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 # сводная статистика Диагностика сетевых проблем# Трассировка маршрута 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 Виртуальные сети: bridge, vlan, veth# 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 nftables: полный конфиг для веб-сервера#!/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 } } Файловые системы Linux: выбор, настройка, оптимизацияОбзор файловых системФС Сильные стороны Слабые стороны Применение ext4 Стабильность, скорость Нет встроенного RAID Общее назначение XFS Большие файлы, параллелизм Нельзя уменьшить Медиа, базы данных Btrfs Снэпшоты, сжатие, RAID Сложность Разработка, NAS ZFS Надёжность, снэпшоты Память, лицензия Продакшн NAS tmpfs Скорость (RAM) Нет персистентности /tmp, кэш Монтирование и /etc/fstab# Посмотреть текущие точки монтирования 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 Производительность ext4# Опции при создании (важно для 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 # расширить до максимума Btrfs: продвинутые функции# Создание с несколькими устройствами 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 # проверка целостности LVM: гибкое управление томами# Структура: 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 Docker и контейнеризация в LinuxПространства имён: основа изоляцииКонтейнеры — это не виртуальные машины, это изоляция через Linux namespaces: # Типы 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 Docker: производительность и безопасность# Просмотр ресурсов контейнеров 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 Docker daemon оптимизация/etc/docker/daemon.json: { "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 } Многоэтапные Dockerfile для PHP# 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 \ && docker-php-ext-configure gd --with-jpeg --with-webp \ && docker-php-ext-install -j$(nproc) \ pdo_mysql \ redis \ gd \ opcache \ intl \ && 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"] Docker Compose для dev-среды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: SSH: продвинутые возможностиSSH туннели: три типаLocal forwarding — пробрасываем удалённый порт локально# Доступ к 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 Remote forwarding — пробрасываем локальный порт на сервер# Даём серверу доступ к нашему локальному сервису (для демо, CI/CD) ssh -R 8080:localhost:3000 user@remote-server # На сервере: curl http://localhost:8080 → ваш локальный порт 3000 Dynamic forwarding — SOCKS5 прокси# Весь трафик через сервер ssh -D 1080 user@remote-server # Настраиваем браузер на SOCKS5 proxy: localhost:1080 # В автоматическом режиме ssh -fND 1080 user@remote-server ~/.ssh/config: удобная конфигурация# Глобальные настройки 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 # Мультиплексирование — одно TCP соединение для нескольких сессий mkdir -p ~/.ssh/sockets # Теперь второй ssh к тому же хосту использует уже открытое соединение ssh prod-web01 # открывает новое соединение ssh prod-web01 # переиспользует существующее (мгновенно!) ssh prod-web01 ls /var/log # команда через существующее соединение SSH-агент и управление ключами# Запуск агента 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 Копирование файлов: rsync через SSH# Основной синтаксис 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/ Ядро Linux: мониторинг и тюнинг/proc и /sys: окна в ядро# Информация о системе 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 > /sys/block/sda/queue/nr_requests # изменяем параметр Настройка планировщика задач CPU# Текущий планировщик 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 > /proc/sys/kernel/numa_balancing # Transparent Huge Pages cat /sys/kernel/mm/transparent_hugepage/enabled echo madvise > /sys/kernel/mm/transparent_hugepage/enabled # Watchdog (отключаем на виртуалках для экономии) echo 0 > /proc/sys/kernel/nmi_watchdog Параметры ядра для highload (полный sysctl)# /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 sysctl -p /etc/sysctl.d/99-highload.conf # Проверяем что применилось sysctl net.ipv4.tcp_congestion_control sysctl vm.swappiness Анализ паник и OOM# Последняя паника ядра 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 — процессы в момент паники Linux — это айсберг. Большинство из нас видит только надводную часть: файлы, процессы, сеть. Но под водой — целый мир механизмов, которые делают его надёжной основой для миллиардов устройств. Понимание этих механизмов — отличие хорошего администратора от великого.
  7. «Как вы узнали о проблеме?» — «Пожаловались пользователи» — так работать нельзя. Правильный мониторинг означает, что вы знаете о проблеме раньше, чем её заметят пользователи. Эта статья о построении полноценного стека мониторинга для Linux-инфраструктуры: от сбора метрик до умных алертов. Архитектура: что и зачемСерверы Мониторинг Визуализация [node_exporter] ──────► [Prometheus] ──────► [Grafana] [php-fpm_exporter] │ │ [mysql_exporter] │ алерты дашборды [nginx_exporter] ▼ [redis_exporter] [Alertmanager] │ [Email/Slack/PagerDuty] Prometheus — это time-series база данных с pull-моделью сбора данных. Exporters на серверах открывают HTTP endpoint с метриками в формате Prometheus, и сервер Prometheus их периодически «скрейпит». Node Exporter: метрики операционной системыУстановка# Через пакет apt install prometheus-node-exporter # Ubuntu # или скачиваем бинарник # Проверяем endpoint curl http://localhost:9100/metrics | head -50 Что собирает node_exporter# CPU node_cpu_seconds_total{cpu="0",mode="idle"} node_cpu_seconds_total{cpu="0",mode="user"} node_cpu_seconds_total{cpu="0",mode="system"} node_cpu_seconds_total{cpu="0",mode="iowait"} # Память node_memory_MemTotal_bytes node_memory_MemAvailable_bytes node_memory_SwapUsed_bytes # Диски node_disk_read_bytes_total{device="sda"} node_disk_written_bytes_total{device="sda"} node_disk_io_time_seconds_total{device="sda"} # Сеть node_network_receive_bytes_total{device="eth0"} node_network_transmit_bytes_total{device="eth0"} node_network_receive_errs_total{device="eth0"} # Файловая система node_filesystem_avail_bytes{mountpoint="/"} node_filesystem_size_bytes{mountpoint="/"} # Нагрузка node_load1 # средняя нагрузка за 1 минуту node_load5 node_load15 Кастомные метрики через textfile collector# Создаём директорию для textfile mkdir -p /var/lib/node_exporter/textfile_collector # Запускаем node_exporter с collector /usr/bin/prometheus-node-exporter \ --collector.textfile.directory=/var/lib/node_exporter/textfile_collector # Скрипт для метрик приложения (запускаем по cron) cat > /usr/local/bin/app-metrics.sh << 'EOF' #!/bin/bash METRICS_FILE="/var/lib/node_exporter/textfile_collector/app.prom" # Количество PHP-FPM процессов fpm_workers=$(ps aux | grep php-fpm | grep -v grep | wc -l) # Количество MySQL соединений mysql_connections=$(mysql -u monitoring -ppassword -e "SHOW STATUS LIKE 'Threads_connected';" | awk 'NR==2{print $2}') # Место в очереди Redis redis_queue_size=$(redis-cli llen myapp:jobs) cat > "$METRICS_FILE" << METRICS # HELP myapp_fpm_workers Number of PHP-FPM worker processes # TYPE myapp_fpm_workers gauge myapp_fpm_workers $fpm_workers # HELP myapp_mysql_connections Active MySQL connections # TYPE myapp_mysql_connections gauge myapp_mysql_connections $mysql_connections # HELP myapp_queue_size Redis job queue size # TYPE myapp_queue_size gauge myapp_queue_size $redis_queue_size METRICS EOF chmod +x /usr/local/bin/app-metrics.sh # Добавляем в cron (каждую минуту) echo "* * * * * root /usr/local/bin/app-metrics.sh" > /etc/cron.d/app-metrics Установка Prometheus# Создаём пользователя useradd --no-create-home --shell /bin/false prometheus # Создаём директории mkdir -p /etc/prometheus /var/lib/prometheus chown prometheus:prometheus /var/lib/prometheus # Скачиваем (проверьте актуальную версию) cd /tmp wget https://github.com/prometheus/prometheus/releases/download/v2.50.1/prometheus-2.50.1.linux-amd64.tar.gz tar xvf prometheus-*.tar.gz cp prometheus-*/prometheus /usr/local/bin/ cp prometheus-*/promtool /usr/local/bin/ cp -r prometheus-*/consoles /etc/prometheus/ cp -r prometheus-*/console_libraries /etc/prometheus/ chown prometheus:prometheus /usr/local/bin/prometheus /usr/local/bin/promtool Конфигурация Prometheus/etc/prometheus/prometheus.yml: global: scrape_interval: 15s # как часто собираем метрики evaluation_interval: 15s # как часто оцениваем правила алертов scrape_timeout: 10s # Правила алертов rule_files: - /etc/prometheus/rules/*.yml # Куда отправлять алерты alerting: alertmanagers: - static_configs: - targets: - localhost:9093 # Источники метрик scrape_configs: # Сам Prometheus - job_name: 'prometheus' static_configs: - targets: ['localhost:9090'] # Node exporters — наши серверы - job_name: 'node' static_configs: - targets: - 'web01:9100' - 'web02:9100' - 'db01:9100' # Добавляем метки для группировки relabel_configs: - source_labels: [__address__] target_label: instance # Статические метки static_configs: - targets: ['web01:9100'] labels: env: production role: web - targets: ['db01:9100'] labels: env: production role: database # MySQL exporter - job_name: 'mysql' static_configs: - targets: ['localhost:9104'] # Nginx exporter - job_name: 'nginx' static_configs: - targets: ['localhost:9113'] # Redis exporter - job_name: 'redis' static_configs: - targets: ['localhost:9121'] # PHP-FPM — через статус страницу - job_name: 'php-fpm' static_configs: - targets: ['localhost:9253'] # Service discovery через файлы (удобно для динамической инфраструктуры) - job_name: 'dynamic-servers' file_sd_configs: - files: - /etc/prometheus/targets/*.yml refresh_interval: 30s Systemd unit для Prometheus[Unit] Description=Prometheus Monitoring Wants=network-online.target After=network-online.target [Service] User=prometheus Group=prometheus Type=simple ExecStart=/usr/local/bin/prometheus \ --config.file=/etc/prometheus/prometheus.yml \ --storage.tsdb.path=/var/lib/prometheus \ --storage.tsdb.retention.time=30d \ --storage.tsdb.retention.size=10GB \ --web.enable-lifecycle \ --web.enable-admin-api Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target PromQL: язык запросовPromQL — мощный язык для работы с time-series. Основные паттерны: # Мгновенные значения node_memory_MemAvailable_bytes # Использование памяти в % (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 # CPU usage (rate нужен для счётчиков) 100 - (avg by (instance) ( rate(node_cpu_seconds_total{mode="idle"}[5m]) ) * 100) # Disk I/O latency rate(node_disk_io_time_seconds_total[5m]) # Свободное место на диске в % (node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100 # Количество TCP соединений по состоянию node_netstat_Tcp_CurrEstab # Nginx requests per second rate(nginx_http_requests_total[5m]) # 95-й перцентиль времени ответа histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]) ) # Агрегация по серверам sum by (instance) (rate(node_cpu_seconds_total{mode!="idle"}[5m])) # Топ 5 серверов по CPU topk(5, 100 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100 ) Правила алертов/etc/prometheus/rules/linux.yml: groups: - name: linux_nodes rules: # CPU - alert: HighCPUUsage expr: | 100 - (avg by (instance) ( rate(node_cpu_seconds_total{mode="idle"}[5m]) ) * 100) > 85 for: 5m labels: severity: warning annotations: summary: "High CPU on {{ $labels.instance }}" description: "CPU usage is {{ printf \"%.1f\" $value }}% on {{ $labels.instance }}" - alert: CriticalCPUUsage expr: | 100 - (avg by (instance) ( rate(node_cpu_seconds_total{mode="idle"}[5m]) ) * 100) > 95 for: 2m labels: severity: critical annotations: summary: "CRITICAL CPU on {{ $labels.instance }}" # Память - alert: HighMemoryUsage expr: | (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90 for: 5m labels: severity: warning annotations: summary: "High memory on {{ $labels.instance }}" description: "Memory usage is {{ printf \"%.1f\" $value }}%" # Диск - alert: DiskSpaceLow expr: | (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 15 for: 2m labels: severity: warning annotations: summary: "Low disk space on {{ $labels.instance }}" description: "Only {{ printf \"%.1f\" $value }}% disk space remaining" - alert: DiskSpaceCritical expr: | (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 5 for: 1m labels: severity: critical annotations: summary: "CRITICAL: Disk almost full on {{ $labels.instance }}" # Инод - alert: DiskInodesLow expr: | (node_filesystem_files_free / node_filesystem_files) * 100 < 10 for: 2m labels: severity: warning # Сервер недоступен - alert: InstanceDown expr: up == 0 for: 1m labels: severity: critical annotations: summary: "Instance {{ $labels.instance }} is DOWN" # Load average - alert: HighLoadAverage expr: node_load1 > (count by (instance)(node_cpu_seconds_total{mode="idle"}) * 2) for: 5m labels: severity: warning # OOM Killer - alert: OOMKillerActive expr: increase(node_vmstat_oom_kill[5m]) > 0 labels: severity: critical annotations: summary: "OOM Killer active on {{ $labels.instance }}" # Много TIME_WAIT соединений - alert: HighTimeWaitConnections expr: node_sockstat_TCP_tw > 10000 for: 5m labels: severity: warning Alertmanager: умная маршрутизация уведомлений/etc/alertmanager/alertmanager.yml: global: smtp_smarthost: 'smtp.gmail.com:587' smtp_from: 'alerts@example.com' smtp_auth_username: 'alerts@example.com' smtp_auth_password: 'password' slack_api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK' # Шаблоны уведомлений templates: - /etc/alertmanager/templates/*.tmpl # Маршрутизация route: group_by: ['alertname', 'instance'] group_wait: 30s # ждём перед первым уведомлением group_interval: 5m # интервал между повторными уведомлениями группы repeat_interval: 4h # когда повторить если не решено receiver: 'slack-warnings' routes: # Критические — немедленно в PagerDuty - match: severity: critical receiver: 'pagerduty-critical' group_wait: 0s repeat_interval: 1h # Ночью тишина для warnings - match: severity: warning receiver: 'slack-warnings' mute_time_intervals: - nights-and-weekends # Отдельный канал для базы данных - match: job: mysql receiver: 'slack-dba-channel' # Время тишины time_intervals: - name: nights-and-weekends time_intervals: - weekdays: [saturday, sunday] - times: - start_time: '22:00' end_time: '08:00' # Получатели receivers: - name: 'slack-warnings' slack_configs: - channel: '#alerts' icon_emoji: ':warning:' title: '{{ .GroupLabels.alertname }}' text: | {{ range .Alerts }} *Instance:* {{ .Labels.instance }} *Description:* {{ .Annotations.description }} {{ end }} send_resolved: true - name: 'pagerduty-critical' pagerduty_configs: - service_key: 'YOUR_PAGERDUTY_KEY' - name: 'slack-dba-channel' slack_configs: - channel: '#dba-alerts' Grafana: визуализация# Установка apt-get install -y apt-transport-https software-properties-common wget -q -O - https://packages.grafana.com/gpg.key | gpg --dearmor | \ tee /usr/share/keyrings/grafana.gpg > /dev/null echo "deb [signed-by=/usr/share/keyrings/grafana.gpg] \ https://packages.grafana.com/oss/deb stable main" | \ tee /etc/apt/sources.list.d/grafana.list apt-get update && apt-get install grafana -y systemctl enable --now grafana-server Provisioning дашбордов через код/etc/grafana/provisioning/datasources/prometheus.yaml: apiVersion: 1 datasources: - name: Prometheus type: prometheus access: proxy url: http://localhost:9090 isDefault: true jsonData: timeInterval: "15s" /etc/grafana/provisioning/dashboards/default.yaml: apiVersion: 1 providers: - name: default orgId: 1 folder: '' type: file disableDeletion: false updateIntervalSeconds: 30 options: path: /var/lib/grafana/dashboards Готовые дашбордыНа grafana.com/dashboards есть тысячи готовых дашбордов. Популярные ID для импорта: 1860 — Node Exporter Full 7362 — MySQL Overview 763 — Redis Dashboard 12708 — PHP-FPM Dashboard 11074 — Node Exporter for Prometheus blackbox_exporter: мониторинг снаружиДля мониторинга HTTP, TCP, DNS, ICMP с внешней точки зрения: # /etc/blackbox_exporter/config.yml modules: http_2xx: prober: http timeout: 5s http: valid_http_versions: ["HTTP/1.1", "HTTP/2.0"] valid_status_codes: [] # 2xx follow_redirects: true tls_config: insecure_skip_verify: false http_post_2xx: prober: http http: method: POST headers: Content-Type: application/json body: '{"probe": "check"}' tcp_connect: prober: tcp timeout: 5s ssl_expiry: prober: http timeout: 5s http: fail_if_ssl: false fail_if_not_ssl: true tls_config: insecure_skip_verify: false В prometheus.yml добавляем: - job_name: 'blackbox' metrics_path: /probe params: module: [http_2xx] static_configs: - targets: - https://myapp.example.com/health - https://api.example.com/status relabel_configs: - source_labels: [__address__] target_label: __param_target - source_labels: [__param_target] target_label: instance - target_label: __address__ replacement: localhost:9115 # Алерт на SSL - alert: SSLCertExpiringSoon expr: probe_ssl_earliest_cert_expiry - time() < 86400 * 30 labels: severity: warning annotations: summary: "SSL cert expires in {{ $value | humanizeDuration }}" Правильный мониторинг — это инвестиция, которая окупается при первом же инциденте, когда вы знаете о проблеме за 10 минут до того, как позвонят пользователи. Начните с node_exporter и базовых алертов, постепенно добавляйте экспортеры для ваших сервисов.
  8. Systemd — это уже не просто init-система. Это целая экосистема для управления сервисами, логами, сетью, таймерами, точками монтирования и многим другим. Большинство администраторов использует 20% его возможностей, не подозревая об остальных 80%. Эта статья закроет пробелы. Архитектура systemd: что за чем стоитsystemd ├── systemd-journald — централизованное логирование ├── systemd-networkd — управление сетью ├── systemd-resolved — DNS resolver ├── systemd-timesyncd — синхронизация времени ├── systemd-logind — управление сессиями ├── systemd-udevd — управление устройствами └── systemd-tmpfilesd — управление временными файлами Всё взаимодействует через D-Bus и сокеты. Это важно понимать для отладки. Unit-файлы: анатомия сервисаБазовая структура[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 Секция [Unit]: зависимости и порядокРазница между After/Before и Requires/Wants критически важна: # ПОРЯДОК ЗАПУСКА (не зависимость!) 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 Типы сервисов — Type=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 # запускается после других, когда нет других задач Для PHP-FPM используем Type=notify — он поддерживает sd_notify: [Service] Type=notify ExecStart=/usr/sbin/php-fpm8.2 --nodaemonize Продвинутые настройки сервисаПеременные окружения и секреты[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 Файл /etc/myapp/environment: DB_HOST=localhost DB_PORT=3306 DB_NAME=myapp DB_USER=myapp DB_PASSWORD=supersecret REDIS_HOST=127.0.0.1 chmod 600 /etc/myapp/environment chown root:www-data /etc/myapp/environment Изоляция и безопасность сервиса[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 # только для порта <1024 AmbientCapabilities=CAP_NET_BIND_SERVICE # Ограничение ресурсов LimitNOFILE=65535 # открытые файловые дескрипторы LimitNPROC=512 # процессы MemoryLimit=2G # максимум памяти CPUQuota=50% # не более 50% CPU Политики перезапуска[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 systemctl: продвинутое использование# Основные команды 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 systemd Timers: замена cronТаймеры systemd мощнее cron: есть логирование, управление зависимостями, можно запустить вручную. Создаём пару service + timer/etc/systemd/system/backup.service: [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 /etc/systemd/system/backup.timer: [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 systemctl enable --now backup.timer systemctl list-timers # все таймеры и когда запустятся systemctl start backup.service # запустить вручную без ожидания таймера Синтаксис OnCalendarOnCalendar=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 # субботу и воскресенье в полдень Проверка синтаксиса: systemd-analyze calendar "Mon-Fri *-*-* 09:00:00" journald: работа с логамиБазовые команды# Логи конкретного сервиса 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 # ошибки при предыдущей загрузке Настройка journald/etc/systemd/journald.conf: [Journal] # Максимальный размер хранилища SystemMaxUse=2G SystemKeepFree=500M # Максимальный размер одного файла журнала SystemMaxFileSize=200M # Хранить журналы N дней MaxRetentionSec=1month # Сжатие Compress=yes # Пересылка в syslog (если нужен rsyslog) ForwardToSyslog=no ForwardToWall=no # Максимальный размер сообщения LineMax=48K Структурированное логирование из приложения# Systemd-cat echo "Application started" | systemd-cat -t myapp -p info # С полями для фильтрации systemd-cat -t myapp <<< "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 Drop-in конфиги: расширяем без изменения оригиналаНикогда не редактируйте файлы в /lib/systemd/system/ — они перезапишутся при обновлении. Используйте drop-in: # Создаём директорию mkdir -p /etc/systemd/system/nginx.service.d/ # Создаём override cat > /etc/systemd/system/nginx.service.d/override.conf <<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 cgroups v2 через systemdСовременные версии systemd используют cgroups v2 для изоляции ресурсов: # Статистика ресурсов для сервиса 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 Анализ времени загрузки# Общее время загрузки systemd-analyze # Детально по сервисам systemd-analyze blame # Визуализация в SVG systemd-analyze plot > boot.svg # Критический путь загрузки systemd-analyze critical-chain # Проверка unit-файла на ошибки systemd-analyze verify /etc/systemd/system/myapp.service Полезные паттерны для PHP-приложенийСервис PHP-FPM с worker isolation[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 для общих лимитов Slice для группировки сервисов# /etc/systemd/system/web.slice [Unit] Description=Web Services Slice Before=slices.target [Slice] MemoryMax=8G CPUQuota=200% Все сервисы в web.slice суммарно не превысят 8GB RAM и 200% CPU (2 ядра). Отладка: когда что-то пошло не так# Быстрая диагностика 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 Systemd — мощный инструмент, который при правильном использовании превращает управление сервисами из искусства в инженерию. Используйте drop-in конфиги, настраивайте изоляцию, пишите таймеры вместо cron — и ваша система станет предсказуемой и управляемой.
  9. Bash — это не «просто командная строка». Это полноценный скриптовый язык, на котором написаны тысячи скриптов деплоя, мониторинга, бэкапов и автоматизации по всему миру. Проблема в том, что большинство bash-скриптов написаны наспех, без понимания подводных камней, и падают в самый неподходящий момент. Эта статья о том, как писать bash-скрипты, которым можно доверять. Основы: заголовок, который должен быть в каждом скрипте#!/usr/bin/env bash # Описание: что делает скрипт # Автор: your@email.com # Версия: 1.0.0 set -euo pipefail IFS=$'\n\t' Разберём set -euo pipefail — это не магия, это защита: set -e — скрипт останавливается при ошибке любой команды set -u — ошибка при обращении к неустановленной переменной set -o pipefail — ошибка в пайпе не скрывается (без этого false | true вернёт 0) IFS=$'\n\t' — разделитель полей только перевод строки и таб, не пробел Без этих строк скрипт будет молча продолжаться после ошибок, что приводит к катастрофическим последствиям на продакшне. Работа с переменными и параметрамиПравильное использование переменных# Всегда кавычки вокруг переменных! name="John Doe" echo "$name" # правильно echo $name # сломается если пробел в имени # Значения по умолчанию database="${DB_NAME:-myapp_production}" timeout="${TIMEOUT:-30}" # Проверка обязательного параметра : "${API_KEY:?'API_KEY is required but not set'}" # Массивы servers=("web01" "web02" "web03") for server in "${servers[@]}"; do echo "Processing $server" done # Ассоциативные массивы (bash 4+) declare -A ports ports[web]=80 ports[mysql]=3306 ports[redis]=6379 for service in "${!ports[@]}"; do echo "$service: ${ports[$service]}" done Обработка аргументов командной строки#!/usr/bin/env bash set -euo pipefail # Продвинутая обработка аргументов с getopts usage() { cat <<EOF Usage: $(basename "$0") [OPTIONS] <environment> Options: -h, --help Show this help -v, --verbose Verbose output -n, --dry-run Dry run, don't make changes -c, --config FILE Config file path (default: /etc/myapp/config.conf) Environment: staging Deploy to staging production Deploy to production Examples: $(basename "$0") -v staging $(basename "$0") --dry-run production EOF exit 1 } # Разбор длинных опций через getopt OPTS=$(getopt -o hvnc: --long help,verbose,dry-run,config: -n "$(basename "$0")" -- "$@") eval set -- "$OPTS" VERBOSE=false DRY_RUN=false CONFIG="/etc/myapp/config.conf" while true; do case "$1" in -h|--help) usage ;; -v|--verbose) VERBOSE=true; shift ;; -n|--dry-run) DRY_RUN=true; shift ;; -c|--config) CONFIG="$2"; shift 2 ;; --) shift; break ;; *) echo "Unknown option: $1"; usage ;; esac done ENVIRONMENT="${1:-}" [[ -z "$ENVIRONMENT" ]] && { echo "Error: environment required"; usage; } [[ "$ENVIRONMENT" != "staging" && "$ENVIRONMENT" != "production" ]] && { echo "Error: environment must be 'staging' or 'production'" exit 1 } $VERBOSE && echo "Config: $CONFIG" $VERBOSE && echo "Environment: $ENVIRONMENT" $DRY_RUN && echo "DRY RUN MODE - no changes will be made" Логирование — правильный подход# Цвета для терминала readonly RED='\033[0;31m' readonly GREEN='\033[0;32m' readonly YELLOW='\033[1;33m' readonly BLUE='\033[0;34m' readonly NC='\033[0m' # No Color # Лог-файл LOG_FILE="/var/log/myapp/deploy-$(date +%Y%m%d-%H%M%S).log" mkdir -p "$(dirname "$LOG_FILE")" log() { local level="$1" shift local message="$*" local timestamp timestamp=$(date '+%Y-%m-%d %H:%M:%S') # В файл — без цветов echo "[$timestamp] [$level] $message" >> "$LOG_FILE" # В терминал — с цветами case "$level" in INFO) echo -e "${GREEN}[INFO]${NC} $message" ;; WARN) echo -e "${YELLOW}[WARN]${NC} $message" ;; ERROR) echo -e "${RED}[ERROR]${NC} $message" >&2 ;; DEBUG) $VERBOSE && echo -e "${BLUE}[DEBUG]${NC} $message" ;; esac } # Использование log INFO "Starting deployment to $ENVIRONMENT" log WARN "Skipping health check in dry-run mode" log ERROR "Failed to connect to database" log DEBUG "Connection string: $DB_URL" Обработка ошибок и cleanuptrap — всегда убирай за собой#!/usr/bin/env bash set -euo pipefail # Временные файлы TEMP_DIR=$(mktemp -d) LOCK_FILE="/tmp/myapp.lock" # Функция очистки — выполняется при любом выходе cleanup() { local exit_code=$? log INFO "Cleaning up..." rm -rf "$TEMP_DIR" rm -f "$LOCK_FILE" if [[ $exit_code -ne 0 ]]; then log ERROR "Script failed with exit code $exit_code" # Отправляем уведомление send_alert "Deployment failed on $(hostname)" "$LOG_FILE" fi exit $exit_code } # Обработка сигналов error_handler() { local line_number="$1" log ERROR "Error on line $line_number" } trap cleanup EXIT trap 'error_handler $LINENO' ERR trap 'log WARN "Interrupted by user"; exit 130' INT TERM # Защита от параллельного запуска через lockfile acquire_lock() { if ! mkdir "$LOCK_FILE" 2>/dev/null; then local pid pid=$(cat "$LOCK_FILE/pid" 2>/dev/null || echo "unknown") log ERROR "Another instance is running (PID: $pid)" exit 1 fi echo $$ > "$LOCK_FILE/pid" log DEBUG "Lock acquired" } acquire_lock Retry-логика для нестабильных операций# Универсальная функция retry retry() { local max_attempts="$1" local delay="$2" local description="$3" shift 3 local cmd=("$@") local attempt=1 while true; do log INFO "[$attempt/$max_attempts] Trying: $description" if "${cmd[@]}"; then log INFO "Success: $description" return 0 fi if [[ $attempt -ge $max_attempts ]]; then log ERROR "All $max_attempts attempts failed: $description" return 1 fi log WARN "Attempt $attempt failed, retrying in ${delay}s..." sleep "$delay" ((attempt++)) # Экспоненциальная задержка (удваиваем каждый раз, макс 60 сек) delay=$(( delay * 2 > 60 ? 60 : delay * 2 )) done } # Использование retry 5 2 "Health check" curl -sf http://localhost/health retry 3 5 "Database backup" pg_dump myapp > /backup/dump.sql Параллельное выполнение задач# Простой способ — запуск в background + wait deploy_to_servers() { local servers=("$@") local pids=() local failed=0 for server in "${servers[@]}"; do log INFO "Starting deploy on $server" ( ssh "$server" 'cd /app && git pull && systemctl restart myapp' log INFO "Deploy done on $server" ) & pids+=($!) done # Ждём все процессы и собираем статусы for i in "${!pids[@]}"; do if ! wait "${pids[$i]}"; then log ERROR "Deploy failed on ${servers[$i]}" ((failed++)) fi done return $failed } # С ограничением параллелизма (не более N одновременно) run_parallel() { local max_jobs="$1" shift local items=("$@") local running=0 local pids=() for item in "${items[@]}"; do # Ждём если достигнут лимит while [[ $running -ge $max_jobs ]]; do # Проверяем завершившиеся процессы local new_pids=() for pid in "${pids[@]}"; do if kill -0 "$pid" 2>/dev/null; then new_pids+=("$pid") else ((running--)) fi done pids=("${new_pids[@]}") sleep 0.1 done process_item "$item" & pids+=($!) ((running++)) done wait } Работа с файлами и текстомБезопасное чтение конфигурации# Загрузка .env файла load_env() { local env_file="${1:-.env}" [[ -f "$env_file" ]] || { log WARN "No $env_file found"; return 0; } # Безопасная загрузка: только KEY=VALUE строки, без выполнения кода while IFS='=' read -r key value; do # Пропускаем комментарии и пустые строки [[ "$key" =~ ^[[:space:]]*# ]] && continue [[ -z "$key" ]] && continue # Удаляем пробелы вокруг ключа key="${key//[[:space:]]/}" # Удаляем кавычки из значения value="${value%\"}" value="${value#\"}" value="${value%\'}" value="${value#\'}" # Экспортируем только валидные имена переменных if [[ "$key" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then export "$key=$value" fi done < "$env_file" } Манипуляции со строками# Встроенные операции (без fork, быстрее) string="Hello, World!" # Длина echo "${#string}" # 13 # Подстрока (offset:length) echo "${string:7:5}" # World # Замена (первое вхождение) echo "${string/Hello/Hi}" # Hi, World! # Замена (все вхождения) path="/usr/local/bin:/usr/bin:/bin" echo "${path//:/ }" # /usr/local/bin /usr/bin /bin # Upper/Lower case (bash 4+) echo "${string^^}" # HELLO, WORLD! echo "${string,,}" # hello, world! # Trim (удаление пробелов) trim() { local str="$*" str="${str#"${str%%[![:space:]]*}"}" str="${str%"${str##*[![:space:]]}"}" echo "$str" } # Разбивка строки на массив IFS=',' read -ra parts <<< "one,two,three" for part in "${parts[@]}"; do echo "$part"; done Реальные примеры: скрипты для продакшнаСкрипт деплоя с zero-downtime#!/usr/bin/env bash set -euo pipefail DEPLOY_DIR="/var/www/myapp" RELEASES_DIR="$DEPLOY_DIR/releases" CURRENT_LINK="$DEPLOY_DIR/current" SHARED_DIR="$DEPLOY_DIR/shared" KEEP_RELEASES=5 deploy() { local release_dir release_dir="$RELEASES_DIR/$(date +%Y%m%d%H%M%S)" log INFO "Creating release directory: $release_dir" mkdir -p "$release_dir" # Клонируем или обновляем код log INFO "Deploying code..." if [[ -d "$DEPLOY_DIR/repo" ]]; then git -C "$DEPLOY_DIR/repo" fetch origin git -C "$DEPLOY_DIR/repo" archive HEAD | tar -x -C "$release_dir" else git clone --depth 1 "$GIT_REPO" "$DEPLOY_DIR/repo" git -C "$DEPLOY_DIR/repo" archive HEAD | tar -x -C "$release_dir" fi # Линкуем shared директории ln -sfn "$SHARED_DIR/uploads" "$release_dir/public/uploads" ln -sfn "$SHARED_DIR/.env" "$release_dir/.env" # Устанавливаем зависимости log INFO "Installing dependencies..." composer install --no-dev --optimize-autoloader -d "$release_dir" # Прогреваем кэш log INFO "Warming up cache..." php "$release_dir/spark" cache:clear # Переключаем симлинк атомарно log INFO "Switching to new release..." ln -sfn "$release_dir" "${CURRENT_LINK}.new" mv -Tf "${CURRENT_LINK}.new" "$CURRENT_LINK" # Перезагружаем PHP-FPM (graceful) kill -USR2 $(cat /var/run/php/php8.2-fpm.pid) # Очищаем старые релизы cleanup_old_releases log INFO "Deploy completed successfully!" } cleanup_old_releases() { local releases releases=$(ls -1t "$RELEASES_DIR") local count count=$(echo "$releases" | wc -l) if [[ $count -gt $KEEP_RELEASES ]]; then echo "$releases" | tail -n +"$((KEEP_RELEASES + 1))" | while read -r release; do log INFO "Removing old release: $release" rm -rf "$RELEASES_DIR/$release" done fi } rollback() { local previous previous=$(ls -1t "$RELEASES_DIR" | sed -n '2p') if [[ -z "$previous" ]]; then log ERROR "No previous release to rollback to" exit 1 fi log INFO "Rolling back to: $previous" ln -sfn "$RELEASES_DIR/$previous" "${CURRENT_LINK}.rollback" mv -Tf "${CURRENT_LINK}.rollback" "$CURRENT_LINK" kill -USR2 $(cat /var/run/php/php8.2-fpm.pid) log INFO "Rollback completed" } case "${1:-deploy}" in deploy) deploy ;; rollback) rollback ;; *) echo "Usage: $0 [deploy|rollback]"; exit 1 ;; esac Скрипт мониторинга и уведомлений#!/usr/bin/env bash set -euo pipefail # Пороговые значения CPU_THRESHOLD=85 MEM_THRESHOLD=90 DISK_THRESHOLD=85 LOAD_THRESHOLD=4.0 ALERT_EMAIL="ops@example.com" WEBHOOK_URL="${SLACK_WEBHOOK:-}" send_alert() { local subject="$1" local body="$2" # Email echo "$body" | mail -s "[$HOSTNAME] ALERT: $subject" "$ALERT_EMAIL" # Slack webhook if [[ -n "$WEBHOOK_URL" ]]; then curl -s -X POST "$WEBHOOK_URL" \ -H 'Content-type: application/json' \ -d "{\"text\":\":warning: *[$HOSTNAME]* $subject\n\`\`\`$body\`\`\`\"}" fi } check_cpu() { local cpu_usage cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1 | cut -d',' -f1) cpu_usage=${cpu_usage%.*} # целая часть if [[ $cpu_usage -gt $CPU_THRESHOLD ]]; then local top_processes top_processes=$(ps aux --sort=-%cpu | head -6 | tail -5) send_alert "High CPU: ${cpu_usage}%" "CPU usage: ${cpu_usage}%\n\nTop processes:\n$top_processes" fi } check_memory() { local mem_total mem_used mem_percent mem_total=$(free -m | awk '/^Mem:/{print $2}') mem_used=$(free -m | awk '/^Mem:/{print $3}') mem_percent=$(( mem_used * 100 / mem_total )) if [[ $mem_percent -gt $MEM_THRESHOLD ]]; then local top_processes top_processes=$(ps aux --sort=-%mem | head -6 | tail -5) send_alert "High Memory: ${mem_percent}%" "Memory: ${mem_used}MB / ${mem_total}MB (${mem_percent}%)\n\n$top_processes" fi } check_disk() { while IFS= read -r line; do local usage mount usage=$(echo "$line" | awk '{print $5}' | tr -d '%') mount=$(echo "$line" | awk '{print $6}') if [[ $usage -gt $DISK_THRESHOLD ]]; then send_alert "High Disk: $mount at ${usage}%" \ "Disk usage on $mount: ${usage}%\n\n$(df -h "$mount")" fi done < <(df -h | grep -E '^/dev/' | grep -v tmpfs) } check_services() { local services=("nginx" "php8.2-fpm" "mysql" "redis") for service in "${services[@]}"; do if ! systemctl is-active --quiet "$service"; then send_alert "Service DOWN: $service" \ "Service $service is not running!\n\n$(systemctl status "$service" --no-pager | tail -20)" # Попытка перезапуска log WARN "Attempting to restart $service..." systemctl restart "$service" && \ send_alert "Service RECOVERED: $service" "Service $service was restarted successfully" fi done } # Запускаем все проверки check_cpu check_memory check_disk check_services Лучшие практикиИспользуйте shellcheck — это ESLint для bash: apt install shellcheck shellcheck myscript.sh # В CI/CD: find . -name "*.sh" -exec shellcheck {} + Тестируйте скрипты с bats: apt install bats # test.bats: @test "cleanup removes old files" { touch /tmp/testfile run cleanup /tmp/testfile [ "$status" -eq 0 ] [ ! -f /tmp/testfile ] } bats test.bats Документируйте через heredoc: show_help() { cat <<'EOF' ...документация... EOF } Bash — это мощный инструмент, но он требует дисциплины. Скрипт, написанный правильно — это надёжный автоматизированный сотрудник. Написанный небрежно — бомба с часовым механизмом.
  10. Сервер работает, но медленно. Страницы грузятся с задержкой, база данных тормозит, под нагрузкой всё падает. Прежде чем покупать новое железо или мигрировать на более дорогой тариф — убедитесь, что вы используете текущее на 100%. Эта статья о том, как профилировать, диагностировать и оптимизировать Linux-сервер под реальную нагрузку. Методология: сначала измеряем, потом меняемГлавная ошибка при оптимизации — начинать с изменений без понимания проблемы. «Давайте увеличим swap» или «добавим RAM» — это стрельба вслепую. Правильный подход: измерить → найти узкое место → устранить → снова измерить. Инструментарий делится на уровни детализации: системный уровень → top, htop, vmstat, iostat, sar процессный уровень → perf, strace, ltrace, lsof сетевой уровень → ss, netstat, iftop, nethogs, tcpdump дисковый уровень → iotop, blktrace, fio профилировщики → perf, bpftrace, flame graphs CPU: диагностика и оптимизацияЧто жрёт процессор# Базовый top с сортировкой по CPU top -o %CPU # htop — интерактивный, красивый htop # Детальная картина по ядрам mpstat -P ALL 1 5 # 5 измерений каждую секунду # pidstat — статистика по процессам pidstat -u 1 10 # каждую секунду, 10 раз Смотрите на us (userspace), sy (kernel), wa (iowait), st (steal — для виртуалок). Высокий wa — проблема с диском или сетью, а не с CPU. Приоритеты процессов — nice и ionice# Запустить процесс с низким приоритетом 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) Привязка процессов к ядрам — CPU affinityДля highload-сервисов критично избегать перепрыгивания между ядрами (cache miss): # Запустить nginx worker на ядрах 0-3 taskset -c 0-3 nginx # Изменить для запущенного процесса taskset -pc 0-3 $(pgrep nginx | head -1) # Посмотреть текущую привязку taskset -p 1234 perf — профилировщик ядра# Установка 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 > perf.out # Flame graph (нужен Brendan Gregg's flamegraph) git clone https://github.com/brendangregg/FlameGraph ./FlameGraph/stackcollapse-perf.pl perf.out | ./FlameGraph/flamegraph.pl > flame.svg Flame graph — это визуализация стека вызовов. Широкие «языки пламени» = много времени в этой функции. Память: диагностика и управлениеПонимаем использование памяти# Читаем правильно — 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 Virtual Memory: tuning через sysctl/etc/sysctl.d/99-vm.conf: # 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 # осторожно, лучше знать что делаете Huge Pages для баз данныхPostgreSQL, MySQL и Java-приложения хорошо работают с huge pages (2MB вместо 4KB): # Проверяем текущее состояние grep -i huge /proc/meminfo # Transparent Huge Pages (THP) — автоматически cat /sys/kernel/mm/transparent_hugepage/enabled # Для баз данных THP лучше отключить! echo never > /sys/kernel/mm/transparent_hugepage/enabled echo never > /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 Диски: I/O оптимизацияДиагностика дисковой подсистемы# Общая нагрузка на диски в реальном времени iostat -xz 1 # Ключевые метрики: # %util — насколько занят диск (>80% = проблема) # await — среднее время ожидания запроса (мс) # r/s, w/s — операции чтения/записи в секунду # Кто именно читает/пишет iotop -o # только активные процессы # Детальная трассировка blktrace -d /dev/sda -o - | blkparse -i - Планировщик I/O# Посмотреть текущий scheduler cat /sys/block/sda/queue/scheduler # Выбор scheduler: # none/mq-deadline — для NVMe SSD (hardware очередь хороша) # mq-deadline — для SATA SSD и HDD # bfq — для десктопа, справедливое распределение echo mq-deadline > /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" Параметры очереди# Размер очереди запросов (для NVMe можно больше) cat /sys/block/nvme0n1/queue/nr_requests echo 1024 > /sys/block/nvme0n1/queue/nr_requests # Read-ahead: сколько данных читать заранее # Для баз данных — уменьшаем, для стриминга файлов — увеличиваем blockdev --setra 256 /dev/sda # 256 * 512 = 128KB Тестирование производительности дисков# 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 Сеть: тюнинг стека TCP/IPДиагностика сетевой подсистемы# Текущие соединения и их состояния 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 Тюнинг TCP стека/etc/sysctl.d/99-network.conf: # Размеры буферов сокетов 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 # Проверяем что BBR загружен sysctl net.ipv4.tcp_congestion_control lsmod | grep bbr Настройка сетевого интерфейса# Проверяем настройки карты 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 Профилирование с bpftrace и eBPFСовременный инструментарий для глубокого анализа без влияния на производительность: # Установка apt install bpftrace # Топ системных вызовов bpftrace -e 'tracepoint:syscalls:sys_enter_* { @[probe] = count(); }' # Медленные запросы к диску (>10мс) bpftrace -e 'tracepoint:block:block_rq_complete { if (args->nr_sector > 0) { $latency = (nsecs - @start[args->sector]) / 1000000; if ($latency > 10) { printf("Slow I/O: %d ms\n", $latency); } } }' # Трассировка TCP retransmits bpftrace -e 'tracepoint:tcp:tcp_retransmit_skb { @[comm] = count(); }' Профилирование PHP-FPM специфическиРаз уж мы работаем с PHP: # Статус 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>&1 | head -50 Система мониторинга производительностиsar — System Activity Reporterapt 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 > /tmp/system_report_$(date +%Y%m%d).txt vmstat — обзор системы одной командой# Запускаем каждую секунду 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 Если r > числа CPU — CPU перегружен. Если wa > 20% — проблема с диском. Тюнинг NUMA-системДля серверов с несколькими CPU-сокетами: # Проверяем топологию NUMA numactl --hardware numastat # Запуск на конкретном NUMA узле numactl --cpunodebind=0 --membind=0 ./your_app # Для Redis на NUMA-системе numactl --interleave=all redis-server # Автоматический балансировщик NUMA echo 1 > /proc/sys/kernel/numa_balancing Чеклист оптимизацииПорядок важен — сначала находим, потом чиним: Измерить базовые метрики (top, iostat, free, ss) Определить узкое место (CPU / RAM / Disk I/O / Network) Детальное профилирование проблемной подсистемы Применить изменение Снова измерить — убедиться в улучшении Применить к продакшну через конфиги (sysctl, udev, systemd) Помните: преждевременная оптимизация — корень всех зол. Оптимизируйте то, что реально тормозит, с данными в руках.
  11. Когда вы разворачиваете новый Linux-сервер, он похож на только что купленный смартфон — всё работает из коробки, но настройки безопасности далеки от идеала. SSH открыт для всего мира, root логинится напрямую, лишние сервисы весело крутятся в фоне. Эта статья — пошаговый гайд по hardening'у: от первого входа до полноценно защищённого продакшн-сервера. Шаг 1. Первый вход и базовая гигиенаОбновление системы — это не опциональноПервое, что делаем после подключения: apt update && apt upgrade -y # Debian/Ubuntu dnf update -y # RHEL/CentOS/Fedora Звучит банально, но большинство взломов происходит через известные уязвимости, для которых патчи уже давно вышли. Настройте автоматические обновления безопасности: # Ubuntu/Debian apt install unattended-upgrades -y dpkg-reconfigure --priority=low unattended-upgrades Файл конфигурации /etc/apt/apt.conf.d/50unattended-upgrades — проверьте, что там включены только security-обновления, а не всё подряд. Автоапгрейд всего и сразу на продакшне — риск, обновления безопасности — необходимость. Создание непривилегированного пользователяРаботать под root — это как ходить с заряженным пистолетом в кармане без предохранителя. Создаём обычного пользователя и даём ему sudo: adduser deploy usermod -aG sudo deploy # Или на RHEL-системах useradd -m -G wheel deploy passwd deploy Проверяем, что sudo работает: su - deploy sudo whoami # должен вернуть root Шаг 2. Настройка SSH — главные ворота на серверSSH-демон — самое атакуемое место на любом публичном сервере. Посмотрите на логи нового сервера через сутки после запуска: grep "Failed password" /var/log/auth.log | wc -l Тысячи попыток — это норма. Потому что боты сканируют интернет 24/7. Ключи вместо паролейГенерируем ключ на локальной машине (если ещё нет): ssh-keygen -t ed25519 -C "your_email@example.com" # ed25519 современнее и быстрее RSA-4096, при той же безопасности Копируем публичный ключ на сервер: ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@your-server-ip Или вручную: mkdir -p ~/.ssh chmod 700 ~/.ssh echo "ваш_публичный_ключ" >> ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys Конфигурация sshdРедактируем /etc/ssh/sshd_config: # Порт — меняем с дефолтного 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 Перезапускаем sshd — но сначала проверяем конфиг: sshd -t # тест конфига без перезапуска systemctl restart sshd Важно: не закрывайте текущую сессию, пока не убедитесь, что можете подключиться с новыми настройками в отдельном окне терминала. Двухфакторная аутентификация через Google AuthenticatorДля особо важных серверов добавляем TOTP: apt install libpam-google-authenticator -y # Запускаем от имени пользователя deploy su - deploy google-authenticator Отвечаем на вопросы (рекомендую: yes, yes, no, yes), сохраняем QR-код и backup-коды. В /etc/pam.d/sshd добавляем: auth required pam_google_authenticator.so В sshd_config: ChallengeResponseAuthentication yes AuthenticationMethods publickey,keyboard-interactive Шаг 3. Firewall — iptables vs nftables vs ufwUFW — простой вариант для Ubuntuapt 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 nftables — современный и мощный подходnftables — замена iptables, доступная в современных дистрибутивах. Конфиг в /etc/nftables.conf: #!/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; } } systemctl enable nftables systemctl start nftables nft list ruleset # проверяем Защита от брутфорса — fail2banfail2ban анализирует логи и банит IP-адреса, которые ломятся брутфорсом: apt install fail2ban -y Создаём /etc/fail2ban/jail.local: [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 systemctl enable fail2ban systemctl start fail2ban fail2ban-client status # проверяем статус fail2ban-client status sshd # статус конкретного jail Шаг 4. Минимизация поверхности атакиОтключаем ненужные сервисыСмотрим, что запущено: systemctl list-units --type=service --state=running Типичные кандидаты на отключение на серверах без GUI: systemctl disable --now avahi-daemon # mDNS — нужен только в локалке systemctl disable --now cups # принтеры — зачем на сервере? systemctl disable --now bluetooth # очевидно systemctl disable --now ModemManager # модемы на сервере? Удаляем ненужные пакетыapt autoremove --purge apt purge telnet rsh-client rsh-redone-client # старые небезопасные протоколы Ограничиваем права на критичные файлы# Проверяем SUID/SGID файлы — потенциальные векторы privilege escalation find / -perm /4000 -o -perm /2000 2>/dev/null | grep -v proc # Устанавливаем строгие права на критичные файлы chmod 600 /etc/shadow chmod 644 /etc/passwd chmod 644 /etc/group chmod 600 /boot/grub/grub.cfg Настройка sysctl — параметры ядраРедактируем /etc/sysctl.d/99-security.conf: # Защита от 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 # если поддерживается sysctl -p /etc/sysctl.d/99-security.conf Шаг 5. Аудит и мониторинг безопасностиauditd — аудит системных вызовов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 AIDE — контроль целостности файловAIDE создаёт базу данных хешей критичных файлов и сигнализирует об изменениях: apt install aide -y aideinit # инициализируем базу (занимает время) cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db # Проверка (запускаем по крону ежедневно) aide --check Добавляем в cron: 0 4 * * * root /usr/bin/aide --check | mail -s "AIDE check $(hostname)" admin@example.com Проверяем открытые порты и соединения# Смотрим что слушает ss -tlnp # лучше чем netstat # или netstat -tlnp # Открытые соединения ss -tnp state established # Проверяем на rootkits apt install rkhunter chkrootkit rkhunter --update rkhunter --check chkrootkit Шаг 6. AppArmor и SELinux — мандатный контроль доступаAppArmor (Ubuntu/Debian)AppArmor ограничивает приложения набором разрешённых действий: apt install apparmor apparmor-utils -y systemctl enable apparmor aa-status # проверяем статус профилей # Переводим профили в enforce mode aa-enforce /etc/apparmor.d/* # Создаём профиль для приложения aa-genprof /usr/sbin/nginx SELinux (RHEL/CentOS)# Проверяем статус getenforce # Enforcing/Permissive/Disabled # Включаем если выключен setenforce 1 # В /etc/selinux/config: SELINUX=enforcing # Просматриваем нарушения audit2why < /var/log/audit/audit.log # Создаём разрешающий модуль из нарушений (осторожно!) audit2allow -a -M mypolicy semodule -i mypolicy.pp Шаг 7. Шифрование и безопасность данныхШифрование диска — LUKSЛучше делать при установке, но можно и на существующей системе (с резервным копированием!): # Создаём зашифрованный контейнер cryptsetup luksFormat /dev/sdb1 # Открываем cryptsetup luksOpen /dev/sdb1 encrypted_data # Монтируем mkfs.ext4 /dev/mapper/encrypted_data mount /dev/mapper/encrypted_data /mnt/secure Безопасное хранение секретовНикогда не кладите пароли и ключи в переменные окружения напрямую или в конфиги в git. Варианты: # 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 - > /etc/credstore/db-password.cred # В unit-файле: # LoadCredential=db-password:/etc/credstore/db-password.cred Чеклист: что проверить перед деплоемФинальный чеклист для продакшн-сервера: [ ] Система обновлена, настроены автоматические security-патчи [ ] Создан непривилегированный пользователь, root-вход запрещён [ ] SSH: только ключи, нестандартный порт, AllowUsers [ ] Firewall настроен, открыты только нужные порты [ ] fail2ban запущен и настроен [ ] Отключены ненужные сервисы [ ] sysctl параметры безопасности установлены [ ] auditd запущен, правила аудита настроены [ ] AIDE инициализирован, проверка по расписанию [ ] AppArmor/SELinux в enforcing mode [ ] Секреты не хранятся в открытом виде [ ] Настроено оповещение о нарушениях безопасности Инструменты автоматической проверки# 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 Безопасность — это не состояние, это процесс. Регулярно проверяйте логи, обновляйте систему, пересматривайте правила по мере изменения инфраструктуры. И помните: самое уязвимое звено всегда находится между монитором и креслом.
  12. Мир IT огромен и многогранен: здесь нужны специалисты самых разных направлений, от продуктовой аналитики до DevOps и дизайна. Разберём ключевые роли, чтобы понять, кто за что отвечает и какие задачи выполняет. 🔹 Продукт и аналитика1. Product Manager (PM)Роль: отвечает за продукт целиком — от идеи до метрик. Задачи: формировать стратегию продукта и ставить цели; проводить исследования, интервью с пользователями и анализ конкурентов; описывать функциональность (PRD, User Stories); расставлять приоритеты: что делаем сейчас, что откладываем; работать с командами разработки, дизайна и маркетинга; анализировать метрики, управлять ростом и оптимизировать продукт. Дополнительно: PM часто взаимодействует с бизнес-аналитиками и UX-командой, чтобы продукт был не только функциональным, но и востребованным. 2. Business Analyst (BA)Роль: связывает бизнес-задачи с технической реализацией. Задачи: формализует требования бизнеса; анализирует процессы и выявляет узкие места; помогает команде разработки понять цели и приоритеты; участвует в тестировании и оценке фичей. Варианты: иногда BA объединяет функции PM или Data Analyst в небольших командах. 🔹 Разработка3. Frontend DeveloperРоль: создаёт интерфейс — всё, что видит пользователь. Задачи: реализовывать дизайн в коде; создавать интерактивные элементы; подключать API и работать с данными; оптимизировать скорость загрузки страниц; поддерживать кроссбраузерность и адаптивность. Дополнительно: участвует в уточнении требований и тестировании UI. 4. Backend DeveloperРоль: отвечает за серверную часть — «мозги» приложения. Задачи: разрабатывать API и бизнес-логику; работать с базами данных; создавать системы авторизации, уведомлений, платежей; обеспечивать безопасность данных; оптимизировать производительность серверов. 5. Mobile DeveloperРоль: работает над мобильными приложениями. Задачи: создавать приложение под iOS и Android; реализовывать дизайн и бизнес-логику; оптимизировать работу под разные устройства; интегрировать API, пуш-уведомления, авторизацию и оплату; выпускать обновления и фиксить баги. 6. Full-stack DeveloperРоль: закрывает фронтенд и бэкенд. Задачи: вести разработку «от интерфейса до базы данных»; быстро создавать прототипы; поддерживать продукт в малых командах; координировать взаимодействие между частями. 7. QA Engineer (тестировщик)Роль: отвечает за качество продукта. Задачи: писать тест-кейсы и чек-листы; искать баги и оформлять отчёты; проверять фичи перед релизом; автоматизировать тесты (QA-автоматизатор); контролировать стабильность продукта после обновлений. 🔹 Архитектура и инфраструктура8. System ArchitectРоль: проектирует структуру продукта. Задачи: выбирать архитектурные паттерны; проектировать взаимодействие сервисов; обеспечивать масштабируемость и надёжность; консультировать разработчиков по сложным задачам; решать, какие технологии использовать. 9. DevOps / SREРоль: отвечает за стабильность, инфраструктуру и автоматизацию. Задачи: настраивать и поддерживать серверы, контейнеры, облака; автоматизировать CI/CD; мониторить систему и устранять сбои; обеспечивать безопасность сервисов; оптимизировать стоимость инфраструктуры. 10. Data EngineerРоль: работает с потоками данных. Задачи: строить ETL-пайплайны; настраивать хранилища, базы данных, Data Lakes; обеспечивать качество и доступность данных; оптимизировать процессы передачи данных. 🔹 Аналитика и ML11. Data AnalystРоль: отвечает за анализ данных и выводы для бизнеса. Задачи: собирать данные из разных источников; строить дашборды и отчёты; искать аномалии, паттерны, точки роста; проводить A/B-тесты; отвечать на вопросы бизнеса цифрами. 12. Data ScientistРоль: создаёт модели машинного обучения. Задачи: разрабатывать модели (рекомендации, прогнозы, классификация); готовить датасеты; обучать и тестировать модели; внедрять модели в продукт; анализировать качество моделей и корректировать алгоритмы. 🔹 Дизайн и пользовательский опыт13. UX/UI DesignerРоль: отвечает за интерфейсы и визуальную часть продукта. Задачи: исследовать пользователей и сценарии; строить CJM и проектировать логику экранов; создавать прототипы и макеты; работать с дизайн-системами; передавать макеты разработчикам. 14. UX ResearcherРоль: связывает продукт с реальными потребностями пользователей. Задачи: проводить интервью и исследования; устраивать юзабилити-тесты; формировать гипотезы и проверять их; давать рекомендации по улучшению продукта; подтверждать продуктовые решения данными. 🔹 Контент и коммуникации15. Technical WriterРоль: создаёт документацию для пользователей и разработчиков. Задачи: писать инструкции, гайды, мануалы; описывать API и технические процессы; обновлять документацию после релизов; работать с разработчиками для уточнений. 16. Community ManagerРоль: выстраивает отношения с пользователями и сообществом. Задачи: отвечать на вопросы в чатах и соцсетях; собирать обратную связь; формировать лояльность пользователей; помогать пользователям решать проблемы; эскалировать важные запросы команде. 🔹 Управление процессами17. Project Manager (PM / PMO)Роль: следит за тем, чтобы задачи выполнялись вовремя, а команда работала слаженно. Задачи: ставить задачи и планировать спринты; контролировать сроки и ресурсы; поддерживать коммуникацию между командами; снимать блокеры у специалистов; управлять рисками и таймлайнами.
  13. Линус Торвальдс и его отказ от C++ для ядра LinuxЛинус Торвальдс, создатель Linux и «великодушный диктатор» проекта, известен своей критикой языка C++. Он не отвергает его просто так — он приводит убедительные технические и практические аргументы против его применения в ядре Linux. В чем же причина неприятия C++? Давайте разберем ключевые доводы Линуса. Почему C++ не подходит для ядра LinuxC и C++ похожи, но не идентичны. C++ — это объектно-ориентированное расширение C, добавляющее классы, конструкторы, деструкторы, шаблоны, обработку исключений, пространства имен и перегрузку операторов. Эти «улучшения» приносят новые парадигмы, но и новые риски. 1. Обработка исключений В C ошибки обрабатываются через возвращаемые значения, что делает поведение программы предсказуемым. В C++ используются исключения, которые могут возникнуть в любой части кода. Для ядра с миллионами строк кода это неприемлемо: Исключения трудно отлаживать; Они меняют парадигму обработки ошибок; Могут привести к нестабильности ядра. Для пользовательских приложений вроде GNOME редактора это мелочь, но для ядра Linux — реальный риск. 2. Управление памятью и RAII C++ использует RAII (Resource Acquisition Is Initialization), автоматическое управление ресурсами через деструкторы. В ядре Linux память контролируется тонко и вручную, чтобы обеспечить максимальную производительность. Автоматизация в C++: Увеличивает зависимость от компилятора; Может снижать скорость работы модулей; Увеличивает вероятность багов. 3. Объектно-ориентированное программирование Линус считает, что ООП полезно, но C++ вносит слишком много лишнего. ООП можно реализовать и на чистом C через структуры и функции. Пример «класса» на C: #include <stdio.h> #include <stdlib.h> typedef struct { int value; void (*increment)(struct Person *self); } Person; void increment_person(Person *self) { self->value++; } Person* person_new(int initial_value) { Person *p = (Person*)malloc(sizeof(Person)); p->value = initial_value; p->increment = increment_person; return p; } void person_free(Person *p) { free(p); } int main() { Person *p = person_new(5); printf("Initial value: %d\n", p->value); p->increment(p); printf("After increment: %d\n", p->value); person_free(p); return 0; } Это доказывает, что функциональность ООП доступна и в чистом C. 4. Стабильность библиотек и зависимостей Пользовательские библиотеки вроде STL или Boost могут быть стабильными для приложений, но не для ядра. Любая новая зависимость: Увеличивает риски безопасности; Требует поддержки и обновлений; Может снизить производительность. Пример — уязвимость CVE-2024–3094 в liblzma, когда сторонний бэкдор повлиял на безопасность. 5. Сложные абстракции ОП-паттерны могут быть удобны для приложений, но в ядре Linux они опасны: Большие иерархии классов трудно поддерживать; Ошибки в логике абстракций могут разрушить архитектуру; Потребуется переработка больших частей кода. Итог: почему ядро Linux на чистом C Аргумент Линуса: разработка ядра требует предсказуемости, производительности и стабильности. C++ добавляет много возможностей, но они создают риски, которые ядро не может себе позволить. Использование чистого C привлекает разработчиков, сосредоточенных на аппаратных и системных задачах, а не на сложностях ООП. Будущее: Rust, Go или Java?Линус открыто обсуждал Rust для ядра. Вопросы применения Go, Java или C# повторяют дискуссию о C++. Основной принцип остается прежним: ядро должно оставаться максимально стабильным и оптимизированным. Выводы для разработчиковВыбор языка и инструментов влияет на стабильность и производительность; Любые зависимости требуют оценки долгосрочных последствий; Иногда простота C — лучший выбор для системного программирования; Разработчики должны балансировать эргономику и стабильность, особенно в критичных проектах. Ключевой урок: Линус демонстрирует, что при разработке системного программного обеспечения важнее стабильность и предсказуемость, чем удобство и новые парадигмы. Иногда лучше использовать проверенные, простые инструменты, чтобы гарантировать работу миллионов устройств по всему миру.
  14. Введение: что мы создаём в GoВ Go есть два больших семейства типов: Value types — обычные значения: int, float64, bool, struct, массивы. Reference types — ссылочные структуры: slice, map, chan. Value types можно размещать где угодно: в стеке, в куче или внутри других объектов. Reference types — это конструкции уровня рантайма с внутренними механизмами: простое объявление через var даст nil и вызовет панику при попытке использовать. Именно поэтому Go использует new и make по-разному. new в Go: простое выделение памятиФункция new(T) выделяет память под тип T, обнуляет её и возвращает указатель на T. type Config struct { Enabled bool Count int } cfg := new(Config) // cfg имеет тип *Config // cfg.Enabled == false // cfg.Count == 0 Внутри вызывается runtime.newobject, который делает malloc нужного размера и очищает память нулями. Подходит для value types: int, string, bool, struct, array. new(T) vs &T{}type User struct { Name string Age int } u1 := new(User) // выделяет объект в куче u2 := &User{} // может быть в стеке или куче Разница: new(T) всегда аллоцирует в куче. &T{} может остаться в стеке, если компилятор считает это безопасным (escape analysis). Используйте &T{} для высокоэффективного кода с минимальными аллокациями. Когда new действительно нуженGeneric‑код: тип T неизвестен на этапе компиляции. func NewPointer[T any]() *T { return new(T) } Явная аллокация в куче: иногда нужно гарантировать объект в heap. pool := sync.Pool{ New: func() any { return new(MyStruct) }, } Опциональные значения через nil: type Options struct { RetryCount *int } o := Options{} o.RetryCount = new(int) *o.RetryCount = 3 Ограничения newnew([]int) вернёт *[]int с nil — использовать как полноценный slice нельзя: s := new([]int) fmt.Println(*s == nil) // true (*s)[0] = 1 // panic Аналогично с map и chan. make в Go: инициализация runtime‑структурmake не просто выделяет память. Он создаёт рабочие slice, map и chan с полностью инициализированными внутренними структурами. Slice: указатель, длина, вместимостьs := make([]int, 10, 100) Создаёт slice header: type sliceHeader struct { Data uintptr // указатель на массив Len int Cap int } Slice готов к использованию и может расширяться до cap без новых аллокаций. var s []int даст nil-слайс — любое обращение к элементу вызовет панику. Map: работа с бакетамиm := make(map[string]int, 100) Инициализирует внутреннюю структуру hmap: count, flags, бакеты, старые бакеты для перестройки. new(map[string]int) создаст только nil-указатель — использовать его нельзя. Chan: синхронизация и буферc := make(chan int, 5) Создаёт полноценную очередь сообщений с буфером и счетчиками. var c chan int даст nil-канал — операции блокируют навсегда. Когда использовать make, а когда newСценарий Выбор Почему Slice, map, chan make Создаёт рабочую структуру, готовую к использованию Value type, generic new Получение указателя на zero-value Явная куча для value type new Гарантированное выделение в heap Опциональные значения new Возможность различать nil и заданное значение Выводnew: простое выделение памяти и указатель на ноль. make: инициализация runtime‑структур, готовых к работе. Понимание разницы между ними — обязательный минимум для любого разработчика на Go. Используйте make для slice, map и chan, а new — для value types и generic-кода.
  15. ВведениеПеренос базы данных PostgreSQL — задача непростая, особенно для больших проектов. Часто это один из самых крупных и ответственных процессов для разработчиков и администраторов. Основные сценарии переноса включают: обновление до новой версии PostgreSQL; перенос базы на другой сервер или хостинг; миграция с минимальным временем простоя. В зависимости от размера базы и ограничений инфраструктуры есть три основных подхода. 1. Перенос с помощью pg_dump и pg_restorepg_dump позволяет создать дамп всей базы, включая схемы, таблицы и специальные объекты. Для небольших баз (50–150 ГБ) это часто самый простой вариант. Пример использования: pg_dump -Fc $SOURCE_DB_URI > dump_file.dump pg_restore --no-acl --no-owner -d $TARGET_DB_URI dump_file.dump Плюсы: Надёжно и просто; Полный дамп базы, включая схему и данные. Минусы: При больших базах (сотни ГБ и выше) процесс может занять часы; Требуется время на восстановление и минимизация простоя. 2. Использование WAL (Write-Ahead Logging)Если у вас настроено резервное копирование на основе WAL, например через pgBackRest, WAL-G или WAL-E, можно выполнить масштабную миграцию: Создаётся полная резервная копия базы; Настраивается потоковая передача WAL на новый сервер; После завершения первичной синхронизации можно переключить приложение на новую базу с минимальным простоем. Плюсы: Подходит для терабайтных баз; Минимизирует простой. Минусы: Требует доступа к WAL (не поддерживается, например, в Amazon RDS). 3. Логическая миграция PostgreSQLЛогическая репликация позволяет переносить данные на новый сервер без доступа к WAL. Принцип работы: текущая база (publisher) передаёт изменения новой базе (subscriber); Репликация распространяется на данные таблиц, но не переносит схему, индексы и последовательности; С помощью дополнительных шагов можно выполнить полную миграцию. Основные шаги логической миграцииШаг 1: Перенос схемыСначала необходимо создать на новом сервере структуру базы: pg_dump -Fc -s $SOURCE_DB_URI | pg_restore --no-acl --no-owner -d $TARGET_DB_URI При активной разработке изменений схемы: синхронизируйте изменения и на подписчике. Шаг 2: Настройка издателя (старый сервер)Включите логическую репликацию: ALTER SYSTEM SET wal_level = logical; Настройте параметры слотов репликации: max_replication_slots max_wal_senders max_logical_replication_workers max_worker_processes max_sync_workers_per_subscription Убедитесь, что сеть разрешает подключения с нового сервера. Создайте пользователя для репликации: CREATE ROLE elizabeth WITH REPLICATION LOGIN PASSWORD 'my_password'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO elizabeth; Определите таблицы без первичных ключей: select tab.table_schema, tab.table_name from information_schema.tables tab left join information_schema.table_constraints tco on tab.table_schema = tco.table_schema and tab.table_name = tco.table_name and tco.constraint_type = 'PRIMARY KEY' where tab.table_type = 'BASE TABLE' and tab.table_schema not in ('pg_catalog', 'information_schema') and tco.constraint_name is null order by table_schema, table_name; Для таких таблиц используйте уникальный индекс или REPLICA IDENTITY FULL: ALTER TABLE tablename REPLICA IDENTITY USING INDEX idx_unique_index; -- или ALTER TABLE tablename REPLICA IDENTITY FULL; Создайте публикацию всех таблиц: CREATE PUBLICATION bridge_migration FOR ALL TABLES; SELECT * FROM pg_publication_tables; Шаг 3: Настройка подписчика (новый сервер)Создаём подписку на публикацию: CREATE SUBSCRIPTION bridge_migration CONNECTION 'host={host} port=5432 dbname={database} user={login} password={password}' PUBLICATION bridge_migration; Для больших баз можно ограничить число одновременно синхронизируемых таблиц через max_sync_workers_per_subscription. Шаг 4: Мониторинг первичной загрузкиПроверяем прогресс через: SELECT * FROM pg_stat_subscription; SELECT * FROM pg_subscription_rel; Состояния таблиц: i — инициализация d — копирование данных f — копирование завершено s — синхронизация выполнена r — обычная репликация Шаг 5: Тестирование и переключениеОстановите запись на исходной базе; Проверьте данные на новом сервере; Переключите приложение на новую базу. Шаг 6: Синхронизация последовательностейЛогическая репликация не переносит последовательности. Используйте команды setval: SELECT 'SELECT setval(' || quote_literal(quote_ident(n.nspname) || '.' || quote_ident(c.relname)) || ', ' || s.last_value || ');' FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace JOIN pg_sequences s ON s.schemaname = n.nspname AND s.sequencename = c.relname WHERE c.relkind = 'S'; Выполните результат на новом сервере, чтобы синхронизировать все последовательности. ЗаключениеЛогическая репликация — безопасный и эффективный способ миграции PostgreSQL, особенно при крупных базах и ограничениях по доступу к WAL. Данные остаются согласованными, если схема подписчика идентична схеме издателя; Репликация однонаправленная, без конфликтующих записей на подписчике; Можно минимизировать простой при переходе на новый сервер.
  16. ВведениеСобытийно-ориентированные архитектуры (EDA) на бумаге выглядят идеальными: продюсеры и консюмеры отделены друг от друга, потоки асинхронны, а система легко масштабируется. Но реальность часто оказывается сложнее. Представьте распродажу на «Чёрную пятницу»: ваша система обработки платежей получает в 5 раз больше трафика. В этот момент серверлесс-функции запускаются «холодно», очереди SQS переполняются, а DynamoDB начинает троттлить. Результат: сбои заказов клиентов. И это не гипотетический сценарий — с этим сталкиваются многие команды eCommerce, SaaS и FinTech. Система EDA в высокоуровневом виде состоит из трёх компонентов: продюсер → буфер/очередь → консюмер. При проектировании важно учитывать не только непрерывную работу, но и предсказуемость системы под нагрузкой. Пиковые нагрузки могут быть вызваны интеграциями, узкими местами потребителей или бесконечными повторными попытками сообщений — всё это проверяет архитектуру на прочность. Задержка — не единственная проблемаКогда говорят о производительности EDA, обычно имеют в виду задержку. Но для отказоустойчивых систем важны также: Пропускная способность Эффективное использование ресурсов Надёжная передача данных между компонентами Пример: Если сервис зависит от SQS и трафик резко возрастает, downstream-системы могут перегрузиться. Это приводит к повторным попыткам, росту задержек и искажению метрик мониторинга. Даже продуманный DLQ, экспоненциальное затухание и троттлинг не решат проблему, если не учитывать контракты между компонентами. Вывод: задержка — это сигнал о «давлении» в системе. Её нужно воспринимать как индикатор накопления нагрузки, а не только минимизировать. Паттерны проектирования для масштабируемости и отказоустойчивости1. Шардирование и перемешивающее шардированиеРазделяйте клиентов или события на несколько шардов, чтобы шумный клиент не перегружал всю систему. Пример: В очереди SQS несколько клиентов могут быть хэшированы на одну очередь. Если один клиент начинает генерировать пик событий, он влияет на всех остальных. Перемешивающее шардирование уменьшает вероятность этого, распределяя клиентов случайным образом по разным очередям. 2. Предварительное выделение ресурсов для критических задачДля задач с высокой чувствительностью к задержке (например, обнаружение мошенничества в FinTech) заранее выделяйте ресурсы. Пример: Для AWS Lambda используйте provisioned concurrency или авто-масштабирование с выделенной параллельностью. Это гарантирует быструю обработку критических событий, сохраняя экономичность при изменении нагрузки. Паттерны инфраструктуры1. Очереди и буферыОчереди SQS, Kafka, Kinesis и EventBridge действуют как буферы между продюсерами и консюмерами, поглощая резкие всплески нагрузки. Пример: Реальное время кликов на рекламной платформе → Kinesis (шардирование по региону) Выставление счетов → FIFO SQS для гарантии порядка и предотвращения дублирования 2. Быстрый сбой и предсказуемый отказЕсли консюмер не может обработать событие (например, база данных недоступна), лучше завершить операцию с ошибкой сразу, чем блокировать очередь на длительное время. Пример: Контейнер Lambda зависал на аутентификации 30 секунд → добавили тайм-аут 5 секунд и явное завершение с ошибкой → очередь перестала накапливать сообщения. Распространённые ошибки и как их избежатьПереоценка средней нагрузки: Систему нужно тестировать под резкие пики (p95, p99), а не под средние значения. Повторные попытки как панацея: Бесконтрольные повторные попытки могут создать петли трафика и троттлинг. Используйте экспоненциальное затухание с джиттером и разделяйте ошибки на повторяемые и нет. Недостаточная наблюдаемость: Метрики должны показывать не только ошибки и время отклика, но и глубину очередей, повторные попытки и масштабируемость компонентов. Одинаковое обращение со всеми событиями: Событие оплаты ≠ событие логирования. Разделяйте критические и низкоприоритетные события с помощью отдельных очередей или маршрутизации в разные Lambdas. ЗаключениеОтказоустойчивость — это не попытка создать «идеальную систему», а способность выдерживать удары и продолжать работу. Основные принципы: Эластичность и буферы, поглощающие пики нагрузки Умные повторные попытки Предсказуемые режимы отказа Наблюдаемость, позволяющая подтверждать работоспособность системы С чего начать: Создайте простое событийно-ориентированное приложение на SQS и Lambda. Попробуйте DLQ, обработку сбоев и маршрутизацию событий через EventBridge. Постепенно добавляйте шардирование, авто-масштабирование и сложные паттерны. Отказоустойчивость — это подход, который строится шаг за шагом. Начните с малого, изучайте поведение системы и постепенно добавляйте сложность.
  17. Ошибки в сетевой инфраструктуре: от планирования до эксплуатацииОшибки, которые могут допустить IT-специалисты, практически неисчерпаемы. Некоторые из них незаметны обычным пользователям — например, отсутствие настроенного журналирования событий. Даже взлом, произошедший из-за этого, может остаться незамеченным, пока об этом не напишут в новостях. Другие ошибки становятся очевидными сразу: проблемы в работе сети замечает каждый сотрудник. В этой статье разберём наиболее частые ошибки при проектировании и эксплуатации сетей и дадим практические рекомендации, как их избежать. 1. Планирование сети: превыше всегоЧасто сети строятся без должного планирования. Причины бывают разные: экономия средств, отсутствие компетенций у проектировщиков, поспешные решения руководства. Пример: интегратор готовит детальную спецификацию сети, а заказчик «подрезает» её, чтобы сэкономить — в итоге сеть строится с недочётами, которые потом приходится исправлять дорого и долго. Правильное планирование включает: оценку количества рабочих мест и будущего расширения; разработку документации и схем сети; расчёт емкости портов на коммутаторах и маршрутизаторах; планирование отказоустойчивости и резервирования. 2. Кабельная инфраструктура: детали имеют значениеДаже при массовом использовании Wi‑Fi проводные соединения остаются критически важными. Ошибки: Недостаток розеток и кабелей. Плохая организация кабельных каналов. Попытка «все на Wi‑Fi», что может привести к перегрузке сети. Решение: Планируйте количество розеток с запасом (+4–6 на кабинет). Разделяйте кабели по цветам: Жёлтые — кроссы, Красные — серверы и NAS, Синие — настенные разъёмы и коммутаторы, Чёрные — инфраструктурные соединения, Зелёные — временные подключения. Маркируйте оба конца кабеля, чтобы быстро находить подключение к устройству. 3. Коммутаторы: порты не должны заканчиватьсяОшибка многих сетевых администраторов — неправильный расчёт портовой емкости. Если все порты заняты «впритык», в критический момент новые устройства подключить будет невозможно. Совет: Для этажных коммутаторов рассчитывайте запас портов. Для коммутаторов ядра — учитывайте отказоустойчивость и настройку Spanning Tree. 4. Маршрутизаторы: не пытайтесь сделать всёСовременные маршрутизаторы умеют почти всё: VPN, DHCP, файрволл, межсетевой экран. Но перегружать маршрутизатор всеми функциями не стоит: Он станет точкой отказа. Пропускная способность упадёт. Риски безопасности увеличатся. Рекомендация: оставьте маршрутизатор только для маршрутизации, VPN и файрволл — на отдельные устройства. 5. Серверная: порядок и скоростьОшибки: Перепутанные кабели, отсутствие маркировки. Серверы, размещённые далеко от магистралей сети, что замедляет работу. Пример: Представьте автостраду и деревенскую дорогу. Магистраль позволяет двигаться быстро без остановок, а локальные дороги замедляют движение. Так же работает и сеть: чем больше устройств между пользователем и сервером, тем медленнее передача данных. Решение: Сразу организуйте кабели аккуратно и с маркировкой. Размещайте серверы так, чтобы минимизировать количество промежуточных устройств. 6. Документирование: бумажки — это важноБез документации можно потерять контроль над сетью: свободные порты окажутся заняты, кабели не будут подписаны, схемы станут нечитаемыми. Советы: Ведите журнал портов на коммутаторах. Создавайте схемы сети на уровнях L1/L2/L3, не перегружая одну схему всеми деталями. Обновляйте документацию регулярно. ЗаключениеПравильное проектирование и эксплуатация сети — это не только вопрос техники, но и организации процессов. Соблюдение этих рекомендаций помогает минимизировать ошибки, повышает стабильность и скорость работы сети. Даже если что-то осталось за кадром — избегая этих типичных ошибок, вы уже делаете сеть более надёжной.
  18. Представьте: вы сидите за монитором, на экране уже есть кнопки, текст, картинки. Всё вроде работает. И тут менеджер кидает новую фичу: «Сделай модалку с формой для отзывов». Казалось бы — мелочь. Но как это сделать так, чтобы не сломать весь фронтенд? Давайте разберём, что реально происходит, когда фронтенд-разработчик «ковыряет» код на React. 💻 Фронтенд – это то, что видит пользовательФронтенд — это интерфейс, всё, что юзер видит и трогает: кнопки, меню, формы, анимации. Фронтенд-разработчик не просто «рисует сайт». Он создаёт UX, разбивает интерфейс на компоненты, следит за рендерингом, а ещё иногда сражается с багами, которые появляются из ниоткуда. ⚛️ React – библиотека для сборки UIReact — это как конструктор LEGO, только в мире фронтенда. Каждый компонент — это независимая деталь: кнопка, форма, карточка товара. Используя React, мы можем комбинировать эти кубики, создавать реактивные интерфейсы и управлять их состояниями (state). 🧩 Фича – это новая функция, не меньшеКогда фронтендер говорит «прикрутить фичу», это не просто «добавить элемент». Это продумать: структуру компонентов, state management, логику валидации, а иногда и асинхронные запросы к серверу через API. В нашем примере фича — это модалка с формой отзывов. 🪟 Модальное окно – всплывающее окно поверх сайтаМодалка — это UI overlay, который блокирует взаимодействие с остальной страницей до закрытия. Каждый фронтендер знает, что модалки могут стать хаотичными: неправильное состояние, баги с z-index, проблемы с focus trap — и вот уже страница ведёт себя как капризный робот. Компоненты, state и хукиКомпонент — это атом интерфейса. Вместо монолитного кода у нас маленькие, тестируемые и переиспользуемые куски. State — внутренняя память компонента. Открыт модал или закрыт? Какие данные ввёл пользователь? Это всё хранится в state. useState — встроенный React Hook, который позволяет создавать и менять state. Ошибки с ним приводят к странному перерендеру или багам, которые тяжело отловить. 🧾 Разметка и стилиHTML = структура страницы CSS/SCSS = как эта структура выглядит JS/TS = как она реагирует на действия юзера «Накидать разметку» — это не просто вставить теги. Нужно учитывать accessibility, semantic HTML, а ещё то, как всё будет работать с React и его виртуальным DOM. ✅ Валидация формыВалидация проверяет, корректно ли пользователь заполняет поля. Для фронтендера это значит: подключить либы типа Yup или Zod, написать кастомные валидаторы, позаботиться о UX ошибок: показать подсказки, подсветить поля, не дать юзеру сломать приложение. ⚙️ Логика и дебагЛогика — это последовательность действий кода: что происходит при клике, при вводе, при отправке формы. Дебаг — это целая философия. Консоль браузера, React DevTools, breakpoints, network tab, иногда даже прямой просмотр state в Redux. Типичная ошибка фронтендера: TypeError: Cannot read property 'value' of nullОзначает: «Ты пытаешься достучаться до элемента, которого нет». Скорее всего, забыл добавить id или неправильно прокинул ref. 🧪 ТестированиеПосле исправления багов наступает этап QA на фронтенде: кликаем все кнопки, вводим разные данные, проверяем в разных браузерах, убеждаемся, что модалка не ломает остальной UI. Только после этого можно быть уверенным: фича рабочая. 🌐 ИтогДаже маленькая задача вроде модалки включает десятки понятий: компоненты, state, рендеринг, хуки, валидацию, дебаг, логику, тесты. И чем больше ковыряешь фронтенд, тем больше начинаешь ценить эти термины. Главное — не бояться лезть в код и разбираться, почему что-то ломается. Потому что именно так становится настоящим фронтендером.
  19. Технические интервью пугают не сложностью задач, а неопределённостью. Кажется, что могут спросить абсолютно всё — и поэтому многие начинают готовиться хаотично: читают десятки статей, пересматривают сотни видео, зубрят определения. Результат? Знаний больше, а уверенности — меньше. На самом деле, техническое собеседование устроено гораздо проще и предсказуемее, чем кажется. Если понимать, что проверяют на самом деле, подготовка превращается из хаотичного марафона в точечный и эффективный процесс. Что реально проверяют на техническом интервьюСмотря на разные компании, подход почти всегда одинаковый: База и понимание, а не зубрёжка. Интервьюера интересует не «знаешь ли ты все методы массива», а понимаешь ли, как работает код. Часто просят объяснить понятие своими словами или разобрать на примере. И сразу видно, где теория заучена, а где есть настоящее понимание. Умение думать, а не угадывать. Даже если точного ответа нет, логическая цепочка рассуждений и умение задавать уточняющие вопросы сильно выигрывают у молчания или попыток вспомнить «правильную формулировку». Опыт применения знаний. Коммерческий проект, учебный проект — не важно. Главное, чтобы ты мог рассказать, где и как применял то, о чём говоришь. Пустые слова сразу считываются. Почему большинство проваливаетсяГотовятся как к экзамену. Заученные ответы срабатывают до первого уточняющего вопроса. Как только интервьюер меняет формулировку или просит пример — «картонная» подготовка разваливается. Перекос в теорию. Можно часами объяснять, что такое замыкания или REST, но теряться при вопросе «покажи, как это выглядит в реальном проекте». Для работодателя это сигнал: знания не применялись на практике. Страх сказать «не знаю». Многие пытаются выкрутиться и уходят в общие слова, а честное «не знаю, но могу подумать» выглядит сильнее и честнее. Как готовиться правильноОриентируйся на вакансии. Реальные описания вакансий показывают, что повторяется из раза в раз. Разбирай эти темы глубоко, не поверхностно. Объясняй вслух. Пока знания живут в голове, кажется, что всё понятно. Попробуй проговаривать темы — сразу видно пробелы. Это один из самых эффективных способов подготовки. Проекты как опора разговора. Проект нужен не для резюме, а как база для диалога. Ты должен уметь рассказать, что делал, почему выбрал подход и с какими трудностями столкнулся. Даже учебный проект может выглядеть убедительно, если ты понимаешь его. Тренируй сам формат интервью. Стресс, паузы, прямые вопросы — к этому нужно привыкнуть. Те, кто имитировал собеседования или уже проходил их, почти всегда выглядят сильнее. ИтогТехническое интервью — это не тест на идеальность, а способ понять, можно ли с тобой работать как с инженером. Если ты: понимаешь базу, умеешь рассуждать, не боишься признавать пробелы, можешь рассказать про свой код — …то ты уже на практически стопроцентной дороге к успеху.
  20. Если ты начинающий frontend-разработчик, рано или поздно сталкиваешься с вечным вопросом: «Знаний много, а опыта работы нет. Что делать?» Ответ простой — делать pet-проекты. Что такое pet-проект и зачем он нуженPet-проект — это личный или учебный проект, который ты создаешь самостоятельно, чтобы: Закрепить знания HTML, CSS, JavaScript. Показать, что умеешь работать с фреймворками (чаще всего React). Продемонстрировать мышление разработчика, а не просто умение сверстать экран. Для работодателя pet-проект — это возможность увидеть, как ты думаешь и решаешь реальные задачи. Почему большинство pet-проектов не помогают найти работуТипичные ошибки новичков: Слишком простые проекты. Todo-листы, калькуляторы или прогноз погоды без архитектуры и логики — это разминка, а не портфолио. Проекты не похожи на реальные задачи. Работодателю важно увидеть работу с состоянием, API, формами, ошибками, а не просто «сделал экран». Невозможно объяснить проект. Если ты не можешь рассказать, что и зачем сделал, проект не работает на собеседовании. Какой pet-проект действительно работаетХороший проект для junior frontend-разработчика: Заменяет коммерческий опыт. Показывает, как ты думаешь. Демонстрирует, что умеешь доводить задачи до конца. Характеристики: Решает понятную задачу. Использует современный стек (JavaScript / React / API). Имеет структуру, а не набор файлов. Содержит состояния, обработку ошибок, пользовательские сценарии. И главное — проект можно разобрать и защитить на собеседовании. Идеи pet-проектов, которые реально ценятся1. Упрощённый интернет-магазинОдин из лучших вариантов для новичка. Что важно реализовать: Каталог товаров. Фильтры и сортировку. Корзину и работу с состоянием. Загрузку данных через API или mock-сервер. Такой проект показывает, что ты понимаешь, как выглядят реальные frontend-задачи. 2. Личный кабинет пользователяПроект ближе к реальным продуктам. Можно добавить: Регистрацию и авторизацию. Формы с валидацией и обработку ошибок. Редактирование профиля. Работа с токенами на базовом уровне. 3. Дашборд с даннымиСильный проект, если сделан грамотно: Аналитика продаж или статистика пользователей. Трекинг задач и прогресса. Показывает умение работать с графиками, таблицами, состояниями. 4. Сервис с поиском и фильтрамиОтличная идея для демонстрации frontend-навыков: Работа с API и асинхронность. Отображение состояний: loading / error / empty. Базовая архитектура компонентов. Примеры: каталог фильмов, база книг, список курсов или вакансий. Сколько pet-проектов нужно junior frontend-разработчикуЧастый вопрос: «Сколько проектов достаточно?» Ответ: 2–4 продуманных проекта лучше, чем 10 слабых. Идеальный набор: 1 крупный проект. 1–2 средних. 1 экспериментальный, где видно твой рост. Как оформить pet-проект для портфолиоЧтобы проект реально работал на трудоустройство: Выложи код на GitHub. Напиши понятный README. Опиши, какие задачи решал. Добавь деплой (GitHub Pages, Vercel). Будь готов объяснить архитектурные решения. Помни: работодатели оценивают не только результат, но и ход мысли. ИтогPet-проекты — это не формальность и не «галочка». Это главный инструмент, который заменяет коммерческий опыт на старте. Если хочешь работать frontend-разработчиком: Делай проекты, похожие на реальные продукты. Думай как инженер, а не ученик. Используй проекты как повод для диалога с работодателем.
  21. ВведениеАдминистратор Linux-систем отвечает за поддержание стабильной работы программного обеспечения, мониторинг состояния системы, управление аппаратными ресурсами и резервное копирование. В этой статье мы рассмотрим основные команды и принципы работы системного администратора, а также приведём практические примеры, которые помогут новичкам освоить базовое администрирование Linux. Базовые настройки LinuxУстановка имени хостаЧтобы изменить имя компьютера (hostname), откройте терминал и выполните команду: sudo hostnamectl set-hostname your_hostname Примечание: Замените your_hostname на желаемое имя. Команда hostnamectl изменяет имя хоста постоянно. В отличие от hostname, которая действует только в текущей сессии. Настройка часового поясаЧтобы установить часовой пояс, создайте символическую ссылку на нужную зону: sudo ln -sf /usr/share/zoneinfo/Asia/Kolkata /etc/localtime Примечание: Путь /usr/share/zoneinfo/Asia/Kolkata замените на ваш регион. Эта команда обновляет системное время в соответствии с выбранной зоной. Управление файлами в LinuxВ Linux всё является файлом: устройства, каталоги, конфигурации и программы. Владение базовыми командами управления файлами критически важно. Команда Назначение cd Переход в другой каталог ls Просмотр списка файлов и папок vi Консольный редактор для конфигурационных файлов nano Удобный редактор с подсказками touch Создание нового файла или обновление временной метки cp Копирование файлов и каталогов mv Перемещение и переименование файлов rm Удаление файлов и папок fdisk Управление таблицами разделов диска mount Монтирование файловой системы Примеры: Создание нового файла: touch example.txt Копирование файла: cp example.txt backup.txt Переименование файла: mv backup.txt old_backup.txt Удаление файла: rm old_backup.txt Сетевые командыСетевые инструменты позволяют администратору проверять соединение, настраивать интерфейсы и удалённо управлять системой. Команда Назначение ping Проверка доступности сервера traceroute Отслеживание маршрута пакетов nslookup Проверка DNS-записей ifconfig / ip Настройка сетевых интерфейсов ssh Удалённый доступ и туннелирование scp Копирование файлов по SSH curl Передача данных на сервер или с него nmap Сканирование сети и аудит безопасности Пример проверки соединения: ping google.com Пример копирования файла на удалённый сервер: scp example.txt user@remote:/home/user/ Управление пользователями и группамиВ Linux есть три типа пользователей: Root — суперпользователь с UID 0. Обычные пользователи — повседневные пользователи с уникальными UID. Системные пользователи — для работы служб, например mysql или www-data. Основные команды управления пользователямиКоманда Назначение adduser Создание нового пользователя useradd Низкоуровневая команда для создания пользователя passwd Установка или изменение пароля deluser / userdel Удаление пользователя usermod Изменение свойств пользователя groups Просмотр групп пользователя groupadd / groupdel Создание и удаление групп Пример: Создание нового пользователя с паролем: sudo adduser newuser Добавление пользователя в группу sudo: sudo usermod -aG sudo newuser Диагностика и мониторинг системыЭффективный администратор следит за состоянием системы и производительностью. Команда Назначение top Просмотр процессов в реальном времени htop Улучшенная версия top с цветным интерфейсом vmstat Статистика процессов, памяти и CPU iostat Мониторинг загрузки дисков lsof Список открытых файлов nmon Комплексный мониторинг системы Работа с логамиЛоги позволяют быстро находить ошибки и анализировать работу системы. Команда Назначение dmesg Сообщения ядра tail Последние строки файла journalctl Управление логами systemd Пример просмотра последних 20 строк системного журнала: journalctl -n 20 ЗаключениеАдминистрирование Linux требует понимания работы с файлами, пользователями, сетью, логами и системными ресурсами. Освоение приведённых команд позволяет: Настраивать и поддерживать систему; Контролировать пользователей и права доступа; Проводить диагностику и мониторинг; Обеспечивать безопасность и стабильность работы системы. Практическое использование этих команд формирует фундаментальные навыки системного администратора, необходимые для работы в реальной среде Linux.
  22. 1. Сколько соединений может обработать один воркерВ NGINX каждый воркер‑процесс способен обслуживать определённое число одновременных соединений. Это задаётся директивой: worker_connections 1024; Здесь учитываются все дескрипторы, включая клиентские соединения и прокси‑сессии к бэкендам. По умолчанию NGINX использует 768 соединений, но для серьёзных нагрузок лучше поднять до 1024+, не забывая про лимит открытых файлов в ОС (ulimit -n). Расчёт максимального числа клиентов: max_clients=worker_processes×worker_connections\text{max\_clients} = \text{worker\_processes} \times \text{worker\_connections}max_clients=worker_processes×worker_connections Пример: worker_processes = 4 worker_connections = 1024 max_clients = 4 × 1024 = 4096 То есть сервер может обслуживать до 4096 соединений одновременно (минус коннекты к upstream). Пример конфигурации events.conf: events { worker_connections 1024; # максимум соединений на воркер # accept_mutex on; # равномерное принятие коннектов (опционально) } 2. Ограничение одновременных соединений: limit_connЧтобы защитить сервер от «дружелюбной» перегрузки, например, когда бот‑краулер открывает сотни соединений, используется ngx_http_limit_conn_module: limit_conn_zone — создаёт область памяти для хранения текущих соединений (обычно по IP). limit_conn — задаёт лимит одновременных соединений. Пример настройки: http { limit_conn_zone $binary_remote_addr zone=perip:10m; server { location / { limit_conn perip 10; # не больше 10 соединений на IP limit_conn_status 429; # выдаём 429 вместо дефолтного 503 limit_conn_log_level info; # уровень логирования при отказе } } } 10m зоны на 64-битной платформе хранят примерно 16 000 уникальных IP. При переполнении NGINX сразу отдаёт ошибку (503/429). Dry-run и логирование: limit_conn_dry_run on; # не блокирует, а только логирует limit_conn_log_level notice; Так можно тестировать лимиты, не блокируя клиентов. 3. Ограничение скорости запросов: limit_reqЕсли нужно контролировать частоту запросов, применяется ngx_http_limit_req_module. Механизм основан на «leaky bucket» — запросы попадают в бакет и «вытекают» с заданной скоростью. http { limit_req_zone $binary_remote_addr zone=login:10m rate=5r/s; server { location /login { limit_req zone=login burst=10 nodelay; limit_req_status 429; limit_req_log_level warn; } } } burst — пиковое количество запросов, которое сервер пропускает сверх лимита. nodelay — отказ сразу при превышении лимита, без задержки. Без nodelay лишние запросы будут ожидать своей очереди. 4. Где хранится состояниеВ NGINX есть shared memory zones — специальная общая память для лимитов: Slab-пул разбивает память на одинаковые блоки для быстрого выделения и освобождения. Для limit_conn используется хеш-таблица или сбалансированное дерево по IP. Для limit_req — красно‑чёрное дерево + очередь «протекающего ведра». Доступ защищён встроенным мьютексом — нет гонок даже при сотнях воркеров. Механизм работы: При новом запросе NGINX берёт ключ (IP), ищет запись в зоне. Если записи нет — создаёт новую. Проверяет счётчики и решает, пропускать запрос или отказать. 5. Реакция сервера при превышении лимитаПо умолчанию: limit_conn → 503 Service Unavailable limit_req → 503 Service Unavailable Лучше отдавать 429 Too Many Requests, чтобы клиенты понимали причину. Пример логов 2025/04/04 13:45:12 [warn] 12345#0: *67890 limiting requests, excess: 5 by zone "login" 2025/04/04 13:45:12 [info] 12345#0: *67890 a client request is temporarily blocked by zone "perip" 6. Практические нюансыРазмер зоны: 1 MB ≈ 16 000 записей; для 100 000 IP/сутки потребуется 10–12 MB. burst vs delay: для login/API можно применять разные подходы: login: burst=1; nodelay API: burst=10; delay 10 Разные зоны для разных endpoint: чтобы лимиты не конфликтовали. Исключения: через map или geo можно исключить внутренние IP: map $remote_addr $limit { 10.0.0.0/8 ""; default $binary_remote_addr; } limit_req_zone $limit zone=api:20m rate=20r/s; 7. Сторонние модули и NGINX Plus7.1 ngx_brotli — снижение трафикаBrotli‑сжатие уменьшает объём передаваемых данных, разгружая лимиты по трафику: http { brotli on; brotli_comp_level 6; brotli_types text/html text/css application/javascript; } 7.2 ngx_http_limit_traffic_ratefilter_module — лимит по байтамПозволяет ограничить скорость передачи, например 100 KB/s на IP: http { limit_traffic_rate_zone $binary_remote_addr zone=bytetraf:10m; server { location /downloads/ { limit_traffic_rate zone=bytetraf rate=100k; } } } 7.3 NGINX Plus — расширенные возможностиСинхронизация зон между нодами — zone_sync. Адаптивное ограничение: скорость передачи зависит от метрик, например: map $upstream_response_time $dyn_rate { "~^[0-9]\.[0-1]" 200k; # быстрые ответы — больше скорости default 50k; # медленные — ограничение } server { location /stream/ { limit_rate $dyn_rate; } }

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.