SSD Nodes Learn 🎉 VPS от $5.50/мес
Руководства Matt ConnorАвтор: Matt Connor

Как изменить machine-id на клонированном VPS

Дубликат machine-id вызывает конфликты DHCP и проблем с D-Bus. Узнайте, как безопасно сбросить идентификатор через systemd-machine-id-setup и правильно подготовить образ.

Что такое /etc/machine-id и почему важна его уникальность

Клонированный VPS загружается с тем же /etc/machine-id, что и исходный сервер, хотя это значение должно быть уникальным для каждой установки. Исправление выполняется четырьмя командами: очистка файла, удаление копии D-Bus (если это обычный файл), генерация нового значения и перезагрузка. Перезагрузка — это шаг, который часто пропускают, хотя именно он делает изменения активными.

В /etc/machine-id хранится 32-символьная шестнадцатеричная строка в нижнем регистре, завершающаяся символом переноса строки. В декодированном виде это 16-байтовое (128-битное) значение. В справочной странице machine-id(5) оно названо конфиденциальным; его не следует передавать по сети, так как любой, кто его получит, сможет идентифицировать вашу машину при повторном обращении. Оно записывается один раз при установке системы и в дальнейшем не меняется.

Здесь часто путают три идентификатора, поэтому их стоит различать. Имя хоста (hostname) — это метка, которую вы выбираете и можете изменить в любой момент. UUID продукта DMI (desktop management interface) в /sys/class/dmi/id/product_uuid предоставляется гипервизором и доступен для чтения только пользователю root. Machine ID — это третий идентификатор: он генерируется операционной системой, и его может прочитать любой пользователь в системе.

Что на самом деле считывает machine ID

DHCP-клиент. Это наиболее критичный момент. В systemd.network(5) в разделе [DHCPv4] указано, что ClientIdentifier= по умолчанию использует duid. Это приводит к отправке идентификатора клиента согласно RFC 4361, который формируется из IAID и DUID (DHCP unique identifier). В networkd.conf(5) указано, что типом DUID по умолчанию является vendor. Значение DUID генерируется с использованием идентификатора производителя 43793 (systemd) и хешированного содержимого machine ID. DHCPv6 использует тот же DUID. Два клона с одинаковым machine ID имеют одинаковый хеш DUID. Если у них совпадают имена сетевых интерфейсов, они отправляют идентичные идентификаторы клиентов. DHCP-сервер воспринимает их как одного клиента и выдает обоим серверам один и тот же адрес. Симптомом является «миграция» IP-адреса между двумя серверами или ситуация, когда один сервер теряет адрес при обновлении аренды другим.

journald. Файлы журналов хранятся в /var/log/journal/<machine-id>/. Имя каталога буквально совпадает с идентификатором. Если отправить журналы с двух клонированных систем на один сервер сбора данных, они попадут в один каталог и будут интерпретироваться как данные одного хоста.

D-Bus. /var/lib/dbus/machine-id — это место, где впервые появился данный формат файла. В Debian и Ubuntu это символическая ссылка на /etc/machine-id. В некоторых системах это отдельный файл, содержащий собственную копию, и именно эта копия является ловушкой в процедуре, описанной ниже.

Агенты, привязанные к хосту. Агенты мониторинга, системы проверки лицензий, инструменты инвентаризации и клиенты резервного копирования часто используют machine ID в качестве идентификатора хоста по умолчанию, так как он стабилен и не требует настройки. Если два сервера сообщают об одной и той же идентичности, это приводит к объединению метрик в один поток или к тому, что одна лицензия будет ошибочно охватывать две машины. Проверьте, как именно ваш агент определяет ID хоста, вместо того чтобы предполагать, что он использует hostname.

Как определить наличие дубликата

Выполните эту команду на обоих серверах и сравните вывод.

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

cat /etc/machine-id

Идентичные machine-id на двух работающих серверах означают, что один был клонирован из другого. hostnamectl выводит то же самое значение в своей Machine ID: строке, если вы предпочитаете использовать одну команду.

Результат ls -l определяет дальнейшие действия. Символическая ссылка выглядит так:

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

lrwxrwxrwx 1 root root 33 ... /etc/machine-id -> /var/lib/dbus/machine-id

Строка, начинающаяся с -rw-r--r--, означает, что это обычный файл, содержащий собственную копию старого идентификатора. Его необходимо удалить, так как systemd-machine-id-setup считывает его в первую очередь.

UUID продукта также имеет значение. systemd-machine-id-setup(1) использует KVM UUID, прежде чем перейти к случайной генерации, поэтому, если провайдер выдал обоим клонам одинаковый SMBIOS (system management BIOS) UUID, повторная генерация даст вам один и тот же machine-id дважды. Разные UUID продукта на двух машинах означают, что в данном случае вам не о чем беспокоиться.

Перегенерация machine ID на клонированной VPS

Порядок действий имеет значение. systemd-machine-id-setup(1) гласит, что если в системе уже настроен корректный 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 выводит отчет о проделанной работе в стандартный поток ошибок. На KVM VPS вы обычно увидите следующее:

Initializing machine ID from KVM UUID.

Initializing machine ID from random generator. — это сообщение, которое появляется, если UUID гипервизора недоступен. Любой из этих результатов допустим, если cat /etc/machine-id теперь выводит значение, отличное от того, что было на другом сервере.

Символическая ссылка позволяет D-Bus и systemd использовать одно и то же значение. Если вы предпочитаете использовать отдельный файл, выполните sudo dbus-uuidgen --ensure: эта команда создаст файл с новым UUID, если он еще не существует. Если D-Bus не установлен, директория /var/lib/dbus отсутствует, ln завершится с ошибкой No such file or directory, и обе эти строки можно пропустить.

Затем выполните перезагрузку.

sudo reboot

Почему перезагрузка обязательна

Любой процесс, который уже считал старое значение, продолжает его использовать. sd_id128_get_machine() кэширует ID внутри вызывающего процесса, поэтому запущенный демон никогда не «заметит», что файл изменился. 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 считывает все каталоги журнала, включая старый. Удалите старый каталог, как только убедитесь, что эти логи вам больше не нужны.

Именно поэтому вы не можете отрепетировать эту процедуру в контейнере. Контейнер использует ядро хоста и никогда не запускает собственный PID 1, а перезагрузка — это ключевой момент всей операции. Тестируйте так, как это происходит в production: клонируйте виртуальную машину, выполните команды, перезагрузитесь, а затем сравните ID с исходной машиной.

Очищайте файл перед созданием снимка, а не после клонирования

Исправление каждого клона по отдельности работает, но лучше исправить сам образ. Любой сервер, восстановленный из некорректного снимка, унаследует те же значения. Выполняйте это действие последним перед выключением шаблона.

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) рекомендует оставлять пустой файл для образов, используемых на нескольких машинах. Пустой файл на месте позволяет примонтировать временный файл поверх реального, если образ используется в режиме только для чтения. На /etc, доступной только для чтения, идентификатор, созданный при загрузке, сохраняется во временном файле, а systemd-machine-id-setup --commit записывает его, как только файловая система становится доступной для записи.

Учтите побочный эффект: пустой machine ID помечает следующую загрузку как первую, поэтому юниты с ConditionFirstBoot=yes выполняются при этой загрузке и пропускаются при всех последующих. Проверьте, что именно будет запущено в вашем образе с помощью grep -rl ConditionFirstBoot /usr/lib/systemd/system/, прежде чем создавать шаблон.

Шаблон и снимок — это разные объекты, и от этого зависит, будет ли скопирована идентификационная информация. Шаблон — это артефакт сборки, который вы подготавливаете целенаправленно, тогда как снимок — это копия работающего сервера на определенный момент времени, которая сохраняет идентификатор сервера вместе с его данными.

Почему облачные образы справляются с этим правильно, а ваш снапшот — нет

Дистрибутивные облачные образы созданы для клонирования, поэтому они поставляются с пустым machine ID, который заполняется при первой загрузке. В cloud-init предусмотрен документированный шаг именно для этого. cloud-init clean --machine-id устанавливает /etc/machine-id в значение uninitialized в системах на базе systemd, и справочник по CLI cloud-init называет это лучшей практикой при клонировании эталонного образа, чтобы при следующей загрузке образа генерировался уникальный machine ID.

Снапшот, который вы сделали самостоятельно, — это другой случай. Файл уже был заполнен в момент создания снапшота, поэтому каждый сервер, восстановленный из него, содержит одно и то же значение, и ни один этап процесса восстановления не очищает его. Это проблема того же класса, что и перенос работающего сервера на новый VPS, где копия является точной, а идентичность — это именно то, что вы не хотели копировать.

Что еще дублируется при клонировании

  • SSH host keys. Файл /etc/ssh/ssh_host_* также копируется, поэтому оба сервера предъявляют клиентам один и тот же отпечаток. Удалите эти файлы и выполните sudo ssh-keygen -A или sudo dpkg-reconfigure openssh-server в Debian и Ubuntu. После этого клиенты выдадут предупреждение об изменении ключа хоста, что является ожидаемым поведением.
  • Имя хоста (hostname). Установите его с помощью sudo hostnamectl set-hostname app02, затем проверьте, что /etc/hosts по-прежнему разрешает новое имя.
  • Статическая конфигурация сети. Клон сервера со статическим адресом создаст конфликт с оригиналом в момент подключения к сети. Ознакомьтесь с /etc/netplan/ перед тем, как клон будет добавлен в сеть.
  • Системные часы. Восстановленный снапшот возобновляет работу с тем временем, которое было на момент создания снимка. Значительный скачок времени на восстановленном VPS нарушает проверку TLS-сертификатов и искажает порядок записей в логах, пока синхронизация времени не восстановит актуальное значение.

Выполните чек-лист первых десяти минут работы нового VPS и на клонированном сервере. Клонированная машина наследует учетные записи пользователей, SSH-ключи, правила межсетевого экрана и запланированные задачи исходного сервера, которые не были проверены на соответствие новым задачам клона.

FAQ

Нужно ли перезагружать сервер после изменения /etc/machine-id?

Да. Процессы считывают machine ID один раз и кэшируют его, поэтому новое значение не применяется к уже запущенным программам. journald продолжает запись в каталог журнала, названный по старому ID, а DHCP-клиент продолжает отправлять идентификатор клиента, основанный на старом значении, что обычно и является причиной изменения. Перезапуск отдельных сервисов исправляет ситуацию лишь частично, так как PID 1 также хранит старое значение. Выполните перезагрузку, затем подтвердите изменения с помощью cat /etc/machine-id и сравните результат с другим сервером.

Является ли /etc/machine-id тем же самым, что и аппаратный UUID?

Нет. DMI product UUID в /sys/class/dmi/id/product_uuid предоставляется гипервизором и доступен для чтения только пользователю root. Machine ID генерируется операционной системой и хранится в обычном файле, который может прочитать любой пользователь. Они связаны в одном направлении: на гостевой системе KVM systemd-machine-id-setup использует UUID гипервизора для генерации нового machine ID, если нет D-Bus ID для копирования. Если два клона имеют одинаковый product UUID, они сгенерируют одинаковый machine ID, поэтому проверяйте этот файл перед тем, как доверять результату.

Следует ли удалять /etc/machine-id или оставить его пустым?

Оставляйте файл пустым при подготовке образа. machine-id(5) предпочитает пустой файл, так как systemd может выполнить bind-mount временного файла поверх него, когда образ запускается с файловой системой /etc, доступной только для чтения. Удаление файла работает в системе с правами на запись, и некоторые скрипты клонирования используют этот метод, но пустой файл — более безопасный вариант по умолчанию. cloud-init записывает слово uninitialized в этот файл для тех же целей.

Почему два моих клонированных сервера получили одинаковый DHCP-адрес?

Потому что оба отправили одинаковый идентификатор клиента. systemd-networkd по умолчанию использует ClientIdentifier=duid для DHCPv4, а стандартный DUID строится на основе хеша /etc/machine-id. В результате одинаковые machine ID создают идентичные идентификаторы на клонах, у которых также сохранились одинаковые имена интерфейсов. DHCP-сервер сопоставляет этот идентификатор, воспринимает оба запроса как одного клиента и выдает одну аренду. Присвойте каждому серверу уникальный machine ID и перезагрузите оба. Если сервер продолжает предлагать старый адрес, очистите устаревшую запись аренды на самом DHCP-сервере.

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