HN 日本語サマリー

← 一覧へ戻る
インフラ・DevOps

ホームサーバーの死と再生

The death and rebirth of my home server (sgt.hootr.club)

125 pointsby steinuil81 コメント

要約

筆者のホームサーバーのmicroSDカードが予期せず故障したことをきっかけに、システム構成を見直しました。SDカードへの書き込みを最小限に抑え、zramをスワップに使用し、外部HDDを冗長化されたBtrfs RAID1プールに移行することで、信頼性とデータ安全性の向上を目指しました。

全文翻訳

数晩前、パートナーと私は特定のLinux ISOをインストールしたい気分でした。Red Letter Systemsの連中がそれを良く話していて、boundingboxdで良いレビューがあり、パートナーはインストール予告編を気に入っていました。 私はPiに既に持っているか確認するためにスマホでTremotesfを開きましたが、トレントリストが読み込めませんでした。SMBマウントもマウントできませんでした。SSHもできませんでした。 その場でデバッグセッションを開始して夜を台無しにしたくなかったので、NixOSの設定に最近加えた変更による単純な問題だと片付け、翌日確認することにしました。 翌日 私はクローゼットにある専用のデバッグディスプレイにPiを接続し、指を鳴らし、「デバッグの時間だ」かそれに類する言葉を言って、ブリック(壊れたデバイス)を再起動しました。 現在のNixOS世代の起動が始まり、カーネルがロードされて…そこでハングしました。それ以上の出力はありませんでした。前の世代も同様でした。さらに検査するためにmicroSDカードを取り出しました。 最初の明白なステップはfsckを実行することでした。数日前に建物全体で電力が不可解に失われており、ブートプロセスに重要なファイルを含むブロックの1つが書き込まれている最中に停電が発生したのかもしれませんでしたか?いずれにせよ、チェックする価値はありました。fsck.ext4は多くのエラーを報告し、修正しました。再度実行するとFSはクリーンだと報告されましたが、カードを取り外して再度挿入するとさらにエラーが発生しました… 長話になりますが、カードはおそらく死んでおり、停電が単に止めを刺しただけでした。Fキーを押して敬意を表しましょう。 棚卸し このRaspberry Pi 4Bはほぼ24時間年中無休で何年も稼働していたので、これは驚きではありませんでした。私の理解では、SDカードは失敗し始める前に比較的少ない書き込みサイクルしか維持できません。驚いたのは、私がどれほどひどく扱っていたかを考えると、私のカードが問題なく so long 稼働できたことです。microSDはルートFSとしてマウントされており、書き込みを最小限に抑えるための対策はほとんど講じていませんでした。 すべてを考慮すると、それほど多くは失いませんでした。重要なものはほとんど外部HDDにあり、設定はほぼすべてNixOSの設定にありました。microSDカード上にあり、バックアップがなかった唯一のものは、.torrentファイルとNavidrome、Jellyfin、slskdのキャッシュであり、これらはすべて簡単に再生成できました。Immichのデータは外部ドライブに安全にバックアップされており、定期的なバックアップを取っていたため、何も失われていません。 私はこれまで「プライマリ」ドライブがこのように壊れたことはありませんでした。最悪だったのは、Linuxを初めてインストールしようとしたときにMacBookのメインOS Xパーティションを誤って削除してしまった10代の頃以来、私の心の中にあった、死んだフラッシュドライブがいくつかあったことですが、今ではそれ以上に悪化しているので、強力な予防策を講じたいと思いました。 新しいセットアップの設定 いくつかの点を念頭に置いてホームサーバーを再構築することにしました。 microSDへの書き込みを最小限に抑えるように努める。 外部ハードドライブに保存されているデータに冗長性を持たせる。 すべて重要なものをバックアップする。 スワップ、/tmp、その他の揮発性データ 8GBのRAMを搭載したPiであってもスワップは有用ですが、長持ちさせたいのであればmicroSDへのスワップは良い考えだとは思いません。そのため、外部HDDへのスワップ(非常に遅い)を行うか、zramを使用することができます。 zram(以前はcompcacheと呼ばれていました)は、RAM内に圧縮されたブロックデバイスを作成するためのLinuxカーネルモジュールです。つまり、オンザフライでディスク圧縮を行うRAMディスクです。 zramの一般的な用途はスワップまたは/tmpです。スワップのためにzramを有効にすることを選択しましたが、/tmpについてはそうしませんでした。これは通常のRAMディスクのままにしました。これらの2つの機能がどのように相互作用するかについては100%確信がありませんが、カーネルが/tmpのコールドページを圧縮スワップにスワップアウトする方法と時期を判断させることにしました。 { # メモリ内圧縮スワップデバイスを有効にする zramSwap.enable = true; # /tmp に RAM ディスクを使用する boot.tmp.useTmpfs = true; } また、ジャーナルログをディスクではなくメモリに記録することを選択しました。メモリにログを記録し、ディスクに時折フラッシュする(その場合、/var/logをHDD上のサブボリュームにマウントする)ことが可能かどうかを調査する必要がありますが、まだその部分は解明していません。 { # ジャーナルログをメモリに /run/log/journal に保存する services.journald.storage = "volatile"; } microSDカードは/にマウントされたままですが、ファイルにアクセスするたびに書き込みが発生し、あまり役に立たないと思われるため、atimeを無効にしました。 { fileSystems."/" = { device = "/dev/disk/by-label/takodachi"; fsType = "ext4"; options = [ "noatime" ]; }; } 外部HDD もしあなたが2026年現在に生きているなら、新しいドライブ(またはRAM、GPU、AIサイコシスに夢中な経営者の懐を潤すデータセンター構築に使用できるものすべて)を購入するのに良い時期ではないことを知っているでしょう。そして、持っているものを活用して工夫を凝らす方が良いアイデアです。 私はあらゆる種類、サイズ、年齢のドライブを大量に持っています。それらはほとんど、保持したい古いデータのコールドストレージとして扱っています。それらはジャンク引き出しのデジタル版です。私は常にそれらをより高次の目的のために燃やしたいと思っていましたが、これまで適切な機会を見つけられませんでした。私はこれらの古い2.5インチHDDを2台、山から取り出しました。 最初は500GBのドライブで、Windowsファイルシステムが含まれていました。これはパートナーのラップトップで使用されていました。これは、別の時代のビジネスマシンからの3KGの巨人で、真にポータブルなマシンというよりは、簡単に別のデスクに移動でき、時折家に持ち帰ることができるデスクトップとして意図されていました。まず、死にかけているラップトップへの慈悲としてHDDをSSDに交換しました。最終的に、パートナーにNixOSを実行するSurface Pro 5を設定し、彼らは古いラップトップとHDDを私に遊びのために親切にくれました。 2番目は320GBのドライブで、…Home Assistantのインストールが含まれているようでしたか?それがどのようにしてそこにあったのか全く分かりません。とにかく、これはAppleロゴが付いたHitachiドライブだったので、私の2006年の白いプラスチック製MacBookか、それに類する時代のMac miniの中にあったはずです。 これらの歴史の断片2つをドッキングステーションに接続し、raid1レプリケーションを備えたbtrfsプールを作成しました。私はそれを適切にポンコツと名付けました。 sudo mkfs.btrfs --data raid1 --metadata raid1 --label ponkotsu /dev/sdX /dev/sdY btrfsを選択したのは、私が知っているもの(デスクトップとラップトップで実行しています)、サブボリュームが好きだからです(後述)、そしてプールの柔軟性が好きだからです。プールのトポロジーを事前に計画する必要はなく、いつでもドライブをプールに追加でき、サイズが同じである必要はありません。 サブボリューム 私の計画は、サービスごとに1つのbtrfsサブボリュームを持つことでした。そして、それを宣言的に行うために、autosubvolモジュールを作成しました。設定は次のようになります。 { services.autosubvol = { enable = true; disks.ponkotsu = { device = "/dev/disk/by-uuid/<pool uuid>"; subvolumes.immich = { mountPoint = "/var/lib/immich"; mountOptions = [ "noatime" ]; requiredBy = [ "immich-server.service" ]; }; }; }; } このモジュールはいくつかのsystemdユニットを作成します。 各ディスクの/run/btrfs-roots/<name>への.mountユニット。 サブボリュームが存在するかどうかを確認し、存在しない場合は作成するスクリプトを持つautosubvol-ensure-<disk>-<subvolume>.service oneshotユニット。サブボリュームの.mountユニットがそれに依存できるように、RemainAfterExit=trueで設定されています。 各サブボリュームの.mountユニット。指定されたmountPointにマウントされます。autosubvol-ensure .serviceに依存し、それがrequiredByであるユニットにBefore=とRequiredBy=を設定します。 これにより、各サービスモジュールが非常にクリーンに見えると思います。 { config, ... }: { services.immich = { enable = true; # ... }; services.autosubvol.disks.ponkotsu.subvolumes.immich = { mountPoint = config.services.immich.mediaLocation; mountOptions = [ "noatime" ]; requiredBy = [ "immich-server.service" ]; }; } 念のため、/var/cache用のサブボリュームも作成しました。 { fileSystems."/var/cache" = { device = "/dev/disk/by-uuid/..."; fsType = "btrfs"; options = [ "subvol=@cache" "noatime" "nofail" ]; }; } バックアップ Immichを設定したとき、私はa