Когда TCP-сервер начинает захлебываться под нагрузкой, проблема не всегда в CPU, памяти или плохом приложении. Иногда упирается сам механизм приема новых соединений. Чтобы понять, что происходит, полезно знать три вещи: SYN flood, backlog и somaxconn
клиент отправляет SYN
сервер отвечает SYN-ACK
клиент присылает ACK
соединение считается установленным
Но между "клиент постучался" и "приложение приняло соединение" есть очереди ядра.
SYN flood. Сервер получает огромное количество SYN, но рукопожатие не завершается. Это может быть атака, кривой балансировщик, сетевые потери или просто всплеск нагрузки. В итоге очередь полуоткрытых соединений заполняется, и новые клиенты начинают теряться.
backlog. Когда приложение вызывает listen(), оно указывает размер очереди ожидающих подключений.
Если приложение не успевает быстро принимать новые соединения через accept(), очередь переполняется.
Тогда часть клиентов начинает видеть: таймауты, connection refused, повторные SYN, странные задержки на подключении.
somaxconn. Это системный лимит ядра на максимальный backlog. Даже если приложение просит большую очередь, ядро все равно ограничит ее значением net.core.somaxconn.
Посмотреть можно так:
sysctl net.core.somaxconn
А заодно часто смотрят:
sysctl net.ipv4.tcp_max_syn_backlog
Первый параметр влияет на очередь установленных, но еще не принятых приложением соединений. Второй на очередь полуоткрытых SYN.
приложение медленно вызывает accept()
очередь backlog заполняется
новые подключения начинают отваливаться
если сверху еще идет SYN flood, ситуация усугубляется
снаружи кажется, что сервер то работает, то нет
ss -lnt
netstat -s | grep -i listen
dmesg | grep -i syn
увеличить somaxconn;
проверить backlog самого приложения;
поднять tcp_max_syn_backlog;
включить или проверить SYN cookies;
разбираться, почему приложение медленно принимает соединения;
выносить защиту от flood на firewall/LB;
Например:
sysctl -w net.core.somaxconn=4096
sysctl -w net.ipv4.tcp_max_syn_backlog=4096
Но увеличение очередей не лечит корень проблемы. Если приложение не успевает обрабатывать подключения, вы просто делаете буфер больше. Это даст время, но не бесконечную защиту. Хорошая диагностика здесь начинается с простого вопроса: сервер не справляется с валидной нагрузкой или его забивают незавершенными SYN? Потому что снаружи это выглядит одинаково: TCP-порт открыт, а подключиться нормально уже нельзя.
#tcp #network
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10
Когда в shell нужно обработать много файлов, хостов или строк, первый импульс обычно такой: написать for-цикл. Рабочий подход, но не всегда самый быстрый и удобный. Во многих таких задачах лучше использовать
xargs - утилиту, которая берет поток данных из stdin и превращает его в аргументы для другой команды.Проще говоря: нашли список объектов -> передали их в xargs -> массово обработали одной командой
cat hosts.txt | xargs ping -c 1
Но тут сразу важный нюанс: далеко не каждая команда умеет принимать много аргументов в таком виде. Поэтому на практике чаще используют
-I или -n.Например, пинговать хосты по одному:
cat hosts.txt | xargs -n1 ping -c 1
Здесь
-n1 говорит: передавать по одному аргументу на запуск.
find /var/log -name "*.gz" | xargs rm -f
Но тут уже есть подводный камень: пробелы в именах файлов.
Безопасный вариант:
find /var/log -name "*.gz" -print0 | xargs -0 rm -f
Связка
-print0 и xargs -0 - это почти обязательная привычка, если работаете с файлами.удаление и перемещение больших списков файлов;
массовый grep, chmod, chown;
обработка списков IP, URL, доменов;
запуск команд по данным из файла;
параллельное выполнение задач.
Например, проверить доступность списка хостов:
cat hosts.txt | xargs -n1 -P4 ping -c 1
-n1 - один хост на запуск-P4 - до 4 процессов параллельноВот здесь xargs уже реально ускоряет работу. Особенно на больших списках, где последовательная обработка была бы слишком долгой.
find /etc -type f -name "*.conf" -print0 | xargs -0 grep -H "listen"
Или массово смотреть размер файлов:
find /backup -type f -print0 | xargs -0 du -h
Когда нужен шаблон с явной подстановкой, используют -I:
cat users.txt | xargs -I{} id {}
Здесь
{} - место, куда подставляется значение из stdin.-n1 - по одному аргументу за запуск-P4 - параллелить выполнение-0 - безопасно работать с пробелами и спецсимволами-I{} - явная подстановка значения в команду#linux #xargs
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤3
Обычно IP-адрес ассоциируется с одним конкретным сервером. Но в случае с Anycast один и тот же IP может быть объявлен сразу из нескольких точек сети. Идея простая: у разных серверов один и тот же IP-адрес, а трафик уходит туда, куда маршрут ближе с точки зрения сети. То есть клиент обращается к одному и тому же адресу, но реально попадает не обязательно на один и тот же физический сервер, а на ближайший или наиболее предпочтительный узел.
Где это удобно:
DNS-сервисы
CDN
DDoS-защита
глобальные edge-сервисы
публичные API
распределенные точки входа
Самый известный пример - публичные DNS. Когда вы отправляете запрос на 1.1.1.1 или 8.8.8.8, за этим IP обычно стоит не один сервер, а целая сеть узлов в разных регионах.
• меньше задержка. Пользователь попадает на ближайшую точку, а не тянется через полмира.
• лучше отказоустойчивость. Если один узел пропал, маршрутизация просто уводит трафик на другой.
• проще масштабирование. Можно добавлять новые точки присутствия, не меняя IP для клиентов.
• распределение нагрузки на уровне сети. Трафик естественным образом разъезжается по разным площадкам.
есть 3 дата-центра - в Германии, Нидерландах и Польше. Во всех трех анонсируется один и тот же IP. Пользователь из Берлина, скорее всего, попадет в немецкий узел. Пользователь из Амстердама - в нидерландский. А если немецкий узел исчезнет из маршрутизации, трафик может уйти в ближайший оставшийся.
Anycast сам по себе не знает, где лучше по задержке для приложения. Он опирается на маршрутизацию, обычно через BGP.
А значит ближайший - это не всегда географически ближайший, а тот, который сеть считает лучшим маршрутом.
• думают, что Anycast - это обычный балансировщик. Не совсем. Балансировка тут происходит на уровне маршрутов, а не L7/L4-логики.
• считают, что клиент всегда попадет в один и тот же DC. Не обязательно. При изменении маршрутов путь может поменяться.
• пытаются использовать Anycast там, где нужна жесткая session affinity. Для stateful-сервисов это уже сложнее, потому что маршрут может измениться.
DNS;
stateless UDP-сервисы;
edge reverse proxy;
глобальные точки входа;
анти-DDoS платформы.
#network #anycast
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
Когда сервер или рабочая машина внезапно начинают “тупить по сети”, первый вопрос обычно очень приземленный: кто именно сейчас жрет канал? Для такой быстрой диагностики есть две простые и очень полезные утилиты:
iftop и nethogs. Обе показывают сетевую активность в реальном времени, но делают это по-разному.iftop - показывает трафик между хостами
nethogs - показывает трафик по процессам
Именно в этом их главное различие.
iftop похож на top, только для сети. Он показывает, какие IP-адреса и соединения прямо сейчас генерируют трафик, и в каких объемах.Установка:
apt install iftop
Запуск:
iftop -i eth0
Что удобно в iftop:
• сразу видно самые тяжелые соединения;
• можно понять, идет ли трафик наружу или внутрь;
• удобно ловить внезапные бэкапы, репликации, выгрузки и подозрительные соединения.
Типичные находки:
сервер льет бэкап в удаленное хранилище;
кто-то качает большой файл по SCP;
приложение внезапно активно общается с внешним API;
контейнер или VM начали активно ходить в сеть.
Но здесь важно помнить: iftop показывает не процессы, а именно сетевые потоки между адресами.
Установка:
apt install nethogs
Запуск:
nethogs eth0
Теперь уже видно, сколько трафика потребляет конкретный процесс:
rsync
curl
docker
python
apt
любой другой живой процесс
Это особенно полезно, когда на хосте много сервисов, и по одним только IP трудно понять, кто виноват.
iftop - если нужно быстро понять: с какими IP идет обмен, какие соединения самые тяжелые, куда вообще уходит канал
nethogs - если нужно понять: какой процесс грузит сеть, кто именно качает или отдает данные, какой сервис стал шумным
Обе утилиты обычно требуют root-права, иначе картина может быть неполной.
И еще: если трафик очень кратковременный, его легко не поймать. В таких случаях лучше смотреть несколько минут, а не пару секунд.
#linux #network
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Иногда сеть вроде настроена правильно, маршруты есть, интерфейсы подняты, а пакеты все равно куда-то пропадают. Особенно часто это всплывает на серверах с несколькими интерфейсами, policy routing, VPN, асимметричной маршрутизацией или в роли роутера. Одна из причин - Reverse Path Filtering (rp_filter). Это механизм в linux, который проверяет: если пакет пришел с какого-то IP, а мы захотели бы отправить ответ до этого IP через другой интерфейс - не выглядит ли это подозрительно? Если выглядит - пакет может быть отброшен.
защита от IP spoofing;
базовая проверка правдоподобности маршрута;
уменьшение шансов принять пакет с поддельным source IP.
То есть идея хорошая: пакет пришел не с той стороны, откуда ядро ожидало бы обратный маршрут - значит, что-то не так. Но на практике именно здесь начинаются странности.
multihomed-серверы с несколькими uplink;
VPN и туннели;
policy based routing;
асимметричные маршруты;
NAT-сценарии;
Kubernetes / overlay-сети;
Linux в роли маршрутизатора
• пакет приходит через eth1;
• ядро считает, что обратный путь до source IP должен идти через eth0;
• rp_filter решает, что пакет подозрительный;
• пакет молча дропается
Снаружи это выглядит очень неприятно: ping иногда работает, иногда нет; сервис доступен только с части адресов; VPN поднимается, но трафик странно теряется; один интерфейс живой, а ответы уходят не туда; tcpdump показывает входящий пакет, но приложение его не видит.
sysctl net.ipv4.conf.all.rp_filter
sysctl net.ipv4.conf.eth0.rp_filter
Обычно значения такие:
0 - выключено
1 - strict mode
2 - loose mode
strict mode хорош для простых хостов с одной понятной маршрутизацией. Но в сложной сетевой схеме он легко становится источником магических багов. Часто более безопасный компромисс - это loose mode:
sysctl -w net.ipv4.conf.all.rp_filter=2
sysctl -w net.ipv4.conf.default.rp_filter=2
Или точечно для интерфейса:
sysctl -w net.ipv4.conf.eth1.rp_filter=2
Полностью отключают обычно только там, где точно понимают, зачем это нужно.
#linux #network
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤1
Иногда нужно ответить на очень простой вопрос: кто это держит? Файл удалили, а место не освободилось. Порт занят, но непонятно кем. Диск не размонтируется, потому что "device is busy". Во всех этих случаях помогает
lsof - утилита, которая показывает открытые файлы. В linux почти все является файлом: обычные файлы, сокеты, сетевые соединения, устройства, точки монтирования. Поэтому lsof умеет находить очень много полезного.
apt install lsof
#или
yum install lsof```
lsof /var/log/app.log
Так можно увидеть процесс, PID и пользователя, который открыл файл. Особенно полезно, когда файл уже удалили, но место на диске не вернулось:
lsof | grep deleted
или аккуратнее:
lsof +L1
Это покажет открытые файлы, у которых уже нет ссылок в файловой системе.
Типичный виновник - сервис, который продолжает писать в старый лог после удаления или неудачной ротации.
lsof -i :8080
Для TCP:
lsof -iTCP:8080 -sTCP:LISTEN
В выводе будет видно имя процесса, PID и пользователь. Это удобно, когда сервис не стартует с ошибкой вроде: address already in use
umount /mnt/backup
А в ответ: target is busy
Смотрим, кто держит файлы внутри:
lsof +D /mnt/backup
После этого можно понять, какой процесс мешает размонтированию: shell с текущей директорией внутри mount point, backup-скрипт, rsync, база или зависший процесс.
lsof -i
Только TCP:
lsof -iTCP
Только UDP:
lsof -iUDP
Это не всегда замена ss, но часто удобно, когда нужно быстро связать соединение с процессом.
lsof /path/to/file
lsof -i :PORT
lsof -iTCP:PORT -sTCP:LISTEN
lsof +D /mount/path
lsof +L1
#linux #lsof
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍4
LVM умеет не только создавать обычные логические тома, но и собирать RAID-массивы. Это удобно, когда хочется управлять дисками, томами и отказоустойчивостью в одном месте, без отдельного mdadm.
Ниже будет шпаргалка по созданию LVM RAID1, замене вылетевшего диска и включению проверки целостности через DM Integrity.
Создаем RAID1 из двух дисков:
apt install lvm2
pvcreate /dev/sdb /dev/sdc
vgcreate vgroup /dev/sdb /dev/sdc
lvcreate --type raid1 -l 100%FREE -n lvraid1 vgroup
Проверяем тип сегмента:
lvs -o+segtype
Создаем файловую систему и монтируем том:
mkfs.ext4 /dev/vgroup/lvraid1
mkdir /mnt/lvraid1
mount /dev/vgroup/lvraid1 /mnt/lvraid1
Смотрим итоговую структуру:
lsblkТеперь можно записать данные и дождаться полной синхронизации:
cp -r /var/log/* /mnt/lvraid1/
Если после этого отключить один из дисков, данные останутся доступны. RAID1 продолжит работать в degraded-состоянии.
Проверить статус можно так:
lvs -a -o name,devices vgroup
Если один диск пропал, в выводе появится устройство со статусом [unknown].
Допустим, вместо старого диска в системе появился новый /dev/sdd.
Добавляем его в VG:
pvcreate /dev/sdd
vgextend vgroup /dev/sdd
Удаляем пропавший диск из volume group:
vgreduce --removemissing vgroup --force
Проверяем, что [unknown] исчез:
lvs -a -o name,devices vgroup
Теперь восстанавливаем RAID и добавляем новый диск в зеркало:
lvconvert --repair vgroup/lvraid1
lvconvert -m1 vgroup/lvraid1 /dev/sdd
На вопросы LVM отвечаем утвердительно. Наблюдать за синхронизацией можно так:
lvs -a -o name,devices vgroup
lvs -o+segtype
В итоге диск заменен, данные на месте, система продолжает работать.
Это один из плюсов LVM RAID: замена диска проходит достаточно логично и без остановки сервера, если железо позволяет hot-swap.
pvcreate /dev/sdc /dev/sde
vgextend vgroup /dev/sdc /dev/sde
lvconvert --type raid5_n vgroup/lvraid1
lvresize -l5118 vgroup/lvraid1
lvconvert --stripes 1 vgroup/lvraid1
lvconvert --stripes 2 vgroup/lvraid1
lvconvert --type raid10 vgroup/lvraid1
lvconvert --type raid10 vgroup/lvraid1
Размер в lvresize нужно подбирать под конкретный LV. LVM в процессе обычно сам подсказывает, что ему нужно: где не хватает места, где конвертация идет в несколько этапов, и какой промежуточный тип требуется. Если RAID10 создается с нуля, все намного проще. Добавляем 4 диска в VG и создаем LV:
lvcreate --type raid10 -l 100%FREE -n lvraid10 vgroup
lvcreate --type raid1 --raidintegrity y -L 9G -n lvraid1 vgroup
Важный момент: под метаданные integrity нужно свободное место. Если указать 100%FREE, можно получить ошибку, потому что LVM не сможет выделить место под служебные данные. Если в VG есть свободное место, integrity можно добавить к уже существующему RAID-томy:
lvconvert --raidintegrity y vgroup/lvraid1
При чтении данных будет проверяться контрольная сумма. Если она не совпадет с сохраненной, LVM попытается восстановить данные из другой копии RAID.
Для каждого диска в составе такого LV создаются дополнительные служебные тома под integrity metadata. Статистику ошибок можно смотреть так:
lvs -o+integritymismatches vgroup/lvraid1_rimage_0_imeta
Нужная колонка:
IntegMismatchesТакже события по DM Integrity будут попадать в системный лог, например:
/var/log/syslog#lvm #raid
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤2
Одна из классических проблем в автоматизации: скрипт еще не успел завершиться, а cron, systemd timer или админ руками запускает его снова. В итоге получаем неприятные эффекты: два бэкапа пишут в один каталог, две задачи одновременно чистят одни и те же файлы, несколько копий скрипта дергают API, повторный запуск ломает промежуточное состояние, база получает дубли или конфликтующие операции и т.д. Чтобы этого избежать, удобно использовать flock.
flock - это утилита для работы с файловыми блокировками. Идея простая: перед запуском скрипт пытается взять lock-файл. Если блокировка уже занята, значит другая копия скрипта еще работает.
flock -n /tmp/myjob.lock /opt/scripts/myjob.sh
/tmp/myjob.lock - файл блокировки-n - не ждать освобождения lock, а сразу завершиться/opt/scripts/myjob.sh - команда, которую нужно выполнитьЕсли первый запуск еще работает, второй просто не стартует.
*/5 * * * * flock -n /tmp/backup.lock /opt/scripts/backup.sh
`
Теперь даже если бэкап длится дольше 5 минут, новая копия не запустится поверх старой.
flock -n /tmp/backup.lock /opt/scripts/backup.sh || echo "backup already running"
#!/usr/bin/env bash
set -euo pipefail
LOCK_FILE="/tmp/myjob.lock"
exec 200>"$LOCK_FILE"
flock -n 200 || {
echo "script already running"
exit 1
}
echo "start job"
# основная логика скрипта
sleep 30
echo "done"
exec 200>"$LOCK_FILE" открывает lock-файл на файловом дескрипторе 200flock -n 200 пытается взять блокировкуесли блокировка занята - скрипт завершается
пока скрипт работает, дескриптор открыт и lock удерживается
После завершения процесса блокировка освобождается автоматически.
/tmp/myjob.lock
Для системных сервисов лучше:
/run/myjob.lock
/run обычно живет в tmpfs и очищается после перезагрузки. Это удобно для runtime-блокировок.
flock /tmp/myjob.lock /opt/scripts/myjob.sh
В этом случае второй запуск будет ждать, пока первый освободит lock.
Можно задать таймаут ожидания:
flock -w 60 /tmp/myjob.lock /opt/scripts/myjob.sh
Так команда подождет до 60 секунд и завершится, если блокировка не освободится.
#bash #flock
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤3
Одна из частых ошибок в мониторинге - считать сервис рабочим только потому, что его процесс запущен.
systemctl status myapp
показывает active (running), PID есть, порт вроде слушается - значит все хорошо? Не всегда.
Процесс может быть живым, но сам сервис уже фактически не работает:
завис на подключении к базе;
потерял доступ к Redis или очереди;
перестал обрабатывать запросы;
отвечает только частью функционала;
уперся в пул соединений;
ловит deadlock внутри приложения;
слушает порт, но возвращает 500.
Поэтому нормальная проверка должна отвечать не на вопрос: "процесс существует?" А на вопрос: "сервис реально выполняет свою задачу?"
curl -fsS http://127.0.0.1:8080/health
Если endpoint возвращает 200 OK, уже лучше, чем просто проверять PID. Но и здесь есть нюанс.
Плохой health check: /health -> 200 OK просто потому что приложение запустилось.
доступна ли база;
работает ли очередь;
можно ли читать конфиг;
не закончились ли critical ресурсы;
готов ли сервис принимать трафик;
В кубере это разделяют на разные проверки:
liveness - процесс жив или его надо перезапустить;
readiness - можно ли отправлять на него трафик;
startup - успел ли сервис нормально подняться.
Эту же логику полезно применять и без k8s. Например:
curl -fsS http://127.0.0.1:8080/ready
/ready может возвращать ошибку, если приложение живо, но база недоступна. В таком состоянии сервис лучше не убивать, но и трафик на него отправлять не надо.
#!/usr/bin/env bash
if curl -fsS http://127.0.0.1:8080/health >/dev/null; then
echo "OK"
exit 0
else
echo "FAIL"
exit 1
fi
Такой скрипт уже можно подключать к мониторингу или балансировщику.
#linux #monitoring
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤1
Если сеть начинает лагать, то обычно первым делом запускают:
ping 8.8.8.8
Потом:
traceroute 8.8.8.8
ping показывает задержку и потери до конечного узла. traceroute показывает маршрут. А mtr объединяет оба подхода в одном инструменте. По сути, mtr - это живая диагностика маршрута: он постоянно отправляет пакеты и показывает статистику по каждому hop`у.
apt install mtr
или:
yum install mtr
Запуск:
mtr 8.8.8.8
через какие узлы идет маршрут;
где растет задержка;
на каком hop’е появляются потери;
как меняется картина во времени.
mtr -rw 8.8.8.8
-r - report mode-w - широкий вывод, чтобы не резались имена хостовМожно указать число пакетов:
mtr -rw -c 100 8.8.8.8
Это уже полезнее, чем скриншот после 5 секунд наблюдения.
Потери на промежуточном hop’е не всегда означают проблему. Многие маршрутизаторы специально ограничивают ответы на ICMP/TTL exceeded. Поэтому если на одном промежуточном узле видно 50% loss, а дальше и до конечного хоста потерь нет - скорее всего, это не авария. Проблема становится реальной, когда потери продолжаются на всех следующих hop’ах, включая конечный адрес. Пример:
hop 5: 30% loss
hop 6: 30% loss
hop 7: 30% loss
target: 30% loss
Вот это уже похоже на реальную потерю по пути. А если так:
hop 5: 80% loss
hop 6: 0% loss
target: 0% loss
то, скорее всего, просто сам hop не любит отвечать на диагностические пакеты.
mtr -4 networkadmin.ru # только IPv4.
mtr -6 networkadmin.ru # только IPv6.
mtr -T networkadmin.ru # TCP-режим, полезно, когда ICMP фильтруется.
mtr -u networkadmin.ru # UDP-режим
mtr показывает сетевую картину с вашей точки. Если проблема плавающая или зависит от обратного маршрута, одного запуска может быть мало.Лучше запускать проверку с двух сторон, если есть такая возможность: от клиента к серверу и от сервера к клиенту.
#linux #network
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍2🔥1
Когда windows начинает вести себя странно, не всегда нужно сразу переустанавливать систему. Симптомы могут быть разные:
не открываются системные компоненты;
падают службы;
не ставятся обновления;
появляются ошибки DLL;
ломаются стандартные приложения;
система загружается, но работает нестабильно.
В таких случаях полезно вспомнить про два штатных инструмента: DISM и SFC. Они не делают магию и не чинят все подряд, но часто помогают восстановить поврежденные системные файлы и компонентное хранилище виндовс.
sfc /scannow
Если SFC найдет поврежденные файлы, он попробует заменить их корректными копиями. Но есть нюанс: SFC берет файлы из компонентного хранилища windows. Если само хранилище повреждено, SFC может не справиться. Вот тут нужен DISM.
DISM /Online /Cleanup-Image /CheckHealth
Быстрая проверка, есть ли признаки повреждения.
DISM /Online /Cleanup-Image /ScanHealth
Более глубокое сканирование.
DISM /Online /Cleanup-Image /RestoreHealth
Восстановление повреждений.
DISM /Online /Cleanup-Image /RestoreHealth
sfc /scannow
Сначала чиним компонентное хранилище, потом проверяем системные файлы. Если Windows Update работает нормально, DISM сам подтянет нужные компоненты. Если нет - может понадобиться ISO-образ windows той же версии.
Пример с указанием источника:
DISM /Online /Cleanup-Image /RestoreHealth /Source:wim:D:\sources\install.wim:1 /LimitAccess
D: - смонтированный ISOinstall.wim - образ установки:1 - индекс редакции внутри WIM/LimitAccess - не ходить в Windows UpdateИндекс можно посмотреть так:
DISM /Get-WimInfo /WimFile:D:\sources\install.wim
после неудачных обновлений;
после внезапного выключения;
при ошибках системных файлов;
когда Windows Update ведет себя странно;
при повреждении системных компонентов.
#windows #dism #sfc
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18
Распространенная ситуация: запускаете приложение руками и все работает.
/opt/myapp/start.sh
А через systemd:
systemctl start myapp
сервис падает, не видит файлы, не находит переменные, не может подключиться к сокету или пишет что-то вроде:
No such file or directory
Permission denied
command not found
И тут важно понять главное: запуск руками и запуск через systemd - это не одно и то же окружение. Когда вы запускаете команду из shell, у вас уже есть:
переменные окружения;
текущий каталог;
пользовательская сессия;
PATH;
ssh-agent;
загруженный профиль
.bashrc / .profile;права вашего пользователя.
А systemd запускает сервис гораздо строже.
Нет нужного PATH. В shell команда находится, а в systemd нет.
Плохо:
ExecStart=python app.py
Лучше:
ExecStart=/usr/bin/python3 /opt/myapp/app.py
Другой рабочий каталог. Скрипт ожидает файлы рядом с собой, а systemd запускает его не оттуда.
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/start.sh
Не хватает переменных окружения. Например, DB_HOST, API_TOKEN, JAVA_HOME.
Environment="DB_HOST=127.0.0.1"
EnvironmentFile=/etc/myapp/myapp.env
Не тот пользователь. Вручную запускали от root, а сервис работает от myapp.
User=myapp
Group=myapp
И внезапно выясняется, что нет прав на логи, конфиги или каталоги данных.
Сервис стартует раньше зависимости. Например, приложение поднимается раньше базы, сети или mount point.
After=network-online.target postgresql.service
Wants=network-online.target
Скрипт требует интерактивную оболочку. Если внутри завязка на .bashrc, алиасы, read, sudo с паролем или интерактивные команды - в systemd это почти гарантированно сломается. Что смотреть первым делом:
systemctl status myapp
journalctl -u myapp -xe
systemctl cat myapp
Полезно вывести окружение процесса:
systemctl show myapp -p Environment
И проверить unit на синтаксис:
systemd-analyze verify /etc/systemd/system/myapp.service
#linux #systemd
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤1
В скриптах часто есть временные файлы, lock-файлы, промежуточные каталоги и другие следы работы. Проблема начинается, когда скрипт падает посередине. Например: временный файл остался в
/tmp; lock-файл не удалился; mount point не размонтировался; сервис был остановлен, но обратно не запущен; промежуточные данные остались в неконсистентном состоянии. Для таких случаев в bash есть trap. Он позволяет выполнить команду или функцию при наступлении события: выход из скрипта, ошибка, Ctrl+C, kill и так далее.
#!/usr/bin/env bash
set -euo pipefail
TMP_DIR="$(mktemp -d)"
cleanup() {
rm -rf "$TMP_DIR"
}
trap cleanup EXIT
echo "working in $TMP_DIR"
# основная логика скрипта
mktemp -d создает временный каталогcleanup() описывает, что нужно убратьtrap cleanup EXIT гарантирует запуск cleanup при выходе из скрипта. Даже если скрипт завершится с ошибкой, временный каталог будет удален.Можно ловить не только обычный выход, но и прерывание с клавиатуры:
trap cleanup EXIT INT TERM
EXIT - любой выход из скриптаINT - прерывание через Ctrl+CTERM - сигнал завершения процесса
#!/usr/bin/env bash
set -euo pipefail
LOCK_FILE="/tmp/myjob.lock"
cleanup() {
rm -f "$LOCK_FILE"
}
trap cleanup EXIT
if [[ -e "$LOCK_FILE" ]]; then
echo "script already running"
exit 1
fi
touch "$LOCK_FILE"
# основная логика
sleep 30
Но тут важный момент: для защиты от повторного запуска лучше использовать flock, а не просто проверку файла. Зато trap отлично подходит, чтобы гарантированно убрать временные следы после работы.
error_handler() {
echo "error on line $LINENO"
}
trap error_handler ERR
Так можно быстрее понять, где именно скрипт упал.
cleanup() {
rm -rf "$TMP_DIR"
}
on_error() {
echo "script failed on line $LINENO"
}
trap cleanup EXIT
trap on_error ERR
trap не делает скрипт надежным сам по себе. Он только помогает корректно завершиться и прибрать за собой.
#bash #scripting
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥3
find - одна из самых полезных и часто используемых утилит в linux. Но именно с ней часто происходят самые неприятные аварии. Хотели удалить старые логи - удалили не тот каталог. Хотели поправить права - сломали половину /var/www. Хотели найти временные файлы - случайно зацепили mount point. И проблема не в find, а в том, что он очень послушный. Что написали - то и сделал.
Поэтому главное правило: сначала показать, потом делать.
find /var/log -name "*.gz" -delete
find /var/log -name "*.gz" -print
Посмотрели список, убедились, что там нет ничего лишнего и только потом добавили действие:
find /var/log -name "*.gz" -delete
Для удаления по возрасту тоже сначала проверяем:
find /backup -type f -mtime +30 -print
И только после проверки:
find /backup -type f -mtime +30 -delete
-type. Если удаляем файлы, явно пишем:
find /backup -type f -name "*.tar.gz" -mtime +14 -delete
Если меняем права каталогов:
find /var/www -type d -exec chmod 755 {} \;
Если меняем права файлов:
find /var/www -type f -exec chmod 644 {} \;
Это защищает от классической ошибки, когда одинаковые права случайно применяют и к файлам, и к директориям.
chmod часто удобнее использовать + вместо \;:
find /var/www -type f -exec chmod 644 {} +
Так команда запускается не для каждого файла отдельно, а пачками. На больших каталогах это заметно быстрее.
Если в путях могут быть пробелы, переводы строк или странные символы, используйте безопасную связку:
find /data -type f -name "*.log" -print0 | xargs -0 rm -f
Но если можно обойтись встроенным
-delete, часто лучше использовать его:
find /data -type f -name "*.log" -mtime +7 -delete
find /var/log -maxdepth 1 -type f -name "*.gz" -print
Так find не уйдет рекурсивно во все вложенные каталоги. И наоборот, если нужно пропустить верхний уровень:
find /data -mindepth 1 -type d -empty -delete
Это помогает не удалить сам корневой каталог поиска, если он вдруг окажется пустым.
#bash #find
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤2
Когда сервис падает, первое желание - открыть
/var/log и начать искать глазами. Но на современных linux-системах часто быстрее идти сразу в journalctl. journalctl показывает логи из systemd-journald: сервисы, kernel-сообщения, boot-логи, ошибки юнитов и многое другое.
journalctl -u nginx
Так смотрим логи конкретного сервиса.
Если нужен только последний запуск:
journalctl -u nginx -b
-b ограничивает вывод текущей загрузкой системы. Это удобно, чтобы не утонуть в старых событиях.Посмотреть последние строки:
journalctl -u nginx -n 100
Следить за логами в реальном времени:
journalctl -u nginx -f
Это аналог tail -f, только для systemd-журнала.
systemctl status nginx
journalctl -u nginx -xe
status дает краткую картину, а journalctl уже показывает детали: ошибки конфига, проблемы с правами, отсутствующие файлы, failed dependency и так далее.
Очень удобно фильтровать по времени:
journalctl -u nginx --since "10 minutes ago"
или так:
journalctl -u nginx --since "2026-06-24 10:00" --until "2026-06-24 11:00"
Для разбора аварий это особенно полезно: сужаете окно до момента инцидента и смотрите только нужный кусок.
journalctl -p err -b
Где
-p err показывает сообщения уровня error и выше за текущую загрузку.
journalctl -k -b
Тут можно увидеть проблемы с дисками, драйверами, OOM, сетевыми интерфейсами и железом. Например, если подозрение на OOM killer:
journalctl -k -b | grep -i "killed process"
Еще полезно смотреть логи предыдущей загрузки, если сервер уже перезагрузился после аварии:
journalctl -b -1
А список доступных загрузок:
journalctl --list-boots
Так можно понять, что происходило перед reboot, panic или зависанием.
journalctl -u nginx --no-pager
Для вывода в JSON, если нужно парсить:
journalctl -u nginx -o json
#linux #journalctl
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍1