systemd/タイマー

提供: ArchWiki
2016年9月23日 (金) 19:55時点におけるKusakata (トーク | 投稿記録)による版 (同期)
ナビゲーションに移動 検索に移動

関連記事

タイマーは名前が .timer で終わる systemd のユニットファイルであり、.service ファイルやイベントを制御します。cron の代わりとしてタイマーを使うことができます (#cron を置き換える を読んで下さい)。タイマーにはカレンダー時刻のイベントとモノトニック時刻のイベントのサポートが入っており、さらに非同期に実行することも可能です。

タイマーユニット

タイマーは拡張子が .timersystemd のユニットファイルです。他のユニット設定ファイルと似ていますが特別に [Timer] セクションが存在します。[Timer] セクションにはタイマーが作動する時間と処理を定義します。タイマーには2つのタイプがあり、どちらか一つを使って定義されます:

  • モノトニックタイマーは刻々と変わる開始点と相応したタイムスパンの後に作動します。様々なモノトニックタイマーが存在しますがどれも次のような形式です: OnTypeSec=OnBootSecOnActiveSec は全てのモノトニックタイマーで共通です。
  • リアルタイムタイマー (別名ウォールクロックタイマー) は (cron のジョブと同じように) カレンダーイベントにあわせて作動します。OnCalendar= オプションを使って定義を行います。

タイマーのオプションについては systemd.timer(5)man ページで説明されています。カレンダーイベントやタイムスパンの引数構文については systemd.time(7)man ページで説明されています。

サービスユニット

それぞれの .timer ファイルには、対応する .service ファイルが存在します (例: foo.timerfoo.service)。.timer ファイルは .service ファイルを作動・制御します。.service[Install] セクションは必要ありません。有効にするのは timer ユニットだからです。必要ならば、タイマーの [Timer] セクションで Unit= オプションを使うことで別の名前のユニットを制御することもできます。

管理

timer ユニットを使用するには、他のユニットと同じように有効化・起動します。起動しているタイマーを全て表示するには、次を実行:

$ systemctl list-timers
NEXT                          LEFT        LAST                          PASSED     UNIT                         ACTIVATES
Thu 2014-07-10 19:37:03 CEST  11h left    Wed 2014-07-09 19:37:03 CEST  12h ago    systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Fri 2014-07-11 00:00:00 CEST  15h left    Thu 2014-07-10 00:00:13 CEST  8h ago     logrotate.timer              logrotate.service
ノート:
  • (有効でないタイマーも含めて) 全てのタイマーを確認したい場合、systemctl list-timers --all を使って下さい。
  • タイマーによって実行するサービスの状態は、今現在作動しているのでない限り、inactive になります。
  • タイマーがズレる場合は、/var/lib/systemd/timers にある stamp-* ファイルを削除してみてください。stamp ファイルはタイマーが実行された最後の時刻を記録しており、ファイルを削除することでタイマーの次の作動時に systemd が再構成を行います。

サンプル

タイマーを使用してサービスを実行するのにあたって、service ユニットファイルに変更を加える必要はありません。以下の例では foo.timer という名前のタイマーを使って foo.service を定期的に実行します。

モノトニックタイマー

起動15分後に実行され、システムが起動している間、一週間ごとに再度実行されるタイマー:

/etc/systemd/system/foo.timer
[Unit]
Description=Run foo weekly and on boot

[Timer]
OnBootSec=15min
OnUnitActiveSec=1w 

[Install]
WantedBy=timers.target

リアルタイムタイマー

一週間ごとに起動するタイマー (月曜日の午前12:00に実行)。システムの電源が切られていたなどの理由で、最後の起動時間を過ぎていた場合、すぐに実行されます (Persistent=true オプション):

/etc/systemd/system/foo.timer
[Unit]
Description=Run foo weekly

[Timer]
OnCalendar=weekly
Persistent=true     
 
[Install]
WantedBy=timers.target

もっと細かく日時を指定する必要がある場合、次の形式で OnCalendar イベントを制御できます: DayOfWeek Year-Month-Day Hour:Minute:Second。アスタリスクをワイルドカードとして使うことができ、カンマで複数の日時を指定することができます。値を .. で区切ることで連続的に日程を指定することも可能です。例えば、以下の場合、毎月の1日から4日まで、月曜日か火曜日の場合にのみ、午後0時にサービスを実行します。詳しくは man systemd.time を見てください。

OnCalendar=Mon,Tue *-*-01..04 12:00:00
ヒント: dailyweekly などの特殊なイベントの表現は特定の起動時間を示しているため、同じカレンダーイベントが設定されているタイマーがあった場合、同時に起動します。そのようなことが発生すると、タイマーのサービスがシステムリソースを取り合って、システムパフォーマンスが落ち込む可能性があります。このような問題は RandomizedDelaySec オプションを使用してタイマーの起動時間を散らすことで解決できます。systemd.timer (5) を参照してください。

サービス

/etc/systemd/system/foo.service
[Unit]
Description=daily foo 
RefuseManualStart=no
RefuseManualStop=yes

[Service]
Type=oneshot
ExecStart=/home/<user>/scripts/foo.sh

一時的な .timer ユニット

systemd-run を使うことで一時的な .timer ユニットを作ることができます。サービスファイルを作成しなくても、特定時刻にコマンドが実行されるように設定することが可能です。例えば、以下のコマンドは30秒後にファイルを作成します:

# systemd-run --on-active=30 /bin/touch /tmp/foo

タイマーファイルが存在しない既存のサービスファイルを指定することもできます。例えば、以下のコマンドは12.5時間後に someunit.service という名前の systemd ユニットを起動します:

# systemd-run --on-active="12h 30m" --unit someunit.service

詳しくは man systemd-run を見てください。

cron を置き換える

おそらく一番有名なジョブスケジューラは cron ですが、systemd のタイマーはその代替手段になりえます。

メリット

タイマーを使う主要なメリットは、それぞれのジョブが固有の systemd サービスを使うというところに根ざします。そのメリットには以下のようなものがあります:

  • タイマーとは別個にジョブを実行することが簡単にできます。これによってデバッグが楽になります。
  • 特定の環境で動作するようにジョブを設定することができます (systemd.exec(5) man ページを参照)。
  • ジョブを cgroups の支配下に置けます。
  • 他の systemd ユニットに依存するようにジョブを設定できます。
  • systemd の journal でジョブが記録されるのでデバッグが簡単です。

注意事項

cron では簡単にできることが、タイマーユニットを使用する場合、難しかったり不可能であったりすることがいくつか存在します。

  • 冗長性: systemd を使って定期的なジョブを設定するには2つのファイルを作成して2回 systemctl コマンドを実行します。それに対して crontab には一行を追加するだけです。
  • メール: ジョブが失敗した時にメールを送信する cron の MAILTO と同等なものは存在しません。ただし各サービスに OnFailure= オプションを使うことで同じような機能を設定することはできます。

MAILTO

Cron の MAILTO のように、systemd で、ユニットが失敗したときにメールを送信するように設定できます。それにはまず2つのファイルが必要です: メールを送信するための実行可能ファイルと、実行可能ファイルを起動するための .service。以下の例では、sendmail を使用するシェルスクリプトが実行可能ファイルになっています:

/usr/local/bin/systemd-email
#!/bin/bash

/usr/bin/sendmail -t <<ERRMAIL
To: $1
From: systemd <root@$HOSTNAME>
Subject: $2
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset=UTF-8

$(systemctl status --full "$2")
ERRMAIL

どんな実行可能ファイルを使うのであれ、上記のシェルスクリプトのように、最低でも2つの引数が必要になります: 送信先のアドレスと状態を取得するユニットファイルです。以下の .service はそれらの引数を渡します:

/etc/systemd/system/status-email-user@.service
[Unit]
Description=status email for %I to user

[Service]
Type=oneshot
ExecStart=/usr/local/bin/systemd-email address %i
User=nobody
Group=systemd-journal

user はメールを送信するユーザーに、address はユーザーのメールアドレスに置き換えてください。受信者はハードコードされますが、インスタンスパラメータとしてユニットファイルを渡すことで、上記のサービスだけで他のユニットのメールも送ることができます。status-email-user@dbus.service起動して、メールが受け取れることを確認してください。

問題ないようでしたら、メールを送って欲しいユニットの [Unit] セクションに OnFailure=status-email-user@%n.service を追加してください。%n はユニットの名前をテンプレートに指定します。

ノート:
  • SSMTP#セキュリティ にしたがって SSMTP を設定した場合、ユーザー nobody/etc/ssmtp/ssmtp.conf にアクセスすることができず、systemctl start status-email-user@dbus.service コマンドは失敗します。status-email-user@.service モジュールの User に root を使うことでこれを回避できます。
  • メールスクリプトで mail -s somelogs address を使用すると、mail はフォークして、systemd はスクリプトの終了を確認したときに mail プロセスを終了します。mail -Ssendwait -s somelogs address として mail がフォークしないようにしてください。

crontab を使用する

crontab を使って定期的なサービスを管理するツールとして systemd-cron-nextAURsystemd-cronAUR があります。どちらも AUR からインストールすることができます。これらのツールには systemd に存在しない MAILTO 機能が実装されています。

もし、スケジュールされたジョブをまとめて見れるから crontab が好きだという場合は、systemctl でそれと同じことが可能です。#管理 を見て下さい。

参照

  • systemd.timer man page on freedesktop.org
  • Fedora Project wikisystemd カレンダータイマーのページ
  • Gentoo wikisystemd タイマーサービスのセクション
  • systemd-cron-next — crontab と anacrontab ファイルからタイマーとサービスを生成するツール
https://github.com/systemd-cron/systemd-cron-next || systemd-cron-nextAUR
  • systemd-cron — cron スクリプトを実行する systemd ユニットが入っています。systemd-crontab-generator を使って crontab を変換。
https://github.com/systemd-cron/systemd-cron || systemd-cronAUR