運用・保守 一覧へ戻る トップ運用・保守 / システムアップデート運用ガイド
Rocky Linux 9 ・ 運用・保守

動いているサーバーを、止めずに正しく更新する

システムアップデート運用ガイド:本番環境での段階適用と切り戻し

dnf update dnf history undo 段階適用 Webサーバー稼働中の実践

自宅であっても、Webサイトやメールサーバーを24時間公開していれば「本番環境」です。dnf update を何となく一括実行して再起動し、気づいたらメールが送受信できなくなっていた——このガイドは、実際にそうした経験をもとに整理した、止めてはいけないサービスを守りながらOSアップデートを適用するための実践手順です。特別な自動化ツールは使わず、Rocky Linux 9標準のdnfだけで実現します。

目次

  1. 1. なぜ「一括update」が危険なのか
  2. 2. 事前準備(Stage 0)
  3. 3. 適用順序の考え方
  4. Stage 1:影響範囲が狭いパッケージから
  5. Stage 2:止められないサービス(メール等)
  6. Stage 3:Webサーバー関連
  7. Stage 4:SSH(最重要・最慎重)
  8. Stage 5:コンテナ基盤
  9. Stage 6:復旧手段(Cockpit等)
  10. Stage 7:カーネル(再起動を伴う)
  11. 4. 切り戻し方法
  12. 5. チェックリストまとめ
  13. 実践記録

1. なぜ「一括update」が危険なのか

sudo dnf update -y を一度に流すこと自体は、多くの環境では問題になりません。しかし、以下の条件が重なるサーバーでは話が変わります。

一括更新は「どのパッケージの更新が、どのサービスに影響したか」を後から特定しづらくします。段階的に適用し、各段階で対象サービスを確認する方が、結果的に早く安全に完了します。

2. 事前準備(Stage 0)

パッケージを1つも更新する前に、必ず「戻れる状態」を作っておきます。

現在の状態を記録する
# dnfのトランザクション履歴から、後で切り戻す起点のIDを控える
sudo dnf history list | head -5

# 主要サービスの現在の稼働状況を記録
systemctl is-active httpd postfix dovecot sshd cockpit.socket

# メール設定が構文的に正しいことを更新前にも確認しておく(比較の基準にする)
sudo postfix check
sudo doveconf -n > ~/dovecot_before.conf

# Webサーバーの設定をバックアップ
sudo cp -r /etc/httpd/conf.d /root/httpd_conf.d.bak.$(date +%Y%m%d)

最大の安全弁:dnf history undo

Rocky Linux(dnf)は、パッケージのインストール・更新をトランザクション単位で記録しています。何かトラブルが起きた場合、sudo dnf history undo <ID> でそのトランザクションを丸ごと取り消せます。dnf history list で確認したIDを、作業前に必ずメモしておいてください。

3. 適用順序の考え方

「止まっても困らないもの」から「止まると困るもの」の順に適用し、各段階で影響範囲を確認してから次に進みます。

段階内容リスク
Stage 1上記以外の一般パッケージ
Stage 2メール(Postfix / Dovecot / DNS / OpenSSL / glibc)高(要individual検証)
Stage 3Webサーバー(httpd / mod_ssl)
Stage 4OpenSSH高(締め出しリスク)
Stage 5コンテナ基盤(podman等)
Stage 6復旧手段(Cockpit等)低(あえて最後)
Stage 7カーネル(再起動要)高(影響範囲が全体)
Stage 1

影響範囲が狭いパッケージから適用する

後述の重要パッケージ(kernel・httpd・postfix・dovecot・openssh・cockpit・podman等)を除外して、残りをまとめて適用します。これにより「基本的なdnf更新自体は問題なく通るか」をまず確認できます。

sudo dnf upgrade --exclude=kernel* --exclude=httpd* --exclude=mod_ssl \
  --exclude=mod_lua --exclude=mod_http2 \
  --exclude=postfix --exclude=dovecot \
  --exclude=bind* --exclude=openssl* --exclude=glibc* \
  --exclude=openssh* --exclude=cockpit* --exclude=podman* -y

【実践記録】除外リストの漏れで一度エラーになった

実際にこの手順を本番サーバーで実行した際、--exclude=httpd*だけでは不十分で、mod_luaが「更新版のhttpd-coreを要求するが、それは除外されている」という依存関係エラーで停止しました。mod_luamod_http2はhttpd本体と密結合しているため、Apache関連は必ずまとめて除外し、Stage 3で一括更新するのが正解です。同様に、Stage 2で個別確認したいbindopensslglibcも、除外し忘れるとStage 1の一括更新に紛れ込んでしまいます(実際に紛れ込みましたが、結果的に実害はありませんでした)。

Stage 2

止められないサービス(メール等)を個別に適用する

ここが最も慎重に進めるべき工程です。1つ更新するごとに、必ず動作確認を挟みます。

sudo dnf upgrade postfix dovecot bind bind-utils bind-libs bind-license openssl openssl-libs glibc glibc-common glibc-all-langpacks glibc-gconv-extra -y
適用直後に必ず実行
# glibcの更新は、稼働中プロセスがメモリ上の旧ライブラリを使い続けるため、
# 明示的な再起動が特に重要
sudo systemctl restart postfix dovecot

sudo postfix check
systemctl status postfix dovecot --no-pager

# 自分宛にテストメールを送り、実際に着信するか確認する

設定ファイルが .rpmnew で残っていないか確認

カスタマイズした設定ファイルがある場合、パッケージ更新によって新しいデフォルト設定がファイル名.rpmnewとして並んで作成されることがあります(既存の設定は上書きされません)。find /etc/postfix /etc/dovecot -name "*.rpmnew" で確認し、必要な差分だけ手動で取り込んでください。

【実践記録】受信テストメールがすぐ届かなくても慌てない

実際にpostfix再起動の直後、Yahoo・Hotmail・Gmail宛に送った受信テストメールが、しばらく届きませんでした。ログを確認すると原因はpostfixの不具合ではなく、postscreen機能が「初めて話す送信元」を一度だけ一時拒否する正常な仕組みによるものでした。

# maillogでこのパターンが出ていれば正常な一時拒否(postfixの不具合ではない)
sudo grep "PASS NEW\|450 4.3.2" /var/log/maillog | tail -n 20

大手プロバイダは送信元IPを多数のプールから使い回すため、postfix再起動直後は「未知の送信元」として毎回1回だけ450 4.3.2 Service currently unavailableで保留されることがあります。正規のメールサーバーはこの応答を受けると自動的に再送するため、数十分〜数時間後には届くのが通常です。すぐ届かない=壊れた、と判断せず、上記コマンドでこのパターンが出ているかをまず確認してください。

Stage 3

Webサーバー関連を適用する

sudo dnf upgrade httpd httpd-core httpd-tools mod_ssl mod_http2 mod_lua -y
sudo apachectl configtest
sudo systemctl restart httpd

適用後、実際に公開URLへアクセスして表示を確認します。リバースプロキシ構成(Caddy等)を挟んでいる場合は、そちらのヘルスチェックも合わせて確認してください。

Stage 4

OpenSSHを適用する(最も慎重に)

締め出し(ロックアウト)を防ぐ鉄則

今開いているSSHセッションは、絶対に閉じないでください。更新・再起動後、別のターミナルから新規に接続できることを確認してから、元のセッションを閉じます。新規接続が失敗した場合、元のセッションがまだ生きていれば復旧の望みがあります。可能であれば、Cockpit等のブラウザ経由の管理コンソールも事前に有効なことを確認しておいてください。

sudo dnf upgrade openssh openssh-server openssh-clients -y
sudo sshd -t              # 設定の構文チェック(起動前に必ず)
sudo systemctl restart sshd
# 元のセッションはそのままに、別ターミナルから新規接続テスト
ssh ユーザー名@サーバーのIP "echo OK"
Stage 5

コンテナ基盤を適用する

Podman等でリバースプロキシやアプリケーションを運用している場合、関連パッケージの更新後にコンテナが正しく起動しているか確認します。

sudo dnf upgrade podman aardvark-dns passt -y
sudo systemctl status (コンテナのユニット名) --no-pager
# 各ドメインへの実際のHTTPS疎通も確認
Stage 6

復旧手段(Cockpit等)を最後に更新する

Cockpit(Web管理コンソール)は、Stage 4のSSH更新時の「保険」として使うため、それまでは意図的に古いバージョンのまま維持し、最後にまとめて更新します。

sudo dnf upgrade cockpit* -y
sudo systemctl restart cockpit.socket
Stage 7

カーネルを適用する(再起動を伴う・別の作業枠で)

カーネル更新は影響範囲が最も広く、再起動が必須です。他のStageとは別の日・別の時間枠で、十分な作業時間を確保してから行うことを推奨します。

sudo dnf upgrade kernel kernel-core kernel-modules kernel-modules-core kernel-tools kernel-tools-libs -y

reboot前に「戻り先」を目視確認する

パッケージ適用の段階で、古いカーネルが自動的に削除されることがあります(installonly_limitの保持世代数を超えた分)。再起動する前に、現在稼働中のカーネルと、フォールバック先が実際に/bootに残っているかを確認してください。

uname -r                 # 現在稼働中のカーネルを確認
ls /boot/vmlinuz-*       # 起動可能なカーネルの一覧(レスキューカーネルも含め複数残っていれば安心)
sudo reboot
再起動後に必ず確認
systemctl status httpd postfix dovecot sshd caddy cockpit.socket --no-pager
uname -r   # 新しいカーネルで起動しているか確認
dnf check-update   # 未適用パッケージが0件になっているか最終確認

【実践記録】「サービスは動いている」≠「外部から見える」

再起動直後は上記の確認で全サービスがactiveと表示され、問題なしと判断しました。しかし後日、外部ネットワーク(モバイル回線経由)からサイトを開くと繋がらないことが判明しました。自宅LAN内から確認すると正常に見えていたため、発見が遅れました。詳しい原因究明と対処の流れは、この章の末尾「障害の原因究明と対処(詳細)」にまとめています。

教訓: リバースプロキシやコンテナ基盤を使っている場合、systemctl statusによる生存確認だけでなく、必ず実際の外部ネットワーク(モバイル回線等)からURLへアクセスして表示を確認してください。自宅LAN内からのテストは、プロキシを経由しない別経路でたまたま繋がってしまうことがあり、当てになりません。

4. 切り戻し方法

いずれかの段階で問題が起きた場合、Stage 0で控えたトランザクションIDを使って戻します。

# 特定のトランザクションだけを取り消す
sudo dnf history undo 

# 現在から特定IDの直後までをまとめて取り消す場合
sudo dnf history rollback 

カーネルの切り戻し

カーネルはパッケージとして「上書き」ではなく「追加」される仕組みのため、GRUBの起動メニューから旧カーネルを選んで起動すれば、パッケージを消さなくても即座に切り戻せます。dnfで明示的に削除しない限り、旧カーネルはしばらく残ります。

5. チェックリストまとめ

タイミング確認事項
作業前dnf history listのID控え/主要サービスの起動状態記録/設定ファイルのバックアップ
メール更新後postfix check/サービス再起動/実際のテストメール送受信
Web更新後apachectl configtest/実URLでの表示確認
SSH更新後既存セッションを残したまま別ターミナルで新規接続確認
カーネル更新後再起動後に全サービスの自動起動を確認/uname -rで反映確認
問題発生時dnf history undo <ID>で切り戻し

アップデート作業は「速く終わらせる」ことより、「途中で何が起きても気づけて、戻せる」状態を保つことの方が重要です。特にメールサーバーのように障害に気づきにくいサービスほど、更新直後の能動的な確認を習慣にしておくことをお勧めします。

実践記録

本ガイドは、実際に本サイトの本番サーバーで実行した記録・教訓をもとに作成・修正しています。パッチ適用は固定スケジュールではなく、都度状況を見て実施しているため、本章では実施日そのものではなく「何が起きて、どう調べて、どう直したか」を中心にまとめます。

Stage 0(事前準備)    :完了
Stage 1(一般107パッケージ):完了(mod_lua依存エラーで一度失敗→除外リスト修正後に成功)
Stage 2(メール系)    :完了(postfix/dovecot再起動、postfix check異常なし)
Stage 3(httpd系)     :完了(configtest OK、実URL表示確認)
Stage 4(OpenSSH)     :完了(新規セッションで接続確認、締め出しなし)
Stage 5(podman系)    :完了(Caddy含む全ドメインHTTPS疎通確認)
Stage 6(Cockpit)     :完了(サービス正常稼働を確認)
Stage 7(カーネル+再起動):完了(新カーネルで起動、全サービス自動起動・全ドメインHTTPS疎通確認)

作業直後の dnf check-update :未適用パッケージ 0件

Stage 2直後にメール受信が一時的に遅延した点(原因はpostfix再起動によるpostscreenの一時挙動)を除けば、Stage 0〜7は計画通り無停止で完了しました。しかし、この直後の確認だけでは見抜けなかった障害が後から発覚しました。以下、その原因究明と対処の全過程を記録します。

障害の発覚

Stage 7(カーネル更新+再起動)の直後、systemctl statusで全サービスのactive表示と、自宅LAN内からのアクセス確認は正常でした。しかし後日、実際に外部ネットワーク(社外・モバイル回線)からサイトを開こうとした利用者から「見れない」と報告があり、初めて発覚しました。自宅LAN内からの確認だけでは検知できなかったのが最大の反省点です。

原因究明の過程

  1. リバースプロキシのエラーログを確認:稼働ログに「バックエンド(ホストOS自身のWebサーバー)への接続がタイムアウトしている」というエラーが、実際の外部アクセス試行と同じタイミングで記録されていることを発見。プロキシ自体は外部からの通信を正しく受け取れているが、その先の転送で失敗していると判明。
  2. DNSの確認:外部の公開DNSで対象ドメインを解決し、現在の正しい公開IPを指していることを確認 → 問題なし(除外)。
  3. ファイアウォールの確認:該当ゾーンでHTTP/HTTPSサービスが許可されていることを確認 → 問題なし(除外)。
  4. バックエンド(ホストOS自身のWebサーバー)の稼働確認:ホストOS上のWebサーバー自体は正常稼働しており、ホストの主IPに対しては問題なく応答することを確認。
  5. ネットワーク構成の再確認で仮説を発見:このリバースプロキシは、コンテナ基盤の「macvlan」という方式のネットワークで動いていました。macvlanは仕様上、「ホストOS自身」と「macvlanで動くコンテナ」の間の通信が双方向に遮断されるという制約があります。カーネル更新に伴う再起動で、プロキシからホストへ到達するための補助的なネットワーク設定が失われ、この制約に引っかかるようになった、という仮説にたどり着きました。

試行錯誤(うまくいかなかった対処)

仮説の検証と対処のため、以下を試しましたが、いずれも根本的な解決にはなりませんでした。

試みた対処結果と理由
ホストOS側に、プロキシが転送する専用のIPアドレスを追加登録失敗。追加したIPも結局「ホストOS自身の名前空間」に属するため、同じmacvlanの制約を受け、プロキシ側から見て届かないまま。
ホストOS側に、プロキシと対等な立場になる専用の仮想ネットワークインターフェース(macvlan方式)を追加作成失敗。同様にホストOS自身のデフォルトの管理領域内に作成する限り、プロキシとの間で正しい「仲間(兄弟)」関係が成立しなかった。

最終的な解決策

根本的な解決策は、「ホストOS側にどうにかしてIPを増やす」方向ではなく、「コンテナ側にホストへ到達するための別経路を持たせる」という発想の転換でした。具体的には次の2点を変更しました。

  1. リバースプロキシのコンテナ定義に、既存の標準ブリッジネットワーク(コンテナ基盤に標準で用意されている、host通信に対応したネットワーク)を2つ目のネットワークとして追加
  2. プロキシの転送先を、macvlan越しの直接IP指定ではなく、コンテナ基盤が標準で提供している「ホストOS自身」を指す特別なホスト名に変更。

この方式なら、ホストOS側のIP設定が再起動等でどう変化しても、コンテナ基盤が自動的に正しいホストへ経路を解決してくれるため、影響を受けなくなります。変更後は、コンテナ内部から直接ホストへの疎通を確認し、続けて実際の外部ネットワークから全ドメイン(メインサイト・認証基盤・VPN管理基盤)とメール送受信を確認し、全て復旧していることを確認しました。

教訓のまとめ

  • コンテナ基盤で「macvlan」方式のネットワークを使っている場合、ホストOS自身との通信は双方向に遮断される仕様であることを前提に設計する(ホスト側にIPを足す方式では解決しない)。
  • コンテナからホストOS自身へ到達させたい場合は、コンテナに通常のブリッジネットワークを追加し、コンテナ基盤標準の「ホストを指すホスト名」を使うのが正攻法。
  • 再起動を伴う更新の後は、systemctl statusによるプロセスの生存確認だけでなく、実際の外部ネットワークからの到達確認を必ずセットで行う。内部LANからの確認は、プロキシを経由しない別経路でたまたま繋がってしまうことがあり、当てにならない。
運用・保守 一覧へ戻る