LINUX.ORG.RU

Python скрипт отрабатывает в cron не так, как мне хотелось бы

 , ,


0

1

Казалось бы.

Скрипт - https://privatebin.net/?7a925a1ae401d088#6XCisGp9xAX7viZZKuKzvGFJRbUWFgNsGKeo2oc4VHHP

Задача скрипта - бэкапнуть содержимое докер контейнеров. Сначала бэкапает на второй диск в NAS, потом бэкапает на внешний диск, потом бэкапает на удаленный сервер. Бэкапы на внешний диск и удаленный сервер делается не с основного, а со второго внутреннего, чтобы быстрее можно было заново запустить контейнеры (да, я тушу контейнеры на время бэкапа, а не использую дампы и прочие тулзы live бэкапов, потому что это домашний сервер и 2 минуты запланированного даунтайма для меня приемлемо).

Проблема в бэкапе на внешний диск, который маунтится в /mnt/mirror. Скрипт выполняется от рута. Как видно, в методе mount класса ExternalDrive есть -v. Так я хотел отдебажить проблему, которая заключается в том, что когда скрипт запускается из крона, по какой-то причине внешний диск после расшифровки не маунтится. Бэкап заливается просто в корень, где нет свободного места под такой объем информации. Скрипт умирает. При этом если запускать вручную, то монтирование всегда успешно.

Проблемное место, видимо, тут

    def mount(self):
        """Mount drive."""
        dev_path = Path(f'/dev/mapper/{self.decrypt_name}')
        subprocess.run([
            'mount',
            '-v',
            dev_path,
            self.mount_point
        ])

self.mount_point тут /mnt/mirror. Он существует, никаких ограничений на нем нет.

Как видно из логики в main, у меня есть дополнительная проверка на успешность монтирования:

    drive.mount()
    if drive.mount_point.is_mount():
        backup_to_external_drive()
        multisync()
        drive.umount()

Если к точке монтирования не промонтировал внешний диск, дальнейший бэкап не должен идти. Судя по документации:

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, timeout=None, check=False) Run the command described by args. Wait for command to complete, then return a CompletedProcess instance.

при использовании subprocess.run ожидается полное выполнение команды. Значит нет проблемы в том, что скрипт идет дальше после запуска одного из subprocess не ожидая его завершения.

При запуске вручную лог выглядит так:

2023-04-13 13:20:03 INFO Script backup_docker started
2023-04-13 13:21:09 INFO Finished in 00h 01m 06s

При запуске из крона так:

2023-04-14 02:00:01 INFO Script backup_docker started

Дальше пусто, т.к. все зависло (системный диск переполнен). В итоге получается так, что доходя до места, где задействуется функция бэкапа на внешний диск, он вроде как смонтирован, дополнительную проверку на наличие точки монтирования прошел, а на самом деле диск не смонтирован.

Почему при ручном выполнении скрипта внешний диск монтируется, а при выполнении из крона нет (или слишком быстро отмонтируется)?



Последнее исправление: Entmatix (всего исправлений: 1)

М.б. как-то связано с тем, что у процессов в кроне нет tty? М.б. cryptsetup или mount нуждаются в нём для чего-то…

Я бы добавил дополнительную диагностику с логгингом того, что они выводят, скорее всего, глядя на вывод можно будет сразу понять, в чем проблема.

emorozov
()

Так я хотел отдебажить проблему, которая заключается в том, что когда скрипт запускается из крона, по какой-то причине внешний диск после расшифровки не маунтится

А он и не будет. При запуске из cron нет никаких переменных окружения, об этом нужно помнить. В т.ч. нет PATH. А у тебя просто вызов «mount». Нужно либо использовать абсолютный путь (не универсально), либо явно устанавливать PATH в crontab

Почему не работает твоя проверка, не знаю. Отлаживать тебе

router ★★★★★
()
Ответ на: комментарий от router

И ещё. Логирование идёт в файл, ок

А у тебя скрипт падает. Ты не обрабатываешь ошибки (тот же вызов внешней команды должен завершиться exception) и забыл проверить почту, на которую cron тебе прислал вывод (stdout и stderr) упавшего скрипта

router ★★★★★
()
Ответ на: комментарий от router

Это самое очевидно, да. Но не в этом проблема.

root@nas ~ # which mount cryptsetup rsync
/usr/bin/mount
/usr/sbin/cryptsetup
/usr/bin/rsync

Почему не срабатывает именно mount? Я пробовал ставить абсолютные пути. Проблема не решилась.

Entmatix
() автор топика
Ответ на: комментарий от zolden

Питон был взят потому, что 1) я его изучаю, 2) на нем удобно писать (он понятен непрофессиональным разработчикам как я), 3) скрипт будет масштабироваться, 4) рестик, борг и прочие утилиты для бэкапа не решают вопрос монтирования внешнего диска.

Entmatix
() автор топика
Ответ на: комментарий от Entmatix

Почему не срабатывает именно mount?

Действительно, почему ты решил, что не срабатывает именно mount? Ещё раз, посмотри почту от cron. Обычно она сыпется в локальный почтовый ящик root (/var/spool/mail/root)

Я пробовал ставить абсолютные пути.

Только три из них? Первым у тебя вызывается docker-compose, которого нет в этом списке

Простейшая примитивная отладка - вывод какой-либо текстовой строки после каждого участка кода, который может не сработать. И чтение лога, на предмет того, какие из строк были выведены. Пробовал?

router ★★★★★
()
Последнее исправление: router (всего исправлений: 1)
Ответ на: комментарий от Entmatix

рестик, борг и прочие утилиты для бэкапа не решают вопрос монтирования внешнего диска.

Решается обычным wrapper-скриптом. У меня лично вот так домашний сервак бэкапится на внешний диск с битлокером

!/bin/bash
set -eu
ret_code=0

if [ ! -b /dev/mapper/backup-crypt ]; then
    cryptsetup open --type bitlk /dev/disk/by-partuuid/7e1a19b8-01 backup-crypt
fi
mountpoint /mnt/backup || ret_code=$?
if [ $ret_code -ne 0 ]; then
    mount /dev/mapper/backup-crypt /mnt/backup/
fi


echo "Enter backup passphrase:"
read -s BORG_PASSPHRASE
export BORG_PASSPHRASE
export BORG_REPO=/mnt/backup/borg/   
mkdir --parents --mode=0700 /tmp/mysqldump/
/usr/bin/mysqldump -u root -x -A > /tmp/mysqldump/mariadb.sql
borg create ::SRV-$(date --iso-8601=minutes) /opt /home /root /etc /tmp/mysqldump/mariadb.sql /var/lib/docker/volumes/
/usr/bin/rm --recursive --force /tmp/mysqldump/
borg list ::
umount /mnt/backup/
cryptsetup close backup-crypt
GLaDOS
()
Ответ на: комментарий от Entmatix

Сделал сервис на systemd и timer к нему. Такой проблемы нет.

Потому, что systemd по умолчанию устанавливает PATH

$PATH Colon-separated list of directories to use when launching executables. systemd uses a fixed value of «/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin» in the system manager.

router ★★★★★
()

Смотри сюда и человек выше правильно написал насчет переменных окружения.
Я сам делал не один десяток хренотеней по крону и вывел правило:
Первое правило бойцовского клуба задач крона - нет никакого бойцовского клуба никакого заноса хвоста. Нет прав, нет sudo, нет PATH.
И HOME нет.
python3 тоже нет, кстати (есть /usr/bin/env python3 или (в крайнем случае) /usr/bin/python3, но это не точно).
Никому не верь, всё прописывай явно.

TI_Eugene ★★
()