Don't dd on the fly at midnight
A man installed Linux with remote dd while something was running, this is how his natural cycle and filesystem broke down.
ZC is a 25-year-old. man, ☝️ presenting to the bootloader rescue shell, with broken GRUB configuration and a corrupted filesystem.
所以,本篇文章您將讀到:
誤入歧途;
初現端倪;
從從容容?
後知後覺!
連滾帶爬。
1. 前情提要
ZC,作為一個 NixOS 用戶,想給他的服務器安裝 NixOS,以享受可復現的便利性,滿足他對系統最極致的控制欲。
在既有 Linux 上安裝 NixOS 的方式(例如 nixos-anywhere)都仰賴於在 kexec 一個可以放進內存的發行版,NixOS 的大小最少也會有 1.5 GiB。然而 ZC 看了看他購買的服務器配置,才發現實例的內存很小,只有 1 GiB 不到,NixOS 發行版根本沒辦法放進 ZC 的服務器內存。
ZC 作為一個還多少有點偏執的 NixOS 用戶,不想用 nixos-infect 這種拖泥帶水的方式去安裝。畢竟裝完服務器會留下很多以前的文件,而且安裝過程照 README 所說未必穩定,he thought。於是當時 ZC 就決定照著一個他從網絡上看到的遠程安裝 NixOS 的博客教程進行了操作。
1.1. 誤入歧途
教程的大意是說,在本地利用 disko 生成一個磁盤鏡像,然後通過 SSH 上傳到遠程服務器,使用 dd 命令寫入目標磁盤,再用文件系統工具將分區的大小從鏡像大小擴充到磁盤的大小。當 ZC 看到教程裡的「磁盤鏡像上傳到 VPS」時,時間已經指向凌晨三點,困頓乏力的 ZC 在 VPS 既有的系統還在跑的時候,敲下了命令,將本地和服務器用 SSH 連接,並將數據通過 dd 發送到服務器。「之前一個服務器也是這樣,這次只不過是重複上一次的做法而已。」「能有什麼問題呢?」 he thought。他甚至還煞有介事地開了一個 VNC 遠程窗口,注視著服務器的一舉一動。
1.2. 從從容容?
這時,VNC 的 tty 輸出跳出了好幾行錯誤信息,提示原來系統的 ext4 文件損壞。ZC 知道 dd 是一個字節一個字節原封不動拷貝到目標文件(或目標設備)的程序,ext4 文件損壞可能代表著 dd 正在複寫舊 OS 文件系統的 metadata。「雖然錯誤看起來很多,時不時還會跳兩三個,但是全部覆蓋了應該就沒有問題了吧,只要重啟就好了。」he thought。
安裝完成後,NixOS 在他的服務器上成功運行了起來,唯獨內核一直在啟動時回報一個錯誤「btrfs ……」。可是再一次忙到凌晨三四點的 ZC 忽略了這些。系統起來了,he thought。安裝過程按照他「遵守」的教程完美地進行了,he thought。
1.3. 後知後覺
兩天後,ZC 在與其他 NixOS 用戶交流時想到自己的這次安裝體驗,他提到:
「我在 dd 的時候目標端的舊系統會時不時報錯文件系統錯誤,
「不知道有沒有么蛾子?
「想了感覺好奇怪,ssh 和 dd 這兩個東西調用文件系統和網絡 syscall 的時候不會碰 disk 嗎?
「萬一 swapped out 怎麼辦?
「dd 可是不長眼的!」
然而不長眼的是 ZC。直到其他用戶提醒他時,他才發現自己搞砸了。此時 ZC 回看教程才發現裡面寫了這樣一句話:
「将磁盘镜像上传到 VPS。
「在 VPS 上启动救援系统,或者 Alpine Linux 等轻量化系统。……」
ZC 立刻登入那台已經「穩定」運行 48 小時的機器,他本想 btrfs check,可搜索了一圈都沒辦法在系統正在運行時做這件事。於是他退而求其次,用 btrfs scrub 在文件系統沒有解掛時先做檢查。於是就有了下面的結果:
Scrub started: Fri Dec 12 16:59:11 2025
Status: finished
Duration: 0:00:03
Total to scrub: 2.54GiB
Rate: 867.67MiB/s
Error summary: csum=5
Corrected: 0
Uncorrectable: 5
Unverified: 0
And this brings us back to where we are now.
2. 連滾帶爬
2.1. 誤入歧途 Season 2
起初,ZC 打算通過 VPS 廠商給的 ISO 光盤鏡像接口,通過 URL 上傳一個緊急救援的 LiveCD 來實現 dd,結果廠商提供的接口無論如何都沒有成功,甚至嘗試了下載之後開 HTTP server,三十分鐘過去了,未果。
於是,ZC 開始聽從其他 NixOS 用戶建議,嘗試退而求其次,用 kexec 啟動 NixOS 和 Alpine Linux,他試了 nixos-images 以及照一些 StackOverflow 教程手動調整參數執行 kexec,但沒有成功,系統還反复重啟了不下十次,一個小時過去了,未果。
2.2. 羊入虎口
他在與 LLM 對話後決定改用新策略,直接把 Alpine 放在固定位置從 GRUB 啟動。本來 ZC 老老實實按照 Alpine wiki 安裝了 kernel 和 initramdisk,可是卻並不奏效,進入 Alpine Linux之後沒有網絡、更不用提 SSHd,他折騰了很久,一個小時過去了,未果。
半夜三點,ZC 再也無法繼續思考了,於是他把和 LLM 提供的 GRUB 一股腦放進系統。這次,按了重啟系統之後出現了一個陌生的界面,他一搜才知道,GRUB 讀取 grub.cfg 失敗,服務器進入了 grub shell,他啟動不了系統了。雖然 ZC 有一個備份的快照可以隨時啟用,但他想在 GRUB 裡嘗試力挽狂瀾,他又折騰了很久,甚至為了啟動之前安裝的 NixOS,手敲 /nix/store 的 sha256 目錄。但是,一個小時過去了,未果。
2.3. 自作聰明
ZC 最後放棄了在 GRUB 裡力挽狂瀾,他決定用舊的快照恢復系統。過了幾分鐘,系統恢復成了他安裝 NixOS 之前的 Debian。這次他再也不相信 AI 了,並且把搜索重心放在 Alpine Linux 的 RAM only 啟動,終於找到一篇寫有安裝方法的博客。吸收了上次的教訓,ZC 這次再也不敢漏看任何一行字了,他仔細跟著教程走完,Alpine Linux 也順利啟動了。網絡和 ssh 也已就緒。
於是,ZC 立刻在本地搓了一個 NixOS 鏡像。為了減少傳輸時間,他把不需要的模組註釋掉,以便生成的鏡像文件盡可能的小。完成這些動作,ZC 立刻把鏡像 SSH 了過去。
然而,就是這一步註釋出了問題。ZC 把 OpenSSH 的服務守護進程從他的模組中註釋掉了,生成的 NixOS 鏡像文件甚至並不包含任何 sshd 二進制文件。雖然後來跟群友提起此事時复盤才發現可以開 nix-shell -p openssh 再從本地推配置上去,但當時沒傳 SSHd 的打擊已經讓他再起不能,ZC 停止了思考。這一次,一個小時過去了,未果。
於是,他再一次刷回原來的快照,再一次照著教程一步一步走,VNC 的操作用不了複製貼上,於是他只能一個鍵一個鍵地敲。他邊敲邊想,
「為什麼一開始要把 OpenSSH 服務從機器配置剝離出來呢?
「為什麼要自說自話不擇手段地把鏡像減小呢?為什麼 VNC 沒有辦法複製貼上呢?
「為什麼網速那麼慢?
「為什麼花了那麼久的時間?
「為什麼我那麼愚蠢?」
終於,最後他在清晨天快亮的時候裝上了一個文件系統不會再爆炸的 NixOS。
3. 尾聲
「啊!終於可以正常推配置到服務器了!」ZC 長舒一口氣,熟練地在本地打出 NixOS rebuild 推送到服務器的命令,等待服務器切換到新的系統構建。
「…… Copy failed: No space left on the device.」
「?」
「糟糕了!」ZC 的疑問變成了驚嚇。 他趕緊嘗試刪除一些文件,卻發現操作系統是新構建的,根本沒有文件可刪。
「……對了,只要我把推過去的還沒有拷貝的文件全部資源回收了,都可以解決的吧!」
「nix-collect-garbage,Enter!」
「IO error.」
「……欸,騙人的吧……」
(完)
评论
登录后方可评论
登录/注册