# VMware migrate to Harvester
透過 VM Import 的功能,使用 vm-import-controller 插件,使用者可以將 VMware 和 OpenStack 中的虛擬機器匯入到 Harvester 中。
## Harvester VM Import 啟用 vm-import-controller 功能

在遷移過程中,大型虛擬機器的節點可能會耗盡此掛載點的空間,導致後續調度失敗。
為避免這種情況,建議使用者啟用 PVC 支援的儲存並自訂所需的儲存容量。最佳實踐是,PVC 的大小應該是待遷移虛擬機器中最大虛擬機器大小的兩倍。這一點至關重要,因為 PVC 用作臨時空間來下載虛擬機器並將磁碟轉換為原始鏡像檔案。

> 這邊是設定 vm-import-controller 他自己要的 pvc 大小,這是遷移 vm 時會暫時儲存的地方
```
hx-1:~ # kubectl -n harvester-system get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
harvester-vm-import-controller Bound pvc-a6ff1269-6b71-4e21-a9d4-83b6f26f60ba 200Gi RWO new-sc <unset> 39s
```
## 遷移目標
* 將在 VC 上的 ubuntu server、windows server 和一做 k8s 叢集都遷移至 harvester

* VSphere 相關資訊
- DC 名稱:Datacenter
- VC FQDN:`vcbm.bbg.com`

## 開始遷移
### 遷移前準備
* 需確認 harvester 與 VC 網路是互通的
```
hx-1:~ # ping -c 1 vcbm.bbg.com
PING vcbm.bbg.com (172.20.0.10) 56(84) bytes of data.
64 bytes from 172.20.0.10 (172.20.0.10): icmp_seq=1 ttl=64 time=0.696 ms
--- vcbm.bbg.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.696/0.696/0.696/0.000 ms
```
* 先把要遷移的 vm 都關機

### 在 harvester 設定 vmware 資訊
```
$ vim vmsource.yaml
apiVersion: migration.harvesterhci.io/v1beta1
kind: VmwareSource
metadata:
name: vcsim
namespace: default
spec:
# 設定 VC 位置
endpoint: "https://vcbm.bbg.com/sdk"
# 選擇要遷移 VM 所在的 datacenter 名稱
dc: "Datacenter"
credentials:
name: vsphere-credentials
namespace: default
---
apiVersion: v1
kind: Secret
metadata:
name: vsphere-credentials
namespace: default
stringData:
"username": "<VC username>"
"password": "<VC password>"
```
```
$ kubectl apply -f vmsource.yaml
```
* 部署後確認狀態是否 ready,代表可以開始遷移 vm
```
$ kubectl get vmwaresource.migration
NAME STATUS
vcsim clusterReady
```
### 遷移 ubuntu linux vm
* ubuntu 規格:
- 4 core cpu
- 8g RAM
- 使用兩顆 30g 硬碟
- 網路介面名稱:Internal VM Network

* ubuntu 遷移前可以先設定固定網卡名稱,可以[參考](https://hackmd.io/@wu-andy/ry11wzTW-e)
* 定義要 import ubuntu 的 yaml 資訊
```
$ vim vmimport-ub.yaml
apiVersion: migration.harvesterhci.io/v1beta1
kind: VirtualMachineImport
metadata:
name: ubuntu-test-import
namespace: default
spec:
# vmware 上的 vm 名稱
virtualMachineName: "ubuntu-test"
networkMapping:
# vmware 上的 vm 所使用的網路介面
- sourceNetwork: "Internal VM Network"
# 這邊要改成在 Harvester 上實際建立的網路名稱
destinationNetwork: "default/bridge"
sourceCluster:
name: vcsim
namespace: default
kind: VmwareSource
apiVersion: migration.harvesterhci.io/v1beta1
```
> 注意:遷移的 vm 名稱中不能有 `.` 和空格
```
$ kubectl apply -f vmimport-ub.yaml
```
* 部署後看到狀態是 ready,此時會開始遷移 vm,這會需要一段時間
```
$ kubectl get virtualmachineimport.migration
NAME STATUS
ubuntu-test-import sourceReady
```
* 可以檢查遷移 vm 時的 log
```
$ kubectl -n harvester-system logs harvester-vm-import-controller-bdd8c546d-gtz4f
......
time="2025-12-02T08:18:38Z" level=info msg="Shutting down guest OS of the source VM" name=ubuntu-test-import namespace=default spec.gracefulShutdownTimeoutSeconds=60 spec.sourceCluster.kind=VmwareSource spec.sourceCluster.name=vcsim spec.virtualMachineName=ubuntu-test
time="2025-12-02T08:18:39Z" level=info msg="Importing client disk images ..." name=ubuntu-test-import namespace=default spec.virtualMachineName=ubuntu-test
time="2025-12-02T08:18:39Z" level=info msg="Importing client disk images ..." name=ubuntu-test-import namespace=default spec.virtualMachineName=ubuntu-test
time="2025-12-02T08:18:39Z" level=info msg="Exporting source VM" name=ubuntu-test-import namespace=default spec.sourceCluster.kind=VmwareSource spec.sourceCluster.name=vcsim spec.virtualMachineName=ubuntu-test
time="2025-12-02T08:18:39Z" level=info msg="Origin spec of the volumes to be imported" name=ubuntu-test-import namespace=default spec="[{\"deviceId\":\"/vm-35/ParaVirtualSCSIController0:0\",\"path\":\"disk-0.vmdk\",\"size\":64424509440,\"cimType\":0,\"create\":false,\"URL\":{\"Scheme\":\"https\",\"Opaque\":\"\",\"User\":null,\"Host\":\"10.10.7.3\",\"Path\":\"/nfc/528911aa-344b-88ef-45d7-5096e6125187/disk-0.vmdk\",\"RawPath\":\"\",\"OmitHost\":false,\"ForceQuery\":false,\"RawQuery\":\"\",\"Fragment\":\"\",\"RawFragment\":\"\"}},{\"deviceId\":\"/vm-35/ParaVirtualSCSIController0:1\",\"path\":\"disk-1.vmdk\",\"size\":64424509440,\"cimType\":0,\"create\":false,\"URL\":{\"Scheme\":\"https\",\"Opaque\":\"\",\"User\":null,\"Host\":\"10.10.7.3\",\"Path\":\"/nfc/528911aa-344b-88ef-45d7-5096e6125187/disk-1.vmdk\",\"RawPath\":\"\",\"OmitHost\":false,\"ForceQuery\":false,\"RawQuery\":\"\",\"Fragment\":\"\",\"RawFragment\":\"\"}},{\"deviceId\":\"/vm-35/nvram\",\"path\":\"disk-2.nvram\",\"size\":270840,\"cimType\":0,\"create\":false,\"URL\":{\"Scheme\":\"https\",\"Opaque\":\"\",\"User\":null,\"Host\":\"10.10.7.3\",\"Path\":\"/nfc/528911aa-344b-88ef-45d7-5096e6125187/disk-2.nvram\",\"RawPath\":\"\",\"OmitHost\":false,\"ForceQuery\":false,\"RawQuery\":\"\",\"Fragment\":\"\",\"RawFragment\":\"\"}}]"
time="2025-12-02T08:18:39Z" level=info msg="Downloading an image" busType=sata deviceId="/vm-35/ParaVirtualSCSIController0:0" name=ubuntu-test-import namespace=default path=ubuntu-test-import-default-disk-0.vmdk size=64424509440 spec.sourceCluster.kind=VmwareSource spec.sourceCluster.name=vcsim spec.virtualMachineName=ubuntu-test
```
* 遷移完成後會自動將 vm 開機
```
$ kubectl get virtualmachineimport.migration
NAME STATUS
ubuntu-test-import virtualMachineRunning
```

* 並且產生了兩顆對應的硬碟

* 如果沒有固定網卡名稱,遷移後 guset os 網卡名稱有變動需要再手動修改

### 遷移 windows server vm
* windows server 規格:
- 8 core cpu
- 16g RAM
- 使用一顆 50g 硬碟
- 網路介面名稱:Internal VM Network

* windows 遷移前可以先安裝 virtio,這樣在遷移後才看得到網卡名稱,可以[參考](https://hackmd.io/@wu-andy/BJQOqMpZWl)
* 定義要 import windows server 的 yaml 資訊
```
$ vim vmimport-win.yaml
apiVersion: migration.harvesterhci.io/v1beta1
kind: VirtualMachineImport
metadata:
name: windows-server-test-import
namespace: default
spec:
# vmware 上的 vm 名稱
virtualMachineName: "windows-server-2025-test"
networkMapping:
# vmware 上的 vm 所使用的網路介面
- sourceNetwork: "Internal VM Network"
# 這邊要改成在 Harvester 上實際建立的網路名稱
destinationNetwork: "default/bridge"
sourceCluster:
name: vcsim
namespace: default
kind: VmwareSource
apiVersion: migration.harvesterhci.io/v1beta1
```
> 注意:遷移的 vm 名稱中不能有 `.` 和空格
```
$ kubectl apply -f vmimport-win.yaml
```
* 檢視遷移狀態
```
$ kubectl get virtualmachineimport.migration
NAME STATUS
ubuntu-test-import virtualMachineRunning
windows-server-test-import sourceRead
```
* 遷移完畢
```
$ kubectl get virtualmachineimport.migration
NAME STATUS
ubuntu-test-import virtualMachineRunning
windows-server-test-import virtualMachineRunning
```

* 並且產生了對應的硬碟

* 登入 windows server

### 遷移 K8s 叢集
* 定義要 import vm 的 yaml 資訊
```
$ vim vmimport-k8s-m1.yaml
apiVersion: migration.harvesterhci.io/v1beta1
kind: VirtualMachineImport
metadata:
name: k8s-m1-import
namespace: default
spec:
# vmware 上的 vm 名稱
virtualMachineName: "k8s-m1"
networkMapping:
# vmware 上的 vm 所使用的網路介面
- sourceNetwork: "Internal VM Network"
# 這邊要改成在 Harvester 上實際建立的網路名稱
destinationNetwork: "default/bridge"
sourceCluster:
name: vcsim
namespace: default
kind: VmwareSource
apiVersion: migration.harvesterhci.io/v1beta1
```
```
$ vim vmimport-k8s-w1.yaml
apiVersion: migration.harvesterhci.io/v1beta1
kind: VirtualMachineImport
metadata:
name: k8s-w1-import
namespace: default
spec:
# vmware 上的 vm 名稱
virtualMachineName: "k8s-w1"
networkMapping:
# vmware 上的 vm 所使用的網路介面
- sourceNetwork: "Internal VM Network"
# 這邊要改成在 Harvester 上實際建立的網路名稱
destinationNetwork: "default/bridge"
sourceCluster:
name: vcsim
namespace: default
kind: VmwareSource
apiVersion: migration.harvesterhci.io/v1beta1
```
> 注意:遷移的 vm 名稱中不能有 `.` 和空格
```
$ kubectl apply -f vmimport-k8s-m1.yaml
$ kubectl apply -f vmimport-k8s-w1.yaml
```
* 如果同時遷移,harvester 會批次作業
```
$ kubectl get virtualmachineimport.migration
NAME STATUS
k8s-m1-import sourceReady
k8s-w1-import
ubuntu-test-import virtualMachineRunning
windows-server-test-import virtualMachineRunning
```
* 遷移完成
```
$ kubectl get virtualmachineimport.migration
NAME STATUS
k8s-m1-import virtualMachineRunning
k8s-w1-import virtualMachineRunning
ubuntu-test-import virtualMachineRunning
windows-server-test-import virtualMachineRunning
```

* 確認遷移後 K8s 狀態是正常的
```
bigred@m1:~$ kubectl get no
NAME STATUS ROLES AGE VERSION
m1 Ready control-plane 5h15m v1.34.1
w1 Ready <none> 5h15m v1.34.1
bigred@m1:~$ kubectl get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system calico-kube-controllers-548476666b-mtmp9 1/1 Running 1 3h38m
kube-system calico-node-7kkwf 1/1 Running 1 5h14m
kube-system calico-node-tmvrv 1/1 Running 0 3h38m
kube-system coredns-66bc5c9577-6gmc5 1/1 Running 1 3h38m
kube-system coredns-66bc5c9577-kmjz4 1/1 Running 1 3h38m
kube-system etcd-m1 1/1 Running 1 5h15m
kube-system kube-apiserver-m1 1/1 Running 1 5h15m
kube-system kube-controller-manager-m1 1/1 Running 1 5h15m
kube-system kube-proxy-csrk9 1/1 Running 1 5h15m
kube-system kube-proxy-thgjm 1/1 Running 0 3h38m
kube-system kube-scheduler-m1 1/1 Running 1 5h15m
```