Ubuntu Weekly Recipe

第718回 needrestartで学ぶパッケージのフック処理

この記事を読むのに必要な時間:およそ 4 分

Ubuntu 21.04から,サーバー版に「needrestart」というパッケージが最初からインストールされるようになりました。これはパッケージの更新時に再起動が必要なデーモンを通知してくれる仕組みです。便利ではあるものの,パッケージの更新を開始して放置したら,最後のほうでメニューが出て止まっていたということも起こりえます。そこで今回はneedrestartの設定方法と,どういう仕組みで動いているのかを解説しましょう。

needrestartの基本

glibcなど特定のライブラリパッケージに脆弱性が見つかり,対応が行われたとき,そのパッケージの更新だけではまだ対応が完了したとは言えません。実際にはそのライブラリを利用しているプロセスが一通り再起動し,ライブラリを再読込することでようやく脆弱性が解消されるのです。

しかしながらどのパッケージが更新され,そのパッケージに属するライブラリファイルは何で,そのライブラリファイルを使っているサービスはどれかを知るには一筋縄では行きません。そこで登場したのがneedrestartです注1⁠。

注1
ライブラリではなく,サービスを提供する本体のパッケージ(たとえばnginxなど)については,原則としてパッケージ更新時に自動的に再起動を行います。よってneedrestartはあくまでサービス本体とは独立したライブラリパッケージ更新時に必要になるプログラムです。

needrestart自体はPerl製のシンプルなスクリプトです。簡単に仕組みを説明すると,実行中のプロセスについて/proc/PID/mapsを精査し,⁠deleted」フラグが含まれているサービスを再起動するというものです。これによりパッケージの更新によってライブラリが差し替わったとしても,必要なサービスのみを再起動して影響範囲を少なくしながら安全な状態を維持することを目指します。

ちなみにneedrestart自体は単にライブラリだけでなく,次のようなデータの更新に対応しています。

  • カーネル本体の更新
  • Intel/AMDのCPUのマイクロコードの更新
  • Java/Perl/Python/Rubyで利用しているライブラリの更新
  • Dockerコンテナ内部のプロセスは無視
  • LXC/LXDコンテナ内のプロセスの場合はインスタンスの再起動を通知
  • QEMU更新時に再起動が必要なVMのリストを通知

その結果,Ubuntu 21.04のサーバーからパッケージの更新時に次のようなメッセージが表示されるようになりました。

図1 パッケージを更新すると最後のほうで表示されるサービスの再起動通知

図1

unattended-upgradeのようにバックグラウンドで自動更新するケースについては,サービスの再起動通知は行いません。手動で確認したい場合は,sudo needrestartを実行すると良いでしょう。また-pオプションを付けることで,Nagiosのプラグインモードとしても動かせます。

ちなみにneedrestartが最初からインストールされるようになったのは,サーバー版とCloud Image版だけです。デスクトップ版はこれまでどおり,パッケージの更新後も特に何もしない状態となります。

特にサーバー版/Cloud Image版の場合,LTSのみ運用している人も多いことでしょう。よって,先日の22.04を導入以降にはじめて,システムを更新するとこのようなメッセージに遭遇したかもしれません。apt upgradeを実施してあとは放置するような運用の場合は,気づいたら変なところで止まっていたとなるとやっかいです。というわけで,基本的な設定方法からまず説明していきましょう。

needrestartの設定は/etc/needrestart/needrestart.confに集約されています。このファイルを直接変更してもいいのですが,/etc/needrestart/conf.d/ディレクトリにfoo.confのようなファイルを置いておけば,設定を上書き可能になります。設定ファイルの管理や将来的なneedrestartパッケージの更新を考えるとconf.d/以下のみを変更するようにしたほうが良いでしょう。ファイル名でソートされた上で読み込まれますので,00-foo.confのようにファイル名の先頭に数字を付けて,読み込み順をコントロールできるようにしておくと便利です。

再起動モードの変更

まずはパッケージ更新時の再起動通知について設定してみましょう。これはneedrestart.confに次のように説明されています。

# Restart mode: (l)ist only, (i)nteractive or (a)utomatically.
#
# ATTENTION: If needrestart is configured to run in interactive mode but is run
# non-interactive (i.e. unattended-upgrades) it will fallback to list only mode.
#
#$nrconf{restart} = 'i';

選択肢は次の3種類です。

  • 再起動が必要なサービスを表示するだけ(l)
  • サービスごとに再起動が必要かどうかを通知する(i)
  • 必要なサービスはすべて自動的に再起動する(a)

未設定時は「i」が設定されています。キュリティ的に安全側に倒すなら「a」ですが,サービスのライフサイクルを手動でコントロールしたい(21.04より前に近い状態にしたい)のであれば「l」を指定してください。

たとえば次のように実行すれば,/etc/needrestart/conf.d/ディレクトリに「a」を設定するファイルを作成できます。

$ echo "\$nrconf{restart} = 'a';" | sudo tee /etc/needrestart/conf.d/50-autorestart.conf

なお,⁠a」を指定した場合は,LXC/LXDインスタンスも自動的に再起動される可能性がある点に注意が必要です。ただしUbuntu 22.04 LTSリリース時点でのneedrestartは,LXC/LXDインスタンスは常に再起動しないという不具合があります。なおDockerインスタンスについては,たとえライブラリの更新を検知しても再起動の必要性の通知は行いません。単純に無視します。

常に再起動を抑止する

needrestartは,⁠i」を指定したインタラクティブモードでパッケージが更新されると,⁠再起動が必要なサービスに最初からチェックが入った状態」でユーザーに問い合わせを行います。つまりその状態で思わずエンターキーを押してしまうと,必要なものがすべて再起動されてしまいます。それを抑止するのがdefnoオプションです。

# Change default answer to 'no' in (i)nteractive mode.
#$nrconf{defno} = 1;

上記をコメントアウトしたり,再起動モードのように別ファイルにおいて,$nrconf{defno}を1にすることで,再起動が必要なサービスも常にチェックが外れた状態で問い合わせを行いますので,間違ってエンターを押しても安心です。

ただしこのままだと「どれを再起動すべきか」がわからなくなる弊害があります。よって手動でneedrestartを実行するときは,UIをDebconfからテキストモードに変更してしまいましょう。

$ sudo needrestart -u NeedRestart::UI::stdio -n
(中略)
Restarting services...
Services to be restarted:
Restart «ModemManager.service»? [yNas?]
Restart «dbus.service»? [yNas?]
Restart «lxd-agent.service»? [yNas?]

このように必要なものだけ,ひとつひとつ問い合わせがされますので,適宜エンター(再起動しない)「y」⁠再起動する)かを切り替えて行けば良いでしょう。なおUIの変更は,needrestart.confでも変更できます。

# Use preferred UI package.
#$nrconf{ui} = 'NeedRestart::UI::stdio';

これをコメントアウトするだけです。指定できるUIはneedrestart -u ?で確認できます。

著者プロフィール

柴田充也(しばたみつや)

Ubuntu Japanese Team Member株式会社 創夢所属。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。