この記事について
この記事は、生成AIを活用した自動生成フローで作成しています。systemdの公式マニュアルでcalendar式とタイマーの精度設定を確認し、読み取り専用で観察できる手順に整理しました。検証ステータス:📘 公式仕様確認済み・対象Ubuntuでの実機操作は未実施
systemdタイマーはなぜ指定時刻ぴったりに動かない?OnCalendarとAccuracySecを読み解く
予定時刻と実行時刻が違うのは故障なのか
Ubuntuで「毎日18時に処理する」と設定したのに、ログでは18時00分30秒に起動している。数十秒の差だけでタイマーが壊れたと判断すると、原因の切り分けを間違える。systemdのタイマーは、カレンダーで決めた基準時刻と、実際に起動を許容する時間帯を別々に扱うからだ。さらにマシンの停止、時刻同期、サービス自体の処理時間が観測結果に影響する。この記事では設定を一切変更せず、予定時刻を計算するコマンドと現在のタイマーの属性を読み取って、この差を説明できるようにする。
ここで重要なのは、タイマーが起動を要求した時刻と、サービスの処理が終わった時刻を混同しないことだ。例えばサービスが起動後にネットワークへ接続し、応答を待ってからログを書けば、ログに残る業務処理時刻はタイマーの起動時刻より後になる。まずはどの時刻を比較しているのかを定義しておこう。
対応環境と今回の観察対象
| 項目 | 条件 |
|---|---|
| OS | systemdを採用したLinux。例はUbuntuを想定 |
| 操作権限 | 自分の環境で参照できるタイマーなら一般ユーザーで可 |
| 変更範囲 | なし。設定ファイル編集、サービス再起動、タイマー起動はしない |
| 主要コマンド | systemd-analyze calendar、systemctl list-timers、systemctl show |
| 実機結果 | 本記事の表示例は期待例であり、対象Ubuntuの実測ではない |
ここでは実際の運用タイマーを触らず、架空の「毎日18時」の設定文字列を使う。systemdのcalendar式は曜日、日付、時刻を組み合わせて指定できるが、まず時刻だけの簡単な式を観察すると、結果を読み間違えにくい。実運用でのタイムゾーンはマシンの設定に依存するため、表示される日付やオフセットを記事の例と一致させる必要はない。
最初の実験:カレンダー式だけを計算する
ターミナルで次のコマンドを実行する。これは指定した式の次回候補を計算して表示するもので、タイマーの設定を書き換えたり、ジョブを実行したりしない。
systemd-analyze calendar --iterations=3 '*-*-* 18:00:00'
出力には正規化した式と、基準時刻から数えた次回の発火候補が並ぶ。実行した時刻が18時を過ぎていれば翌日から、18時より前なら当日からの候補が示されることを期待する。ここで観察したいのは、毎日という周期と18時00分00秒という基準が維持されることだ。表示の具体的な年月日を固定して暗記する必要はない。端末の時計やタイムゾーンが違えば年月日も違う。
もし「毎日18時」と書いたつもりでも次回候補が思いどおりでなければ、サービスを疑う前に式そのものを見直す。カレンダー式の検査と実行の検査を分けることで、調査の対象が半分に絞れる。コマンドの終了状態やエラー表示も記録しておけば、式の誤記とタイマーが未起動である状態を混同しにくい。
二つ目の実験:実際のタイマー一覧を見る
次は既存のタイマーを一覧する。自分の環境で動いているものだけを対象にし、名前が分からない段階で操作コマンドを実行しない。サービスの起動や停止はこの調査に不要だ。
systemctl list-timers --all
一覧のNEXTは次に予定されている時刻、LASTは直近の起動に関する時刻を調べる手掛かりになる。ただし、この一覧だけで「指定秒ちょうどに実行された」とまでは判断できない。タイマーが停止中なら次回予定が空欄になる場合もある。表示項目はsystemdのバージョンによって異なるため、まず列見出しを確認する。
気になるタイマーが見つかったら、表示された名前を指定して詳細を読み取る。以下の名前は架空の例なので、実際の一覧に存在するタイマー名へ置き換えてから実行する。存在しない名前をそのまま指定しても設定を変更することはないが、期待した属性は得られない。
systemctl show example-report.timer -p OnCalendar -p AccuracyUSec -p RandomizedDelayUSec -p Persistent -p NextElapseUSecRealtime
この例で大事なのは、予定時刻を表すOnCalendarだけでなく、AccuracyUSecとRandomizedDelayUSecを同時に見ることだ。設定ファイルの記述が省略されていても、実際に適用された値がプロパティとして確認できる場合がある。なお、showの表示名は設定ファイル中のキーと必ずしも同じ綴りではない。空欄や見慣れない値が出たときは、対象ユニットが正しいか、システムとユーザーのどちらのマネージャーを調べているかも確かめる。
なぜ18時00分00秒にならないのか
OnCalendarが表すのは発火の基準となる日時だ。一方、AccuracySecは近接したタイマーイベントをまとめて処理するための許容精度を指定する。systemd.timerの説明では、AccuracySecの既定値は通常1分とされている。したがって、基準が18時00分00秒でも、精度設定がそのままなら秒単位で完全一致することを保証しない。ここでいう精度は、処理を必ず指定秒に終えるという意味ではない。
さらにRandomizedDelaySecが設定されていれば、基準時刻にランダムな遅延を追加する。AccuracySecとRandomizedDelaySecは別の目的を持つ。前者はイベントの集約、後者は複数マシンが同時に処理を開始しないようにする負荷分散などで使う。両者を一つの「遅れる設定」として扱うと、設定変更の判断を誤る。
次の図は、基準時刻から実際の起動要求までに何が関わるかだけを表す。サービス処理の完了時刻は別の話として図から外している。
flowchart TD
A["OnCalendarで基準時刻を決定"] --> B{"RandomizedDelaySecを設定?"}
B -->|はい| C["ランダムな遅延を加える"]
B -->|いいえ| D["遅延を追加しない"]
C --> E["AccuracySecの精度範囲で調整"]
D --> E
E --> F["サービスの起動を要求"]
図の上から下へ進むと、カレンダー式がそのままサービス完了時刻になるわけではないことが分かる。時刻をずらす設定がなくても、システム負荷やスケジューリングの事情で実際の処理には時間差が生じ得る。したがって「精度を1秒にしたから絶対に1秒以内に業務処理が終わる」とは解釈しない。
一条件だけ変える比較:精度を狭めたら何が変わるか
比較のため、二つの設定例を紙の上で考える。どちらも毎日18時というカレンダー式は同じで、AccuracySecだけが異なる。実機の設定を変更する必要はない。
[Timer] OnCalendar=*-*-* 18:00:00 AccuracySec=1min
[Timer] OnCalendar=*-*-* 18:00:00 AccuracySec=1s
前者は近接したタイマーイベントをまとめる余地が比較的大きく、後者はその余地を小さくする。狙いは「より厳密な起動要求のタイミング」だが、後者でも実時間処理の締切保証にはならない。実際のサービスに採用するなら、CPU負荷、ディスク待ち、同時刻に動く別ジョブとの関係を確認する必要がある。精度を小さくすることは無条件に正解ではなく、システム全体の起動集中を招く可能性も考える。
この比較から分かる意外な点は、タイマーの「予定時刻が正しい」ことと「ログの秒が一致する」ことが独立している点だ。時刻のずれを見つけたときに、すぐカレンダー式を直してしまうと、本来正しかった周期まで変えてしまうおそれがある。まず基準時刻を確かめ、次に許容精度を見るという順序が安全だ。
停止中に予定時刻を過ぎた場合
Persistentは、カレンダー式のタイマーについて、停止中に過ぎた実行機会を起動後に補うかどうかに関わる設定だ。例えば毎日18時のジョブを設定したPCが18時に電源オフで、翌朝に起動した場合を考える。Persistentが有効なら、停止中に実行機会があったことを踏まえて起動が要求される場合がある。ただし、停止中の毎日の回数をそのまま全部繰り返す機能だと考えてはいけない。
反対にPersistentが無効なら、過去の実行機会を補う動作は期待できない。どちらが適切かは処理の性質で変わる。日次の在庫集計なら欠けた日の処理が必要かもしれないが、外部への通知を一日分まとめて大量に送ると困る場合もある。業務要件として「過去分を一回補う」「欠損を検知して担当者に知らせる」「次の定刻まで待つ」を区別することが大切だ。
失敗をどう切り分けるか
最初の分岐は、カレンダー式の計算結果が正しいかどうかだ。systemd-analyze calendarの候補が期待と違うなら式とタイムゾーンを確認する。候補が正しいのに実行が遅れるなら、AccuracySecやRandomizedDelaySecを確認する。そもそも実行されていないなら、タイマーの有効化状態、サービス側の失敗、PCの停止履歴を別々に調べる。異なる現象を「タイマーの遅延」という一語でまとめないことが重要だ。
次に、サービスが実際に開始したかと、サービスが正常終了したかを分ける。タイマーの一覧でLASTが更新されていても、関連サービスが失敗していれば業務処理は完了していない。逆に、処理結果が遅れて出力されたとしても、タイマー自体の発火が遅れたとは限らない。業務ログ、サービスの起動ログ、終了状態を時系列で比較すると、原因を絞り込める。
systemctl status example-report.timer systemctl status example-report.service journalctl -u example-report.service --no-pager -n 30
これらは状態とログの参照用であり、サービスを起動しない。実際のサービス名を指定し、ログに利用者名や接続先などの情報が含まれていれば、公開前に伏せる。権限が足りず一部のログを読めない場合もあるが、そのことだけでサービス障害とは判定しない。ログの時刻がUTCかローカル時刻かも確認し、違う基準の時刻を並べて比較しない。
業務での使い分け:秒単位の締切が必要か
例えばバックアップの開始は多少前後してもよいが、決済の締切は厳密であるべきだ。この二つを同じタイマー設定で実現しようとすると無理がある。バックアップのように同時起動を避けたい処理なら、ある程度の起動時刻の分散が合理的だ。一方、締切の厳密な判定が必要なら、タイマーだけに責任を負わせず、処理側でも対象時刻や処理済みIDを確認する仕組みを検討する。
定時公開や日次レポートでは、まず「何時までに完了していればよいか」を決める。そのうえで、タイマーの基準時刻、許容遅延、処理時間、失敗時の再試行、二重実行の防止を分けて設計する。タイマーの精度を詰めても、処理側のネットワーク障害や重複投稿は解決しない。むしろ二重実行を避ける台帳や、成功後に結果を照合する処理が業務上の信頼性を左右する。
今回の調査では設定を変更していないので、現行サービスの動作に影響しない。もし将来設定を変えるなら、対象ユニットの現状を保存し、テスト環境で比較し、元へ戻す手順を用意してから変更する。稼働中のサービスを突然再起動して時刻の差を調べる必要はない。読み取り専用の観察だけでも、式の誤り、精度設定、サービスの処理時間という三つの原因を切り分けられる。
公式資料
systemd.timer: https://www.freedesktop.org/software/systemd/man/latest/systemd.timer.html
systemd.time: https://www.freedesktop.org/software/systemd/man/latest/systemd.time.html
systemd-analyze: https://www.freedesktop.org/software/systemd/man/latest/systemd-analyze.html
systemctl: https://www.freedesktop.org/software/systemd/man/latest/systemctl.html
公式仕様の記載を元にした期待挙動であり、掲載コマンドを対象Ubuntu上で実行した結果ではない。環境ごとのバージョンや管理者設定によって表示は異なる。
