SSD Nodes Learn 🎉 VPS od $5.50/mies.
Przewodniki Matt ConnorAutor: Matt Connor

Jak naprawić zduplikowany machine-id po klonowaniu VPS

Zduplikowany plik /etc/machine-id powoduje konflikty w dzierżawie DHCP. Dowiedz się, jak bezpiecznie zregenerować identyfikator i poprawnie przygotować obraz systemu do klonowania.

Czym jest /etc/machine-id i dlaczego duplikaty stanowią problem

Sklonowany VPS uruchamia się z tym samym /etc/machine-id, co serwer źródłowy, podczas gdy wartość ta powinna być unikalna dla każdej instalacji. Rozwiązanie wymaga wykonania czterech poleceń: opróżnienia pliku, usunięcia kopii D-Bus, jeśli jest ona zwykłym plikiem, wygenerowania nowej wartości i restartu. Restart jest krokiem często pomijanym, a to właśnie on sprawia, że zmiany wchodzą w życie.

/etc/machine-id przechowuje 32-znakowy ciąg szesnastkowy zakończony znakiem nowej linii. Po zdekodowaniu jest to wartość 16-bajtowa (128-bitowa). Strona podręcznika systemowego machine-id(5) określa ten identyfikator jako poufny i wskazuje, że nie powinien być on ujawniany w sieci, ponieważ każdy podmiot, który go odczyta, może później zidentyfikować daną maszynę. Wartość ta jest zapisywana jednorazowo podczas instalacji systemu i nie zmienia się w późniejszym czasie.

W tym kontekście często mylone są trzy rodzaje identyfikatorów, dlatego warto je rozróżnić. Nazwa hosta (hostname) to etykieta, którą użytkownik wybiera i może w dowolnym momencie zmienić. Identyfikator produktu DMI (desktop management interface) w /sys/class/dmi/id/product_uuid pochodzi z hypervisora i jest dostępny do odczytu tylko dla użytkownika root. Identyfikator maszyny (machine ID) jest trzecim z nich: generuje go system operacyjny i każdy użytkownik w systemie może go odczytać.

Co faktycznie odczytuje machine ID

Identyfikator klienta DHCP. To jest najbardziej problematyczny element. Dokumentacja systemd.network(5) opisuje ClientIdentifier= w sekcji [DHCPv4] jako domyślnie ustawione na duid, co wysyła identyfikator klienta zgodny z RFC 4361, zbudowany z IAID oraz DUID (DHCP unique identifier). Dokumentacja networkd.conf(5) określa domyślny typ DUID jako vendor, gdzie wartość DUID jest generowana przy użyciu 43793 jako identyfikatora dostawcy (systemd) oraz haszowanej zawartości machine ID. Protokół DHCPv6 używa tego samego DUID. Dwie klony z tym samym machine ID generują ten sam hasz DUID, a jeśli zachowały również tę samą nazwę interfejsu, wysyłają identycznie wyglądający identyfikator klienta. Serwer DHCP widzi wtedy jednego klienta zamiast dwóch i oferuje obu maszynom ten sam dzierżawiony adres. Objawem jest adres IP, który przeskakuje między dwoma serwerami, lub sytuacja, w której jeden serwer traci adres, gdy drugi go odnawia.

journald. Pliki dziennika znajdują się w /var/log/journal/<machine-id>/. Katalog jest nazwany bezpośrednio na podstawie tego identyfikatora. Przesłanie dzienników z dwóch klonów do jednego kolektora spowoduje, że trafią one do tego samego katalogu i będą odczytywane jako jeden host.

D-Bus. /var/lib/dbus/machine-id to miejsce, w którym ten format pliku powstał. W systemach Debian i Ubuntu jest to dowiązanie symboliczne do /etc/machine-id. W niektórych systemach jest to oddzielny, rzeczywisty plik zawierający własną kopię, a ta kopia stanowi pułapkę w poniższej procedurze.

Agenci działający na hoście. Agenci monitorujący, mechanizmy sprawdzania licencji, narzędzia inwentaryzacyjne oraz klienci kopii zapasowych często używają machine ID jako domyślnego identyfikatora hosta, ponieważ jest on stabilny i nie wymaga konfiguracji. Dwa serwery zgłaszające tę samą tożsamość oznaczają jedną scaloną serię metryk lub jedną licencję przypisaną do dwóch maszyn. Sprawdź, w jaki sposób agent wyznacza identyfikator hosta, zamiast zakładać, że używa on nazwy hosta (hostname).

Jak sprawdzić, czy występuje duplikat

Uruchom poniższe polecenie na obu serwerach i porównaj wyniki.

cat /etc/machine-id
ls -l /var/lib/dbus/machine-id
sudo cat /sys/class/dmi/id/product_uuid

Identyczne identyfikatory maszyn na dwóch działających serwerach oznaczają, że jeden został sklonowany z drugiego. hostnamectl wyświetla tę samą wartość w linii Machine ID:, jeśli preferujesz jedno polecenie.

Wynik ls -l decyduje o kolejnym kroku. Dowiązanie symboliczne wygląda następująco:

lrwxrwxrwx 1 root root 15 Aug 21 09:12 /var/lib/dbus/machine-id -> /etc/machine-id

Linia rozpoczynająca się od -rw-r--r-- oznacza, że jest to rzeczywisty plik zawierający własną kopię starego identyfikatora. Należy go usunąć, ponieważ systemd-machine-id-setup odczytuje go przed wykonaniem jakichkolwiek innych operacji.

Ważny jest również identyfikator UUID produktu. systemd-machine-id-setup(1) używa identyfikatora UUID KVM przed przejściem do losowego generowania, więc jeśli dostawca nadał obu klonom ten sam identyfikator UUID SMBIOS (system management BIOS), ponowne wygenerowanie spowoduje uzyskanie dwukrotnie tego samego identyfikatora maszyny. Różne identyfikatory UUID produktu na obu maszynach oznaczają, że w tym zakresie nie ma powodów do obaw.

Generowanie nowego identyfikatora maszyny na sklonowanym VPS

Kolejność działań ma znaczenie. systemd-machine-id-setup(1) stanowi, że jeśli w systemie skonfigurowano już poprawny identyfikator maszyny D-Bus, jest on kopiowany i używany do inicjalizacji /etc/machine-id. Pozostawienie istniejącego /var/lib/dbus/machine-id spowoduje wygenerowanie tej samej wartości, którą próbowano usunąć.

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

Wcześniejsze wyczyszczenie pliku jest wymagane, ponieważ narzędzie działa tylko wtedy, gdy plik nie istnieje lub jest pusty; nie podejmuje żadnych akcji, jeśli plik zawiera już poprawny identyfikator. systemd-machine-id-setup raportuje wykonane operacje na standardowe wyjście błędów. Na VPS typu KVM zazwyczaj widoczny jest komunikat:

Initializing machine ID from KVM UUID.

Initializing machine ID from random generator. to komunikat wyświetlany, gdy identyfikator UUID hypervisora jest niedostępny. Oba wyniki są poprawne, o ile cat /etc/machine-id zwraca teraz wartość inną niż na pozostałych serwerach.

Dowiązanie symboliczne zapewnia spójność wartości dla D-Bus oraz systemd. Jeśli preferowany jest osobny, rzeczywisty plik, należy wykonać sudo dbus-uuidgen --ensure: tworzy on plik z nowym UUID, jeśli plik jeszcze nie istnieje. Jeśli pakiet dbus nie jest zainstalowany, katalog /var/lib/dbus nie istnieje, ln kończy się błędem No such file or directory i oba te kroki można pominąć.

Następnie należy zrestartować system.

sudo reboot

Dlaczego restart nie jest opcjonalny

Każdy proces, który odczytał starą wartość, nadal z niej korzysta. sd_id128_get_machine() buforuje ID wewnątrz procesu wywołującego, więc działający demon nigdy nie wykryje zmiany w pliku. journald ma już otwarte /var/log/journal/<old-id>/system.journal i kontynuuje dopisywanie do niego danych. systemd-networkd wyznaczył swój DUID podczas uruchomienia i przy każdym odnowieniu wysyła stary identyfikator klienta, co zazwyczaj jest dokładnie tą awarią, którą użytkownik chce naprawić. D-Bus również odczytał swoje ID podczas startu. Można restartować usługi pojedynczo, jednak zawsze pominie się jedną z nich, a PID 1 również przechowuje starą wartość.

Po restarcie sprawdź obie części:

cat /etc/machine-id
ls /var/log/journal/

/var/log/journal/ zawiera teraz drugi katalog nazwany zgodnie z nowym ID, do którego trafiają nowe wpisy. Standardowe journalctl odczytuje tylko katalog bieżącej maszyny, więc historia sprzed klonowania znika z domyślnego widoku. Dane nadal znajdują się na dysku: journalctl --merge odczytuje każdy katalog dziennika, w tym stary. Usuń stary katalog, gdy będziesz mieć pewność, że te logi nie są już potrzebne.

Dlatego właśnie nie można przeprowadzić tej procedury w kontenerze. Kontener współdzieli jądro hosta i nigdy nie uruchamia własnego PID 1, a restart jest kluczowym elementem całego procesu. Przetestuj to w sposób, w jaki odbywa się to na produkcji: sklonuj maszynę wirtualną, wykonaj polecenia, zrestartuj system, a następnie porównaj ID z maszyną źródłową.

Wyzeruj plik przed wykonaniem snapshotu, nie po klonowaniu

Naprawianie każdego klona z osobna jest skuteczne. Lepszym rozwiązaniem jest jednak poprawienie obrazu, ponieważ każdy serwer przywrócony z błędnego snapshotu dziedziczy tę samą wartość. Należy wykonać tę czynność jako ostatnią przed wyłączeniem maszyny wzorcowej (template).

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

Opróżnij plik. Nie usuwaj go. machine-id(5) zaleca pozostawienie pustego pliku w obrazach używanych na wielu maszynach. Pusty plik w docelowej lokalizacji umożliwia zamontowanie (bind-mount) pliku tymczasowego w miejsce właściwego, gdy obraz jest używany w trybie tylko do odczytu. Na systemie plików /etc w trybie tylko do odczytu, identyfikator wygenerowany podczas startu systemu jest przechowywany w tym pliku tymczasowym, a systemd-machine-id-setup --commit zapisuje go trwale, gdy system plików staje się zapisywalny.

Należy uwzględnić jeden efekt uboczny: pusty identyfikator maszyny oznacza, że kolejny start systemu zostanie potraktowany jako pierwszy start. Jednostki posiadające ConditionFirstBoot=yes zostaną uruchomione przy tym starcie i pominięte przy wszystkich kolejnych. Przed utworzeniem szablonu sprawdź, co zostanie uruchomione w obrazie za pomocą grep -rl ConditionFirstBoot /usr/lib/systemd/system/.

Szablon (template) i snapshot to różne obiekty, a różnica ta decyduje o tym, czy tożsamość zostanie skopiowana. Szablon to artefakt budowy przygotowany celowo, podczas gdy snapshot jest kopią działającego serwera w określonym punkcie czasowym i przenosi tożsamość tego serwera wraz z jego danymi.

Dlaczego obrazy chmurowe radzą sobie z tym poprawnie, a własne migawki nie

Dystrybucyjne obrazy chmurowe są przygotowane do klonowania, dlatego są dostarczane z pustym identyfikatorem maszyny, który jest uzupełniany przy pierwszym uruchomieniu. Narzędzie cloud-init posiada udokumentowany krok przeznaczony dokładnie do tego celu. cloud-init clean --machine-id ustawia /etc/machine-id na ciąg znaków uninitialized w systemach z systemd, a dokumentacja CLI cloud-init wskazuje to jako najlepszą praktykę przy klonowaniu obrazu wzorcowego, dzięki czemu kolejne uruchomienie tego obrazu generuje unikalny machine ID.

Własna migawka działa inaczej. Plik był już uzupełniony w momencie tworzenia migawki, więc każdy serwer przywrócony z niej posiada tę samą wartość, a proces przywracania nie czyści tego pola. Jest to ten sam rodzaj problemu, co przenoszenie działającego serwera na nowy VPS, gdzie kopia jest wierna, a tożsamość jest elementem, którego nie chciano kopiować.

Co jeszcze powiela klonowanie

  • Klucze hosta SSH. Plik /etc/ssh/ssh_host_* również jest kopiowany, przez co oba serwery prezentują klientom ten sam odcisk klucza. Należy usunąć te pliki i wykonać sudo ssh-keygen -A lub sudo dpkg-reconfigure openssh-server w systemach Debian i Ubuntu. Klienci otrzymają ostrzeżenie o zmianie klucza hosta, co jest prawidłowym zachowaniem.
  • Nazwa hosta. Należy ją ustawić za pomocą sudo hostnamectl set-hostname app02, a następnie sprawdzić, czy /etc/hosts nadal poprawnie rozpoznaje nową nazwę.
  • Statyczna konfiguracja sieci. Klon maszyny ze statycznym adresem IP wywoła konflikt z oryginałem w momencie połączenia z siecią. Przed podłączeniem klona do sieci należy zapoznać się z /etc/netplan/.
  • Zegar systemowy. Przywrócona migawka wznawia pracę z czasem, który obowiązywał w momencie jej wykonania. Duży skok czasu na przywróconym VPS powoduje błędy walidacji certyfikatów TLS i zaburza kolejność wpisów w dziennikach, dopóki synchronizacja czasu nie zostanie zakończona.

Na sklonowanej maszynie należy również wykonać listę kontrolną pierwszych dziesięciu minut dla nowego VPS. Klon dziedziczy konta użytkowników, klucze SSH, reguły zapory sieciowej oraz zaplanowane zadania z serwera źródłowego, a żadne z nich nie zostało zweryfikowane pod kątem nowego przeznaczenia klona.

FAQ

Czy po zmianie /etc/machine-id konieczny jest restart?

Tak. Procesy odczytują identyfikator maszyny jednorazowo i przechowują go w pamięci podręcznej, więc nowa wartość nie dotrze do już uruchomionych usług. journald kontynuuje zapisywanie do katalogu dziennika nazwanego według starego identyfikatora, a klient DHCP wciąż wysyła identyfikator klienta wywiedziony ze starej wartości, co zazwyczaj jest powodem zmiany. Restartowanie poszczególnych usług naprawia niektóre z nich, ale PID 1 również przechowuje starą wartość. Należy wykonać restart, a następnie potwierdzić zmianę za pomocą cat /etc/machine-id oraz porównując plik z drugim serwerem.

Czy /etc/machine-id to to samo co sprzętowy UUID?

Nie. Identyfikator DMI product UUID w /sys/class/dmi/id/product_uuid pochodzi z hypervisora i jest dostępny do odczytu tylko dla użytkownika root. Identyfikator maszyny jest generowany przez system operacyjny i znajduje się w zwykłym pliku, który może odczytać każdy użytkownik. Są one powiązane jednostronnie: w przypadku gościa KVM, systemd-machine-id-setup inicjuje nowy identyfikator maszyny na podstawie UUID hypervisora, jeśli nie ma identyfikatora D-Bus do skopiowania. Jeśli dwa klony współdzielą ten sam product UUID, wygenerują ten sam identyfikator maszyny, dlatego przed zaufaniem wynikowi należy porównać również ten plik.

Czy należy usunąć /etc/machine-id, czy pozostawić go pustym?

Podczas przygotowywania obrazu należy pozostawić plik pusty. machine-id(5) preferuje pusty plik, ponieważ systemd może zamontować na nim tymczasowy plik w trybie bind-mount, gdy obraz działa z systemem plików /etc w trybie tylko do odczytu. Usuwanie pliku działa w systemach z możliwością zapisu i niektóre skrypty klonujące tak właśnie robią, ale pusty plik jest bezpieczniejszym ustawieniem domyślnym. cloud-init wpisuje słowo uninitialized do pliku w tym samym celu.

Dlaczego moje dwa sklonowane serwery otrzymały ten sam adres DHCP?

Ponieważ oba wysłały ten sam identyfikator klienta. systemd-networkd domyślnie używa ClientIdentifier=duid dla DHCPv4, a domyślny DUID jest budowany na podstawie skrótu /etc/machine-id, więc identyczne identyfikatory maszyn generują identyczne identyfikatory klienta na klonach, które zachowały również tę samą nazwę interfejsu. Serwer DHCP dopasowuje żądania na podstawie tego identyfikatora, traktuje oba żądania jako jednego klienta i przydziela jedną dzierżawę. Należy nadać każdej maszynie własny identyfikator maszyny i zrestartować obie. Jeśli serwer nadal oferuje stary adres, należy wyczyścić nieaktualną dzierżawę bezpośrednio na serwerze DHCP.

#machine-id#systemd#cloning#snapshots#dhcp