そこになかった空白:暗闇で Fedora をアップグレードする

Sway · Dell XPS 13 · Fedora 43 → 44

そこになかった空白:暗闇で Fedora をアップグレードする

唯一の本当の傷が、たった一つの見えない文字だった Fedora 43 → 44 リリースアップグレード。

Fedora 43 → 44 · 6000+ パッケージ · 暗闇でアップグレード

Fedora 43 → 44。フルリリースアップグレード——そしてこのシリーズの人格に違わず、私をほぼ沈めたのはたった一つの見えない文字だった。

このシリーズの前の二章は、Sway デスクトップを動かす話だった——黒い画面をわが家に変える、続いてずっと動いていた音量キーを追い詰める。今回はもっと怖い話だ。土台ごと意図的に引き抜き、その下へ新しいものを差し込む——しかも稼働中、手持ちの唯一のノートで。


00記録 · ここはどこ?

「continue os update.」

発掘 · dnf history

恥ずかしながら、三つの単語で始まった。新しいセッションを開き「continue os update」と打った——私が何をしていたか何も覚えていない文脈に。いったい何を続けろと?

だから何かに触れる前に、発掘調査。dnf history は、つけている自覚のなかった日記だ:

shell
dnf history list | head

日記はこう言った。数日前に system-upgrade download、今朝 dnf upgrade --refresh、その直後に kmod-v4l2loopback の再ビルド。rpm -E %fedora と稼働中のカーネルと突き合わせると、判決は明白だった。私はすでに Fedora 43 にいて、起動したてだった。大きなアップグレードは済んでいた。「続ける」はアップグレードをやることではなく——その後始末をして、次を並べることだった。教訓ゼロ:過去の自分が曖昧なメモを残したら、メモを信じる前に、今の自分にログを読ませよ。

01記録 · 孤児

ゴミではなかった残り物

監査 · まだ誰が必要としている?

どのリリースアップグレードも堆積物を残す。古い Fedora 向けにビルドされ、再ビルドされないまま、古い .fc42 や .fc41 のタグでそこに居座るパッケージたち。怠惰な一手は dnf autoremove して立ち去ること。誠実な一手は、一つずつに問うことだ。まだ誰が君を必要としている?

shell
rpm -qa | grep -E '\.fc4[12]\.'

十数件の容疑者。だが逆依存チェックが、その半分を「孤児」から「荷重を支える存在」へと変えた:

  • webkit2gtk4.0 (fc42) ← いまもリモートデスクトップのクライアントが引き込んでいる
  • javascriptcoregtk4.0 (fc42) ← その webkit が必要とする
  • vamp-plugin-sdk (fc42) ← それ自体は最新の Audacity が必要とする

「残り物」は「ゴミ」の同義語ではない。どれも引き抜いて安全ではなかった。依存しているアプリはまったくの最新で、ただ新しいライブラリに対してまだ再ビルドされていなかっただけだ。(webkit の連鎖は覚えておいてほしい——終盤に静かな見返りがある。)本当に死んだ重しは古いカーネル一式だけで、それすら次のカーネルインストールで自動的に刈られる。私はただ片付けのドーパミンのために手で外した。

02記録 · .rpmnew

設定マーカーと、採用に値するディレクティブ

確認 · .rpmnew / .rpmsave

アップグレードは .rpmnew と .rpmsave のファイルを撒く——パッケージマネージャの「この設定について意見はあったが、君のを上書きしたくなかった」という言い方だ。反射的に消すより読む価値のあるものが二つあった:

  • cups-browsed.conf: アップグレード後のスクリプトレットが静かに BrowseRemoteProtocols none を設定していた——cups-browsed の CVE 余波からの堅牢化デフォルトだ。私の古い保存コピーには、よりおしゃべりな dnssd cups があった。施錠された none を残し、マーカーを消した。ここで親切ぶって古い値を「復元」するな。
  • chrony.conf: 自作の NTP サーバー行は無傷だったが、新しいデフォルトが非推奨のうるう秒ディレクティブを近代的なものに置き換えていた:
/etc/chrony.conf
  # old, deprecated
  leapsectz right/UTC
  # new
  leapseclist /usr/share/zoneinfo/leap-seconds.list
  

採用しても無害に見えた。読者よ、無害ではなかった。

03記録 · 一文字

そこになかった空白

バグ · 欠けた空白

ディレクティブを差し替えるため sed に手を伸ばした。そして sed はこれを返した:

/etc/chrony.conf
leapseclist/usr/share/zoneinfo/leap-seconds.list

よく見てほしい。leapseclist の後に空白がない。一文字の傷——次の解析で chrony がきっぱり拒否する不正なディレクティブだ。キーワードとその引数が、一つの無意味なトークンに融合していた。

これに気づけたのは、この旅の早い段階で身につけた反射のおかげだ。勝利を宣言する前に検証せよ。目ではなく——目は欠けた空白を素通りする——空白を可視化する道具で:

shell
cat -A /etc/chrony.conf | grep leap
# leapseclist/usr/share/zoneinfo/leap-seconds.list$

cat -A は論評しない。欠けた隙間がそこにあり、$ に固定された空白はどこにも見えなかった。空白は噛みつくまで見えない。cat -A は先に噛みつかせる。

04記録 · 積み重なった罠

……そして「直した」設定は一度も読み込まれていなかった

診断 · ファイル ≠ プロセス

そして二つめの罠が、待っていたかのように一つめの上に積み重なった。たとえ私の編集がきれいでも関係なかった——システムで動いている chronyd は、まだ起動時のものだったからだ。そのログは誇らしげに「うるう秒データを得るために right/UTC タイムゾーンを使用」と言っていた——古いディレクティブだ。私は (a) 不正で、(b) そもそも読み込まれてすらいない変更を「採用」していた。

設定ファイルを編集しても、稼働中のデーモンは変わらない。口にすれば当たり前。その瞬間には見えない。直しには両方の半分が要った:

shell
sed -i 's|^leapseclist/usr|leapseclist /usr|' /etc/chrony.conf   # the space
systemctl restart chronyd                                        # the reload

そしてその時になって初めて、ログは真実を語った:

journalctl -u chronyd
chronyd[34630]: Using leap second list /usr/share/zoneinfo/leap-seconds.list

再起動はリロードではなく、ファイルはプロセスではない。ディスク上のものではなく、動いているものを確認せよ。

再起動はリロードではなく、ファイルはプロセスではない。

05記録 · 時計

fritz.box はサーバーではなく場所だ

修正 · 移動する時刻源

せっかく chrony を開いていたので、何年も前に問うべきだった問いを立てた。これはそもそも同期しているのか? していなかった。

shell
chronyc sources
chronyc tracking   # Leap status : Not synchronised

私の設定は server fritz.box iburst を指していた——自宅のルーターだ。そして fritz.box は自宅にいるときしか解決しない。これはノートだ。時計をリビングのルーターに固定するということは、地球上のほかのどこでも時刻源がまったくないということ。だから、どのカフェにいても存在するサーバーを与えた:

/etc/chrony.conf
pool 2.fedora.pool.ntp.org iburst
sourcedir /run/chrony-dhcp     # still let the home router chime in as a bonus

移動する機械が家具に依存してはいけない。

移動する機械が家具に依存してはいけない。

06記録 · 43 → ?

次に立つ床を選ぶ

決定 · ミラーに投票させる

さて本番のアップグレード。最初の問い:43 → 何へ? リリース日を記憶で信用しなかったので、ミラー網に投票させた。一般提供(GA)のリリースには完全なミラー艦隊がある。まだ Branched のものはまばらだ。メタリンクを探ると、F44 は健全なミラー数で、F45 はまだ薄く、プレリリースだった。43 → 44 に決まった——カレンダーの記憶ではなく、インフラが決めた。

そして誰もが速くクリックして通り過ぎる部分——GPG キーのインポート:

dnf system-upgrade
Importing key 0x6D9F90A6:
 Userid     : "Fedora (44) <fedora-44-primary@fedoraproject.org>"
 Fingerprint: 36F6 12DC F27F 7D1A 48A8 35E4 DBFC F71C 6D9F 90A6

これは、OS を上書きしようとしているすべてに対して、見知らぬ者の署名を信用するよう求められる瞬間だ。だから y を打つ前に確認した。キーファイルは公式の fedora-gpg-keys パッケージの所有で、rpm -V はディスク上で改変されていないと言い、指紋はプロンプトと一字一句一致した。キーを盲目的に受け入れないための三十秒。価値はある。

07記録 · dnf5

dnf5 の手がかり

細部 · dnf4 と dnf5

アップグレードの途中、小さなことが私に気づかせた。ステータス行が「Offline-Transaktion testen」と返ってきた——そのドイツ語の言い回しは、私の知る dnf の話し方ではなかった。この箱の dnf がいまや dnf5 で、古い dnf4 は dnf-3 バイナリとして潜んでいる、という証拠だった。その一つの細部が、以前の謎を説明した(漁った dnf4 の状態ディレクトリがどれも空だった理由——私は間違った時代を発掘していた)。そしてアップグレード自体について私を安心させた。dnf5 の system-upgrade では、パッケージの衝突は一つのファイルもダウンロードされる前、解決の時点で失敗する。そもそもステージングできたこと自体が、荷重を支える孤児たちが何もブロックしていなかった証拠だった。

08記録 · コミット

ステージング完了、消灯

実行 · 暗闇でアップグレード

ダウンロードは、停められ準備の整ったオフライントランザクションを残した。実際の入れ替えは、再起動後の最小環境で、本物のシステムを止めたまま行われる——稼働中のプログラムの下で書き換えられる生きたファイルシステムはない。フライト前チェックリスト:AC 電源で、作業を保存し、深呼吸。

shell
dnf system-upgrade download --releasever=44   # already done, days ago
dnf system-upgrade reboot                      # commit

すると画面は、あの素っ気ない、黒地に進捗バーのオフライン更新画面に入り、六千あまりのパッケージが行進し、機械はかつて一度もなったことのない Fedora へと再起動する。文字どおり、暗闇でのアップグレードだ。

09記録 · ヘルスチェック

きれいに着地したか?

検証 · 何も信用するな

着地した。そしてこのシリーズは穏やかに見える部分を信用しないことを教えてくれたので、ログイン画面の言い分を鵜呑みにしなかった。ヘルスチェック、順番に:

shell
cat /etc/fedora-release      # Fedora release 44 (Forty Four)
systemctl --failed           # 0 loaded units listed
dnf check                    # (silence — no broken deps, no duplicates)

失敗したサービスなし。依存の残骸なし。新しいカーネル(7.0.12-200.fc44)は一発で起動し、二つの fc43 カーネルがフォールバックとして残された。

アップグレードのログに、心臓が止まりかけた瞬間が一つあった:

upgrade log
systemd ... Failed to start jobs: Invalid argument
systemd ... Remote peer disconnected

純粋なアドレナリン——それがどこで走ったかを思い出すまでは。オフラインアップグレード環境で発火するパッケージスクリプトレットは、まだ完全には生きていないシステムマネージャと話せない。あれらのエラーは想定内で無害だ。穏やかに見えるログイン画面が真実を語り、恐ろしげなログが無実のほうだった。黒いワークスペース、そして完璧な音量キーと同じ教訓——今回はシステム管理者の衣装をまとって。

そしてメモのファイルが最も答えを欲しがっていた二つの問い:

  • chrony はこの跳躍を生き延びたか? 完全に。アップグレード後:Leap status: Normal、四つのプールサーバーすべてが完全到達、サブミリ秒のオフセット、そしてログはなお Using leap second list /usr/share/zoneinfo/leap-seconds.list と読む。欠けた空白のために戦ったあのディレクティブは、リリースアップグレードを無傷で通り抜けた。
  • 孤児たちはついに壊れたか? それ以上だ——いくつかは自分で解決した。残すか散々悩んだ fc42 の webkit 連鎖まるごと——webkit2gtk4.0、javascriptcoregtk4.0、vamp-plugin-sdk——は、古いライブラリをもう必要としない fc44 ビルドに静かに置き換えられ、ただ消えた。朝の入念な「これらは消すな」という探偵仕事は、アップグレード自体によって無意味にされた。fc42 に二つの居残りが残る(xl2tpd、hfsutils)。まだ fc44 の再ビルドがない純粋なリーフアプリ——まさに、残すべきだと私が主張した無害な種類の残り物だ。
10フィールドノート

過去の自分に言いたいこと(アップグレード編)

  1. メモを信じる前にログを読め。 「続ける」は、dnf history が実際に何をすでにやったかを教えるまで、何の意味もなかった。過去の自分は信頼できない語り手だ。トランザクションログは違う。
  2. 設定の編集は cat -A で検証せよ。 空白は噛みつくまで見えない。正しく見えるディレクティブと正しいディレクティブは、目が描画を拒む文字の分だけ違う。
  3. 再起動はリロードではなく、ファイルはプロセスではない。 私は、稼働中のデーモンが一度も読んでいない設定を「直して」いた。ディスク上のファイルだけでなく、常に生きたプロセスを確認せよ。
  4. 移動するノートの時計をリビングに固定するな。 fritz.box は場所だ。モバイルな機械には、行く先々のどこにでも存在する源を与えよ。
  5. GPG 指紋は信用する前に読め。 三十秒の確認は、一度も見ていない署名に root を渡すより勝る。
  6. 自分が実際にどの dnf を走らせているか知れ。 dnf4 と dnf5 は状態を別の場所に保ち、言い回しも違う。間違った時代のディレクトリを掘って、実際に時間を無駄にした。

このシリーズ全体の繰り返される教訓が、何度も自己主張してくる。このシステムでは何も自分のためには起きない、だからどの直しも実在し腑に落ちる——だが厄介事は、最初に指さす場所にはまずない。今回は、見えない一文字だった。デーモンが読んでいないファイルの中で、そこにないルーターに同期しながら。

11追記

やることのなかった後片付け

追記 · やることなし

リリースの残り物を拭き取る、きちんとした儀式がある——dnf distro-sync、はぐれパッケージをすべて新リリースに揃える。私はそれを満足のいく最後の一拍として取っておいた。あの fc43 の堆積を一つのカタルシスのトランザクションで一掃する、壮大な後片付けだ。

走らせた。少し考えて、こう言った:やることなし。

どの残り物も、すでに存在する最新のものだった。粛清するつもりだった孤児たちは、アップグレード中に自分で溶けたか、本当に入手可能な最新ビルドだった。並べておいた劇的な後片付けは、設計どおりのアンチクライマックスだった——私が見ていない間にシステムは静かに自分の後始末を済ませ、すでに掃き終えた部屋で私に箒を握らせたまま残した。

どこか似つかわしい。シリーズで最も怖い操作——OS を抉り、暗闇へ再起動する——はドラマもなく終わり、勝利の締めに取っておいた部分は no-op だった。黒い画面のパニック、幻の音量キー、自分を撃つ pkill——そして今度は、唯一の本当の傷が自分でつけた欠けた空白だったアップグレード。私はだんだん、機械はもとから問題ではなかったのではと疑い始めている。

コメント (0)

まだコメントがありません。最初のコメントを書いてみましょう!

コメントを書く