ディスクアクセスでsystemdサービスを終了する[バックアップ]

ディスクアクセスでsystemdサービスを終了する[バックアップ]

小さなバックアップスクリプト(btrfsスナップショット+ USBに転送)システムがシャットダウンしたとき。

次のサービスは終了時に完全に機能しますが、再起動すると期待どおりに機能しません。

[Unit]
Description=Backup on poweroff to external encrypted USB disk.
DefaultDependencies=no
Before=shutdown.target

[Service]
Type=oneshot
ExecStart=/usr/bin/sleep 30
TimeoutSec=infinity
RemainAfterExit=yes

[Install]
WantedBy=poweroff.target

サービスが期待どおりに機能すると、/usr/bin/sleep 30実際のバックアップスクリプトに置き換えられます。


ただし、バックアップスクリプトが機能するには、起動する前にcryptsetupを使用して外部USBディスクの復号化が必要です。これらの操作はすべて、/etc/crypttabsystemd()とsystemd()noautoの組み合わせを介してバックグラウンドで処理されます。これは、上記のユニットで参照できるsystemd-cryptsetup-generatorsystemdユニットファイルを自動的に生成します。systemd-cryptsetup@<name>.service

しかし、一度[unit]セクションに次の要件を追加したら:

[email protected]
[email protected]

システムのシャットダウン/停電中にサービスは開始されなくなりました。

サービスを手動で起動しても問題はないので、systemctl start poweroff-backup.serviceサービスファイル自体に問題はありません... :-/


考える問題は、poweroff.targetviaがすべてのデバイス(cryptsetupを含む)を順番に削除するsystemd-poweroff.service必要があることです。umount.target')を組み合わせて紛争=そして以降 =systemd マウントおよび cryptsetup デバイスの説明です。ただし、他の要件を追加しても、umount.target次の内容は変更されません。

Before=umount.target

umount.target追加の要件がなくても、サービスが常に到着するとすぐに開始されることを確認できます。

答え1

さて、私はまだsystemdワークフローに従う適切に動作するソリューションを構築することができるこの質問を見つけました。ほとんどの場合)。

質問

次のサービスは一見すると正確ですが、実行されません。

# poweroff-backup.service
[Unit]
Description=Backup on poweroff to external encrypted USB disk.
DefaultDependencies=no
Before=shutdown.target

# This causes the service to be discarded/skipped due to
# a dependency cycle conflict. See below... 
[email protected]
[email protected]
Before=umount.target

[Service]
Type=oneshot
# Note:  The script executed here needs access to '/', '/tmp' 
# (or Systemd's PrivateTemp) and '/dev/mapper/ext' which is the 
# decrypted USB partition managed by /etc/crypttab and 
# systemd-cryptsetup-generator (as [email protected])
ExecStart=/usr/bin/sleep 30
TimeoutSec=infinity
# Note: This does not make any difference
RemainAfterExit=yes

[Install]
WantedBy=poweroff.target

サービスが開始されないのは、シャットダウン(電源オフ/再起動)と電源オフ - バックアップの前に暗号化されたすべてのディスクが切断されるようにするために、shutdown.targetとcryptsetup.targetの間に依存ループの問題/衝突があるためです。 poweroff.target経由。poweroff.target -[require]-> shutdown.target -[conflicts]-> cryptsetup.target <-[require]- [email protected] <-[require]- poweroff-backup.service <-[wants]- poweroff.target

解決策

これを知ると、明らかな問題はsystemdがpoweroff-backup.serviceが」ワンタイムストライク"poweroff-backup.serviceが有効になって非アクティブ/完了に切り替えられると、システムは通常のシャットダウン切り替えを続行しながらサービスを最初に解決できます。

以下は、ExecStart=の代わりにExecStop=を使用するサービスです。これにより、依存関係のサイクルを回避できますが、いくつかのマイナーな注意があります。

# poweroff-backup.service
[Unit]
Description=Backup external encrypted USB disk on poweroff.
[email protected]
After=multi-user.target
[email protected]

[Service]
Type=oneshot
ExecStart=/usr/bin/echo "Waiting for poweroff..."
# Note: We need this, since there is no other way to detect poweroff vs reboot now.
ExecStop=/usr/bin/systemctl list-jobs | /usr/bin/egrep -q 'poweroff.target.*start'
# Note: This should be replaced with your script
ExecStart=/usr/bin/sleep 30
TimeoutSec=infinity
RemainAfterExit=true

[Install]
WantedBy=multi-user.target

これは次の理由で機能します。

  • shutdown.target、cryptsetup.target、およびpoweroff-backup.serviceによる依存関係サイクルの問題の回避
  • systemdは終了順序が開始順序と反対であることを保証するため、正しい作業順序は維持されます(After =およびBefore =で提供されています)。

このソリューションには2つのマイナーな苦情があります。

  • サービスはログイン時に開始されるため、ログイン時に外部USBディスクのパスワードも復号されます。私は通常のシステムのAfter =とRequire =の順序付けと依存関係のメカニズムを使い続けながら、実際のバックアップスクリプトが実行されたときにのみこれが起こりたいと思います。これは以下を追加して処理できますが、これはUSBディスクを復号化/分離するためにcryptsetupサービスを使用しないことを意味します。

    ExecStop=/usr/lib/systemd/systemd-cryptsetup attach ext
    [...]
    ExecStop=/usr/lib/systemd/systemd-cryptsetup detach ext
    
  • shutdown.targetを参照して、終了時にのみExecStop =を条件付きで実行する方法はなく、使用されているいくつかのヘルパーコマンドに依存する必要がありますsystemctl list-jobs

質問:これらの点を改善する方法を知っている人はいますか?

完全なサービス文書

これは現在のサービスファイルです。 USBディスクをマウントした後にのみ、multi-user.targetの代わりにUSBデバイス(UUID経由)に接続(WantedBy =)してサービスを開始します。

# poweroff-backup.service
[Unit]
Description=Backup external encrypted USB disk on poweroff.
[email protected]
Requires=-.mount
Requires=tmp.mount
After=dev-disk-by\<uuid here>.device
[email protected]
After=-.mount
After=tmp.mount

[Service]
Type=oneshot
ExecStart=/usr/bin/echo "Waiting for poweroff to trigger snapshot and archive transfer..."
ExecStop=/usr/bin/systemctl list-jobs | /usr/bin/egrep -q 'poweroff.target.*start'
ExecStop=-/usr/local/bin/btrfs-snapshots.sh --device='UUID=<uuid>' @ @boot @home
ExecStop=/usr/local/bin/btrfs-archive.sh --source='UUID=<uuid>' --target=/dev/mapper/ext
TimeoutSec=infinity
RemainAfterExit=true

[Install]
WantedBy=dev-disk-by\<uuid here>.device

答え2

私のユースケースは少し異なります。他のすべてのサービスが停止していることを確認し、再起動中にRsyncを使用してバックアップを実行したいと思います。以下はUbuntu 22.04で私に効果的でした。後で終了時にスクリプトを実行したい人のために、この答えをここに残してください。

源泉:https://documentation.suse.com/smart/systems-management/html/reference-managing-systemd-targets-systemctl/index.html

サービスファイルは次のとおりです。

[Unit]
Description=Run my custom task at shutdown
DefaultDependencies=no
Before=systemd-reboot.service
After=final.target

[Service]
Type=oneshot
ExecStart=/usr/local/scripts/custom_script.sh
TimeoutStartSec=0

[Install]
WantedBy=systemd-reboot.service

私はどのようにcustom_script.sh見えますか?

#!/bin/sh
ps aux > /usr/local/scripts/processes.txt
df -Th > /usr/local/scripts/df.txt
mount > /usr/local/scripts/mount.txt
sleep 180

sudo chmod 777 -R /usr/local/scripts/これは単にテスト用なので、権限の問題がある場合に備えて実行しています。

私がテストしたステップは次のとおりです。

  1. 終了して再起動したときにスクリプトを実行するには、sleep 180次の手順を実行しますcustom_script.sh。その後、オペレーティングシステムをシャットダウンします。わずか3分後、オペレーティングシステムは完全にシャットダウンします。スクリプトの実行中(待機中)Ubuntuロードロゴが表示されます。システムを起動して再起動しました。同じUbuntuロードロゴが表示され、3分後にシステムが再起動します。これは、終了/再起動時にスクリプトが実行されていることを証明します。
  2. custom_script.sh他のすべてのサービスが停止していることを確認してください。実行中に実行されていたすべてのプロセスを記録しましたps aux > processes.txt。の出力にはprocesses.txt他のサービスは含まれません。これは、他のすべてのサービスが停止したことを証明します。
  3. ファイルシステムがまだマウントされていることを確認するために、df -Th > df.txtmountを使用して記録しますmount > mount.txt。ファイル出力には、すべてのパーティションがマウントされ、読み取り/書き込みモードになっていることが示されます。

関連情報