1 vm实时迁移
vm实时迁移(Live Migration)¶
官档 实时迁移是一个过程,在此期间,正在运行的虚拟机实例将移动到另一个计算节点,同时在该虚拟机实例上的工作负载将不受影响继续运行并保持可访问性。
启用实时迁移¶
使用热迁移前提条件是要先启用LiveMigration特性。 不过,在最新版本的KubeVirt中默认已经启用了实时迁移特性。而在v0.56之前的版本,必须在特性开关中(feature_gates)中启用
LiveMigration。
实时迁移局限性¶
虚拟机要满足实时迁移必须具备如下条件: - 使用persistentvolumecclaim (PVC)的虚拟机必须具有共享的ReadWriteMany (RWX)访问模式才能进行实时迁移。 - 添加了
- 实时迁移需要为相应的filesystem类型的设备不支持实时迁移。 - 对于虚拟机使用了桥接类型的pod网络绑定,不允许进行实时迁移,即虚拟机的网络类型不能是bridge模式。virt-launcher pod提供49152,49153端口,这端口应该是nodePort端口,可以从迁移状态targetDirectMigrationNodePorts中的描述看到。如果在masquerade接口中显式指定了这些端口,则实时迁移将不起作用。 issue 5514但在issue 6070 提到将使用spec: domain: devices: interfaces: - name: default masquerade: {} ports: - port: 22222 - port: 49152 - port: 49153unix socket代理来启动实时迁移,而不是使用tcp代理方式。 因为libvirt支持了unix socket,具体可参考[https://libvirt.org/html/libvirt-libvirt-domain.html#VIR_MIGRATE_PARAM_DISKS_URI]。 libvirt迁移使用的端口范围49152-49215 待验证该功能
使用实时迁移¶
实时迁移是通过向集群发布一个VirtualMachineInstanceMigation (VMIM)对象来启动的。下面的示例是以
yaml方式实时迁移虚拟机vm-ubuntu01## cat live-vm-ubuntu01.yaml apiVersion: kubevirt.io/v1 kind: VirtualMachineInstanceMigration metadata: name: migration-vm-ubuntu01 spec: vmiName: vm-ubuntu01 ## kubectl apply -f live-vm-ubuntu01.yaml也可以使用virtctl命令进行实时迁移
迁移方法¶
在启动虚拟机实例时,kubevirt会计算当前实例是否可以实时迁移,并将结果存储在VMI VMI.status.conditions中。 可以根据VMI的多个参数进行计算,但目前主要是根据VMI卷的Access Mode进行计算。仅当卷访问模式设置为ReadWriteMany时,支持实时迁移。 当请求迁移一个
non-livemigratable的实例时,VMI的请求将被拒绝。迁移方法 即
migrationMethod也会在 VMI 启动期间计算出来: -BlockMigration表示某些VMI的磁盘需要从源复制到目标。 -LiveMigration表示仅复制实例内存即可完成实时迁移。
迁移状态¶
迁移的进度状态保存在
VMI.status;最重要的是,它指示迁移是否已经完成Completed或者Failed。 以下是一个vmi做了多次热迁移后的yaml信息,其中记录了迁移状态、迁移次数# kubectl get vmi vm-ubuntu01 -o yaml apiVersion: kubevirt.io/v1 kind: VirtualMachineInstance metadata: annotations: kubevirt.io/instancetype-name: p1-2 kubevirt.io/latest-observed-api-version: v1 kubevirt.io/storage-observed-api-version: v1alpha3 creationTimestamp: "2022-10-12T11:03:28Z" finalizers: - kubevirt.io/virtualMachineControllerFinalize - foregroundDeleteVirtualMachine generation: 37 labels: kubevirt.io/domain: vm-ubuntu01 kubevirt.io/migrationTargetNodeName: node02 kubevirt.io/nodeName: node02 kubevirt.io/size: small name: vm-ubuntu01 namespace: default ownerReferences: - apiVersion: kubevirt.io/v1 blockOwnerDeletion: true controller: true kind: VirtualMachine name: vm-ubuntu01 uid: 3c70a95e-bb3c-41c3-b3e9-96752ead2b81 resourceVersion: "961562" uid: 842bd69c-f0bf-4f95-a662-dba9e67557a7 spec: domain: cpu: cores: 1 model: host-passthrough sockets: 1 threads: 1 devices: disks: - disk: bus: virtio name: root-disk - disk: bus: virtio name: cloudinitdisk interfaces: - masquerade: {} name: default features: acpi: enabled: true firmware: uuid: 283a4163-7f22-5bf7-89a1-729ef455f525 machine: type: q35 memory: guest: 2Gi resources: requests: memory: 2Gi networks: - name: default pod: {} volumes: - name: root-disk persistentVolumeClaim: claimName: vm-ubuntu01-bootdisk - cloudInitNoCloud: userData: |- #cloud-config hostname: test.com ssh_pwauth: True timezone: Asia/Shanghai disable_root: false chpasswd: {"list":"root:123456",expire: False} runcmd: - sed -i "/PermitRootLogin/s/^.*$/PermitRootLogin yes/g" /etc/ssh/sshd_config - systemctl restart sshd.service - useradd -d /home/test -m test - echo root:12345678 |chpasswd - echo ubuntu:ubuntu |chpasswd - echo test:test123 |chpasswd name: cloudinitdisk status: activePods: 134bfeff-e0d4-48ad-b544-5cda219c1fe6: node01 ab428932-02df-4fa3-b6db-b598a83f85d8: node02 cfca99a9-db01-4d08-8fff-d50843440381: node01 ec59ca23-23f5-4fc3-987d-ee072e7f38ce: node02 conditions: - lastProbeTime: null lastTransitionTime: "2022-10-12T11:34:08Z" status: "True" type: Ready - lastProbeTime: null lastTransitionTime: null status: "True" type: LiveMigratable guestOSInfo: {} interfaces: - infoSource: domain ipAddress: 10.244.0.11 ipAddresses: - 10.244.0.11 mac: 52:54:00:11:90:4e name: default queueCount: 1 launcherContainerImageVersion: quay.io/kubevirt/virt-launcher:v0.57.1 migrationMethod: BlockMigration migrationState: completed: true endTimestamp: "2022-10-12T11:34:08Z" migrationConfiguration: allowAutoConverge: false allowPostCopy: false bandwidthPerMigration: "0" completionTimeoutPerGiB: 800 nodeDrainTaintKey: kubevirt.io/drain parallelMigrationsPerCluster: 5 parallelOutboundMigrationsPerNode: 2 progressTimeout: 150 unsafeMigrationOverride: false migrationUid: b1ef1903-8efa-4429-8d86-f42885a5b6a2 mode: PreCopy sourceNode: node01 startTimestamp: "2022-10-12T11:33:58Z" targetDirectMigrationNodePorts: "39745": 49152 "42847": 0 "45937": 49153 targetNode: node02 targetNodeAddress: 10.244.0.98 targetNodeDomainDetected: true targetPod: virt-launcher-vm-ubuntu01-fmcmf migrationTransport: Unix nodeName: node02 phase: Running phaseTransitionTimestamps: - phase: Pending phaseTransitionTimestamp: "2022-10-12T11:03:28Z" - phase: Scheduling phaseTransitionTimestamp: "2022-10-12T11:03:28Z" - phase: Scheduled phaseTransitionTimestamp: "2022-10-12T11:03:40Z" - phase: Running phaseTransitionTimestamp: "2022-10-12T11:03:41Z" qosClass: Burstable runtimeUser: 0 virtualMachineRevisionName: revision-start-vm-3c70a95e-bb3c-41c3-b3e9-96752ead2b81-1 volumeStatus: - name: cloudinitdisk size: 1048576 target: vdb - name: root-disk persistentVolumeClaimInfo: accessModes: - ReadWriteMany capacity: storage: 40Gi filesystemOverhead: "0" requests: storage: 40Gi volumeMode: Block target: vda # 主要关注以下 Status: Conditions: Status: True ## 是否满足迁移条件 Type: LiveMigratable Migration Method: BlockMigration ## 迁移方法 migrationState: ## 迁移是否完成 completed: true
取消实时迁移¶
实时迁移还可以通过简单地删除迁移对象来取消实时迁移。成功中止迁移时,在迁移状态中会看到
- 通过删除迁移对象取消实时迁移Abort Requested: true,并且Abort Status: Succeeded。 在这种情况下,迁移将是Completed: true和Failed: true。- 通过virtctl命令取消实时迁移 热迁移过程中,IP并没有发生变化,因为使用的kube-ovn插件,其能保证热迁移过程IP固定,vm整个生命周期IP固定。# vm-ubuntu01在node01上 [root@master 20221012]# kubectl get vmi NAME AGE PHASE IP NODENAME READY vm-ubuntu01 69m Running 10.244.0.11 node01 True # 使用命令执行迁移 [root@master 20221012]# virtctl migrate vm-ubuntu01 VM vm-ubuntu01 was scheduled to migrate # 查看迁移对象任务 [root@master 20221012]# kubectl get vmim NAME PHASE VMI kubevirt-migrate-vm-2tx99 Succeeded vm-ubuntu01 kubevirt-migrate-vm-67jfg Succeeded vm-ubuntu01 kubevirt-migrate-vm-h9p6x Scheduling vm-ubuntu01 migration-vm-ubuntu01 Succeeded vm-ubuntu01 # 删除迁移对象,来取消实时迁移 [root@master 20221012]# kubectl delete vmim kubevirt-migrate-vm-h9p6x virtualmachineinstancemigration.kubevirt.io "kubevirt-migrate-vm-h9p6x" deleted [root@master 20221012]# kubectl get vmim NAME PHASE VMI kubevirt-migrate-vm-2tx99 Succeeded vm-ubuntu01 kubevirt-migrate-vm-67jfg Succeeded vm-ubuntu01 migration-vm-ubuntu01 Succeeded vm-ubuntu01 [root@master 20221012]# kubectl get vmi NAME AGE PHASE IP NODENAME READY vm-ubuntu01 70m Running 10.244.0.11 node01 True
更改集群范围的迁移限制¶
KubeVirt设置了一些限制,这样迁移就不会使集群不堪重负。默认情况下,它被配置为只并行运行"5"个迁移,每个节点最多只能运行"2"个出站迁移,最后,每次迁移的带宽限制为64MiB/s。即kubevirt默认限制了实时迁移的并行数。
并且这些限制并不能直接通过
kubectl get kubevirt -n kubevirt -o yaml中查看到。 可通过kubevirt的CR进行修改apiVersion: kubevirt.io/v1 kind: Kubevirt metadata: name: kubevirt namespace: kubevirt spec: configuration: developerConfiguration: featureGates: - CPUManager - ExpandDisks migrations: # 指定每个集群可以同时进行的迁移数量 parallelMigrationsPerCluster: 5 # 指定每个节点可以同时进行的出站迁移数量 parallelOutboundMigrationsPerNode: 2 # 指定每个迁移的带宽限制 bandwidthPerMigration: 64Mi # 指定每GB数据迁移完成的超时时间,表示每GB数据迁移完成的超时时间为800秒 completionTimeoutPerGiB: 800 # 指定迁移进度的超时时间,迁移进度的超时时间为150秒 progressTimeout: 150 # 指定是否禁用TLS加密,禁用,可以加快迁移速率 disableTLS: false # 指定节点排空的污点键,在进行虚拟机迁移时,节点排空是一种常见的操作,它可以确保在迁移期间不再调度新的Pod到目标节点上。通过指定这个污点键,KubeVirt可以在迁移期间自动对目标节点进行排空操作,以确保迁移的顺利进行。 # kubectl taint nodes node01 kubevirt.io/drain=true:NoSchedule后,节点上的虚拟机会自动完成迁移 nodeDrainTaintKey: "kubevirt.io/drain" # 指定是否允许自动收敛,自动收敛是一种优化虚拟机迁移性能的技术,它可以在迁移过程中自动调整虚拟机的性能参数,以提高迁移的效率和成功率。通过设置这个配置项,可以控制是否允许KubeVirt在迁移时自动进行性能收敛 allowAutoConverge: false # 指定是否允许后拷贝。后拷贝是一种虚拟机迁移的优化技术,它可以在迁移过程中延迟一部分内存数据的传输,以减少迁移过程中的数据传输量。通过设置这个配置项,可以控制是否允许KubeVirt在迁移时使用后拷贝技术。 allowPostCopy: false # 指定是否允许不安全的迁移覆盖,在某些情况下,可能会出现迁移失败或中断的情况,这时可以通过设置这个配置项来允许KubeVirt进行不安全的迁移覆盖,以尝试恢复迁移操作。 unsafeMigrationOverride: false
了解不同的迁移策略¶
实时迁移(live migration)是一个复杂的过程。在迁移过程中,源 VM 需要将其整个状态(主要是 RAM)传输到目标 VM。如果有足够的可用资源(如网络带宽和CPU能力),则迁移应该会很好地收敛。 但是,如果不是这种情况,则迁移可能会陷入僵局,无法继续进行。 影响迁移的主要因素之一是VM的"脏页率"(dirty rate),即虚拟机内存被修改的速率。具有高脏页率的VM会导致迁移过程中的竞争。 一方面,内存会持续传输到目标VM,另一方面,相同的内存会被VM修改。在这种情况下,可以考虑使用更高级的迁移策略。
预拷贝(Pre-Copy):预拷贝是默认策略,用于大多数情况,预拷贝的迁移方式如下:¶
- 创建目标虚拟机(The target VM is created, but the guest keeps running on the source VM.):首先,在目标节点上创建一个新的虚拟机实例,但是原始虚拟机上的操作系统实例(guest)仍然在继续运行。
- 传输虚拟机状态数据(The source starts sending chunks of VM state (mostly memory) to the target. This continues until all of the state has been transferred to the target.):源节点开始向目标节点传输虚拟机的状态数据,主要是内存中的数据。这个过程会持续进行,直到所有的状态数据都被成功传输到目标节点。
- 在目标节点上启动虚拟机(The guest starts executing on the target VM.):一旦所有的状态数据都被成功传输到目标节点,目标节点上的虚拟机实例开始执行,原始虚拟机上的操作系统实例开始在目标节点上继续运行。
- 移除源节点上的虚拟机(The source VM is being removed.):在虚拟机迁移完成后,原始节点上的虚拟机实例被移除,迁移过程结束。
总之:虚拟机迁移的基本流程,包括创建目标VM、传输状态数据、在目标节点上启动VM以及移除原始节点上的VM。这个过程可以确保VM在不中断服务的情况下从一个节点迁移到另一个节点。
在大多数情况下,预复制是最安全、最快捷的策略。此外,它可以轻松取消,可以利用多线程等等。如果没有真正的理由使用其他策略,这绝对是要采用的策略。 但是, 在某些情况下,迁移可能不容易收敛,即源VM状态的数据在传输到目标VM之前可能已经被源VM改变。 这可能导致迁移失败,原因可能包括高脏页率(dirty-rate)或低网络带宽和CPU资源等。在这种情况下,需要考虑使用其他替代策略。
后拷贝(Post-Copy):后拷贝是预拷贝的替代策略,用于解决预拷贝的不足。后拷贝的迁移方式如下:¶
- 创建目标VM。
- 在目标VM上运行客户机(guest)。
- 源VM开始向目标VM发送VM状态的分块(主要是内存)。
- 当在目标VM上运行的客户机(guest)访问内存时: 如果内存存在目标VM上,客户机(guest)可以访问它;否则,目标VM会向源VM请求一块内存。
- 一旦目标VM上的所有内存状态都更新完成,源VM将被移除。
这种方式的主要思想是客户机立即在目标VM上运行。这种方式的优点和缺点如下:
优点: * 相同的内存块不会被多次传输,因为后拷贝不会在乎页面是否被修改,因为客户机已经在目标VM上运行。 * 这意味着高脏页率的影响要小得多。 * 消耗的网络带宽较少。
缺点:
- 使用后拷贝时,VM状态没有一个真正的来源。当客户机(在目标VM上运行)写入内存时,这部分内存是客户机状态的一部分,但其他部分可能仍然只在源VM上更新。这种情况通常很危险,因为如果目标客户机上的VM崩溃,状态将无法恢复。
- 启动缓慢:当客户机开始执行时,目标VM上没有内存。因此,客户机需要在短时间内等待大量内存。
- 在大多数情况下比预拷贝慢。
- 取消迁移更困难。
自动收敛(Auto-converge): 它是一种帮助预拷贝(pre-copy)迁移更快收敛的技术,自动收敛技术的工作原理如下:¶
- 高脏页率通常是导致迁移无法收敛的最重要因素。自动收敛技术通过简单地限制客户机的CPU使用率来解决这个问题。如果迁移能够快速收敛,客户机的CPU将不会被限制,或者只会受到极小的限制。 但是,如果迁移不能够快速收敛,随着时间的推移,CPU将会受到越来越严重的限制。
- 这种技术显著增加了迁移最终收敛的概率。通过限制客户机的CPU使用率,可以有效地减缓脏页率高的客户机对迁移的影响,从而提高迁移最终收敛的可能性。
kubevirt支持配置不同的网络来进行VM迁移,具体步骤如下:¶
- 需要一个独立的物理网络,这意味着集群中的每个节点必须至少有2个网卡,用于迁移的网卡需要相互连接,即全部插入到同一台交换机上。
- 需要安装 multus 来支持多网络。
- 如果所需网络不包括 DHCP 服务器,则还需要安装 whereabouts 。
- 最后,在安装了KubeVirt的命名空间中需要创建一个NAD(NetworkAttachmentDefinition)。示例中展示了如何创建一个名为 "migration-network" 的 NetworkAttachmentDefinition, 它使用了 macvlan 类型的网络,并配置了 IP 地址池。
假设multus已完成安装。 以下先是使用的是
whereabouts示例,并假设将使用节点eth1网卡进行迁移。 1. 创建NetworkAttachmentDefinition和子网## 如果使用的网络插件是kube-ovn,则忽略whereabouts和以下yaml apiVersion: "k8s.cni.cncf.io/v1" kind: NetworkAttachmentDefinition metadata: name: migration-network namespace: kubevirt spec: config: '{ "cniVersion": "0.3.1", "name": "migration-bridge", "type": "macvlan", "master": "eth1", "mode": "bridge", "ipam": { "type": "whereabouts", "range": "10.1.1.0/24" } }'
上述 whereabouts 可能会和kube-ovn冲突,所以直接配置kube-ovn的macvlan,并忽略上面的whereabouts的配置。
- 以下是用kube-ovn创建 NetworkAttachmentDefinition
2. 配置KubeVirt迁移使用该网络
## 创建NAD,并配置为macvlan apiVersion: "k8s.cni.cncf.io/v1" kind: NetworkAttachmentDefinition metadata: name: migration-network namespace: kubevirt spec: config: '{ "cniVersion": "0.3.1", "type": "macvlan", "master": "eth1", "mode": "bridge", "ipam": { "type": "kube-ovn", "server_socket": "/run/openvswitch/kube-ovn-daemon.sock", "provider": "migration-network.kubevirt" } }' --- ## 创建子网 apiVersion: kubeovn.io/v1 kind: Subnet metadata: name: migration-network spec: protocol: IPv4 provider: migration-network.kubevirt cidrBlock: 10.168.59.0/24 excludeIps: - 10.168.59.0..10.168.59.10 - 10.168.59.230..10.168.59.250只需要将NetworkAttachmentDefinition的名称添加到KubeVirt CR中即可配置 KubeVirt 使用该网络进行VM迁移。 这个改变将触发 virt-handler pods 重启,因为它们将连接到新的网络。从此时开始,迁移将使用该网络进行。
3. 接下来就可以验证迁移是否走eth1网络了
迁移超时设置¶
- 完成超时(Completion time)
在某些情况下,VM可能以较高的速率写入不同的内存页/磁盘块,而这些写入速度超过了复制的速度,这将导致迁移过程无法在合理的时间内完成。在这种情况下,如果迁移运行了很长时间,那么迁移将被中止。 超时时间是根据VM的大小、内存和需要复制的临时磁盘来计算的。可配置的参数
completionTimeoutPerGiB默认为 800 秒,表示每GiB的数据在中止迁移之前等待迁移完成的时间。 例如,具有8GiB内存的虚拟机实例将会在6400秒后超时。
- 迁移进度超时(Progress timeout)
当发现复制内存没有任何进展时,VM实时迁移将被中止。等待VM实时迁移在传输数据方面取得进展的时间由
progressTimeout参数进行配置,默认为150秒。
apiVersion: kubevirt.io/v1
kind: Kubevirt
metadata:
name: kubevirt
namespace: kubevirt
spec:
configuration:
developerConfiguration:
featureGates:
- CPUManager
- ExpandDisks
migrations:
# 指定每个集群可以同时进行的迁移数量
parallelMigrationsPerCluster: 5
# 指定每个节点可以同时进行的出站迁移数量
parallelOutboundMigrationsPerNode: 2
# 指定每个迁移的带宽限制
bandwidthPerMigration: 64Mi
# 指定每GB数据迁移完成的超时时间,表示每GB数据迁移完成的超时时间为800秒
completionTimeoutPerGiB: 800
# 指定迁移进度的超时时间,迁移进度的超时时间为150秒
progressTimeout: 150
# 指定是否禁用TLS加密,禁用,可以加快迁移速率
disableTLS: false
# 指定节点排空的污点键,在进行虚拟机迁移时,节点排空是一种常见的操作,它可以确保在迁移期间不再调度新的Pod到目标节点上。通过指定这个污点键,KubeVirt可以在迁移期间自动对目标节点进行排空操作,以确保迁移的顺利进行。
# kubectl taint nodes node01 kubevirt.io/drain=true:NoSchedule后,节点上的虚拟机会自动完成迁移
nodeDrainTaintKey: "kubevirt.io/drain"
# 指定是否允许自动收敛,自动收敛是一种优化虚拟机迁移性能的技术,它可以在迁移过程中自动调整虚拟机的性能参数,以提高迁移的效率和成功率。通过设置这个配置项,可以控制是否允许KubeVirt在迁移时自动进行性能收敛
allowAutoConverge: false
# 指定是否允许后拷贝。后拷贝是一种虚拟机迁移的优化技术,它可以在迁移过程中延迟一部分内存数据的传输,以减少迁移过程中的数据传输量。通过设置这个配置项,可以控制是否允许KubeVirt在迁移时使用后拷贝技术。
allowPostCopy: false
# 指定是否允许不安全的迁移覆盖,在某些情况下,可能会出现迁移失败或中断的情况,这时可以通过设置这个配置项来允许KubeVirt进行不安全的迁移覆盖,以尝试恢复迁移操作。
unsafeMigrationOverride: false
通过禁用TLS来提升迁移性能¶
有时可能需要禁用迁移的TLS加密以提高迁移性能。使用disableTLS可做到这一点:
注意 禁用TLS虽然提高了迁移性能,但它可能允许apiVersion: kubevirt.io/v1 kind: Kubevirt metadata: name: kubevirt namespace: kubevirt spec: configuration: developerConfiguration: featureGates: - CPUManager ...... migrationConfiguration: disableTLS: trueMITM攻击。
使用中存在的问题¶
(1) 热迁移过程中vnc会话会中断,需要重新刷新vnc窗口 (2) 配置独立网络进行虚拟机迁移时,先前存量虚拟机在使用独立网络迁移后会出现网卡ip丢失问题。 (3) 虚拟机中拿到的ip与kube-ovn分配的ip不同,且网段都不同!!!!未找到原因!