Cron запустил второй экземпляр скрипта: блокировка через flock
Каждые пять минут запускается импорт. Обычно он заканчивается за две, но при задержке внешней системы работает восемь. Cron не знает, что предыдущая копия занята, и запускает следующую. Обе начинают читать один каталог и отправлять одинаковые данные. Прежде чем увеличивать расписание, нужно определить допустимость параллельной работы.
Для одной Linux-машины удобно использовать flock: процесс получает блокировку на открытый файл и удерживает её во время работы. Другие согласованные участники, использующие тот же объект, либо ждут, либо отказываются от запуска. Это локальный механизм взаимного исключения, а не глобальная очередь задач.
Обернём полезную команду
flock -n -E 75 /var/lib/myapp/import.lock /opt/myapp/bin/import
Каталог /var/lib/myapp должен заранее существовать и быть доступен пользователю задания. По умолчанию блокировка эксклюзивная. -n запрещает ожидание, а -E 75 выбирает код конфликта. Если lock свободен, запускается import; его код затем возвращается вызывающему. Если занят, полезная команда не выполняется.
Код 75 здесь выбран как часть контракта скрипта. Если сама команда тоже может возвращать 75, снаружи эти причины будет сложнее различить. Решите, где фиксировать пропуск: в журнале, отдельной метрике или сообщении планировщика. Молчаливые пропуски месяцами способны скрыть застрявшее задание.
Встроим блокировку в Bash-скрипт
#!/usr/bin/env bash
exec 9>/var/lib/myapp/import.lock || exit 1
if flock -n -E 75 9; then
/opt/myapp/bin/import
exit "$?"
else
rc=$?
printf 'import lock not acquired: %s\n' "$rc" >&2
exit "$rc"
fi
Дескриптор 9 остаётся открытым во время выполнения полезной команды. После завершения скрипта блокировка освобождается через закрытие дескриптора. Файл на диске может остаться, и это нормально: его существование не означает, что lock занят. Не заменяйте проверку блокировки проверкой наличия имени.
Общий lock-файл должен оставаться одним и тем же объектом. Если удалить его во время работающего задания, следующая копия может создать новый inode и заблокировать уже другой файл. Тогда две копии будут считать, что получили эксклюзивное право. Используйте стабильный путь в управляемом каталоге и не удаляйте файл после каждого запуска.
Выберем поведение при долгой работе
Для задания, которое каждый раз обрабатывает всё накопившееся, пропуск пересечения часто допустим: следующая удачная копия подберёт оставшиеся данные. Если каждый запуск несёт уникальный период или сообщение, простой пропуск теряет работу. В этом случае нужен журнал заданий, очередь или иной устойчивый учёт, а не только lock.
Вместо -n можно выбрать ограниченное ожидание через -w. Но если новые cron-копии приходят быстрее, чем заканчивается обработка, ожидание создаст очередь процессов на машине. Блокировка ограничивает параллелизм; производительность и накопление задач она не исправляет.
Проверим границы действия
Два экземпляра должны использовать один путь в одной файловой системе. Разные контейнеры с собственными каталогами не разделят lock только из-за одинаковой строки /var/lib/myapp/import.lock. При нескольких узлах нужна координация, подходящая общей системе обработки. На NFS и CIFS поведение flock зависит от поддержки и mount-настроек.
Если скрипт создаёт фонового потомка, унаследованный дескриптор может удерживать блокировку дольше родителя. И наоборот, раннее закрытие дескриптора позволит следующей копии стартовать до завершения фактической работы. Срок lock должен соответствовать сроку критической операции, включая дочерние действия.
На тестовой машине запустите два коротких задания с общим lock, подтвердите пропуск второго и возможность старта после завершения первого. Затем проверьте код самой полезной команды при ошибке. Наконец, сохраните защиту от повторных побочных действий в импорте: lock предотвращает локальное пересечение, но повтор после аварии остаётся отдельным сценарием.
Ещё по теме
- Bash-скрипт сообщил об успехе после ошибки: код возврата и pipefail
- EnvironmentFile в systemd: почему переменная работает в терминале, но не в сервисе
Продолжить изучение
Хотите перейти от отдельных команд к сопровождению Linux-серверов? Работа с системой, скриптами и сервисами входит в программу «Linux и Bash для DevOps-инженеров».
Продолжить на практике
- Linux и Bash для DevOps-инженеров ↗
Linux для работы с серверами: процессы, права, systemd, сеть, диски и логи. Автоматизируйте повседневные задачи на Bash и разберите типичные сбои сервиса — от занятого порта до переполненного диска.
