systemd-сервис зависает при загрузке, когда ожидается обновление

Вопрос или проблема

У меня есть система Linux Mint 22 (Wilma, основанная на Ubuntu 24.04) с интерфейсом Mate. Я использую двойную загрузку с Windows 10. Windows 10 почти никогда не используется, так что фактически это машина на Linux.

У меня есть служба systemd, которая запускает резервное копирование на внешний USB-диск при загрузке и другую при выключении. У меня резервное копирование работает как при выключении, так и при загрузке. Я заметил, что если при выключении выполняется резервное копирование больших файлов (например, видео), резервное копирование не всегда завершается успешно, поэтому я запускаю резервное копирование при загрузке. Если резервное копирование при выключении завершилось, резервное копирование при загрузке не задержит ничего. Если резервное копирование при выключении не завершено, резервное копирование при загрузке выполнит работу до конца.

Обычно все работает вполне хорошо, поэтому настройка systemd выполнена правильно.

Однако, когда ожидается обновление, загрузка зависает. Обновление НЕ обязательно должно быть обновлением ядра; это может быть просто обновление пакета. Загрузка НЕ зависает, если я удаляю скрипты, поэтому проблема связана с инициацией службы systemd. Элементы systemd были правильно включены и запущены.

Я использую таймер для запуска резервного копирования при загрузке, так как процесс загрузки может немного задерживаться из-за включения внешнего USB-диска.

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

См. ниже мои файлы.

Вопросы:

  • Кто-нибудь когда-нибудь сталкивался с этой проблемой?

    Вы видите что-то не так с моей настройкой systemd?

    Есть ли что-то, что я упускаю, чтобы гарантировать, что загрузки всегда будут проходить, даже если обновление ожидается?

===============================================================

Настройка systemd при загрузке – с использованием таймера
cat /etc/systemd/system/backup_to_external_drive_on_startup.timer

[Unit]
Description=Запуск резервного копирования после задержки, чтобы дать внешнему диску раскрутиться
[Timer]
OnBootSec=5min
Unit=backup_to_external_drive_on_startup.service
[Install]
WantedBy=timers.target

cat /etc/systemd/system/backup_to_external_drive_on_startup.service

[Unit]
Description=Резервное копирование домашнего каталога mardi на внешний диск
RequiresMountsFor=/home/mardi /mnt/c988b046-5349-498e-ac96-d5fe46314205
Requires=home-mardi.mount  mnt-c988b046\x2d5349\x2d498e\x2dac96\x2dd5fe46314205.mount
After=network.target network-online.target local-fs.target home-mardi.mount  mnt-c988b046\x2d5349\x2d498e\x2dac96\x2dd5fe46314205.mount
[Service]
User=mardi
Group=mardi
Type=oneshot
ExecStart=/bin/bash /home/mardi/Scripts/backup_to_external_drive.sh

Настройка systemd при выключении – с использованием службы oneshot напрямую
cat /etc/systemd/system/backup_to_external_drive_on_startup.service

[Unit]
Description=Резервное копирование домашнего каталога mardi на внешний диск
RequiresMountsFor=/home/mardi /mnt/c988b046-5349-498e-ac96-d5fe46314205
Requires=home-mardi.mount  mnt-c988b046\x2d5349\x2d498e\x2dac96\x2dd5fe46314205.mount
After=network.target network-online.target local-fs.target home-mardi.mount  mnt-c988b046\x2d5349\x2d498e\x2dac96\x2dd5fe46314205.mount
[Service]
User=mardi
Group=mardi
Type=oneshot
ExecStart=/bin/true
RemainAfterExit=true
ExecStop=/bin/bash /home/mardi/Scripts/backup_to_external_drive.sh
TimeoutSec=infinity
[Install]
WantedBy=multi-user.target

Фактический скрипт резервного копирования, запускаемый каждым из вышеупомянутых
cat /home/mardi/Scripts/backup_to_external_drive.sh

#!/bin/bash
#
# Определите функцию для проверки статуса монтирования 
# В bash это должно быть расположено перед его вызовом скриптом 
# это не поднимается, как в php
# Эта функция работает только в системах с версией bash >= 4.3
# возможность nameref используется для возврата значения из функции
# и возможность nameref была введена только в bash 4.3
isItMounted() {
    mountPointShown=$(findmnt -lo target $1)
    declare -n functionReturnValue=$2 
    if [[ "${mountPointShown}" == "" ]]
        then
            functionReturnValue="NotMounted"
        else
            functionReturnValue="Mounted"
    fi
}
# Создайте начальное значение для инициализации переменной 
# которая будет хранить результат функции isItMounted
returnValue=""
homeMardiMount="/home/mardi/"
isItMounted "${homeMardiMount}" returnValue
homeMardiMountStatus=${returnValue}
backupTargetMount="/mnt/c988b046-5349-498e-ac96-d5fe46314205"
isItMounted "${backupTargetMount}" returnValue
backupTargetMountStatus=${returnValue}

# Используйте функцию, чтобы определить, что делать дальше
if [[ "${homeMardiMountStatus}" == "NotMounted" ]]
    then
        printf "Ошибка, домашний каталог Mardi не смонтирован\n"
        printf "Кому: Mardi_Admin<[email protected]>\nТема: резервное копирование mardi не удалось в $(date)\nРезервное копирование для Linux Mint Mardi не удалось в $(date), потому что домашний каталог для Mardi не смонтирован" | msmtp "Mardi_Admin<[email protected]>"
        sleep 3s
        exit 98
elif [[ "${backupTargetMountStatus}" == "NotMounted" ]]
    then
        printf "Ошибка, внешний диск не смонтирован\n"
        printf "Кому: Mardi_Admin<[email protected]>\nТема: резервное копирование mardi не удалось в $(date)\nРезервное копирование для Linux Mint Mardi не удалось в $(date), потому что внешний диск не смонтирован" | msmtp "Mardi_Admin<[email protected]>"
        sleep 3s
        exit 99
    else
        printf "Резервное копирование инициализировано. Оба, домашний каталог для Mardi и внешний диск смонтированы\n"
        rsync -h --progress --ignore-errors --stats -r -tgo -p -l -D --update --exclude={'.aptitude','.gvfs','.cache/dconf','.kde/share/apps/kwallet'} "${homeMardiMount}" "${backupTargetMount}"
        printf "Кому: Mardi_Admin<[email protected]>\nТема: резервное копирование mardi успешно выполнено в $(date)\nРезервное копирование для Linux Mint Mardi успешно выполнено в $(date)" | msmtp "Mardi_Admin<[email protected]>"
        sleep 3s
fi

exit 0

Ответ или решение

Ответ на вопрос о зависании службы systemd на старте при наличии ожидающего обновления

Уважаемый пользователь,

Проблема, с которой вы столкнулись, может быть связана с рядом факторов, касающихся конфигурации ваших сервисов systemd и процесса загрузки при наличии обновлений. Давайте рассмотрим возможные причины и решения.

Возможные причины зависания

  1. Ожидание зависимых служб: Если ваш бэкап-сервис зависит от других служб, таких как монтирование файловых систем, он может ожидать их завершения выполнения. Если обновления пакетов затрагивают эти службы, это может привести к зависанию.

  2. Таймауты по умолчанию: Если какой-либо из ваших сервисов или целевых точек монтирования требует больше времени на выполнение, это может вызывать задержки. Обратите внимание, что при обновлениях система может изменять состояние файловых систем или зависеть от initrd, что может вызвать дополнительные задержки.

  3. Скрипт резервного копирования: Возможно, ваш скрипт резервного копирования содержит команды, которые могут блокировать процесс загрузки. Например, использование rsync без явного указания, как обрабатывать отключения и ошибки, может остановить процесс.

Рекомендации по решению проблемы

  1. Добавление ограничения времени для служб: Попробуйте добавить в ваши unit-файлы настройки таймаута, которые позволят системе продолжить загрузку, даже если служба не завершилась в установленное время. Например, добавьте TimeoutStartSec=10s в секцию [Service].

  2. Проверка зависимостей и порядка инициализации: Убедитесь, что все зависимости вашего бэкап-сервиса корректны, и попробуйте изменить порядок инициализации, если это необходимо. В особенно сложных случаях может помочь добавление параметра BindsTo= для указания дополнительных зависимостей между службами.

  3. Логирование: Включите более подробное логирование для вашего бэкап-скрипта, чтобы выяснить, где именно происходит зависание. Это поможет вам понять, из-за какой именно части скрипта или вызова службы может возникать проблема.

  4. Изменение типа службы на simple: Часто тип oneshot может заниматься ожиданием завершения сценария, что иногда не совсем соответствует необходимому поведению. Попробуйте изменить тип на simple для вашего сервиса. Это может позволить системе продолжить загрузку, даже если ваш скрипт выполняется.

  5. Наличие обновлений: Убедитесь, что перед запуском вашей системы нет критически важных обновлений, которые могут блокировать загрузку. Настройте автоматическое обновление, чтобы избежать ситуаций, когда пользователи запускают систему с неразрешенными зависимостями.

  6. Отладка монтирования: Убедитесь, что ваш скрипт правильно проверяет монтирование необходимых директорий, особенно в контексте системы обновлений. Иногда обновления могут временно отключить определенные монтирования.

Пример изменённого unit-файла

Вот пример того, как может выглядеть изменённый unit-файл для вашего бэкап-сервиса:

[Unit]
Description=Backup mardi home directory to an external drive
RequiresMountsFor=/home/mardi /mnt/c988b046-5349-498e-ac96-d5fe46314205
After=network.target local-fs.target
Wants=home-mardi.mount mnt-c988b046\x2d5349\x2d498e\x2dac96\x2dd5fe46314205.mount

[Service]
User=mardi
Group=mardi
Type=simple
ExecStart=/bin/bash /home/mardi/Scripts/backup_to_external_drive.sh
TimeoutStartSec=10s
TimeoutStopSec=10s
RemainAfterExit=true

[Install]
WantedBy=multi-user.target

Заключение

Эти изменения и рекомендации могут помочь вам решить проблему с зависанием службы при наличии ожидающего обновления. Не забудьте протестировать каждое изменение на тестом окружении перед применением на рабочей системе. Это поможет убедиться, что новый конфигурационный файл работает без ошибок и не вызывает дополнительных проблем.

Удачи вам в решении данной проблемы! Если у вас есть дополнительные вопросы или требуется помощь, не стесняйтесь обращаться.

С уважением,
[Ваше Имя]

Оцените материал
Добавить комментарий

Капча загружается...