VPS 複製後 machine-id 重複怎麼修正?
VPS 複製後若兩台主機共用同一個 /etc/machine-id,可能爭用 DHCP 租約。安全重建 machine ID,並在建立 golden image 前清空檔案。
/etc/machine-id 的用途,以及重複值為何重要
複製出的 VPS 會與來源伺服器使用相同的 /etc/machine-id,但該值應只屬於一個安裝環境。修正方式只需執行 4 個命令:清空該檔案、移除 D-Bus 的副本(如果它是一般檔案)、重新產生 machine ID,然後重新開機。重新開機是最常被略過的步驟,也是讓變更生效的步驟。
/etc/machine-id 儲存以換行結尾、由小寫字母組成的 32 字元十六進位字串。解碼後,這是 16 位元組(128 位元)的值。machine-id(5) 手冊頁稱其為機密資訊,並表示不得將它暴露在網路上,因為任何讀取它的對象之後都能再次辨識這台機器。它會在系統安裝時寫入一次,之後不會再被變更。
這裡有 3 個容易混淆的識別碼,因此有必要分開說明。主機名稱是由你選擇的標籤,可以隨時變更。/sys/class/dmi/id/product_uuid 中的 DMI(桌面管理介面)產品 UUID 來自 hypervisor,只有 root 可以讀取。machine ID 是第三個識別碼:它由作業系統產生,這台主機上的每個使用者都能讀取。
實際上哪些元件會讀取 machine ID
DHCP 用戶端識別碼。 這是最容易造成問題的項目。systemd.network(5) 在 [DHCPv4] 區段中說明,ClientIdentifier= 預設為 duid;這會傳送由 IAID 與 DUID(DHCP unique identifier)組成的 RFC 4361 用戶端識別碼。networkd.conf(5) 說明預設的 DUID 類型為 vendor,其中 DUID 值會使用 43793 作為廠商識別碼(systemd),並以 machine ID 的內容雜湊產生。DHCPv6 也使用相同的 DUID。兩個 clone 若具有相同的 machine ID,其雜湊結果就會相同;如果介面名稱也相同,兩者傳送的用戶端識別碼會逐位元組完全一致。DHCP 伺服器因此會將兩台主機視為一個用戶端,並為兩台主機提供相同的租約。常見現象是 IP 位址在兩台伺服器之間移動,或其中一台伺服器在另一台續租時失去 IP 位址。
journald。 Journal 檔案位於 /var/log/journal/<machine-id>/。該目錄的名稱就是 machine ID。若將兩個 clone 的 journal 傳送至同一個收集器,檔案就會寫入同一個目錄,並被視為同一台主機的日誌。
D-Bus。 /var/lib/dbus/machine-id 是這種檔案格式的起源。在 Debian 和 Ubuntu 上,它是指向 /etc/machine-id 的符號連結。在某些系統上,它是獨立的實體檔案,內含自己的副本;而該副本正是下述程序中的陷阱。
每台主機上的代理程式。 監控代理程式、授權檢查工具、資產清查工具和備份用戶端通常會將 machine ID 作為預設的主機識別碼,因為它穩定且不需要設定。兩台伺服器回報相同的識別碼,會導致監控指標合併成一條序列,或讓一個授權席次涵蓋兩台機器。請確認代理程式如何衍生主機識別碼,不要假設它使用 hostname。
如何判斷是否存在重複的 machine ID
在兩台伺服器上執行以下命令,並比較輸出結果。
cat /etc/machine-id
ls -l /var/lib/dbus/machine-id
sudo cat /sys/class/dmi/id/product_uuid兩台仍在運作的伺服器具有相同的 machine ID,表示其中一台是從另一台複製而來。如果偏好只使用一個命令,hostnamectl 會在其 Machine ID: 行輸出相同的值。
ls -l 的結果會決定下一步。符號連結如下所示:
lrwxrwxrwx 1 root root 15 Aug 21 09:12 /var/lib/dbus/machine-id -> /etc/machine-id以 -rw-r--r-- 開頭的行表示這是儲存舊 ID 副本的實體檔案。你必須將其移除,因為 systemd-machine-id-setup 在執行其他操作前會先讀取該檔案。
product UUID 也很重要。systemd-machine-id-setup(1) 會先使用 KVM UUID,無法使用時才改為隨機產生。因此,如果供應商提供給兩個複製執行個體相同的 SMBIOS(system management BIOS)UUID,重新產生後仍會得到兩個相同的 machine ID。兩台伺服器的 product UUID 不同,就不必擔心這個問題。
複製的 VPS 重新產生 machine ID
執行順序很重要。systemd-machine-id-setup(1) 說明,如果系統已設定有效的 D-Bus machine ID,就會複製該 D-Bus machine ID,並用它初始化 /etc/machine-id。請保留真正的 /var/lib/dbus/machine-id,否則重新產生的會是你原本想移除的完全相同值。
sudo truncate -s 0 /etc/machine-id
sudo rm -f /var/lib/dbus/machine-id # only if ls -l showed a real file
sudo systemd-machine-id-setup
sudo ln -sf /etc/machine-id /var/lib/dbus/machine-id
cat /etc/machine-id必須先截斷檔案,因為該工具只會在檔案不存在或為空時執行;如果檔案已包含有效 ID,則不會進行任何操作。systemd-machine-id-setup 會將執行結果輸出至 standard error。在 KVM VPS 上,通常會看到:
Initializing machine ID from KVM UUID.Initializing machine ID from random generator. 表示找不到 hypervisor UUID。只要 cat /etc/machine-id 現在輸出的值與另一台伺服器不同,兩種結果都可以接受。
此符號連結可讓 D-Bus 與 systemd 使用同一個值。如果偏好使用獨立的實體檔案,請改為執行 sudo dbus-uuidgen --ensure:檔案不存在時,該指令會以新的 UUID 建立檔案。如果未安裝 dbus,根本不存在 /var/lib/dbus 目錄,ln 會以 No such file or directory 失敗,此時可以略過這兩行。
接著重新開機。
sudo reboot為什麼重新開機不是可選項目
所有已讀取舊值的程序仍會繼續使用該值。sd_id128_get_machine() 會將 ID 快取在呼叫程序中,因此執行中的 daemon 不會發現檔案已變更。journald 已經開啟 /var/log/journal/<old-id>/system.journal,並持續將內容附加到其中。systemd-networkd 在啟動時就計算出 DUID,之後每次續租都會繼續傳送舊的用戶端識別碼;這通常正是你原本要修正的問題。D-Bus 也會在啟動時讀取 ID。你可以逐一重新啟動服務,但一定會漏掉某個服務,而且 PID 1 也仍持有舊值。
重新開機後,檢查以下兩部分:
cat /etc/machine-id
ls /var/log/journal//var/log/journal/ 現在會包含以新 ID 命名的第二個目錄,新的項目會寫入該目錄。單純執行 journalctl 只會讀取目前機器的目錄,因此複製前的歷史記錄會從預設檢視中消失。這些記錄仍在磁碟上:journalctl --merge 會讀取所有 journal 目錄,包括舊目錄。確認不再需要舊日誌後,再刪除舊目錄。
這也是你無法在容器中演練此程序的原因。容器會共用主機核心,且不會啟動自己的 PID 1;而重新開機正是此程序的重點。請按照正式環境中的流程測試:複製一台 VM、執行命令、重新開機,然後將 ID 與來源機器進行比對。
在建立複製品前截斷,不要等到複製後才處理
逐一修正複製品可以解決問題。修正映像檔更好,因為每台從錯誤 snapshot 還原的伺服器都會繼承相同的值。請在關閉範本前,將這項工作作為最後一個步驟。
sudo truncate -s 0 /etc/machine-id
sudo rm -f /var/lib/dbus/machine-id
sudo ln -s /etc/machine-id /var/lib/dbus/machine-id
sudo shutdown -h now清空檔案,但不要刪除檔案。machine-id(5) 建議多台機器共用的映像檔使用空檔案,因為保留空檔案可讓暫存檔在映像檔以唯讀方式使用時,透過 bind mount 掛載到原始檔案上。對唯讀的 /etc 而言,開機時產生的 ID 會儲存在該暫存檔中,檔案系統可寫入後,systemd-machine-id-setup --commit 會將該 ID 寫入檔案。
請預先規劃一項副作用:未填入的 machine ID 會讓下一次開機被視為首次開機,因此帶有 ConditionFirstBoot=yes 的單元會在該次開機執行,之後的每次開機都會略過。建立範本前,先使用 grep -rl ConditionFirstBoot /usr/lib/systemd/system/ 查看映像檔會執行哪些內容。
範本與 snapshot 是不同的物件,而這項差異會決定 identity 是否一併複製。範本是刻意準備的建置產物,而 snapshot 是某台執行中伺服器在特定時間點的複本,會連同其資料一起保留該伺服器的 identity。
為什麼雲端映像檔能正確處理,而你的 snapshot 不行
Distribution cloud images 的設計目的是供複製使用,因此其中的 machine ID 預設不會填入值,並會在第一次開機時產生。cloud-init 為此提供了明確的步驟。cloud-init clean --machine-id 會在 systemd 系統上將 /etc/machine-id 設為字面值 uninitialized。cloud-init CLI 參考文件也將此列為複製 golden image 的最佳做法,因此該映像檔下次開機時會產生唯一的 machine ID。
你自行建立的 snapshot 則不同。按下 snapshot 時,該檔案已經填入值,因此從中還原的每台伺服器都會帶有相同的值,而還原流程也不會清除它。這與將執行中的伺服器移轉到新的 VPS屬於同一類問題:複製內容會保持一致,但你不希望複製的正是該身分資訊。
複製還會重現哪些內容
- SSH host keys。
/etc/ssh/ssh_host_*也會一併複製,因此兩台伺服器會向用戶端呈現相同的指紋。刪除這些檔案並執行sudo ssh-keygen -A;在 Debian 和 Ubuntu 上則執行sudo dpkg-reconfigure openssh-server。之後,用戶端會警告主機金鑰已變更,這是正確的行為。 - 主機名稱。 使用
sudo hostnamectl set-hostname app02設定,然後確認/etc/hosts仍能解析新的名稱。 - 靜態網路設定。 具有靜態位址的伺服器一旦啟動複本,就會立即與原始伺服器發生位址衝突。複本連上網路前,請先查看
/etc/netplan/。 - 系統時間。 還原的快照會以建立快照時的時間繼續執行。還原的 VPS 發生大幅時間跳動 會導致 TLS 憑證驗證失敗,並打亂日誌順序,直到時間同步追上為止。
也請在複本上執行新 VPS 的前 10 分鐘檢查清單。複製的伺服器會繼承來源伺服器的使用者帳號、SSH 金鑰、防火牆規則與排程工作,但這些內容都尚未針對複本即將執行的工作進行檢查。
FAQ
修改 /etc/machine-id 後,是否必須重新開機?
是。程序只會讀取一次 machine ID,之後便會快取該值,因此新值不會套用至已在執行的程序。journald 會繼續寫入以舊 ID 命名的 journal 目錄,而 DHCP client 也會繼續傳送由舊值衍生的 client identifier;這通常正是您修改該值的原因。個別重新啟動服務只能修正其中部分問題,因為 PID 1 也保留著舊值。請重新開機,然後使用 cat /etc/machine-id 確認,並與另一台伺服器的值比較。
/etc/machine-id 是否等同於硬體 UUID?
否。/sys/class/dmi/id/product_uuid 中的 DMI product UUID 來自 hypervisor,且只有 root 可讀取。machine ID 由作業系統產生,儲存在任何使用者都能讀取的純文字檔案中。兩者只有單向關聯:在 KVM guest 上,當沒有可複製的 D-Bus ID 時,systemd-machine-id-setup 會使用 hypervisor UUID 作為產生新 machine ID 的種子。如果兩個 clone 共用相同的 product UUID,便會重新產生相同的 machine ID。因此,在確認結果前,也要比較該檔案。
應刪除 /etc/machine-id,還是保留空檔案?
準備 image 時,請保留空檔案。machine-id(5) 偏好空檔案,因為當 image 以唯讀 /etc 執行時,systemd 可以在該檔案上方繫結掛載暫存檔案。可寫入的系統可以刪除該檔案,部分 clone script 也會採用這種方式,但保留空檔案是較安全的預設做法。cloud-init 會基於相同目的,將單字 uninitialized 寫入該檔案。
為什麼兩台 clone 伺服器取得相同的 DHCP 位址?
因為兩台伺服器傳送了相同的 client identifier。systemd-networkd 對 DHCPv4 預設使用 ClientIdentifier=duid,而預設 DUID 是由 /etc/machine-id 的雜湊值建立,因此在同樣保留介面名稱的 clone 上,相同的 machine ID 會產生相同的 identifier。DHCP server 會依該 identifier 進行比對,將兩個請求視為同一個 client,並發出租約。請為每台伺服器設定各自的 machine ID,然後重新啟動兩台伺服器。如果 server 仍提供舊位址,請直接在 DHCP server 上清除過期的租約。