Я пишу сценарий bash для задачи резервного копирования. Среди прочего (зависание файловой системы, блокировка базы данных, моментальные снимки, ...) это включает в себя завершение работы Apache и его перезапуск после резервного копирования. Поскольку этот сценарий должен выполняться как cronjob, а время выполнения может сильно различаться (особенно если сценарий ожидает «хорошего момента» для создания резервной копии), я попытался защитить его от многократного выполнения, используя flock
.
Однако flock
сохраняет блокировку даже после выхода из сценария резервного копирования. Это поведение не зависит от того, как я использую flock
(используйте каталог, файл или дескриптор файла, открытый из скрипта).
Я выследил проблему до перезапуска apache2 и вижу ее в следующей однострочной
flock -n /var/lock/startapache service apache2 start
См. Следующую интерактивную сессию, чтобы проиллюстрировать проблему:
root@fermat:/home/ubuntu# service apache2 stop
* Stopping web server apache2 ... waiting . [ OK ]
root@fermat:/home/ubuntu# flock -n /var/lock/startapache service apache2 start || echo failed
* Starting web server apache2 [ OK ]
root@fermat:/home/ubuntu# flock -n /var/lock/startapache service apache2 start || echo failed
failed
Это потому, что запущенный скрипт Apache сохраняет мои дескрипторы файлов блокировки открытыми:
root@fermat:/home/ubuntu# lsof /var/lock/startapache
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
apache2 23651 root 3u REG 0,17 0 6673997 /run/lock/startapache
apache2 23656 trac 3u REG 0,17 0 6673997 /run/lock/startapache
apache2 23674 trac 3u REG 0,17 0 6673997 /run/lock/startapache
apache2 23675 root 3u REG 0,17 0 6673997 /run/lock/startapache
apache2 23676 root 3u REG 0,17 0 6673997 /run/lock/startapache
apache2 23677 root 3u REG 0,17 0 6673997 /run/lock/startapache
apache2 23694 root 3u REG 0,17 0 6673997 /run/lock/startapache
apache2 23696 root 3u REG 0,17 0 6673997 /run/lock/startapache
После того, как я выключаю Apache, блокировка снова освобождается:
root@fermat:/home/ubuntu# service apache2 stop
* Stopping web server apache2 ... waiting [ OK ]
root@fermat:/home/ubuntu# flock -n /var/lock/startapache service apache2 start || echo failed
* Starting web server apache2
Поэтому мой вопрос ( s): Как процесс Apache «наследует» эти файловые дескрипторы? Почему такое же поведение не происходит с другими сценариями запуска (например, «запуск службы mysql»)? И есть ли способ избежать этого?
Так как никто не записал ответ (несмотря на щедрость), я собираюсь объясниться, как я наконец решил это.
комментарий muru (большое спасибо!) было правильным и принес мне на правильном пути: В Ubuntu 14.04 запуск Apache организован sysv init сценарий и быть сценарием оболочки, это наследовало все (переменные среды, открытые дескрипторы файлов, и т.д.), и апачский процесс, запущенный им, наследует все те также. Mysql, в свою очередь, управляет Выскочка, которая гарантирует чистую среду для запущенных сервисов. Это объясняет другое поведение для Mysql и Apache.
Вооруженный с тем знанием я знал лучше, что искать и нашел этот ответ на unix.stackexchange.com . Это предлагает закрыть дескриптор файла в подоболочке, которая называет сценарии, которые не должны наследовать его, в то время как хранение дескриптора открывается во внешней оболочке, чтобы удостовериться, что блокировка остается эффективной.
, К сожалению, это означает, что я должен был переместиться flock
вызовы к в рамках сценария оболочки, иначе мой сценарий не будет знать который дескриптор файла закрыться. Таким образом, мой новый сценарий оболочки (который также взял некоторое вдохновение от это сообщение в блоге приблизительно flock
) имеет следующую структуру:
#!/bin/bash
FLOCK_FILE="/var/lock/backup-lock"
FLOCK_FD=20
# Locking
eval "exec $FLOCK_FD>'$FLOCK_FILE'"
if ! flock -n $FLOCK_FD
then
echo "FAILED! There is a backup script already running."
exit 1
fi
(
# Unlock in sub-shell, so daemons with bad startup scripts
# (like Apache) don't inherit the look.
# Note that the lock is still alive in general because it's
# held by the outer shell.
eval "exec $FLOCK_FD>-"
# ... normal backup stuff from the original script ...
# Among other stuff the mentioned vicious line:
service apache2 start
# ... normal backup stuff from the original script ...
)
# Unlock in outer shell because we're done.
eval "exec $FLOCK_FD>-"