A
Admin Guides | Сисадмин
17.07.2026 09:50 · 👁 465
Как дебажить приложение в продакшне через coredump не останавливая его
Классическая дилемма: приложение ведёт себя странно, нужно посмотреть что внутри, но остановить нельзя. gdb attach останавливает процесс на время работы.
Решение: снять coredump живого процесса и анализировать его отдельно пока процесс продолжает работать.
Снимаем дамп без остановки процесса
gcore снимает coredump с живого процесса. Процесс кратко останавливается только на момент снятия дампа, обычно доли секунды:
gcore -o /tmp/myapp.core <PID>
Для минимальной паузы на больших процессах используем checkpoint через /proc:
cp /proc/<PID>/mem /tmp/mem.dump 2>/dev/null
Альтернатива через gdb с немедленным detach:
gdb -p <PID> -batch -ex "gcore /tmp/myapp.core" -ex detach
Процесс остановится на время записи дампа и сразу продолжит работу.
Анализируем дамп
gdb /usr/bin/myapp /tmp/myapp.core
Смотрим стек всех потоков:
(gdb) thread apply all bt
Смотрим переменные в конкретном фрейме:
(gdb) frame 3
(gdb) info locals
(gdb) print variable_name
Настраиваем автоматический сбор coredump
Чтобы дамп снимался автоматически при падении, а не только вручную:
ulimit -c unlimited
echo "/tmp/core.%e.%p" > /proc/sys/kernel/core_pattern
Через systemd это уже настроено, дампы идут в journald:
coredumpctl list
coredumpctl gdb <PID>
coredumpctl gdb сразу открывает дамп в gdb с правильным бинарником.
Если нужны символы
Без debug symbols стек будет нечитаемым. Ставим отдельный пакет с символами:
apt install myapp-dbgsym
dnf debuginfo-install myapp
Или указываем путь вручную:
(gdb) set debug-file-directory /usr/lib/debug
A
Admin Guides | Сисадмин
16.07.2026 09:45 · 👁 782
Как найти какой поток внутри процесса жрёт CPU
Процесс грузит CPU, но внутри него десятки потоков и непонятно кто виноват. top показывает только суммарную нагрузку по PID, нужно копать глубже.
Смотрим потоки через top
top -H -p <PID>
-H разворачивает потоки. Каждый поток виден отдельно со своим TID и процентом CPU.
Запоминаем TID самого жирного.
perf для профилирования конкретного потока
Снимаем профиль всего процесса на 30 секунд:
perf record -g -p <PID> sleep 30
perf report
-g включает call graph, видно не просто функцию но и стек вызовов который к ней привёл. В интерактивном меню perf report сортируем по overhead, самые тяжёлые функции сверху.
Если нужен конкретный поток:
perf record -g -t <TID> sleep 30
perf report --tid <TID>
gdb для быстрого снимка стека
Если perf недоступен или нужно просто посмотреть что поток делает прямо сейчас без долгого профилирования:
gdb -p <PID>
(gdb) info threads
(gdb) thread <N>
(gdb) bt
info threads покажет все потоки и их текущую функцию. Переключаемся на подозрительный и смотрим полный backtrace.
Снимаем стек всех потоков сразу без интерактива:
gdb -p <PID> -batch -ex "thread apply all bt" 2>/dev/null
Удобно для анализа потом, а не в реальном времени.
Связываем TID из top с потоком в gdb
TID из top это системный thread ID, в gdb нумерация своя. Смотрим соответствие:
(gdb) info threads
В выводе будет LWP <TID> рядом с номером потока gdb. Находим нужный TID и переключаемся:
(gdb) thread <N>
(gdb) bt full
A
Admin Guides | Сисадмин
16.07.2026 05:05 · 👁 951
💬 Вопрос на собеседовании для сисадмина
Давайте разберем один из частых вопросов, который может быть задан на собеседовании и как на него отвечать.
❓Вопрос: Что такое Linux slab allocator и как он отличается от buddy allocator?
✅Ответ: Slab allocator — это механизм управления памятью ядра Linux, оптимизированный для частого выделения и освобождения объектов фиксированного размера, таких как дескрипторы файлов, структуры процессов и сетевые буферы. Он использует предварительно выделенные кэши (slabs) с объектами, что снижает фрагментацию и ускоряет работу ядра.
Buddy allocator, напротив, управляет страницами памяти переменного размера. Он эффективно выделяет большие блоки памяти, но плохо подходит для частого создания мелких структур, так как приводит к фрагментации и затратам на обработку.
A
Admin Guides | Сисадмин
15.07.2026 09:35 · 👁 890
Что такое vDSO и почему gettimeofday() не делает syscall
Системный вызов это дорого: переключение контекста из user space в kernel space, сохранение регистров, проверка привилегий, обратно. На современном железе это 100-300 наносекунд за вызов.
Для функций которые вызываются миллионы раз в секунду это заметно.
vDSO (virtual Dynamic Shared Object) это небольшая библиотека которую ядро маппит в адресное пространство каждого процесса автоматически.
Код из vDSO выполняется в user space, но читает данные которые ядро обновляет в разделяемой памяти. Никакого переключения контекста, никакого syscall.
Проверяем что vDSO реально примаплено:
cat /proc/self/maps | grep vdso
Смотрим какие функции экспортирует:
nm /proc/self/maps
objdump -T /usr/lib/x86_64-linux-gnu/vdso.so
Или через readelf на извлечённом образе:
cat /proc/self/maps | grep vdso | awk '{print $1}' | head -1
readelf -s /proc/kcore 2>/dev/null | grep vdso
Как это работает для gettimeofday()
Ядро поддерживает структуру vvar в разделяемой памяти: текущее время, частота таймера, всё что нужно для расчёта времени. vDSO содержит код который читает эту структуру и вычисляет результат прямо в user space. gettimeofday() в libc просто вызывает реализацию из vDSO.
Проверяем через strace что syscall не происходит:
strace -e gettimeofday ./myprogram
В выводе gettimeofday не будет, хотя программа его вызывает. strace перехватывает только реальные syscall-ы, а vDSO их не делает.
Что ещё идёт через vDSO
clock_gettime()
clock_getres()
getcpu()
time()
Все функции связанные с временем и CPU affinity, то есть те которые вызываются очень часто и не требуют привилегий.
A
Admin Guides | Сисадмин
14.07.2026 16:25 · 👁 1.1K
Непрерывная разработка — проще с Evolution Repo 🌐
Внешние факторы влияют на доступность инструментов, поэтому важно сохранять непрерывность и темп разработки.
Evolution Repo обеспечивает гибкость:
1️⃣ Полный перенос. Перенесите репозиторий с тегами, ветками и историей коммитов в стабильную среду Cloud․ru.
2️⃣ Дублирование. Настройте зеркалирование для автоматической отправки изменений в GitHub и Evolution Repo. Так вы сохраните рабочий процесс и получите резервную копию в надежной инфраструктуре.
Сделать бэкап
A
Admin Guides | Сисадмин
14.07.2026 09:25 · 👁 1K
Почему fwrite() быстрее write() и когда это не так
write() это системный вызов: каждый вызов это переключение из user space в kernel space.
Дорогая операция даже если записываешь один байт. fwrite() это обёртка из libc с буфером внутри: данные накапливаются в памяти и уходят в ядро одним write() когда буфер заполнился или явно сбросили через fflush().
Разница заметна когда приложение делает много мелких записей. Тысяча вызовов fwrite() по 10 байт это один реальный write() на 10KB в конце. Тысяча вызовов write() по 10 байт это тысяча syscall-ов.
Проверяем сколько syscall-ов реально делает процесс:
strace -e write -c ./myprogram
Покажет количество вызовов write() и суммарное время. Если вызовов много и они мелкие, fwrite() с буферизацией даст заметный выигрыш.
Когда fwrite() медленнее или неприемлем
При падении процесса данные в буфере libc теряются. write() гарантирует что данные попали в page cache ядра, fwrite() не гарантирует ничего до fflush() или fclose(). Для логов и критичных данных это проблема.
Несколько потоков пишут в один FILE*, нужна синхронизация на уровне приложения. write() на уровне ядра атомарен для записей меньше PIPE_BUF, fwrite() нет.
Если нужна точная синхронизация с диском, fwrite() + fflush() + fsync() это три операции вместо write() + fsync(). Буферизация здесь не помогает.
Смотрим размер буфера FILE* и меняем если нужно:
setvbuf(fp, NULL, _IOFBF, 65536);
_IOFBF полная буферизация, _IOLBF построчная (дефолт для терминалов), _IONBF без буферизации (эквивалент write()).
A
Admin Guides | Сисадмин
14.07.2026 05:05 · 👁 1.1K
9 июля вышел Rust 1.97.
В релизе разработчики улучшили Cargo, обновили обработку сообщений компоновщика и стабилизировали новые API стандартной библиотеки.
Главные изменения:
• схема именования символов v0 теперь используется по умолчанию, что упрощает работу с обобщёнными типами и улучшает совместимость инструментов;
• в Cargo появилось встроенное управление предупреждениями (allow, warn, deny) без инвалидирования кэша сборки, что особенно полезно для CI;
• rustc теперь показывает предупреждения от компоновщика вместо их скрытия, помогая быстрее находить потенциальные проблемы;
• добавлены новые стабильные API для работы с битовыми операциями, типами NonZero, RepeatN и другими компонентами стандартной библиотеки.
A
Admin Guides | Сисадмин
13.07.2026 09:16 · 👁 1.1K
Как сделать chroot-окружение для тестирования без Docker
chroot меняет корневую директорию для процесса. Всё что выше новой точки монтирования становится невидимым.
Никакого daemon, никакого network namespace, никакого образа, просто директория которая притворяется корнем файловой системы.
Полезно, когда нужно протестировать пакет или скрипт в изолированном окружении без разворачивания контейнера, или починить сломанную систему загрузившись с live-USB.
⏺Создаём минимальное окружение
На Debian/Ubuntu используем debootstrap, он разворачивает минимальный rootfs:
apt install debootstrap
debootstrap --arch=amd64 bookworm /opt/chroot-env http://deb.debian.org/debian
Занимает пару минут, создаёт полноценное минимальное окружение в /opt/chroot-env.
⏺Монтируем необходимые псевдофайловые системы
Без них многие команды внутри не работают:
mount -t proc proc /opt/chroot-env/proc
mount -t sysfs sysfs /opt/chroot-env/sys
mount --bind /dev /opt/chroot-env/dev
mount --bind /dev/pts /opt/chroot-env/dev/pts
⏺Входим
chroot /opt/chroot-env /bin/bash
Теперь / это /opt/chroot-env. Можно ставить пакеты, менять конфиги, тестировать скрипты, ничего не затрагивая хостовую систему.
Чистим после работы
umount /opt/chroot-env/proc
umount /opt/chroot-env/sys
umount /opt/chroot-env/dev/pts
umount /opt/chroot-env/dev
rm -rf /opt/chroot-env
⏺Типичные сценарии
Починить сломанный загрузчик или пересобрать initramfs на системе которая не загружается: загружаемся с live-USB, монтируем раздел, делаем chroot и работаем как будто внутри живой системы.
Тестирование установки пакетов в чистом окружении без виртуалки и без Docker когда нужно просто проверить зависимости или поведение скрипта после установки.
A
Admin Guides | Сисадмин
13.07.2026 05:05 · 👁 1.2K
💬 Вопрос на собеседовании для DevOps-инженера
Давайте разберем один из частых вопросов, который может быть задан на собеседовании и как на него отвечать.
❓Вопрос: Что такое CFS bandwidth control и как оно управляет CPU-ресурсами в Linux?
✅Ответ: CFS bandwidth control — это механизм планировщика Completely Fair Scheduler (CFS) для ограничения доли процессорного времени, доступного группе задач (cgroup). Он позволяет задавать квоты CPU для контейнеров и процессов, предотвращая «захват» CPU одним нагрузочным процессом.
Как это работает:
• Задаётся период (cpu.cfs_period_us) и квота (cpu.cfs_quota_us).
• Планировщик распределяет CPU среди процессов так, чтобы группа не превышала выделенную квоту за период.
• Если квота исчерпана, процессы группы ждут до следующего периода, а остальные задачи продолжают выполняться.
A
Admin Guides | Сисадмин
10.07.2026 09:10 · 👁 1.4K
Проверка соответствия установленных пакетов списку разрешённых
В regulated-окружениях или просто на продакшн-серверах где важна воспроизводимость, лишний пакет это потенциальный риск: расширенная поверхность атаки, неожиданные зависимости, или просто кто-то поставил curl для отладки и забыл убрать.
Создаём эталонный список
Берём чистый сервер в нужном состоянии и фиксируем:
dpkg --get-selections | awk '{print $1}' | sort > /etc/compliance/allowed_packages.txt
Для RPM-систем:
rpm -qa --queryformat '%{NAME}\n' | sort > /etc/compliance/allowed_packages.txt
Сравниваем текущее состояние с эталоном
dpkg --get-selections | awk '{print $1}' | sort > /tmp/current_packages.txt
diff /etc/compliance/allowed_packages.txt /tmp/current_packages.txt
Строки с + это пакеты которых нет в эталоне, то есть поставленные после фиксации baseline.
Скрипт с алертом
#!/bin/bash
BASELINE="/etc/compliance/allowed_packages.txt"
CURRENT=$(mktemp)
CHAT_ID="ваш_chat_id"
BOT_TOKEN="ваш_токен"
dpkg --get-selections | awk '{print $1}' | sort > "$CURRENT"
NEW_PACKAGES=$(comm -13 "$BASELINE" "$CURRENT")
if [ -n "$NEW_PACKAGES" ]; then
MSG="⚠️ Несанкционированные пакеты на $(hostname):\n${NEW_PACKAGES}"
curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
-d chat_id="${CHAT_ID}" \
-d text="$MSG"
fi
rm "$CURRENT"
comm -13 показывает строки которые есть только во втором файле, то есть новые пакеты не из baseline.
В cron раз в день:
0 8 * * * /usr/local/bin/check_compliance.sh
Проверка версий а не только наличия
Если важна не только наличие пакета но и конкретная версия:
dpkg -l | awk '{print $2, $3}' | sort > /tmp/current_versions.txt
diff /etc/compliance/allowed_versions.txt /tmp/current_versions.txt