Proxmox クラスタを自動更新する — ただし本番VMが載っているノードは再起動しない
自宅サーバーの更新は、必ず放置される
自宅で動かしているサーバーの apt upgrade を、あなたは最後にいつ実行したでしょうか。
1台なら気が向いたときにやれます。 4台になると、やらなくなります。 1台ずつ SSH して、更新して、再起動の要否を判断して、という作業を月に何度もやる人はいません。
そして更新されないまま放置されたサーバーは、セキュリティ的にただの負債になります。
筆者の環境は Proxmox VE の4ノードクラスタで、 そのうち3ノードに本番として動かしている VM が載っています。 止められないものが載っている以上、「全部自動で更新して再起動」も選べません。
そこで、こういう方針にしました。
- 更新は全ノードで自動化する
- 再起動は、安全が確認できたノードだけ自動で行う
- 安全でないノードは通知だけ出して人間に判断を委ねる
この記事が向いていない人
- 単一ノードで運用している方 — quorum の話が不要になるので、もっと単純に組めます
- 本番用途で SLA がある方 — 自動更新自体を見直した方がいいと思います
- Proxmox のエンタープライズリポジトリを使っている方 — 本記事は無償リポジトリ前提です
なぜ unattended-upgrades では足りないか
Debian 系には unattended-upgrades があり、Proxmox でも使えます。
セキュリティ更新を自動で当てる、という点では十分に機能します。
足りないのは再起動の判断です。
カーネルが更新されても、再起動するまで動いているのは古いカーネルのままです。 更新した気になっているだけ、という状態になります。
かといって無条件に再起動させると、本番VMが載っているノードが勝手に落ちます。 Proxmox の場合、ノードの再起動はその上の全 VM の停止を意味します。
つまり必要なのは「更新するかどうか」ではなく、 「このノードは今、再起動してよいか」を判定するロジックでした。
構成
systemd のタイマーとシェルスクリプト1本です。
# /etc/systemd/system/pve-auto-update.timer
[Unit]
Description=Weekly Proxmox automatic package update
[Timer]
OnCalendar=Sat *-*-* 03:00:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
毎週土曜の午前3時に走らせています。ポイントが3つあります。
Persistent=true— その時刻にマシンが落ちていたら、次の起動時に実行する。 自宅環境では停電や手動シャットダウンがあるので、これが無いと更新が飛びますRandomizedDelaySec=300— 最大5分のランダム遅延。 4ノードが同時にapt-get updateを始めると、回線とミラーに集中します。 ばらけさせることで、片方の更新が終わってからもう片方が動くようにしています- 週次にした理由 — 日次だと再起動が必要な状況が頻発します。 自宅環境では、週1回まとめて当てるくらいがちょうどよいバランスでした
サービス側では優先度を落としています。
# /etc/systemd/system/pve-auto-update.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/pve-auto-update.sh
Nice=10
IOSchedulingClass=idle
Nice=10 と IOSchedulingClass=idle で、更新処理が VM の動作を邪魔しないようにしています。
深夜とはいえ、本番VMが動いている上での作業なので、ここは効きます。
更新の実行
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get -y \
-o Dpkg::Options::="--force-confdef" \
-o Dpkg::Options::="--force-confold" \
dist-upgrade
apt-get -y autoremove --purge
apt-get clean
DEBIAN_FRONTEND=noninteractive と --force-confdef / --force-confold の組み合わせが要点です。
これが無いと、設定ファイルに変更がある場合にプロンプトで止まります。
誰も見ていない深夜3時に「設定ファイルをどうしますか?」と聞かれても、答える人がいません。
--force-confold は「変更済みの設定ファイルはそのまま残す」という指定で、
自分でいじった設定が勝手に上書きされるのを防ぎます。
再起動が必要かの判定
ここが本題です。
NEWEST_KERNEL=$(ls /lib/modules | sort -V | tail -1)
RUNNING_KERNEL=$(uname -r)
if [ -f /var/run/reboot-required ] || [ "$NEWEST_KERNEL" != "$RUNNING_KERNEL" ]; then
...
fi
/var/run/reboot-required だけを見ていては不十分です。
このファイルは update-notifier-common などが作るもので、環境によっては存在しません。
より確実なのは、インストール済みの最新カーネルと、現在動いているカーネルを比較することです。
/lib/modules のディレクトリ名がインストール済みカーネルの一覧なので、
sort -V(バージョン順ソート)の最後と uname -r を比べれば判定できます。
sort -V は重要です。単純な文字列ソートだと 6.8.10 より 6.8.9 が後ろに来ます。
自動再起動してよい条件
再起動が必要と分かったうえで、実際に再起動するかどうかを2つの条件で判定します。
条件1: このノードに本番VMが載っていないこと
PROD_VM_REGEX='akashi-web-server|astrokit-web-server|nextcloud'
if qm list | awk '$3=="running" {print $2}' | grep -Eq "$PROD_VM_REGEX"; then
# 手動再起動に回す
fi
VMID ではなく VM 名で判定しているのが要点です。
Proxmox はクラスタなので、VM はノード間を移動します。 「VMID 102 が載っているノードは再起動しない」という書き方をすると、 マイグレーションした瞬間に判定が壊れます。移動先のノードは 102 を知らないからです。
VM 名で見ていれば、qm list はそのノードで動いているものだけを返すので、
どのノードに移動していても正しく判定されます。
$3=="running" で状態も見ています。停止中の VM が載っているだけなら、再起動して構いません。
条件2: クラスタが quorate であること
elif ! pvecm status | grep -q 'Quorate:.*Yes'; then
# quorum 異常 -> 再起動しない
fi
すでにクラスタが不安定なときに、さらに1ノード落としてはいけません。
Proxmox のクラスタは過半数のノードが生きていることを前提に動きます。 何らかの理由で既に quorum を失っている状態で再起動をかけると、 復旧がさらに難しくなります。
「更新のための再起動」は急ぐ作業ではないので、異常時は何もしないのが正解です。
どちらかを満たさない場合
wall "pve-auto-update: $(hostname) は再起動が必要です (新カーネル $NEWEST_KERNEL / 本番VM稼働中のため手動で)"
touch /var/run/pve-auto-update-reboot-required
wall で全ログインセッションに通知し、フラグファイルを置きます。
次に SSH したときに気づけるようにしておく、という程度ですが、
「再起動が必要なノードがある」という事実が残るのが大事です。
自動化で一番まずいのは、判断を保留したことを誰も知らない状態になることです。
条件を満たした場合
shutdown -r +1 "pve-auto-update kernel reboot"
1分の猶予を置いてから再起動します。即時ではなく1分にしているのは、 その瞬間に誰かが作業していた場合に気づけるようにするためです。
運用してみて
良かった点
- 4ノードの更新を意識しなくなった。 これが一番大きい
- 本番VMの載っていないノードは勝手に最新カーネルになる
- ログが
/var/log/pve-auto-update.logに残るので、後から追える
注意している点
- 本番VMが載っているノードは、結局手で再起動している。 自動化できるのは半分まで
- 更新によって何かが壊れた場合、気づくのは翌朝以降になる
- 無償リポジトリを使っているので、更新内容はエンタープライズ版と異なります
自動化の目的は「全部を無人で回すこと」ではなく、 **「人がやるべき判断だけを人に残すこと」**だと考えています。 再起動してよいかどうかは機械が判定でき、 「本番を止めるタイミングをいつにするか」は人が決めるべきことでした。
まとめ
- 更新の自動化より、再起動してよいかの判定の方が難しい
/var/run/reboot-requiredは当てにならない。/lib/modulesの最新版とuname -rを比較する- 本番VMの判定は VMID ではなく名前で行う。 マイグレーションで壊れないため
- quorum が異常なら何もしない。 不安定なクラスタをさらに揺らさない
- 自動再起動しなかったノードは、通知とフラグを残して人間に渡す
RandomizedDelaySecで全ノード同時実行を避ける