cron不要?AlmaLinuxで systemd-timer による月1自動dnfアップデートを実現!

mac に UTM 入れて、さらにその中に AlmaLinux を入れた。 最低限のセキュリティ対策を施したく、ネット調査していたところ、
Linux サーバーの脆弱性対策の基本は、脆弱性のあるパッケージを迅速にアップデートすることです
と書かれている記事を発見。

sudo dnf upgrade で簡単にアップデートできるが、今後もアップデートする必要があるわけで、忘れてしまいそうだ。

それで、月に一度自動でアップデートする仕組みを実装した。 注意すべきこととしては、 AlmaLinux は常時起動しているわけではないので、たまに起動した時に当月のアップデートがすでに済んでいたら実行しないようにするってことだ。

この記事では systemd を使って実装した内容を説明する。(chatGPTの助けを借りた)

✅ systemdとは?

systemd とは、Linuxの起動・サービス管理を担う「initシステム」の一つです。 Linuxにおける「サービスの起動・停止・管理」「ログ収集」「タイマー実行」などを一元的に行います。

つまり、「Linuxが起動してから、その後に動くすべてのサービスや処理を司る司令塔」です。


🔧 何のためにあるの?

Linuxの起動直後から、次のような一連の作業を行うためです:

役割具体的な例
サービスの起動・停止sshd(SSHサーバ)や nginx などの起動・停止
起動順序の管理ネットワーク → サーバ のような依存関係の制御
ログの収集・確認journalctl で確認できるログの仕組み
定期実行(cronの代替).timer を使った定期実行
システムの状態管理(target)multi-user.targetgraphical.target など
パラレル起動(高速ブート)起動速度の最適化(必要なものだけ並列に起動)

🏗️ なぜ「systemd」という名前なの?

  • 「system daemon(システムの常駐プロセス)」の略
  • init の進化版として設計され、従来の SysVinit よりも高速で柔軟です

📁 systemdの構成要素(例)

ファイル/ユニットの種類目的拡張子
Serviceユニットプログラムやサービスの起動定義.service
Timerユニット定期実行などのスケジューリング.timer
Targetユニット起動レベル・状態のグループ.target
Mountユニットマウントポイントの管理.mount
Socketユニットソケット起動の設定.socket

🧪 systemd コマンド一覧

以下では monthly-upgrade というファイル名を使用した例を示している。このファイル名は任意に変更可能。

コマンド例説明(目的)
systemctl start monthly-upgrade.timerタイマーを即時起動(次回スケジュール実行のセット)
systemctl stop monthly-upgrade.timerタイマーを停止(スケジューリング解除)
systemctl restart monthly-upgrade.timerタイマーを再スケジュール(OnCalendar 再評価)
systemctl enable monthly-upgrade.timer再起動後もタイマーが有効になるよう設定
systemctl disable monthly-upgrade.timerタイマーを自動起動から除外
systemctl enable --now monthly-upgrade.timerタイマーを即時起動&再起動後も自動有効化(実質的に最もよく使う)
systemctl daemon-reload.service.timer を新規作成/編集後に systemd に反映
systemctl list-timers --all登録されたすべてのタイマーの一覧と次回実行時刻などを確認
systemctl status monthly-upgrade.timerタイマーの現在の状態(有効/無効、次回実行、最近の実行結果)を確認
systemctl status monthly-upgrade.serviceサービスの実行履歴・状態(成功/失敗、最後の実行)を確認
journalctl -u monthly-upgrade.serviceサービス実行時のログを表示
systemctl cat monthly-upgrade.service実際に systemd に登録された .service の内容確認
systemctl cat monthly-upgrade.timer.timer の内容確認

✅ まとめ

systemd とは、Linux システム全体のサービス起動・管理・監視・ログ収集を担う中心的な仕組みです。 安定した起動、高速なサービス起動、柔軟な構成管理を実現します。

次に、 systemd が参照するサービスファイルとタイマーファイルについて。

🔧 1. サービスファイル(.service

何か?

  • systemd に「何を実行するか」を伝えるファイル
  • 一言で言えば「実行内容そのもの」を定義する設定ファイル

目的

  • 特定のコマンドやスクリプト、アプリケーションなどを起動・停止・監視する
  • 一回限りの処理でも、常駐プロセスでも、両方に使える

使用例

例えば dnf upgrade を月に一回自動で実行したいとき:

1# /etc/systemd/system/monthly-upgrade.service
2[Service]
3Type=oneshot
4ExecStart=/usr/bin/dnf -y upgrade

これは「dnf -y upgrade を1回だけ実行する」という指示を systemd に出す。


⏰ 2. タイマーファイル(.timer

何か?

  • systemd に「いつ実行するか」を伝えるファイル
  • cron の代替として使える「スケジュール機能」

目的

  • .service ファイルを「いつ実行するか」を制御する
  • 毎日・毎月・起動後●分後など、柔軟なスケジュールが可能

使用例

上記の monthly-upgrade.service を毎月自動で実行させたいなら:

1# /etc/systemd/system/monthly-upgrade.timer
2[Timer]
3OnCalendar=monthly
4Persistent=true

これで「毎月1日 0:00 に monthly-upgrade.service を起動せよ」という意味になる。


💡 サービスとタイマーの関係まとめ

種別目的主なキーワード
.service何を実行するかを定義ExecStart, Type
.timerいつ実行するかを定義OnCalendar, Persistent
名前が同じ自動でリンクされるmonthly-upgrade.*
名前が違う場合Unit= で明示的にリンクするUnit=my-task.service

🎯 なぜ systemd でタイマーを使うのか?

  • cron より柔軟(起動後の遅延実行、スリープ中の補填など)
  • journal によるログ管理が簡単
  • サービスの依存関係や起動順制御ができる
  • .service.timer を分離できるため、同じ処理を手動実行にも使える

✅ まとめ

systemd におけるサービスファイルは「何を実行するか」、タイマーファイルは「いつ実行するか」を指定する設定ファイルです。 この2つを組み合わせることで、柔軟で管理しやすい自動化が可能になります。

以下が実装したシェルスクリプト。

 1#!/bin/bash
 2
 3set -e  # エラーがあったら即座にスクリプトを停止する
 4
 5SERVICE_FILE="/etc/systemd/system/monthly-upgrade.service" # TIMER_FILEのファイル名と一致していること
 6TIMER_FILE="/etc/systemd/system/monthly-upgrade.timer"
 7LOG_FILE="/var/log/monthly-upgrade.log"  # ログファイルのパス
 8
 9echo "systemdサービスファイルを作成(ログファイル出力対応)..."
10
11# systemdサービスユニットファイルを作成(#によるコメントはsystemdにより解釈されるので危険。行頭ならOK)
12cat <<EOF | sudo tee "$SERVICE_FILE" > /dev/null
13[Unit]
14Description=Monthly dnf upgrade with log file output
15After=network-online.target
16Wants=network-online.target
17
18[Service]
19Type=oneshot
20ExecStart=/bin/bash -c '/usr/bin/dnf -y upgrade 2>&1 | tee -a $LOG_FILE'
21EOF
22
23echo "systemdタイマーユニットファイルを作成..."
24
25# systemdタイマー設定ファイルを作成(#によるコメントはsystemdにより解釈されるので危険。行頭ならOK)
26cat <<EOF | sudo tee "$TIMER_FILE" > /dev/null
27[Unit]
28Description=Run dnf upgrade monthly
29
30[Timer]
31OnCalendar=monthly
32Persistent=true
33
34[Install]
35WantedBy=timers.target
36EOF
37
38echo "ログファイルを作成し、権限を設定..."
39
40# ログファイルを作成(なければ)し、パーミッションを設定
41sudo touch "$LOG_FILE"
42sudo chmod 644 "$LOG_FILE"    # 誰でも読み込み可能に設定
43sudo chown root:root "$LOG_FILE"  # 所有者をrootに設定
44
45echo "systemdデーモンの再読み込みを実行..."
46sudo systemctl daemon-reload  # 新規作成・変更したユニットを反映
47
48echo "タイマーの有効化と起動を実行..."
49sudo systemctl enable --now monthly-upgrade.timer  # 自動起動設定+即時起動
50
51echo "デプロイ完了しました。"
52echo "タイマーの状態確認コマンド:"
53echo "  systemctl list-timers --all | grep monthly-upgrade"
54echo "サービスのログ確認コマンド:"
55echo "  journalctl -u monthly-upgrade.service"
56echo "ログファイルの場所:"
57echo "  $LOG_FILE"
58

以下に、systemd に関係する箇所についてのみ、一行ずつ詳細に説明します。 (ファイル操作や一般的なbash制御などは除外します)


🔧 systemd 関連部分の詳細説明


SERVICE_FILE="/etc/systemd/system/monthly-upgrade.service"

  • 作成されるサービスユニットファイルのパスを変数として定義。
  • .timer ファイルと名前を揃えることで systemd が自動的に対応付けられる。

TIMER_FILE="/etc/systemd/system/monthly-upgrade.timer"

  • 作成されるタイマーユニットファイルのパス。
  • .service ファイルと同名であることで systemd により自動リンクされる。

cat <<EOF | sudo tee "$SERVICE_FILE" > /dev/null

  • systemd サービスユニット(実行定義)を /etc/systemd/system/ に書き出している。

[Unit] セクション

1[Unit]
2Description=Monthly dnf upgrade with log file output
3After=network-online.target
4Wants=network-online.target
  • Description: サービスの説明。systemctl status 等で表示される。
  • After=network-online.target: このサービスはネットワーク接続後に開始される。
  • Wants=network-online.target: ネットワークが起動していないときは、起動を試みる依存指定(必須ではない)。

[Service] セクション

1[Service]
2Type=oneshot
3ExecStart=/bin/bash -c '/usr/bin/dnf -y upgrade 2>&1 | tee -a $LOG_FILE'
  • Type=oneshot:

    • 一度だけ実行するサービス(終了後は systemd によって停止されたとみなされる)
    • デーモンのように常駐しない
  • ExecStart=...:

    • 実行コマンドを定義。
    • bash -c によって複雑なパイプ処理が可能。
    • dnf upgrade を実行し、標準出力と標準エラーをログファイルに追記。

※ 注意: $LOG_FILE はシェルスクリプト内で展開され、サービスファイルには「固定文字列」として書き込まれる。


cat <<EOF | sudo tee "$TIMER_FILE" > /dev/null

  • タイマーユニットファイルを /etc/systemd/system/ に書き出す。

[Unit] セクション

1[Unit]
2Description=Run dnf upgrade monthly
  • 説明コメント。

[Timer] セクション

1[Timer]
2OnCalendar=monthly
3Persistent=true
  • OnCalendar=monthly:

    • 毎月1日0時0分にこのタイマーが発火し、.service を実行。
    • systemd のカレンダー式(man systemd.time 参照)で表現。
  • Persistent=true:

    • 電源がオフになっていて指定時刻に実行できなかった場合でも、次回起動時に遅れて実行される。

[Install] セクション

1[Install]
2WantedBy=timers.target
  • タイマーを timers.target に紐付けることで、タイマーが system 起動時に有効化される。

sudo systemctl daemon-reload

  • .service および .timer/etc/systemd/system/ に新規作成・変更した後は必須。
  • systemd の内部キャッシュを更新し、定義の内容を再読込する。

sudo systemctl enable --now monthly-upgrade.timer

  • enable: ブート時に自動でタイマーが起動するよう設定。
  • --now: 現在のセッションでも即座に起動(=即タイマー起動)

systemctl list-timers --all | grep monthly-upgrade

  • 作成したタイマーの状態を確認するためのコマンド例。

journalctl -u monthly-upgrade.service

  • サービス実行時の systemd ログ(journal)を確認するコマンド。
  • ログ出力は標準出力・標準エラーの内容に基づく。

なお、このシェルスクリプトでは journald とは別でログファイルに出力しています。その理由は以下です。

journalctl のログは一時的である可能性がある

  • journald はログの保存容量や保存期間に制限がある場合があり、古いログが自動で削除されることもあります。
  • Persistent=true が /etc/systemd/journald.conf で有効になっていなければ、ログはRAM上の一時的なもので、再起動で消えます。
  • そのため、確実な永続記録が必要なケースでは、tee や >> /var/log/xxx.log のようなファイル出力を勧めることがあります。

✅ まとめ

このスクリプトは:

  • monthly-upgrade.service: 月に一回実行する内容(dnf upgrade)を定義
  • monthly-upgrade.timer: その実行タイミング(月に1回)を定義
  • 上記を systemd に登録・有効化・即起動する

systemd の基本設計思想「何を」と「いつを」分離する構造をそのまま活かした典型的なタイマーユニット活用例です。

あとは、このシェルスクリプトを実行するだけです。 このシェルスクリプトの実行方法は以下の通りです。手順をわかりやすく説明します。


✅ 前提条件

  • OS:AlmaLinux(systemd 対応 Linux)
  • ユーザー:sudo権限を持っている一般ユーザー
  • スクリプトファイルが完成している(例:deploy-monthly-upgrade.sh

🔧 実行手順


① 実行権限を付与

1chmod +x ~/deploy-monthly-upgrade.sh

これで実行可能になります。


② スクリプトを実行(sudo付きで)

1sudo ~/deploy-monthly-upgrade.sh

※ スクリプト内で sudo を多用しているので、本体には sudo 不要でも動作しますが、 一括して root 権限で走らせたい場合は sudo付きで実行が安全です。


🧪 実行後の確認

以下のコマンドで、systemd による登録が成功しているか確認できます。

✔ タイマーの状態確認:

1systemctl list-timers --all | grep monthly-upgrade

✔ サービスの定義確認:

1systemctl cat monthly-upgrade.service

✔ タイマーの定義確認:

1systemctl cat monthly-upgrade.timer

✅ ログ確認(実行後)

1journalctl -u monthly-upgrade.service

または、ログファイルが指定されているので:

1cat /var/log/monthly-upgrade.log

dnf upgrade の実行内容も確認できます。


🧹 アンインストール(削除したい場合)

1sudo systemctl disable --now monthly-upgrade.timer
2sudo rm /etc/systemd/system/monthly-upgrade.{service,timer}
3sudo rm /var/log/monthly-upgrade.log
4sudo systemctl daemon-reload

関連ページ