VMware Workstation không bật được VM sau khi máy tính tắt đột ngột
Mình thấy các tác giả/chuyên gia của hệ sinh thái chia sẻ rất nhiều kinh nghiệm thực tế hữu ích, mình học hỏi được rất nhiều. Tuy nhiên, mình thấy một vài kiến thức đơn giản mà nhiều bạn mới hay gặp thì chưa được chia sẻ nhiều (chắc tại research AI là ra hoặc các bác pro thường không có nhiều thời gian nên chỉ chia sẻ kiến thức hữu ích) nên mình nay chia sẻ case này dành cho các bạn mới nhé.
Anh em học và làm DevOps việc lab tryhard để lên trình là điều rất thường ngày rồi và VMware Workstation là công cụ rất điển hình để tạo các server làm lab.
Và trường hợp tắt máy tính đột ngột và khi mở lại không bật được server là điều rất hay gặp nên mình chia sẻ để mọi người gặp đúng trường hợp này thì đỡ mất thời gian nhé.
Bài toán
Máy Windows của đang chạy bị tắt đột ngột trong lúc một số VM vẫn đang chạy.
Sau khi bật máy lên lại thì các VM khác vẫn chạy bình thường, riêng một VM không thể start được. VMware báo lỗi kiểu:
The process cannot access the file because another process has locked a portion of the file
Cannot open the disk
Module Disk power on failed
Failed to start the virtual machine
Nhìn qua thì khá dễ nghĩ VMDK có vấn đề vì máy vừa bị tắt đột ngột.
Nguyên nhân là VMware vẫn còn các lock từ lần chạy trước.
Điểm quan trọng là không nên thấy .lck rồi xóa ngay. Trước khi xóa cần biết lock đó là lock cũ do máy tắt ngang hay vẫn đang được một process VMware sử dụng.
1. Đầu tiên tìm xem process nào đang giữ VM
Khi VM chạy, VMware Workstation sử dụng process:
vmware-vmx.exe
Ngay sau khi gặp lỗi, mở PowerShell và chạy:
Get-CimInstance Win32_Process |
Where-Object Name -eq 'vmware-vmx.exe' |
Select-Object ProcessId, CommandLine |
Format-List
Nếu còn vmware-vmx.exe, phần CommandLine sẽ cho mình khá nhiều thông tin.
Ở cuối command thường có đường dẫn tới file .vmx của VM.
Dạng như:
...\vmware-vmx.exe ... msgs=ui D:\...\server.vmx
Nhờ vậy mình không cần biết trước VM nằm ở thư mục nào.
Nếu đang chạy nhiều VM thì sẽ có nhiều vmware-vmx.exe. Có thể dùng đoạn PowerShell dưới đây để nhìn rõ PID, tên VM và đường dẫn VMX của từng process.
$VMXList = Get-CimInstance Win32_Process |
Where-Object Name -eq 'vmware-vmx.exe' |
ForEach-Object {
if ($_.CommandLine -match 'msgs=ui\s+(.+\.vmx)\s*$') {
$vmx = $Matches[1]
$displayName = Select-String `
-LiteralPath $vmx `
-Pattern '^displayName\s*=' |
Select-Object -First 1
[PSCustomObject]@{
ProcessId = $_.ProcessId
DisplayName = (
$displayName.Line -replace '^displayName\s*=\s*', ''
).Trim([char]34)
VMX = $vmx
}
}
}
$VMXList | Format-Table -AutoSize
Kết quả sẽ có dạng:
ProcessId DisplayName VMX
--------- ----------- ---
19136 my-server D:\...\my-server.vmx
Từ đây mình biết chính xác file VMX của con VM đang lỗi.
2. Lưu VMX vào biến để khỏi phải sửa path trong từng lệnh
Sau khi đã nhìn thấy PID của VM đang lỗi, mình chọn PID đó:
$TargetPid = [int](Read-Host 'Nhap ProcessId cua VM dang loi')
$VMX = (
$VMXList |
Where-Object { $_.ProcessId -eq $TargetPid }
).VMX
$VMX
Từ đây các lệnh phía sau đều dùng $VMX.
Không cần hardcode đường dẫn của máy mình hay của bất kỳ ai.
3. Từ VMX tìm toàn bộ VMDK mà VM đang sử dụng
Một VM có thể chỉ có một disk nhưng cũng có thể attach thêm nhiều VMDK nằm ở thư mục khác.
Mình không đoán disk nằm ở đâu mà đọc trực tiếp từ file VMX.
$VMDir = Split-Path -LiteralPath $VMX -Parent
$VMDKs = Select-String `
-LiteralPath $VMX `
-Pattern 'fileName\s*=\s*"([^"]+\.vmdk)"' |
ForEach-Object {
$DiskPath = $_.Matches[0].Groups[1].Value
if ([System.IO.Path]::IsPathRooted($DiskPath)) {
[System.IO.Path]::GetFullPath($DiskPath)
}
else {
[System.IO.Path]::GetFullPath(
(Join-Path $VMDir $DiskPath)
)
}
}
$VMDKs
Ví dụ VMX có:
scsi0:0.fileName = "Ubuntu-server.vmdk"
scsi0:1.fileName = "D:\vm-disks\data.vmdk"
Script sẽ tự chuyển thành đường dẫn đầy đủ của cả hai VMDK.
Chỗ này khá quan trọng vì case của mình có một disk nằm cùng VM và một disk nằm ở thư mục khác.
4. Trước khi xóa lock phải chắc chắn VM đã tắt
Sau khi đã biết VMX và các VMDK liên quan, mình shutdown bình thường toàn bộ VM còn đang chạy rồi đóng VMware Workstation.
Sau đó kiểm tra lại:
Get-CimInstance Win32_Process |
Where-Object Name -eq 'vmware-vmx.exe'
Nếu không có output thì hiện tại không còn vmware-vmx.exe.
Mình kiểm tra thêm bằng vmrun.
Đoạn này tự tìm vmrun.exe ở hai vị trí cài VMware Workstation thường gặp:
$VMRun = @(
(Join-Path ${env:ProgramFiles(x86)} 'VMware\VMware Workstation\vmrun.exe'),
(Join-Path $env:ProgramFiles 'VMware\VMware Workstation\vmrun.exe')
) |
Where-Object { Test-Path -LiteralPath $_ } |
Select-Object -First 1
& $VMRun list
Kết quả mình cần thấy là:
Total running VMs: 0
Case của mình lúc này có đủ hai điều kiện.
Total running VMs: 0
và không còn:
vmware-vmx.exe
Máy trước đó lại vừa bị tắt đột ngột trong lúc VM đang chạy.
Lúc này khả năng các .lck còn lại là lock cũ khá rõ.
5. Tìm đúng các lock liên quan đến VM
Thay vì scan cả ổ đĩa rồi xóa tất cả .lck, mình chỉ lấy lock liên quan đến VMX và các VMDK vừa tìm được.
$Locks = @()
$VMXLock = $VMX + '.lck'
if (Test-Path -LiteralPath $VMXLock) {
$Locks += Get-Item -LiteralPath $VMXLock -Force
}
foreach ($VMDK in $VMDKs) {
$VMDKLock = $VMDK + '.lck'
if (Test-Path -LiteralPath $VMDKLock) {
$Locks += Get-Item -LiteralPath $VMDKLock -Force
}
}
$Locks += Get-ChildItem `
-LiteralPath $VMDir `
-Force `
-Filter '*.vmem.lck' `
-ErrorAction SilentlyContinue
$Locks = $Locks |
Sort-Object FullName -Unique
$Locks |
Select-Object FullName, PSIsContainer
Trong case của mình nó tìm ra các dạng:
server.vmx.lck
system-disk.vmdk.lck
data-disk.vmdk.lck
xxxxxxxx.vmem.lck
Bên trong các thư mục này có thể còn những file kiểu:
M31454.lck
M06851.lck
Đó cũng là một phần của lock.
6. Xóa stale lock
Chỉ làm bước này khi mọi người đã kiểm tra:
Total running VMs: 0
và:
vmware-vmx.exe
không còn tồn tại.
Mình cho PowerShell hiện lại toàn bộ thứ sắp xóa trước:
$Locks |
Select-Object FullName, PSIsContainer
Sau đó mới xác nhận:
$Confirm = Read-Host 'Nhap YES neu da chac chan khong con VM nao dang chay'
if ($Confirm -ceq 'YES') {
$Locks |
Sort-Object { $_.FullName.Length } -Descending |
Remove-Item -Recurse -Force
Write-Host 'Da xoa cac stale lock'
}
else {
Write-Host 'Khong co gi bi xoa'
}
Mình cố tình không viết script tự xóa ngay.
Với loại lỗi này, mình vẫn muốn nhìn danh sách lock một lần trước khi cho Remove-Item chạy.
7. Những file không được đụng tới
Phần mình xóa ở trên chỉ là .lck.
Không xóa các file như:
.vmdk
-flat.vmdk
-delta.vmdk
-sesparse.vmdk
.vmx
.vmem
.vmsd
.vmsn
Nhất là:
-flat.vmdk
-delta.vmdk
-sesparse.vmdk
Đây có thể là dữ liệu disk hoặc snapshot thật của VM.
Xóa nhầm những file này thì câu chuyện không còn là xử lý lock nữa.
8. Kết quả
Sau khi xóa các .lck cũ, mình mở VMware Workstation và start lại VM.
Máy lên bình thường.
Không cần repair VMDK, không cần restore snapshot và cũng không phải tạo lại VM.
Nguyên nhân trong case này đơn giản là máy tính bị tắt đột ngột khi VM vẫn chạy, VMware không kịp dọn lock, lần boot sau nhìn thấy lock cũ nên không cho mở disk.
Một lưu ý cuối
Nếu sau khi xử lý .lck mà lỗi lock biến mất nhưng VMware chuyển sang báo những lỗi như:
The file specified is not a virtual disk
The parent virtual disk has been modified
The disk needs repair
thì nên dừng ở đó.
Lúc này vấn đề có thể đã chuyển sang VMDK descriptor hoặc snapshot chain, không còn là case stale lock như bài này nữa.
Còn nếu mọi người vừa bị mất điện, Windows crash hoặc máy bị tắt ngang trong lúc VMware đang chạy, sau đó gặp:
The process cannot access the file because another process has locked a portion of the file
thì mình nghĩ nên kiểm tra vmware-vmx.exe, tìm VMX, lần từ VMX ra VMDK rồi xác nhận không còn VM nào chạy trước khi đụng tới .lck.
Đó cũng chính là cách mình xử lý case này và VM đã chạy lại bình thường.