はじめに:「更新したはずなのに」が起きる理由
apt upgradeやdnf updateを実行すれば安全——そう思っていないだろうか。実際にはパッケージの更新後も古いライブラリをメモリに読み込んだままのプロセスが動き続けるという問題が残る。例えばnginxを更新してもOpenSSLの脆弱性を抱えた古いバイナリが稼働しているのはそのためだ。
さらに自動更新を仕掛けても、新たなカーネルがインストールされていても再起動するまで新しいカーネルは使われない(Ubuntuの無停止カーネルパッチ運用は除く)。ログを見ていないと更新自体が失敗していても気づかないといった事が起こる。
この記事では、筆者が運用している11台のLinux(Debian / RHEL / OracleLinux / Fedora / Proxmox混在)に対して以下の組み合わせで「本当に安全な自動更新運用」を構築した手順と、実際に遭遇したトラブルを記録している。この構成で20年程度運用しているが問題は起きていない。
- cron-apt(Debian系)/ dnf-automatic(RHEL/Fedora系): パッケージの自動更新
- Logwatch: 毎朝のログダイジェストで異常検知
- needrestart / needs-restarting: 更新後にサービスを自動再起動し、古いバイナリを確実に追い出す
- Ansible: 11ホストへの一括展開
自動パッケージ更新の設定
cron-apt(Debian / Ubuntu / Proxmox)
cron-apt は毎晩 apt を実行し、更新を自動適用する。
apt install cron-apt
/etc/cron-apt/config で動作を制御する。デフォルトでは毎日午前4時に実行される。
# /etc/cron-apt/config
MAILTO="admin@example.com"
MAILONLY="no"
DEBUG="0"
/etc/cron-apt/action.d/3-download を確認し、upgrade アクションが有効か確認する。
# 取得から自動適用まで
apt-get -o quiet=2 dist-upgrade -y
dnf-automatic(RHEL / OracleLinux / Fedora)
dnf install dnf-automatic
/etc/dnf/automatic.conf で自動適用を有効化する。
[commands]
# ダウンロードだけなら "download"、自動適用なら "default"
upgrade_type = default
random_sleep = 0
[emitters]
emit_via = stdio
[email]
email_from = root@localhost
email_to = admin@example.com
email_host = localhost
[base]
debuglevel = 1
systemd timer で有効化する。
systemctl enable --now dnf-automatic.timer
systemctl status dnf-automatic.timer
フリートへのAnsible展開
複数ディストリビューション混在環境では Ansible の ansible_os_family で条件分岐するのが定石である。
- name: cron-apt をインストール (Debian系)
apt:
name: cron-apt
state: present
when: ansible_os_family == "Debian"
- name: dnf-automatic をインストール (RedHat系)
dnf:
name: dnf-automatic
state: present
when: ansible_os_family == "RedHat"
- name: dnf-automatic timer を有効化 (RedHat系)
systemd:
name: dnf-automatic.timer
enabled: yes
state: started
when: ansible_os_family == "RedHat"
Logwatch:毎朝のログダイジェストで異常を検知
logwatch は毎日サーバーの各種ログ(syslog、auth、ssh、postfix、httpど)を集約し、デイリーレポートをメール配信する。自動更新が実行されたか、デーモン起動障害がないか、不権アクセス試行がないかを毎朝目視するだけで検知が完結する。
インストール
# Debian / Ubuntu / Proxmox
apt install logwatch
# RHEL / OracleLinux / Fedora
dnf install logwatch
設定
/etc/logwatch/conf/logwatch.conf(存在しなければ新規作成)に最低限以下を記載する。
Output = mail
MailTo = yoshi@example.com
MailFrom = logwatch@hostname
Detail = Med
Service = All
Range = yesterday
cron で自動実行する場合は /etc/cron.daily/00logwatch が自動生成される(Debian系)。RHEL系は /etc/cron.d/0logwatch を確認する。
レポートの読み方
Logwatch のメールで特に注目するセクション:
| セクション | 読み方 |
|---|---|
| sshd | Failed password / Invalid user の数を確認。異常に多い IP は fail2ban 等でブロック |
| sudo | 予期せぬ sudo 実行がないか確認 |
| package updates (dpkg/dnf) | 更新が自動実行されたことを確認。「0パッケージ」が続いたら更新失敗を調査 |
| disk | ディスク満杯警告を確認 |
| postfix / sendmail | 山橋が異常なメールがないか |
drift-check は不要?
単一管理者環境では Logwatch の目視 + cron-apt/dnf-automatic の実行記録確認で十分である。ansible-playbook --check --diff による drift-check は複数人がサーバーに触れるチーム環境で初めて意味を持つ。
needrestart:更新後のサービス再起動漏れを防ぐ
needrestart はパッケージ更新後に「古いバージョンのライブラリを使い続けているサービス」を検出し、自動または手動で再起動するツール。Debian系標準。
インストールと自動モード設定
apt install needrestart
/etc/needrestart/needrestart.conf で自動モードを有効化する。
# デフォルト (interactive)
# $nrconf{restart} = 'i';
# 自動モードに変更
$nrconf{restart} = 'a';
動作確認
needrestart -b
出力の読み方:
NEEDRESTART-VER: 3.6
NEEDRESTART-KCUR: 6.1.0-28-amd64 ← 現在稼働中のカーネル
NEEDRESTART-KEXP: 6.1.0-28-amd64
NEEDRESTART-KSTA: 1 ← 1=OK、 3=再起動必要
NEEDRESTART-SVC: php8.4-fpm.service ← 古いライブラリを使い続けているサービス
KSTA:3 はカーネル再起動が必要なことを示す。KSTA:1 はカーネルも最新で問題なし。
RHEL系でのneedrestart代替:needs-restarting
RHEL/RHELファミリーでは EPEL の needrestart が perl(Locale::TextDomain) 依存性の問題でインストールできないケースがある(CRB リポジトリが利用不可なサブスクリプション)。その場合は標準添付の dnf-utils(yum-utils)に包まれる needs-restarting を代わりに使う。
# 再起動が必要なサービスを一覧
needrestart -s
# 再起動が必要かどうか確認(rc=1 なら再起動必要)
needs-restarting -r; echo $?
注意:
needs-restarting -rの成功終了コードは rc=1(再起動必要)。シェルスクリプトで&&で纋げると次のコマンドが実行されないので||か条件分岐を使うこと。
Ansibleでフリートに一括展開
11台のホスト(Debian 12 / RHEL 9 / OracleLinux 10 / Fedora / Proxmox VE混在)に対して needrestart-deploy.yml Playbook で一括展開した。
Playbook
---
- name: needrestart を全サーバーに展開
hosts: all:!windows
become: yes
tasks:
- name: needrestart をインストール (Debian / Proxmox 系)
apt:
name: needrestart
state: present
when: ansible_os_family == "Debian"
- name: EPEL を有効化 (RHEL のみ。OracleLinuxは不要)
dnf:
name: epel-release
state: present
when:
- ansible_os_family == "RedHat"
- ansible_distribution == "RedHat"
- name: needrestart をインストール (RedHat / Fedora 系)
dnf:
name: needrestart
state: present
when: ansible_os_family == "RedHat"
- name: needrestart を自動モードに設定
lineinfile:
path: /etc/needrestart/needrestart.conf
regexp: '^#?\$nrconf\{restart\}'
line: "$nrconf{restart} = 'a';"
when: ansible_os_family in ["Debian", "RedHat"]
- name: 動作確認(再起動が必要なサービスを一覧)
command: needrestart -b -r l
register: nr_check
changed_when: false
when: ansible_os_family in ["Debian", "RedHat"]
- name: 再起動が必要なサービスを表示
debug:
msg: "{{ nr_check.stdout_lines }}"
when:
- ansible_os_family in ["Debian", "RedHat"]
- nr_check is defined
- nr_check.stdout_lines | length > 0
実際に発生したトラブルと解決
OracleLinux 10 で EPEL リポジトリへのアクセス失敗
OracleLinux 10 は epel-release パッケージではなく、ol10_u1_developer_EPEL リポジトリがプリビルトで入っている。Playbook の EPEL 有効化ステップを ansible_distribution == "RedHat" だけに絞ることで解決。OracleLinux はそのまま needrestart を dnf install で導入できる。
RHEL で perl(Locale::TextDomain) 依存性エラー
RHEL のこのサブスクリプションでは CRB (CodeReady Builder)リポジトリが利用不可。needrestart のインストールが失敗するため、dnf-utils に標準添付の needs-restarting を代替ツールとして使用する。
GCPインスタンス再起動時のSSHタイムアウト
GCP インスタンスは再起動後の SSH 接続が 120秒を超えることがある。Ansible の reboot_timeout を 300 以上に設定することで回避できる。
ansible -i hosts.ini dns01 -m reboot -a "reboot_timeout=300" --become
実行時の注意点
- ansible.cfg はカレントディレクトリで読み込まれる:
~/ansible/から実行することが必須。~から実行すると inventory が読めずホスト横断が失敗する。 - DNSサーバーの同時再起動は禁物: dns01(Primary DNS)と dns02(Secondary DNS)を同時に再起動すると DNS が停止する。必ず順次再起動する。
カーネル更新後の再起動管理
needrestart が自動再起動するのはサービスのみであり、カーネルの再起動は必ず手動で行う必要がある。
カーネル再起動が必要なホストの確認
# needrestart で KSTA:3 のホストを確認
ansible all -i hosts.ini -m command -a "needrestart -b" --become 2>/dev/null | grep -A2 'KSTA: 3'
# RHEL系は needs-restarting -rで確認
ansible rhel -i hosts.ini -m command -a "needs-restarting -r" --become
# rc=1 が返ったホストは再起動必要
Ansibleで再起動する
cd ~/ansible
# 単一ホスト再起動
ansible -i hosts.ini <hostname> -m reboot -a "reboot_timeout=300" --become
# 再起動後の確認
ansible -i hosts.ini <hostname> -m command -a "uname -r" --become
今回の実施結果
| ホスト | ディストリビューション | カーネル再起動 | 検出ツール | 備考 |
|---|---|---|---|---|
| web01 | Debian 12 | 済み | needrestart | KSTA:1 |
| app01 | RHEL 9 | 済み | needs-restarting | 26秒で正常再起動 |
| backup01 | Debian 12 | 済み | needrestart | 32秒で正常再起動 |
| dns01 | Debian 12 | 済み | needrestart | GCP、SSH再接続に120秒超 |
| dns02 | Debian 12 | 未実施 | needrestart | dns01 の確認後に実施 |
| ol01 | OracleLinux 10 | 保留中 | needrestart | EPEL問題解決済み |
| pve01 / pve02 / pve03 | Proxmox VE | 保留中 | needrestart | VM配置計画が必要 |
DNSサーバー再起動の注意
dns01(Primary DNS)と dns02(Secondary DNS)を同時に再起動すると DNS 解決が完全停止する。必ず片方が起動してから、正常を確認してもう片方を再起動する。
Proxmox VE の再起動
PVE ホスト再起動は VM が停止するため、メンテナンスウィンドウを設けて実施する。VMを事前に live migration するか、シャットダウン再起動するかを決める。
Claude メモリへの環境登録
自動化環境が整ったら、今後の AI 支援運用のために Claude のメモリへ環境情報を登録しておくと便利だ。**本セクションは、ここまでの設定(cron-apt / dnf-automatic / Logwatch / needrestart / Ansible 展開)がすべて完了していることを前提とする。**次回以降のセッションでホスト一覧や Ansible のパスを都度説明する手間が省ける。
Claude.ai にはセッションをまたいで情報を記憶できる「メモリ」機能がある。フリートのインベントリや運用ルールをここに登録しておけば、将来のセッションでも即座にコンテキストを把握した状態で作業を再開できる。
登録する情報
- フリートインベントリ: ホスト名・OS・役割
- Ansible のパス: Playbook の場所(
~/ansible/)、ansible.cfgの前提条件 - ツール導入状況: 各ホストの cron-apt / dnf-automatic / needrestart の有無
- 運用ルール: DNS 二重再起動禁止、Proxmox VE はメンテナンスウィンドウ必要、など
登録プロンプト例
以下は記入例。実際のホスト名・ OS・ディレクトリパス・注意事項は自分の環境に合わせて書き換えてから、Claude との会話に貼り付ける。
以下の情報をメモリに保存してください:
フリート構成:
- web01: Debian 12(Webサーバー)
- app01: RHEL 9(needs-restarting を使用)
- backup01: Debian 12(バックアップサーバー)
- dns01, dns02: Debian 12(Primary / Secondary DNS)
- ol01: OracleLinux 10
- pve01, pve02: Proxmox VE
Ansible: ~/ansible/ に配置、必ず cd ~/ansible してから実行
注意事項:
- dns01 と dns02 の同時再起動禁止(DNS 停止)
- Proxmox VE の再起動は VM 停止を伴うため要計画
Claude が /areas/galaxies-infra.md などのメモリファイルに自動記録する。設定を変えたり台数が増えたりしたときは「galaxies-infra.md のメモリを更新して」と伝えれば即座に反映される。
次回セッションからの活用
メモリ登録後は Claude が環境を把握した状態で会話を開始できる。
- 「KSTA:3 が出ているホストを確認したい」→ 各ホストのツール構成を把握済みで回答
- 「dns01 を再起動したい」→ SSH タイムアウト注意点も自動で踏まえる
- 「needrestart を全ホストに再展開したい」→
~/ansible/のパスや Playbook 名を聞かずに提案
都度コンテキストを貼り直す手間がなくなり、インフラ作業のスタート時間が大幅に短縤できる。
まとめ:自動更新運用の完全形
これで「パッケージを更新しても古いバイナリが動き続ける」問題が解決される。各ツールの役割分担を整理する。
| ツール | 役割 | 対象 |
|---|---|---|
| cron-apt | パッケージ自動更新 | Debian / Ubuntu / Proxmox |
| dnf-automatic | パッケージ自動更新 | RHEL / OracleLinux / Fedora |
| Logwatch | 更新・異常・不権アクセス検知 | 全ディストリビューション |
| needrestart | 更新後のサービス自動再起動 | Debian系、OracleLinux |
| needs-restarting | 更新後のサービス確認(導入までの代替) | RHEL |
| Ansible | 全ホストへの設定一括展開 | フリート全体 |
自動化できない部分
- カーネル再起動: KSTA:3 を検出したら手動で計画再起動する。Proxmox VE は VM の現班稺認等が必要
- パッケージ更新の破抑性変更への対応: Logwatch が警告したら手動指対応
- DNSサービスの同時再起動回避: Primary / Secondary の順次再起動は必ず手動管理
参考リンク
※本記事は当サイト管理人の個人的な備忘録です。情報内容には配慮しておりますが、本記事の参照または付随ソースコード利用後にいかなる損害が発生しても、当サイト及び管理人はいっさいの責任を負いません。
