Troubleshooting — Operating Systems для Junior
База знаний типичных ошибок курса Operating Systems для Junior.
Категория
Симптомы
- Команды `uptime`, `top`, `cat /proc/loadavg` показывают высокие числа: например, `load average: 12.50, 14.20, 10.85` на машине с 4 CPU. Сервисы тормозят, отклик растёт, но что именно перегружено -- неясно.
Причина
Load average -- это среднее количество процессов в состоянии RUNNING + UNINTERRUPTIBLE (D-state) за 1/5/15 минут. Не равно `%CPU` Высокий loadavg при низком CPU = много процессов в D-state, обычно зависли на медленном disk I/O или NFS Высокий loadavg при высоком CPU = реальный CPU contention; queue runnable > cores Контейнеры/cgroups имеют свои квоты -- общий loadavg может быть низким, а конкретный сервис задушен Иногда фоновые задачи (backup, virus scan, indexing) забивают I/O и тащат loadavg Memory pressure -> kernel свопит -> процессы блокируются на I/O -> loadavg растёт
Решение
- Если CPU contention (us+sy высокие) -- найти процесс-пожиратель и снизить ему nice или ограничить через cgroup
- Если iowait высокий -- найти процесс через
iotop -oPилиpidstat -d 1; перенести нагрузку или ускорить диск - Если D-state из-за NFS -- проверить сетевой сервер, mount-options (soft, intr), сетевую связность
- Если swap активен -- увеличить RAM, понизить vm.swappiness, найти memory leak
- Для нечастых spikes -- настроить мониторинг (Prometheus node-exporter) с алертом на 1m loadavg > N*cores
- Хорошее правило большого пальца: 1m loadavg > 2*cores на протяжении 5+ минут -- инцидент
Симптомы
- top показывает CPU busy на 100%, но в детализации `us` (user) низкий, зато высокий `wa` (iowait) или `st` (steal). Программа не успевает, hex/diags не отвечает.
Причина
wa (iowait) высокий: процесс ждёт диск/NFS/сетевую FS. CPU технически 'занят' ожиданием, но не полезной работой st (steal) > 0: вы в виртуалке (KVM/Xen/EC2), и hypervisor отнимает CPU в пользу других VM. Часто на дешёвых VPS Hardware interrupt storm: устройство флудит interrupt'ами (битый USB, плохой драйвер), `hi` показывает >5% Softirq overhead: высокий `si` (softirq) при высокой сетевой нагрузке -- одно ядро обрабатывает все packet'ы Битый thermal sensor -- CPU throttling, скорость падает в 10x, но 'usage' 100% Hyperthreading: 2 виртуальных HT-thread на одно физическое ядро дают суммарную CPU= 200%, но реальный throughput x1.2-1.5
Решение
- iowait -- найти источник через
iotop -oP,pidstat -d 1; убрать конкурентов или ускорить storage (NVMe вместо HDD) - steal на VM -- если устойчивый > 5% и платите за полные vCPU, жаловаться провайдеру или брать выделенный тариф
- softirq на сети -- разрешить RPS/RSS на NIC чтобы interrupts шли на разные CPU, или использовать многоканальный NIC
- Hyperthreading misleading -- для CPU-bound workload лучше думать в физических ядрах (lscpu -e показывает CORE)
- Thermal throttling -- чистить от пыли, менять термопасту, или ограничить TDP через cpufreq governor
- Для долгого диагноза держать
dstat -tcdngy 1илиnode-exporter -> Grafanaс разбивкой по CPU-метрикам
Симптомы
- Процесс внезапно умер без явной причины. В `dmesg` или `journalctl -k`: `Out of memory: Killed process 12345 (myapp) total-vm:8123456kB, anon-rss:7012345kB, file-rss:1234kB, ...`. Иногда вместо явного OOM -- просто отсутствие записи о graceful shutdown в логах приложения.
Причина
Сумма требуемой процессами памяти превысила RAM + swap. Kernel выбрал жертву по oom_score и убил Memory leak в долгоживущем приложении -- постепенно растёт RSS, в итоге OOM Кратковременный пик памяти: загрузил большой файл в RAM, fork сделал copy-on-write -> moment-of-truth превысил overcommit лимит В контейнере: достигнут `memory.max` cgroup -- внутри-cgroup OOM, host этого может не показать; искать в логе kubelet/docker, не dmesg Кэш ZFS ARC съел всю RAM, kernel не успел освободить под пиковую нагрузку Огромный malloc, неверно учтённый аллокатором -- например, через mmap без MAP_POPULATE; реальный fault приходит позже
Решение
- Найти memory leak:
valgrind --leak-check=full ./appдля C/C++; tracemalloc / memray для Python; jemalloc profiling, pprof для Go - Уменьшить memory footprint: GC tuning (JVM -Xmx, GOGC), connection pool sizes, batch sizes ETL
- Увеличить RAM/swap либо ограничить cgroup чтобы пик внутри был контролируемым
- Понизить overcommit:
sysctl vm.overcommit_memory=2-- предупредит ENOMEM на malloc вместо OOM позже - Защитить критичный процесс:
echo -1000 > /proc/<PID>/oom_score_adj(но тогда жертвой будет что-то другое) - Мониторинг: alert на
node_memory_MemAvailable / total < 10%за 5 минут даёт время среагировать
Симптомы
- RSS процесса (через `ps -o pid,rss,cmd` или мониторинг) постепенно растёт час за часом, в итоге процесс либо упирается в OOM, либо начинает свопить. Перезапуск 'лечит' симптомы, но проблема возвращается.
Причина
Классический heap-leak: malloc/new без free/delete, объекты в Python в кэше без TTL GC-managed языки: ссылки в долгоживущих коллекциях (глобальный dict, observer list) держат объекты живыми Native-расширения: ctypes/cython/JNI аллоцируют в C, GC языка про них не знает Memory fragmentation: всё чисто по логике, но glibc allocator не возвращает память kernel'у; RSS высокий, но usable свободно много Файловые буферы: тысячи открытых файлов с буферами в kernel space (выглядит как пик anon, но это file-backed pages) Утечка thread'ов: каждый pthread берёт 8 MB stack по умолчанию; 1000 'забытых' потоков -- +8 GB
Решение
- Найти источник через профайлер: tracemalloc/memray (Python), pprof (Go), jemalloc + jeprof (C), pyflame, Async-profiler (JVM)
- Для GC-языков: проверить glubal caches без размера, observer-паттерны, замыкания, держащие большие объекты
- Native-leaks: ASan компиляция для C/C++, valgrind, инструменты типа heaptrack
- Кратко-срочный фикс:
malloc_trim(0)в C для возврата фрагментированного heap'а, или периодический restart сервиса (12-factor disposability) - Архитектурно: переписать на short-lived workers (один request -> один процесс), либо memory budget на worker + auto-restart
- Мониторинг: алерт на slope of RSS over time, не на абсолютный уровень -- так ловите раньше OOM
Симптомы
- `ps -eo pid,stat,comm | grep ' Z'` показывает много `<defunct>` процессов. Системные ресурсы исчерпываются (PID space, process table). `kill -9 <ZOMBIE_PID>` не помогает -- процесс остаётся.
Причина
Родитель не вызывает wait()/waitpid() после смерти ребёнка. Это баг в родителе, не в зомби Шёл-скрипты, запускающие фоновые процессы без `wait` или `disown`, плюс долго работающий родитель Сервис без proper SIGCHLD handler -- классическая ошибка в самописных daemon Огромное количество fork()'ов в minute (build-system, CI-runner) -- 1-2 зомби в момент это норма, но если родитель сам падает, остаются осиротевшие зомби В контейнере с PID 1 = ваше приложение, оно ОБЯЗАНО reap'ить зомби. По умолчанию nodejs/python/jvm не делают этого -- зомби копятся
Решение
- Главное -- починить родителя: сделать
wait()после fork(), или поставитьsignal(SIGCHLD, SIG_IGN)чтобы kernel сам делал reap - Быстрый workaround: убить родителя -- его сирот заберёт init/systemd, оно их reap'нет
- В Docker/k8s: использовать tini (
--initфлаг docker) или включить shareProcessNamespace -- запускает proper init с reap'ом - В shell-скриптах с фоновыми задачами:
waitилиtrap 'wait' SIGCHLD - Для расследования:
strace -fp <ROOT_PARENT_PID>-- видно, делает ли он wait4() / SIGCHLD handling - Если зомби много и мешают -- /etc/sysctl:
kernel.pid_max=4194304чтобы PIDs не закончились пока ищете root cause
Симптомы
- Процесс не отвечает, kill -9 не убивает, в `ps` STAT = `D`. Чаще всего на server'ах с NFS, проблемами диска или kernel-багами.
Причина
Заблокирован на disk I/O syscall -- например, fsync, read() с проблемного диска. Kernel ждёт ответа от железа NFS share с упавшим сервером в hard mount-mode (без soft, intr) -- процесс ждёт сетевого ответа Драйверы устройств с ошибкой: завис в обработчике прерывания, не отдаёт управление Kernel-bug в файловой системе -- особенно в edge cases btrfs/zfs при snapshot/scrub futex_wait с потерянным wake (deadlock в userspace lock) -- хотя обычно тогда S state Загрузка/выгрузка USB-устройства в момент I/O запроса
Решение
- Диск-проблемы: посмотреть
smartctl -a /dev/sda-- если SMART показывает ошибки, диск нужно менять. SCSI-rescan:echo 1 > /sys/class/scsi_host/host0/scan - NFS-висит: проверить серверы NFS, добавить в mount-options
soft,intr,timeo=N-- даст возможность kill процессы при недоступном сервере - USB hang:
usbresetдля конкретного устройства, или physical-unplug - Driver/kernel bug -- обновить kernel, проверить changelog. Workaround: rmmod / modprobe модуль драйвера
- Если процесс долго в D и блокирует важное -- единственный надёжный способ -- reboot. SIGKILL не работает на D
- Профилактика: на серверах с NFS использовать
soft,intrmount-options, отделить мониторинг от 'production' mount-points
Симптомы
- В логах: `Too many open files`, `EMFILE: too many open files`, в Java `java.nio.file.FileSystemException: ... Too many open files in system`. nginx падает, accept() возвращает ошибку, новые соединения отбрасываются.
Причина
Лимит per-process: ulimit -n (soft) обычно 1024 -- мало для серверного приложения Лимит system-wide: /proc/sys/fs/file-max, /proc/sys/fs/nr_open Утечка fd -- приложение открывает файлы/сокеты и не закрывает (бага: забытый close в exception path) Соединения в CLOSE_WAIT/FIN_WAIT много -- сокет жив, держит fd Inotify watches тоже считаются как fd (inotify_init); часто переполняется на dev-окружении с file-watcher'ами
Решение
- Увеличить ulimit для сервиса: в systemd unit:
LimitNOFILE=1048576; для login-shell -- /etc/security/limits.conf - Найти leak:
lsof -p <PID> | sort | uniq -c | sort -rn | head-- если 1000 одинаковых файлов, явный leak - Использовать
with open(...)в Python, try-with-resources в Java, defer file.Close() в Go -- гарантированный close - Закрыть idle-соединения через keepalive_timeout / connection pool eviction
- Системный лимит:
sysctl fs.file-max=2097152, в /etc/sysctl.conf - Для inotify-всплесков:
sysctl fs.inotify.max_user_watches=524288иmax_user_instances=8192
Симптомы
- `cat /home/me/file.txt` -> `Permission denied`. `ls -la file.txt` показывает, что вы owner. Бесит.
Причина
Нет 'x' (execute) на одной из родительских директорий -- нельзя зайти внутрь. На директорию x значит 'войти/обращаться к содержимому' Файл попал на mount с опцией noexec/nosuid (например, /tmp на некоторых дистрибутивах) -- для запуска не катит SELinux/AppArmor контекст блокирует -- ls покажет права как ваши, но MAC-policy запрещает ACL: `ls -l` показывает `+` после permissions; полные права в `getfacl <file>` Sticky bit (`t`) на директории: можно создавать, но удалять/переименовывать -- только owner файла (типично /tmp) Файл в snapshot/read-only mount -- даже chmod не сработает Filesystem read-only mount (после ошибки автоматически перемонтировался ro)
Решение
- Дать x на родительскую директорию:
chmod o+x ~, или сменить ownership - Mount-options: переcмотр fstab, убрать noexec если нужно. Затем
mount -o remount,exec /tmp - SELinux:
restorecon -v <file>восстановит дефолтный контекст; для постоянной policy --chcon -t user_home_t - ACL:
setfacl -x u:user <file>снять конкретный entry, илиsetfacl -b <file>снять все - Read-only из-за ошибок FS:
fsckна размонтированном разделе, потом mount заново. Из эмерженси-rescue режима - Если файл в snapshot (btrfs/zfs) -- работать со 'свежей' версией, snapshot R/O by design
Симптомы
- `mount: /mnt/data: mount point is busy` или `device is already mounted on /mnt/old`. `umount` тоже падает с `target is busy`.
Причина
Кто-то держит fd внутри mount point: процесс с cwd в /mnt, открытый файл, mmap-region Loop-device или nested mount -- внутри /mnt смонтирована ещё одна FS FUSE-mount не реагирует (например, sshfs с разорванной сессией) -- его 'нормальный' umount замирает Bind-mount: один блочный device может быть смонтирован в нескольких местах -- ошибка about другой mount NFS mount от мёртвого сервера -- umount висит, требуется -l (lazy) или -f (force) Кто-то открыл шелл и chdir в /mnt, потом отключился, но процесс жив
Решение
- Найти и убить/перенаправить процессы:
fuser -km /mnt/data-- посылает SIGKILL всем, кто держит - Lazy unmount:
umount -l /mnt/data-- отвязывает сразу, фактически освобождается когда последний fd закрывается - Force unmount:
umount -f /mnt/data-- особенно для NFS - Если уже смонтировано в другом месте:
findmnt --source /dev/sdb1-- покажет все точки;umountпо нужной - FUSE-zombie:
fusermount -uz /mnt/sshfs(lazy umount FUSE) - Reboot в крайнем случае -- иногда единственный способ для kernel-уровневых зависших mount'ов
Симптомы
- `df -h /` показывает 100% used; `du -sh /*` суммирует существенно меньше, чем размер раздела. Удаление файлов не освобождает место.
Причина
Удалённый, но всё ещё открытый файл: процесс держит fd, kernel не отпустит inode до закрытия. Классический случай с logfile, переименованным logrotate, но не reload'нутым приложением Sparse-файлы: показываются в du маленькими, но фактически держат больше Snapshots ZFS/btrfs/LVM держат удалённые данные Скрытый mount поверх каталога: что-то лежит под mount-point, du не видит Reserved space на ext4: по умолчанию 5% зарезервировано для root (`tune2fs -m`) Inodes исчерпаны (см. отдельный issue) -- df показывает inode-100%, выглядит как 'место кончилось'
Решение
- Удалённые открытые: либо restart процесса (
systemctl restart nginx), либо truncate через: > /proc/<PID>/fd/N - Snapshot cleanup: удалить старые snapshot'ы. На ZFS:
zfs destroy pool/data@old - Reserved space:
tune2fs -m 1 /dev/sda1-- сократить с 5% до 1% (для не-/, где не нужен root reserve) - Logrotate с copytruncate или reload-приложения после ротации (postrotate: systemctl reload <app>)
- Скрытый mount:
mount --bind / /mnt/realroot(без пере-mount'ов) -- видно, что физически на / - Best practice: мониторить
node_filesystem_avail_bytesиnode_filesystem_files_freeчтобы заранее реагировать
Симптомы
- `mkdir`, `touch`, `cp` падают с `No space left on device` (ENOSPC); `df -h` показывает 50% free. Загадка.
Причина
Исчерпан пул inode -- размер свободного места не важен, новые файлы создать нельзя. Видно в `df -i` Превышен per-user/per-group quota: `quota -u <user>` показывает used >= limit Это не тот mount -- ENOSPC на /tmp, а df смотрит на /. Каждый mount имеет свои inode/space tmpfs (/tmp, /run, /dev/shm) с указанным `size=` mount-option заполнен Subvolume btrfs в полном parent-volume, даже если конкретный subvolume пустой Reserved blocks: на ext4 5% зарезервировано для root, обычные пользователи получают ENOSPC раньше абсолютного 0
Решение
- Inodes исчерпаны -- удалить миллионы мелких файлов (типичный case: /var/cache, mail spool, logs)
- Если структурная проблема -- mkfs заново с большим bytes-per-inode:
mkfs.ext4 -i 4096 /dev/sdb1(1 inode на 4KB) - Quota: увеличить через
edquota -u <user>(root) - tmpfs full:
mount -o remount,size=2G /tmp - btrfs full: balance + cleanup snapshot'ов:
btrfs balance start /; btrfs subvolume delete <old-snapshot> - Reserved blocks:
tune2fs -m 1 /dev/sda1(не для root partition, чтоб не сломать систему)
Симптомы
- Приложение, читающее/пишущее файлы, тормозит. fsync долгий. Сборка проекта 'крутит SSD'. Backup съел пол-ночи.
Причина
Реальная насыщенность диска: %util = 100, await > 50ms на SSD означает очередь забита HDD на random I/O -- максимум 100-200 IOPS, дальше всё в очередь fsync дорог: SSD-consumer = 1-5ms, enterprise SSD = 100us, HDD = 10-50ms Bcache/dm-cache hit ratio упал -- нагрузка пошла на медленный backing-disk RAID resync/scrub в фоне забирает пропускную способность Filesystem fragmentation (особенно на ext4 с почти-полными разделами) Network filesystem (NFS, SMB): сеть стала узким горлом, не диск
Решение
- Если диск перегружен -- найти потребителя через iotop, понизить ему ionice или ограничить через cgroup IO
- Перенести hot-data на NVMe (10x random IOPS vs SATA SSD)
- Batch'ить fsync: одна транзакция вместо ста маленьких; для DB -- group commit
- Изменить I/O scheduler:
echo bfq > /sys/block/sda/queue/schedulerдля desktop с фоновой нагрузкой;noneдля NVMe -- они сами справляются - Тюнить readahead:
blockdev --setra 4096 /dev/sdaдля sequential workload - RAID-scrub перенести на ночь:
echo 'KSTREAM_MIN_BANDWIDTH=10000 KSTREAM_MAX_BANDWIDTH=50000' >> /etc/mdadm.confилиsysctl dev.raid.speed_limit_max=50000 - fsync-latency мониторить отдельно (eBPF) -- может быть проблема даже на 'свободном' диске
Симптомы
- В `vmstat 1` колонка `cs` (context switches per second) показывает >100k. Высокое sys% CPU, но мало полезной работы. Производительность падает с ростом нагрузки.
Причина
Слишком много потоков на ядро: scheduler гоняет их, тратя время на сохранение/восстановление state Thundering herd: тысячи воркеров просыпаются на одно событие, борются за lock, большинство снова засыпает Чрезмерный lock contention: потоки часто блокируются, ждут, переключаются Network-heavy workload без RPS/RSS: один CPU обрабатывает все packet'ы, ksoftirqd жрёт CS Misuse of signals: SIGALRM, real-time signals в шумном цикле VM или container с маленьким `cpu.shares` под нагрузкой постоянно preempt'ится
Решение
- Снизить количество потоков -- использовать thread pool с size = num_cores, не 'thread per request'
- Использовать lockless / read-mostly структуры (RCU, atomics) вместо тяжёлых mutex
- Async I/O вместо thread-per-connection: epoll, io_uring, async/await
- RPS/RSS на NIC:
ethtool -L eth0 combined 8, чтобы interrupt'ы распределялись по CPU - Coarse-grained locks: блокировать большие куски кода реже, не fine-grained на каждый bit
- Pin процесса на конкретные CPU:
taskset -c 0-3 ./appчтоб минимизировать migration и L1/L2 cache thrashing
Симптомы
- В `top` процесс `kswapd0` (или несколько) ест CPU. В vmstat колонки si/so (swap-in/swap-out) пляшут постоянно. Latency приложений растёт в 10-100 раз -- 'тормозит всё'.
Причина
RAM реально исчерпана: рабочее множество (working set) больше physical RAM, kernel постоянно свопит vm.swappiness слишком высокий (60 по умолчанию агрессивный для server'а) Memory leak в большом процессе -- сам себя выгоняет в swap Кратковременный пик памяти (запуск тяжёлой задачи) обрушил cache, kernel пытается восстановить Numa: память аллоцируется на чужой node, фактически 'swap-like' цена доступа kswapd работает над освобождением страниц для slab/dentry cache -- иногда из-за фрагментации
Решение
- Главное -- найти и понизить аппетит. Самый частый случай -- memory leak; см. отдельный issue
- Уменьшить swappiness:
sysctl vm.swappiness=10(или 1 для DB-серверов) - Добавить RAM или ограничить cgroup приложений
memory.max-- получить контролируемый OOM вместо медленного swap - Отключить swap для критичных DB-серверов (PostgreSQL рекомендует low swappiness, но не полностью off)
- Swap на zram (compressed RAM):
zramctl+ swapon -- быстрее обычного swap при низкой нагрузке на CPU - Если NUMA: использовать
numactlчтобы прибить процесс к конкретному node + memory binding - vm.vfs_cache_pressure=50 -- меньше давить кэши, что снижает kswapd-нагрузку
Симптомы
- `kill -9 <PID>` отрабатывает молча, но `ps -p <PID>` показывает процесс живым. STAT в ps -- `D` (uninterruptible) или `Z` (zombie).
Причина
Процесс в D-state -- ждёт kernel-level операцию, kill подвисает в очереди. См. отдельный issue про D-state Процесс уже zombie -- технически он мёртв, но не reap'нут. kill -9 на зомби бессмысленно (он уже не выполняет код); см. отдельный issue Процесс в trace-режиме (ptrace) -- gdb/strace/perf держит Привязан к kernel-thread, который не отдаёт управление Процесс с PR_SET_PDEATHSIG/PR_SET_DUMPABLE манипуляциями
Решение
- Если TracerPid != 0 -- убить трейсер (gdb, strace), процесс отпустится; либо
kill -SIGCONT <PID>если был SIGSTOP - Зомби -- лечить родителя (см. zombie issue); сам не уйдёт
- D-state -- ждать I/O разморозки или reboot (см. D-state issue)
- Если устанавливался SIGKILL ignore через ptrace -- detach gdb (
detachв gdb-shell) - В контейнерах с одним процессом и аномалией -- проще удалить контейнер (
docker rm -f) и пересоздать - Reboot -- крайний вариант для D-state-залипаний
Симптомы
- `systemctl start nginx` -> `Job for nginx.service failed because the control process exited with error code.` или 'failed to start'. `systemctl status` показывает failed без подробностей.
Причина
Ошибка в самом приложении при старте: missing config, bad permissions, port уже занят Опечатка в unit-файле: неверный ExecStart, ошибочный User=, не найденный путь Зависимости (After=, Requires=) не подтянулись или упали раньше ulimit/MemoryHigh/CPUQuota в unit ограничивают так, что процесс убивается сразу SELinux/AppArmor контекст блокирует операции Restart=on-failure + StartLimitBurst -- сервис уже несколько раз падал, systemd рефьюзит дальше пытаться Capabilities/PrivateNetwork ограничения unit'а слишком жёсткие
Решение
- Читать journalctl -- 90% случаев точная причина прямо там. ExecStart-команду можно запустить вручную как unit-User для воспроизведения
- Опечатки --
systemd-analyze verify /etc/systemd/system/myapp.service - После правок unit:
systemctl daemon-reloadобязательно, иначе systemd видит старое - Если упёрся в StartLimit:
systemctl reset-failed; systemctl start ... - Если ограничения unit мешают: временно
systemctl edit nginx.service->[Service]\nNoNewPrivileges=false\nMemoryHigh=infinityдля теста; потом вернуть нормально - Permission/selinux:
audit2why -al,journalctl _AUDIT_TYPE_NAME=AVC -e,restorecon -v - Port уже занят:
ss -ltnp | grep :80-- кто конкурирует
Симптомы
- `df -h /` показывает 90%+ used. `du -sh /var/log/*` указывает на /var/log/journal как главный пожиратель -- иногда десятки GB.
Причина
journald по умолчанию хранит логи долго: SystemMaxUse не задан, на больших серверах разрастается до 4 GB или больше Сервис флудит логами (DEBUG-уровень в проде, лог-цикл) Конкретный noisy unit (например, kernel-сообщения от плохого драйвера, SELinux audit) Старые boots: каждый boot создаёт отдельный файл, давно не чистились
Решение
- Быстро освободить:
sudo journalctl --vacuum-size=500Mили--vacuum-time=7d - Постоянно ограничить: в /etc/systemd/journald.conf:
SystemMaxUse=1G
SystemMaxFileSize=100M
MaxRetentionSec=14day
Затем
systemctl restart systemd-journald - Понизить уровень логирования шумного сервиса в его конфиге, или fwd-only-к-stderr (без journal)
- Для DEBUG-сервиса -- временный rate-limit в /etc/systemd/journald.conf: RateLimitIntervalSec=30s, RateLimitBurst=10000
- Persistent journal off вообще:
journalctl --persistent=noилиStorage=volatile-- логи только в RAM - Для централизованного логирования -- forward в syslog/rsyslog и удалённое хранилище, не накапливать локально
Симптомы
- `rm bigfile.log` сработало, файла нет в `ls`, но `df` показывает то же занятое место. Перезапуск приложения 'лечит'.
Причина
Процесс держит open fd на удалённый файл. Inode остаётся живым пока fd не закроется -- освобождение блоков лишь при final close Классика: logrotate переименовал или удалил файл, приложение продолжает писать в свой fd, и newfile растёт Сторонние bind-mount или container-mount, держащие исходный inode Open mmap-регион на файл -- то же что fd
Решение
- Перезапустить процесс:
systemctl restart nginx-- fd закрывается, inode освобождается - Без рестарта: усечь через /proc:
: > /proc/<PID>/fd/<N>-- работает только на regular files, не sockets - Logrotate -- использовать
copytruncateилиpostrotateсkill -USR1 <PID>(nginx reopens log) - Архитектурно: писать логи в stdout, journal/k8s сам управляет ротацией
- Найти все 'leaked' файлы по cron'у и алертить если общий размер > N MB
- Для приложений, не реагирующих на SIGHUP/SIGUSR1 -- использовать syslog/journald напрямую
Симптомы
- После reboot: 'You are in emergency mode. After logging in, type "journalctl -xb" to view system logs', либо grub перекидывает в rescue. На экране сообщения от fsck про bad sectors, corrupted journal.
Причина
Невыключение питания / kernel panic в момент write -- FS в inconsistent state, journal не дописался Реальный hardware-fault диска -- bad sectors, контроллер диска Ошибка в драйвере / kernel-баг (особенно после kernel update на свежий) Перемонтирование R/O в результате kernel-обнаруженных ошибок при previous run -- fsck при boot не справился Битый UUID/label в fstab -- mount промахивается, fsck падает
Решение
- fsck ext4 руками (FS должна быть unmounted):
sudo fsck.ext4 -y /dev/sda1-- 'yes на все вопросы'. На большие файлы FS может занять часы - Если есть badblocks:
sudo badblocks -sv /dev/sda > /tmp/badblocks.txt; sudo fsck -l /tmp/badblocks.txt /dev/sda1 - Для xfs:
xfs_repair -v /dev/sda1(xfs_check -- read-only check) - Если ошибка в fstab (неверный UUID): загрузиться с live-USB, исправить /etc/fstab, перезагрузиться
- Если повторяется на разных bootах -- скорее всего железо. SMART, замена диска
- Профилактика: ext4 с journal обязательно (не -O ^has_journal); регулярные fsck force через
tune2fs -cилиmount-countpolicy
Симптомы
- Скрипт в crontab/systemd-timer должен был запуститься, но не запустился. Логов от него нет. `cat /var/log/cron` (если есть) тоже не помогает.
Причина
Синтаксическая ошибка в crontab line: непарсимое расписание, неэкранированные %, неправильное количество полей PATH в cron очень ограничен (/usr/bin:/bin) -- скрипт ссылается на команды вне PATH Среда cron минимальна: нет HOME, нет export'ов из bashrc -- скрипт, работающий вручную, в cron падает Запись cron вообще не подхватилась -- cron не перечитал, или редактировали не тот crontab Для systemd-timer: timer не enabled, или OnCalendar спецификация неверная Скрипт пытается писать в файл без прав / не в той рабочей директории Output идёт в /var/spool/mail/<user> через MAILTO, который никто не читает
Решение
- Полные пути к командам:
/usr/local/bin/myappа неmyapp - В начале скрипта выставить PATH явно:
export PATH=/usr/local/bin:/usr/bin:/bin - Перенаправить stdout/stderr в файл:
0 3 * * * /opt/backup.sh >> /var/log/backup.log 2>&1 - Проверить crontab синтаксис:
crontab -l,cron -n(debian) - Для systemd-timer:
systemctl enable --now mybackup.timer - Если запуски нерегулярные (skip'ы) -- сервер выключался; для гарантии не пропустить -- use systemd-timer с Persistent=true
- Для тестов -- эмулировать cron-env:
env -i /bin/sh -c '/path/to/script'
Симптомы
- Сервис запустился, но создаёт файлы от root или от какого-то wrong-юзера. Или наоборот: должен быть от dedicated-юзера, фактически от root. Permission denied на собственных файлах после restart.
Причина
В systemd unit не указан User=/Group=; сервис стартует от root ExecStart запускает обёртку (например, sudo внутри), которая ломает downgrade Использован setuid-binary, который повышает privileges -- effective UID отличается от real В Docker не указан USER в Dockerfile; контейнер работает от root Скрипт запущен из cron под /etc/crontab (system-wide) с явным юзером, но забыли его umask неверный -- файлы создаются с нежелательными правами
Решение
- В systemd unit явно:
[Service]\nUser=appuser\nGroup=appgroup, потом daemon-reload - Docker:
USER appuserв Dockerfile, илиdocker run --user 1000:1000 - ExecStart запускать прямой binary, без sudo внутри (sudo внутри unit -- антипаттерн)
- Для setuid-binary: проверить нужно ли вообще, заменять capabilities (см. CAP_NET_BIND_SERVICE)
- Cron user-wide:
crontab -u appuser -e(root делает -u) - umask в unit:
UMask=0027-- создаваемые файлы будут 640/750 - Если файлы уже созданы wrong:
chown -R appuser:appgroup /var/lib/myappпосле фикса
Симптомы
- Поставили SUID на shell-script (`chmod 4755 myscript.sh`), запустили обычным юзером -- но эффективный UID остался 1000, а не root. Команды внутри скрипта не имеют root-прав.
Причина
Linux ИГНОРИРУЕТ SUID на script'ах (#!/bin/bash, #!/usr/bin/python...) из соображений безопасности. Только на native binary FS примонтирована с опцией `nosuid` (часто /tmp, /home в paranoid-setup'ах) SELinux/AppArmor policy блокирует privilege escalation У NFS-mount по умолчанию `nosuid` для безопасности В контейнере: NoNewPrivileges=true в unit или 'no-new-privileges' в Docker -- SUID полностью отключен Файл owner стал не-root после перемещения/копирования (chmod -- preserve SUID, mv -- yes; cp без -p -- сбрасывает)
Решение
- Главное: SUID на shell-script Linux НЕ РАБОТАЕТ. Использовать wrapper-binary на C:
setuid(0); execvp('/path/to/script', args), ставить SUID на этот binary - Альтернатива: sudo с NOPASSWD для конкретной команды (правильный путь для admin-таsks)
- Mount nosuid -- перемонтировать без nosuid:
mount -o remount,suid /home, или вынести binary в /usr/local/bin - SELinux: создать правильную policy через
audit2allow -a -M mypolicy,semodule -i mypolicy.pp - В Docker/k8s: убрать allowPrivilegeEscalation=false или NoNewPrivileges если SUID действительно нужен
- Capabilities как замена SUID:
setcap cap_net_raw=+ep /path/to/binary-- даёт только нужное
Симптомы
- Машина грузится 60+ секунд. systemd-analyze показывает большие values. Кажется, что-то висит в начале или конце boot-process'а.
Причина
Один noisy service блокирует ready event (After=network-online.target без надобности; ждёт сеть, которая идёт минуту) fsck выполняется при boot (выставлен pass>0 в fstab) -- может занять много времени на больших FS Дисковая медлительность (HDD, плохой SSD) -- множество мелких read'ов при init DHCP-таймаут на отсутствующем интерфейсе ZFS pool import при больших pool'ах Initrd медленно собирается (плохая kmod) -- проверить /boot/initrd.img GPU-driver init (особенно nvidia) при некоторых конфигурациях Plymouth (boot splash) глючит на каких-то GPU
Решение
- Отключить NetworkManager-wait-online.service если он не критичен:
systemctl disable NetworkManager-wait-online.service - Убрать noisy сервисы из boot path: оставить только то, что реально нужно при старте; остальное -- по требованию
- Если fsck тормозит -- настроить периодичность через
tune2fs -i 0 -c 0(не делать fsck при каждой N-й mount или периодически) - Disk slow -- профайл через biolatency, обновить SSD если HDD
- DHCP-timeout: в NetworkManager выставить timeout-low или использовать static IP / dhclient-timeout
- Для каждого 'оранжевого' unit в blame -- расследовать journalctl -u <unit>; часто решается изменением конфига
- Профилактика: после установки нового сервиса проверять
systemd-analyze blameне сильно ли он добавил
Симптомы
- Логи в неправильном времени, cron срабатывает 'не вовремя', `date` показывает время другой зоны. Особенно после копирования VM или образа из шаблона.
Причина
Образ VM был с GMT/UTC по умолчанию, после клонирования timezone не настроена под локацию /etc/localtime -- symlink на конкретную зону, мог не быть установлен или указывать на старое Hardware clock (RTC) был в local time на одной системе, в UTC на другой -- разница после reboot'а Container inheriting timezone хоста, который менялся NTP не синхронизировался (нет интернета / неверный сервер)
Решение
- Сменить timezone:
sudo timedatectl set-timezone Europe/Moscow(использует список из /usr/share/zoneinfo) - Перезапустить сервисы, которые кэшируют tz: rsyslog/journald (
systemctl restart), и приложения с собственным cache - Включить NTP если выключен:
sudo timedatectl set-ntp true - Hardware clock в UTC (правильно для Linux):
sudo timedatectl set-local-rtc 0 - В контейнерах: пробросить /etc/timezone и /etc/localtime через
docker run -v /etc/timezone:/etc/timezone:roили установить tzdata + setup внутри - Профилактика: в шаблоне VM/образе -- автоматическая настройка tz по геолокации/конфигу cloud-init
Симптомы
- Приложения начинают падать с ошибками вида 'cannot create temp file', 'no space left on /tmp'. df показывает /tmp 100%.
Причина
Старые pip/cargo/build temp-файлы накопились и никто их не убрал Aborted-процесс оставил файлы в /tmp (например, упавший компилятор оставляет .o файлы) Crash-dumps приложений (core dumps в /tmp), особенно при отсутствии лимита tmpfs (RAM-based /tmp) ограничен по size= mount-option -- быстро заполняется при заливке больших файлов X-server / Xorg .X*-lock файлы при крашах Snap/flatpak temp-кэши Долгоиграющие приложения с retention в /tmp (mysql temp-tables, элементы php session)
Решение
- Быстро почистить старое:
sudo find /tmp -type f -atime +7 -delete(атрибут access time старше 7 дней) - Для одноразового освобождения большой части:
sudo systemctl restart systemd-tmpfiles-clean.service - Настроить автоматическую чистку: /etc/tmpfiles.d/*.conf плюс systemd-tmpfiles-clean.timer (на большинстве дистро уже включён, но политика может быть мягкая)
- Увеличить tmpfs если /tmp в RAM:
mount -o remount,size=4G /tmp - Переместить /tmp на нормальный disk если RAM мала: убрать tmpfs entry в fstab, перезагрузка
- Core-dumps вынести:
sysctl kernel.core_pattern=/var/coredumps/core-%e-%p-- сразу в выделенное место с ротацией - Установить TMPDIR в env для скриптов: чтоб build-temp писалось в нужное место