SSD Nodes Learn 🎉 VPS từ $5.50/tháng
Hướng dẫn Matt ConnorBởi Matt Connor

Sửa machine-id bị trùng sau khi clone VPS

Hai VPS clone dùng chung /etc/machine-id có thể tranh DHCP lease. Làm rỗng file, xóa bản sao D-Bus, tạo lại ID và reboot trước khi snapshot.

/etc/machine-id là gì và vì sao giá trị trùng lặp lại đáng lo

Một VPS được clone sẽ khởi động với cùng /etc/machine-id như server nguồn, trong khi giá trị đó chỉ được phép thuộc về đúng một installation. Cách sửa gồm 4 lệnh: làm rỗng file, xóa bản sao D-Bus nếu đó là một file thực, tạo lại giá trị rồi reboot. Reboot là bước nhiều người bỏ qua, nhưng đây là bước khiến thay đổi có hiệu lực.

/etc/machine-id chứa một chuỗi hexadecimal viết thường gồm 32 ký tự và kết thúc bằng ký tự xuống dòng. Khi giải mã, chuỗi này là một giá trị 16-byte (128-bit). Trang manual của machine-id(5) gọi đây là thông tin confidential và nói rằng không được để lộ trên network, vì bất kỳ thứ gì đọc được giá trị này đều có thể nhận diện lại máy của bạn sau đó. Giá trị này được ghi một lần khi system được cài đặt và không thay đổi về sau.

Có 3 identifier thường bị nhầm lẫn ở đây, vì vậy cần phân biệt rõ. Hostname là nhãn do bạn chọn và có thể thay đổi bất kỳ lúc nào. Product UUID của DMI (desktop management interface) trong /sys/class/dmi/id/product_uuid do hypervisor cung cấp và chỉ root mới đọc được. Machine ID là identifier thứ ba: operating system tạo ra nó và mọi user trên máy đều có thể đọc.

Thành phần thực sự đọc machine ID

DHCP client identifier. Đây là trường hợp gây vấn đề nghiêm trọng nhất. systemd.network(5) ghi rõ ClientIdentifier= trong phần [DHCPv4] mặc định dùng duid. Giá trị này gửi client ID theo RFC 4361, được tạo từ IAID và DUID (DHCP unique identifier). networkd.conf(5) ghi rõ loại DUID mặc định là vendor. Với loại này, giá trị DUID được tạo bằng cách dùng 43793 làm vendor identifier (systemd) và hash nội dung của machine ID. DHCPv6 cũng dùng DUID tương tự. Hai bản clone có cùng machine ID sẽ tạo cùng một DUID. Nếu chúng cũng giữ cùng tên interface, chúng sẽ gửi client identifier giống hệt nhau theo từng byte. DHCP server khi đó chỉ thấy một client thay vì hai và cấp cùng một lease cho cả hai máy. Triệu chứng là địa chỉ IP chuyển qua lại giữa hai server, hoặc một server mất địa chỉ mỗi khi server kia renew.

journald. Các file journal nằm trong /var/log/journal/<machine-id>/. Tên thư mục chính là ID đó. Nếu gửi journal của hai bản clone đến cùng một collector, chúng sẽ được lưu trong cùng một thư mục và được coi là cùng một host.

D-Bus. /var/lib/dbus/machine-id là nơi định dạng file này bắt đầu được sử dụng. Trên Debian và Ubuntu, đây là symlink trỏ đến /etc/machine-id. Trên một số hệ thống, đây là một file thực riêng chứa bản sao của chính nó. Bản sao này là nguyên nhân gây lỗi trong quy trình bên dưới.

Per-host agents. Monitoring agent, licence check, inventory tool và backup client thường dùng machine ID làm host identifier mặc định vì giá trị này ổn định và không cần cấu hình. Nếu hai server báo cáo cùng một identity, hệ thống có thể gộp chúng thành một metrics series hoặc dùng một licence seat cho cả hai máy. Hãy kiểm tra cách agent tạo host ID thay vì mặc định cho rằng nó dùng hostname.

Cách xác định bạn có machine ID trùng lặp

Chạy lệnh này trên cả hai server rồi so sánh output.

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

Hai server đang hoạt động có machine ID giống nhau nghĩa là một server đã được clone từ server kia. hostnamectl in cùng giá trị trên dòng Machine ID: nếu bạn muốn dùng một lệnh duy nhất.

Kết quả của ls -l quyết định bước tiếp theo. Symlink có dạng như sau:

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

Dòng bắt đầu bằng -rw-r--r-- nghĩa là đây là một file thực chứa bản sao riêng của ID cũ. Bạn phải xóa file này vì systemd-machine-id-setup đọc nó trước khi thực hiện bất kỳ thao tác nào khác.

Product UUID cũng quan trọng. systemd-machine-id-setup(1) sử dụng KVM UUID trước khi chuyển sang tạo giá trị ngẫu nhiên. Vì vậy, nếu provider cấp cùng một SMBIOS (system management BIOS) UUID cho cả hai bản clone, việc tạo lại sẽ cho ra cùng một machine ID hai lần. Product UUID khác nhau trên hai máy nghĩa là bạn không cần lo về vấn đề này.

Máy chủ được clone: tạo lại machine ID

Thứ tự thực hiện rất quan trọng. systemd-machine-id-setup(1) cho biết rằng nếu hệ thống đã có D-Bus machine ID hợp lệ, D-Bus machine ID đó sẽ được sao chép và dùng để khởi tạo /etc/machine-id. Hãy giữ lại một /var/lib/dbus/machine-id thực tế, rồi bạn sẽ tạo lại đúng giá trị mà mình đang cố xóa.

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

Trước tiên phải truncate file, vì tool chỉ hoạt động khi file không tồn tại hoặc rỗng. Tool sẽ không làm gì với file đã chứa ID hợp lệ. systemd-machine-id-setup ghi kết quả xử lý ra standard error. Trên KVM VPS, bạn thường sẽ thấy:

Initializing machine ID from KVM UUID.

Initializing machine ID from random generator. là thông báo khi không có hypervisor UUID. Kết quả nào cũng được, miễn là cat /etc/machine-id hiện in ra một giá trị khác với server còn lại.

Symlink này giữ cho D-Bus và systemd dùng cùng một giá trị. Nếu muốn dùng một file thực riêng, hãy chạy sudo dbus-uuidgen --ensure: lệnh này tạo file với UUID mới khi file chưa tồn tại. Nếu dbus chưa được cài đặt thì hoàn toàn không có thư mục /var/lib/dbus, ln sẽ fail với No such file or directory, và bạn có thể bỏ qua cả hai dòng đó.

Sau đó reboot.

sudo reboot

Vì sao không thể bỏ qua việc reboot

Mọi process đã đọc giá trị cũ vẫn tiếp tục sử dụng giá trị đó. sd_id128_get_machine() cache ID bên trong process gọi nó, nên daemon đang chạy không bao giờ nhận biết file đã thay đổi. journald đã mở /var/log/journal/<old-id>/system.journal và tiếp tục ghi nối vào đó. systemd-networkd đã xác định DUID khi khởi động và tiếp tục gửi client identifier cũ trong mỗi lần gia hạn. Đây thường chính là lỗi bạn muốn khắc phục. D-Bus cũng đã đọc ID khi khởi động. Bạn có thể restart từng service, nhưng sẽ có service bị bỏ sót. PID 1 cũng đang giữ giá trị cũ.

Sau khi reboot, kiểm tra cả hai phần:

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

/var/log/journal/ hiện chứa thêm một directory có tên theo ID mới, và các entry mới được ghi vào đó. journalctl thông thường chỉ đọc directory của machine hiện tại, nên lịch sử trước khi clone biến mất khỏi chế độ xem mặc định. Dữ liệu vẫn còn trên disk: journalctl --merge đọc mọi journal directory, bao gồm cả directory cũ. Chỉ xóa directory cũ khi bạn chắc chắn không còn cần các log đó.

Đây cũng là lý do bạn không thể diễn tập quy trình này trong container. Container dùng chung kernel của host và không bao giờ boot PID 1 riêng, trong khi reboot là phần cốt lõi của quy trình. Hãy test đúng như trong production: clone một VM, chạy các command, reboot, rồi so sánh ID với máy nguồn.

Xóa nội dung trước khi tạo snapshot, không phải sau khi clone

Sửa từng bản clone vẫn có tác dụng. Sửa image tốt hơn, vì mọi server được khôi phục từ snapshot lỗi sẽ kế thừa cùng giá trị đó. Hãy thực hiện việc này cuối cùng, ngay trước khi tắt 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

Xóa nội dung của file. Không xóa file. machine-id(5) khuyến nghị dùng file rỗng cho image được sử dụng trên nhiều máy, vì file rỗng vẫn tồn tại cho phép bind-mount một file tạm lên file thật khi image được dùng ở chế độ chỉ đọc. Trên /etc chỉ đọc, ID được tạo khi boot nằm trong file tạm đó, còn systemd-machine-id-setup --commit ghi ID này lại sau khi filesystem có thể ghi.

Cần tính trước một tác động phụ: machine ID chưa được ghi sẽ khiến lần boot tiếp theo được xem là lần boot đầu tiên, vì vậy các unit có ConditionFirstBoot=yes sẽ chạy trong lần boot đó và bị bỏ qua ở mọi lần boot sau. Hãy dùng grep -rl ConditionFirstBoot /usr/lib/systemd/system/ để xem image của bạn sẽ chạy những gì trước khi tạo template.

Template và snapshot là hai đối tượng khác nhau, và điểm khác biệt này quyết định identity có được sao chép hay không. Template là một build artifact được chuẩn bị có chủ đích, còn snapshot là bản sao tại một thời điểm của một server đang chạy và mang theo identity của server đó cùng với dữ liệu.

Vì sao cloud image xử lý đúng còn snapshot của bạn thì không

Cloud image của các distribution được xây dựng để có thể clone, nên chúng được phát hành khi machine ID chưa được điền và lần boot đầu tiên sẽ tạo giá trị này. cloud-init có một bước được ghi rõ cho đúng trường hợp này. cloud-init clean --machine-id đặt /etc/machine-id thành chuỗi literal uninitialized trên các hệ thống systemd. Tài liệu tham chiếu CLI của cloud-init cũng mô tả đây là best practice khi clone golden image. Vì vậy, lần boot tiếp theo của image đó sẽ tạo một machine ID duy nhất.

Snapshot do bạn tự tạo lại là chuyện khác. File đã được điền giá trị khi bạn bấm snapshot. Vì vậy, mọi server được restore từ snapshot đều mang cùng một giá trị đó, và không có bước nào trong quy trình restore xóa giá trị này. Đây là cùng một dạng vấn đề như chuyển một server đang chạy sang VPS mới, trong đó bản sao giữ nguyên dữ liệu nhưng lại sao chép cả identity mà bạn không muốn sao chép.

Các thành phần khác mà clone sao chép

  • SSH host keys. /etc/ssh/ssh_host_* cũng được sao chép, vì vậy cả hai server đều cung cấp cùng một fingerprint cho client. Xóa các file đó rồi chạy sudo ssh-keygen -A hoặc sudo dpkg-reconfigure openssh-server trên Debian và Ubuntu. Sau đó, client sẽ cảnh báo host key đã thay đổi. Đây là hành vi chính xác.
  • Hostname. Đặt hostname bằng sudo hostnamectl set-hostname app02, rồi kiểm tra xem /etc/hosts vẫn phân giải được tên mới hay không.
  • Cấu hình mạng tĩnh. Clone của một máy có địa chỉ tĩnh sẽ xung đột với máy gốc ngay khi khởi động. Đọc /etc/netplan/ trước khi clone kết nối vào mạng.
  • Clock. Snapshot được khôi phục sẽ tiếp tục với thời gian tại thời điểm snapshot được tạo. Clock nhảy quá xa trên VPS được khôi phục sẽ làm hỏng việc xác thực chứng chỉ TLS và đảo lộn thứ tự log cho đến khi đồng bộ thời gian bắt kịp.

Thực hiện cả checklist 10 phút đầu cho VPS mới trên clone. Clone kế thừa user account, SSH key, firewall rule và scheduled job của máy nguồn, nhưng chưa thành phần nào trong số đó được kiểm tra cho công việc mà clone sắp thực hiện.

FAQ

Tôi có phải reboot sau khi thay đổi /etc/machine-id không?

Có. Các process chỉ đọc machine ID một lần rồi cache giá trị đó, nên giá trị mới không được áp dụng cho những process đang chạy. journald vẫn ghi vào thư mục journal có tên theo ID cũ, còn DHCP client vẫn gửi client identifier được tạo từ giá trị cũ. Đây thường là lý do bạn thay đổi machine ID. Restart từng service chỉ xử lý được một phần, vì PID 1 cũng đang giữ giá trị cũ. Hãy reboot, sau đó xác nhận bằng cat /etc/machine-id và so sánh với server còn lại.

/etc/machine-id có giống hardware UUID không?

Không. DMI product UUID trong /sys/class/dmi/id/product_uuid đến từ hypervisor và chỉ root mới đọc được. machine ID do operating system tạo và nằm trong một plain file mà mọi user đều có thể đọc. Hai giá trị này chỉ liên hệ theo một chiều: trên KVM guest, systemd-machine-id-setup dùng hypervisor UUID để tạo machine ID mới khi không có D-Bus ID để sao chép. Nếu hai clone dùng chung product UUID, chúng sẽ tạo lại cùng một machine ID. Vì vậy, hãy so sánh cả file đó trước khi tin vào kết quả.

Tôi nên xóa /etc/machine-id hay để file trống?

Hãy để file trống khi bạn chuẩn bị image. machine-id(5) ưu tiên file trống, vì systemd có thể bind-mount một file tạm thời lên đó khi image chạy với /etc ở chế độ read-only. Xóa file vẫn hoạt động trên writable system, và một số clone script thực hiện theo cách này, nhưng file trống là mặc định an toàn hơn. cloud-init ghi từ uninitialized vào file cũng vì mục đích đó.

Tại sao 2 server clone của tôi nhận cùng một địa chỉ DHCP?

Vì cả hai đều gửi cùng một client identifier. systemd-networkd mặc định dùng ClientIdentifier=duid cho DHCPv4, còn DUID mặc định được tạo từ hash của /etc/machine-id. Vì vậy, các clone có cùng machine ID và vẫn giữ cùng interface name sẽ tạo ra các identifier giống nhau. DHCP server đối chiếu theo identifier đó, xem cả 2 request là của cùng một client và cấp cùng một lease. Hãy đặt machine ID riêng cho từng server rồi reboot cả 2. Nếu server vẫn cấp địa chỉ cũ, hãy xóa stale lease trực tiếp trên DHCP server.

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