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.target や graphical.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
参照)で表現。
- 毎月1日0時0分にこのタイマーが発火し、
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