天天看點

【KVM系列08】使用 libvirt 遷移 QEMU/KVM 虛機和 Nova 虛機 

                                                                 第八章 使用 libvirt 遷移 QEMU/KVM 虛機和 Nova 虛機 

1. QEMU/KVM 遷移的概念

1.1 遷移效率的衡量

1.2 KVM 遷移的原理

1.3 使用指令行的方式做動态遷移

2. OpenStack Nova QEMU/KVM 執行個體動态遷移的環境配置

2.1 基礎環境配置

2.2 Live migration 環境配置

3. 遷移過程

3.0 Nova 有關遷移的指令

3.1 靜态遷移(migrate 或者 resize 不使用新的 flavor)

 3.2 實時遷移 (Live migration)

3.3 遇到過的問題

【KVM系列文章】https://blog.csdn.net/baidu_37107022/article/details/88812463

1. QEMU/KVM 遷移的概念

     遷移(migration)包括系統整體的遷移和某個工作負載的遷移。系統整理遷移,是将系統上所有軟體包括作業系統完全複制到另一個實體機硬體機器上。虛拟化環境中的遷移,可分為靜态遷移(static migration,或者 冷遷移 cold migration,或者離線遷移 offline migration) 和 動态遷移 (live migration,或者 熱遷移 hot migration 或者 線上遷移 online migration)。靜态遷移和動态遷移的最大差別是,靜态遷移有明顯一段時間客戶機中的服務不可用,而動态遷移則沒有明顯的服務暫停時間。

    虛拟化環境中的靜态遷移也可以分為兩種,一種是關閉客戶機後,将其硬碟鏡像複制到另一台主控端上然後恢複啟動起來,這種遷移不能保留客戶機中運作的工作負載;另一種是兩台主控端共享存儲系統,這時候的遷移可以保持客戶機遷移前的記憶體狀态和系統運作的工作負載。

    動态遷移,是指在保證客戶機上應用服務正常運作的同時,讓客戶機在不同的主控端之間進行遷移,其邏輯步驟和前面的靜态遷移幾乎一直,有硬碟存儲和記憶體都複制的動态遷移,也有僅複制記憶體鏡像的動态遷移。不同的是,為了保證遷移過程中客戶機服務的可用性,遷移過程隻能有非常短暫的停機時間。動态遷移允許系統管理者将客戶機在不同實體機上遷移,同時不會斷開通路客戶機中服務的用戶端或者應用程式的連接配接。一個成功的遷移,需要保證客戶機的記憶體、硬碟存儲和網絡連接配接在遷移到目的主機後任然保持不變,而且遷移的過程的服務暫停時間較短。

1.1 遷移效率的衡量

(1)整體遷移時間

(2)伺服器停機時間:這時間是指源主機上的客戶機已經暫停服務,而目的主機上客戶機尚未恢複服務的時間。

(3)對服務性能的影響:客戶機遷移前後性能的影響,以及目的主機上其它服務的性能影響。

  其中,整體遷移時間受很多因素的影響,比如 Hypervisor 和遷移工具的種類、磁盤存儲的大小(是否需要複制磁盤鏡像)、記憶體大小及使用率、CPU 的性能和使用率、網絡帶寬大小及是否擁塞等,整體遷移時間一般分為幾秒鐘到幾十分鐘不等。動态遷移的服務停機時間,也有這些因素的影響,往往在幾毫秒到幾百毫秒。而靜态遷移,其暫停時間較長。是以,靜态遷移一般适合于對服務可用性要求不高的場景,而動态遷移适合于對可用性要求高的場景。

動态遷移的應用場景包括:負載均衡、解除硬體依賴、節約能源 和異地遷移。

1.2 KVM 遷移的原理

1.2.1 靜态遷移

    對于靜态遷移,你可以在主控端上某客戶機的 QEMU monitor 中,用 savevm my_tag 指令儲存一個完整的客戶機鏡像快照,然後在主控端中關閉或者暫停該客戶機,然後将該客戶機的鏡像檔案複制到另一台主控端中,使用在源主機中啟動該客戶機時的指令來啟動複制過來的鏡像,在其 QEMU monitor 中 loadvm my_tag 指令恢複剛才儲存的快照即可完全加載儲存快照時的客戶機狀态。savevm 指令可以保證完整的客戶機狀态,包括 CPU 狀态、記憶體、裝置狀态、科協磁盤中的記憶體等。注意,這種方式需要 qcow2、qed 等格式的磁盤鏡像檔案的支援。

1.2.2 動态遷移

    如果源主控端和目的主控端共享存儲系統,則隻需要通過網絡發送客戶機的 vCPU 執行狀态、記憶體中的内容、虛機裝置的狀态到目的主機上。否則,還需要将客戶機的磁盤存儲發到目的主機上。共享存儲系統指的是源和目的虛機的鏡像檔案目錄是在一個共享的存儲上的。

    在基于共享存儲系統時,KVM 動态遷移的具體過程為:

  1. 遷移開始時,客戶機依然在主控端上運作,與此同時,客戶機的記憶體頁被傳輸到目的主機上。
  2. QEMU/KVM 會監控并記錄下遷移過程中所有已被傳輸的記憶體頁的任何修改,并在所有記憶體頁都傳輸完成後即開始傳輸在前面過程中記憶體頁的更改内容。
  3. QEMU/KVM 會估計遷移過程中的傳輸速度,當剩餘的記憶體資料量能夠在一個可以設定的時間周期(預設 30 毫秒)内傳輸完成時,QEMU/KVM 會關閉源主控端上的客戶機,再将剩餘的資料量傳輸到目的主機上,最後傳輸過來的記憶體内容在目的主控端上恢複客戶機的運作狀态。
  4. 至此,KVM 的動态遷移操作就完成了。遷移後的客戶機盡可能與遷移前一直,除非目的主機上缺少一些配置,比如網橋等。

注意,當客戶機中記憶體使用率非常大而且修改頻繁時,記憶體中資料不斷被修改的速度大于KVM能夠傳輸的記憶體速度時,動态遷移的過程是完成不了的,這時候隻能靜态遷移。

1.3 使用指令行的方式做動态遷移

1.3.1 使用 NFS 共享存儲

(1)在源主控端上挂載 NFS 上的客戶機鏡像,并啟動客戶機

mount my-nfs:/raw-images/ /mnt/ kvm /mnt/rh1.img -smp 2 -m 2048 -net nic -net tap
           

(2)在目的主控端上也挂載鏡像目錄,并啟動一個客戶機用于接收動态遷移過來的記憶體内容

mount my-nfs:/raw-images/ /mnt/ kvm /mnt/rh1.img -smp 2 -m 2048 -net nic -net tap -incoming tcp:0:6666
           

注意:(1)NFS 挂載目錄必須一緻 (2)“-incoming tcp:0:6666” 參數表示在 6666 端口建立一個 TCP socket 連接配接用于接收來自源主機的動态遷移的内容,其中 0 表示運作來自任何主機的連接配接。“-incoming“ 使 qemu-kvm 程序進入到監聽模式,而不是真正以指令行中的檔案運作客戶機。

(3)在源主控端的客戶機的 QEMU monitor 中,使用指令 ” migrate tcp:host2:6666" 即可進入動态遷移的流程。

1.3.2 不使用共享存儲的動态遷移

過程類似,包括使用相同backing file 的鏡像的客戶機遷移,以及完全不同鏡像檔案的客戶機的遷移。唯一的差別是,migrate 指令中添加 “-b” 參數,它意味着傳輸塊裝置。

1.3.3 其它 QEMU monitor migrate 指令

  • migrate_cancel:取消遷移
  • migrate_set_speed:設定最大遷移速度,機關是 bytes
  • migrate_set_downtime:設定最大允許的服務暫停時間,機關是 秒
  • info migrate:顯示遷移進度

2. OpenStack Nova QEMU/KVM 執行個體動态遷移的環境配置

除了直接拷貝磁盤鏡像檔案的冷遷移,OpenStack 還支援下面幾種虛機熱遷移模式:

  • 不使用共享存儲時的塊實時遷移(Block live migration without shared storage)。這種模式不支援使用隻讀裝置比如 CD-ROM 和 Config Drive。塊實時遷移不需要 nova compute 節點都使用共享存儲。它使用 TCP 來将虛機的鏡像檔案通過網絡拷貝到目的主機上,是以和共享存儲式的實時遷移相比,這種方式需要更長的時間。而且在遷移過程中,主機的性能包括網絡和 CPU 會下降。
  • 基于共享存儲的實時遷移 (Shared storage based live migration):兩個主機可以通路共享的存儲。
  • 從卷啟動的虛機的實時遷移(Volume backed VM live migration)。這種遷移也是一種塊拷貝遷移。

實時遷移的過程并不複雜,複雜在于環境配置。

2.1 基礎環境配置

2.1.1 SSH 權限配置

這種方式需要配置源(compute1)和目的主機(compute2)之間能夠通過 SSH 互相通路,以確定能通過 TCP 拷貝檔案,已經可以通過 SSH 在目的主機建立目錄。

使用 nova 使用者在compute1 上執行操作:

usermod -s /bin/bash nova

su nova

mkdir -p -m 700 .ssh


#建立 config 檔案如下

[email protected]:~/.ssh$ cat config

Host * StrictHostKeyChecking no

UserKnownHostsFile=/dev/null #産生 key

ssh-keygen -f id_rsa -b 1024 -P "" cat id_rsa.pub >> authorized_keys


#将 id_rsa id_rsa.pub 拷貝到 compute2 上面

cat id_rsa.pub >> authorized_keys
           

使用 root 使用者在每個主機上進行操作:

[email protected]:/var/lib/nova/.ssh# chown -R nova:nova /var/lib/nova

[email protected]:/var/lib/nova/.ssh# chmod 700 /var/lib/nova/.ssh

[email protected]:/var/lib/nova/.ssh# chmod 600 /var/lib/nova/.ssh/authorized_keys
           

測試 SSH 無密碼通路:

[email protected]:~/.ssh$ ssh [email protected] ls

Warning: Permanently added 'compute2,192.168.1.29' (ECDSA) to the list of known hosts.

...


[email protected]:~/.ssh$ ssh [email protected] ls

Warning: Permanently added 'compute1,192.168.1.15' (ECDSA) to the list of known hosts.

...
           

2.1.2 其它配置

 每個node 上的 DNS 或者 /etc/hosts,確定互聯互通。

2.2 Live migration 環境配置

2.2.1 libvirtd 配置

在 compute1 和 compute2 上做如下配置:

->Edit /etc/libvirt/libvirtd.conf

listen_tls = 0 listen_tcp = 1 auth_tcp = “none” ->Edit /etc/init/libvirt-bin.conf env libvirtd_opts="-d -l"
           
->Edit /etc/default/libvirt-bin
           

  # options passed to libvirtd, add "-l" to listen on tcp

  libvirtd_opts="-d -l"

->Restart libvirtd

service libvirt-bin restart


root     12088     1  2 07:48 ?        00:00:00 /usr/sbin/libvirtd -d -l
           
做完上述操作後,可以使用如下指令來檢查是否設定正确:
           
[email protected]:~# virsh -c qemu+tcp://compute1/system list --all Id Name State ---------------------------------------------------- 4 instance-0000000d running 5 instance-00000006 running - instance-00000005 shut off


[email protected]:~# virsh -c qemu+tcp://compute2/system list --all Id Name State ----------------------------------------------------
           

Nova 設定:

->Edit /etc/nova/nova.conf, add following line:

[libvirt]

block_migration_flag = VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER, VIR_MIGRATE_LIVE,VIR_MIGRATE_TUNNELLED,VIR_MIGRATE_NON_SHARED_INC

live_migration_flag = VIR_MIGRATE_UNDEFINE_SOURCE, VIR_MIGRATE_PEER2PEER, VIR_MIGRATE_LIVE,VIR_MIGRATE_TUNNELLED

live_migration_uri = qemu+tcp://%s/system
           

2.2.2 共享存儲 Live migration 環境配置

其實共享存儲的實時遷移配置的要求和塊拷貝的實時遷移的配置差不多,除了下面幾點:

  1. Hypervisor 要求:目前隻有部分 Hypervisor 支援 live migraiton,可查詢該表。
  2. 共享存儲:存放虛機檔案的檔案夾 NOVA-INST-DIR/instances/ (比如 /var/lib/nova/instances,該路徑可以由 state_path 配置變量來配置) 必須是挂載到共享存儲上的。當Nova 使用 RBD 作為鏡像的backend時,這個要求不是必須的,具體見下面的說明。
  3. 必須在 nova.conf 中配置 vncserver_listen=0.0.0.0 (關于這個,社群認為這個配置具有安全風險,會通過這個 ticket 來解決)
  4. 不使用預設配置的話,必須在每個 nova compute 上的 nova.conf 中配置相同的 instances_path 和 state_path 。
  5. 在 Kilo 版本之前,Nova 是預設不支援 live migriation 的。在做實時遷移之前,需要在 nova.conf 中做如下配置
live_migration_flag=VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_TUNNELLED
           

 注意:對于上面第二點,在 Kilo 版本中(前面版本的情況未知),當 Nova 使用 RBD 作為 image backend 時,Nova 會認為是在共享存儲上:

def check_instance_shared_storage_local(self, context, instance): """Check if instance files located on shared storage."""  if self.image_backend.backend().is_shared_block_storage(): return None
           

在 class Rbd(Image): 類中:

@staticmethod

def is_shared_block_storage():

"""True if the backend puts images on a shared block storage.""" return True
           

目前,隻有 RBD 作為 image backend 時,該函數才傳回 true。對于其它類型的 backend,Nova 會在目的 host 上的 instance folder 建立一個臨時檔案,再在源 host 上檢視該檔案,通過判斷是否該檔案在共享存儲上來判斷是否在使用共享存儲。

常見問題:

(1)在源 host 上,出現  ”live Migration failure: operation failed: Failed to connect to remote libvirt URI qemu+tcp://compute2/system: unable to connect to server at 'compute2:16509': Connection refused“

其原因是 2.1.1 部分的 libvirt 設定不正确。

(2)在目的 host 上,出現 ”libvirtError: internal error: process exited while connecting to monitor: 2015-09-21T14:17:31.840109Z qemu-system-x86_64: -drive file=rbd:vms/6bef8898-85f9-429d-9250-9291a2e4e5ac_disk:id=cinder:key=AQDaoPpVEDJZHhAAu8fuMR/OxHUV90Fm1MhONQ==:auth_supported=cephx\;none:mon_host=9.115.251.194\:6789\;9.115.251.195\:6789\;9.115.251.218\:6789,if=none,id=drive-virtio-disk0,format=raw,cache=writeback,discard=unmap: could not open disk image rbd:vms/6bef8898-85f9-429d-9250-9291a2e4e5ac_disk:id=cinder:key=AQDaoPpVEDJZHhAAu8fuMR/OxHUV90Fm1MhONQ==:auth_supported=cephx\;none:mon_host=9.115.251.194\:6789\;9.115.251.195\:6789\;9.115.251.218\:6789: Could not open 'rbd:vms/6bef8898-85f9-429d-9250-9291a2e4e5ac_disk:id=cinder:key=AQDaoPpVEDJZHhAAu8fuMR/OxHUV90Fm1MhONQ==:auth_supported=cephx\;none:mon_host=9.115.251.194\:6789\;9.115.251.195\:6789\;9.115.251.218\:6789': Operation not permitted“

原因:目的 host 上的使用者操作 RBD 的權限設定不正确,檢查 secret 設定。

3. 遷移過程

3.0 Nova 有關遷移的指令

Nova 有三個與遷移有關的指令:migrate,live-migrate 和 resize。

Nova CLI REST API Action 行為
nova live-migration  --block-migrate  --disk_over_commit 8352e969-0a25-4abf-978f-d9d0ec4de0cd compute2 os-migrateLive 塊拷貝動态遷移
nova live-migration  8352e969-0a25-4abf-978f-d9d0ec4de0cd compute2 os-migrateLive 共享存儲動态遷移
nova migrate  8352e969-0a25-4abf-978f-d9d0ec4de0cd migrate 靜态遷移
nova resize --poll 8352e969-0a25-4abf-978f-d9d0ec4de0cd 1 resize 靜态遷移并且改變 flavor
nova resize --poll 8352e969-0a25-4abf-978f-d9d0ec4de0cd resize 靜态遷移
nova resize-confirm 9eee079e-0353-44cb-b76c-ecf9be61890d confirmResize 确認 resize 使得完整操作得以完成 
nova resize-revert 9eee079e-0353-44cb-b76c-ecf9be61890d revertResize 取消 resize 使得操作被取消虛機回到原始狀态 

3.1 靜态遷移(migrate 或者 resize 不使用新的 flavor)

[email protected]:~$ nova migrate --poll 9eee079e-0353-44cb-b76c-ecf9be61890d


Server migrating... 100% complete

Finished

[email protected]:~$ nova list

+--------------------------------------+-------+---------------+------------+-------------+------------------------------------------+

| ID | Name | Status | Task State | Power State | Networks |

+--------------------------------------+-------+---------------+------------+-------------+------------------------------------------+

| 02699155-940f-4401-bc01-36220db80639 | vm10 | ACTIVE | - | Running | demo-net2=10.0.10.17; demo-net=10.0.0.39 |

| 9eee079e-0353-44cb-b76c-ecf9be61890d | vm100 | VERIFY_RESIZE | - | Running | demo-net2=10.0.10.20 |

+--------------------------------------+-------+---------------+------------+-------------+------------------------------------------+ [email protected]:~$ nova resize-confirm 9eee079e-0353-44cb-b76c-ecf9be61890d

[email protected]:~$ nova list

+--------------------------------------+-------+--------+------------+-------------+------------------------------------------+

| ID | Name | Status | Task State | Power State | Networks |

+--------------------------------------+-------+--------+------------+-------------+------------------------------------------+

| 02699155-940f-4401-bc01-36220db80639 | vm10 | ACTIVE | - | Running | demo-net2=10.0.10.17; demo-net=10.0.0.39 |

| 9eee079e-0353-44cb-b76c-ecf9be61890d | vm100 | ACTIVE | - | Running | demo-net2=10.0.10.20 |

+--------------------------------------+-------+--------+------------+-------------+------------------------------------------+ 
           

3.1.1 遷移過程

 直接使用流程圖來說明:

【KVM系列08】使用 libvirt 遷移 QEMU/KVM 虛機和 Nova 虛機 
【KVM系列08】使用 libvirt 遷移 QEMU/KVM 虛機和 Nova 虛機 

1. migrate 和 resize 都是執行靜态遷移。

2. 靜态遷移分為三個階段:

(1)調用 Scheduler 算法選擇目的 node(步驟5),并通過 RPC 遠端調用 prep_resize 做些遷移前的準備工作

(2)在源主機上,調用 libvirt driver 做一系列操作:

  1. 使用 ssh 在目的 node 上建立虛機的鏡像檔案的目錄
  2. 将虛機關機
  3. 斷開所有 volume connections
  4. 針對每一個非 swap 分區的磁盤,如果是 qcow2 格式,則執行 qemu-img merge 操作将稀疏檔案和backing 檔案合并成單個檔案,并通過 “rysnc” 或者 “scp”指令将檔案拷貝到目的 node 上
  5. 開始遷移需要的網絡工作

(3)通過 RPC,調用目的 node 上的 Nova 的 finish_resize 方法。該方法會在自己本機上設定網絡、結束網絡設定工作,并調用 libvirt driver 來:

  1. 建立 image
  2. 擷取 guest xml
  3. 建立 domain 和 network
  4. 需要的話啟動虛機

至此,虛機已經被拷貝到目的主機上了。接下來,使用者有兩個選擇:resize_confirm 和 resize_revert。

3.1.2 确認遷移 (resize_confirm)

遷移确認後,在源主機上,虛機的檔案會被删除,虛機被 undefine,虛機的 VIF 被從 OVS 上拔出,network filters 也會被删除。

【KVM系列08】使用 libvirt 遷移 QEMU/KVM 虛機和 Nova 虛機 

3.1.3 取消遷移 (resize_revert)

取消遷移的指令首先發到目的 node 上,依次 tear down network,删除 domain,斷掉 volume connections,然後調用源主機上的方法來重建 network,删除臨時檔案,啟動 domain。這樣,虛機就會需要到 resize 之前的狀态。

【KVM系列08】使用 libvirt 遷移 QEMU/KVM 虛機和 Nova 虛機 

 3.2 實時遷移 (Live migration)

可以 Nova client 的 live-migration 指令來做實時遷移,除了要被遷移的 虛機 和 目的 node 外,它可以帶兩個額外的參數:

  • “block-migrate“:使用的話,做 block copy live migration;不使用的話,做共享存儲的 live migration;
  • ”disk_over_commit“:使用的話,計算所有磁盤的 disk_size 來計算目的 node 上所需空間的大小;不使用的話,則計算磁盤的 virtual size。在下面的例子中,如果使用 disk_over_commit,那麼計算在目的主機上需要的空間的時候,該 disk 的 size 為 324k,否則為 1G:
[email protected]:/home/s1# qemu-img info /var/lib/nova/instances/8352e969-0a25-4abf-978f-d9d0ec4de0cd/disk.local

image: /var/lib/nova/instances/8352e969-0a25-4abf-978f-d9d0ec4de0cd/disk.local

file format: qcow2 virtual size: 1.0G (1073741824 bytes)

disk size: 324K

cluster_size: 65536 backing file: /var/lib/nova/instances/_base/ephemeral_1_default

Format specific information:

compat: 1.1 lazy refcounts: false
           

REST API request body 執行個體: {"os-migrateLive": {"disk_over_commit": false, "block_migration": true, "host": "compute2"}}

實時遷移的主要步驟如下:

【KVM系列08】使用 libvirt 遷移 QEMU/KVM 虛機和 Nova 虛機 
【KVM系列08】使用 libvirt 遷移 QEMU/KVM 虛機和 Nova 虛機 

其過程也可以分為三個階段:

3.2.1 實時遷移前的準備工作 (步驟 2 - 7)

Nova 通過 RPC 調用目的主機上 nova comute manager 的 pre_live_migration 方法,它依次:

(1)準備 instance 目錄:

      (1)建立 instance dir

(2)如果源和目的虛機不共享 instance path:擷取鏡像類型,為每一個disk,如果不使用 backing file 的話則調用 “qemu-img create” 方法來建立空的磁盤鏡像;否則,依次建立空的 Ephemeral disk 和 Swap disk,以及從  Glance 中擷取 image 來建立 Root disk

(3)如果不是 block migration 而且 不 is_shared_instance_path,則 _fetch_instance_kernel_ramdisk

(2)調用 volumer driver api 為每一個volume 建立目的主機和 volume 的連接配接

(3)調用 plug_vifs(instance, network_info) 将每一個 vif plug 到 OVS 上

(4)調用 network_api.setup_networks_on_host 方法,該方法會為遷移過來的虛機準備 dhcp 和 gateway;

(5)調用 libvirt driver 的 ensure_filtering_rules_for_instance 方法去準備 network filters。

3.2.2 調用 libvirt API 開始遷移虛機 (步驟 8 - 9)

  這部分的實作在 libvirt driver 代碼中。因為 libvirt 的一個 bug (說明在這裡),當 libvirt 帶有 VIR_DOMAIN_XML_MIGRATABLE flag 時,Nova 會調用 libvirt 的 virDomainMigrateToURI2 API,否則調用 virDomainMigrateToURI API。

首先比較一下 block live migration 和 live migration 的 flags 的差別:

#nova block live migration flags:VIR_MIGRATE_UNDEFINE_SOURCE, VIR_MIGRATE_PEER2PEER, VIR_MIGRATE_LIVE, VIR_MIGRATE_TUNNELLED, VIR_MIGRATE_NON_SHARED_INC #nova live migration flags:      VIR_MIGRATE_UNDEFINE_SOURCE, VIR_MIGRATE_PEER2PEER, VIR_MIGRATE_LIVE, VIR_MIGRATE_TUNNELLED 
           

各自的含義如下:

  • VIR_MIGRATE_UNDEFINE_SOURCE: 遷移完成後,将源虛機删除掉。(If the migration is successful, undefine the domain on the source host.)
  • VIR_MIGRATE_PEER2PEER 和 VIR_MIGRATE_TUNNELLED 一起使用:點對點遷移,必須指定目的 URI,QEMU在兩者之間建立 TCP Tunnel 用于資料傳輸
  • VIR_MIGRATE_LIVE: 執行 live migration,不要停機 (Do not pause the VM during migration)
  • VIR_MIGRATE_NON_SHARED_INC: 使用非共享存儲式遷移即 block migration (Migration with non-shared storage with incremental disk copy)

再看看兩個 API 的參數:

int virDomainMigrateToURI2        (virDomainPtr domain, const char * dconnuri, # 格式為 qemu+tcp:///system const char * miguri, #為 none const char * dxml, #指定遷移後的虛機的 XML。Nova 對 “/devices/graphics” 部分做了一點點更改。 unsigned long flags, # nova.conf 中的配置 const char * dname, #none unsigned long bandwidth) # 由 CONF.libvirt.live_migration_bandwidth 指定,預設為 0 表示由 libvirt 自己選擇合适的值
           

如果 libvirt 不帶 VIR_DOMAIN_XML_MIGRATABLE flag,則調用的 API 是:

int virDomainMigrateToURI (virDomainPtr domain, const char * duri,

unsigned long flags, const char * dname,

unsigned long bandwidth)
           

可見,兩個 API 唯一的差別是不能指定新的虛機使用的 XML 配置。這時候你必須手動配置 VNC 或者 SPICE 位址為 0.0.0.0 or :: (接收全部)或者 127.0.0.1 or ::1 (隻限本機)。

調用 API 後,接下來就是等待其完成。這其中的過程應該主要包括:

(1)根據傳入的 domain xml,啟動一個虛機,它處于等待 TCP incoming 狀态

(2)從源 node 上将 domain 的資料傳過來

(3)快完成時,關閉源 node 上的虛機,傳輸最後一次資料,打開目的 node 上的虛機

(4)将源 node 上的虛機删除

Nova 每個0.5 秒檢查源虛機的狀态,直到它被删除。

遷移完成後,需要執行後續的操作(_post_live_migration)。

3.2.3 遷移完成後在源和目的主機上的後續操作(步驟 10 -29)

在源主機上,依次執行下面的操作:

  1. 調用 volume driver 的 disconnect_volume 方法和 terminate_connection 方法,斷開主機和所有 volume 的連接配接
  2. 調用 firewall driver 的 unfilter_instance 方法,删除 domain 的 iptables 中的所有 security group ingress 規則 (self.iptables.ipv4['filter'].remove_chain(chain_name))
  3. 調用 network api 的 migrate_instance_start 方法,開始将網絡從源主機上遷移到目的主機上(實際上沒做什麼事情,隻是 pass)
  4. 調用 vif driver 的 unplug 方法,将每個 vif 從 OVS 上删除                                                                                                                                      
    brctl delif qbr59cfa0b8-2f qvb59cfa0b8-2f
    
    ip link set qbr59cfa0b8-2f down
    
    brctl delbr qbr59cfa0b8-2f
    
    ovs-vsctl --timeout=120 -- --if-exists del-port br-int qvo59cfa0b8-2f
    
    ip link delete qvo59cfa0b8-2f
               
  5. 通過 RPC 調用目的主機上的 nova manager 的 post_live_migration_at_destination 方法,該方法會:
    1. 調用 network api 的 setup_networks_on_host 方法來設定網絡(處理 vpn,dhcp,gateway)
    2. 調用 network api 的 migrate_instance_finish 方法
    3. 調用 libvirt driver 的 post_live_migration_at_destination方法,它會調用 libvirt _conn.listDefinedDomains 方法檢視遷移過來的主機的 domain是否存在;不存在的話,生成其 xml,然後調用 libvirt API  _conn.defineXML(xml) 去定義該 domain。
    4. 将新的 domain 資料更新到資料庫(包括新的 host,power_state,vm_state,node)
    5. 調用 network api 的 setup_networks_on_host 方法 (不了解為什麼重複上面第1步)
  6. 調用 libvirt driver 的 driver.cleanup 方法去 _unplug_vifs (如果上面第四步失敗,則再次嘗試删除所有 vif 相關的 bridge 和 OVS 連接配接),firewall_driver.unfilter_instance (和上面第2步重複了),_disconnect_volume(斷開 domain 和 所有 volume 的連接配接),_delete_instance_files (删除 domain 相關的檔案),_undefine_domain (删除 domain)
  7. 調用 network_api.setup_networks_on_host 去 tear down networks on source host
  8. 至此,live migration 完全結束。

3.2.4 遷移過程中失敗時的復原

 遷移的三個步驟中,前面第一個和第二個步驟中出現失敗的話,會調用 _rollback_live_migration 啟動復原操作。該方法

(1)将虛機的狀态由 migrating 變為 running。

(2)調用 network_api.setup_networks_on_host 方法重做源主機上的網絡設定

(3)通過 RPC 調用,去目的主機上将準備過程中建立的 volume 連接配接删除。

(4)通過 RPC 調用,去目的主機上調用 compute_rpcapi.rollback_live_migration_at_destination 函數,該方法會

(1)調用 network_api.setup_networks_on_host 方法去 tear down networks on destination host

(2)調用 libvirt driver 的 driver.rollback_live_migration_at_destination 方法,它會将 domain 删除,并且清理它所使用的資源,包括  unplug vif,firewall_driver.unfilter_instance,_disconnect_volume, _delete_instance_files, _undefine_domain。

3.2.5 測試

環境:準備兩個虛機 vm1 和 vm2,作業系統為 cirros。打算将 vm1 遷移到另一個node 上。在 vm2 上檢視 vm1 在遷移過程中的狀态。

遷移前:在 vm1 中運作 “ping vm2”,并在 vm2 中 ssh 連接配接到 vm1。

結果:vm1 遷移過程中,vm2 上 ssh 的連接配接沒有中斷,vm1 中的 ping 指令繼續執行。在另一次測試結果中,vm2 ping vm1 在整個遷移過程中 time 出現了一次 2ms 的時間增加。

3.3 遇到過的問題

3.3.1 apparmor

将虛機從 compute1 遷移到 compute2 成功,再從 compute2 遷移到 compute1 失敗,報錯如下:

An error occurred trying to live migrate. Falling back to legacy live migrate flow. Error: unsupported configuration: Unable to find security driver for label apparmor
           

經比較遷移前後的虛機的 xml,發現 compute2 上的虛機的 xml 多了一項: 。

分别在 compute 1 和 2 上運作 “virsh capabilities”,發現 compute1 沒有使用 apparmor,而 compute2 使用了 apparmor。

#compute 1 上

none

0

#compute2 上

apparmor

0
           

    最簡單的方法是在兩個 node 上都 disable apparmor(在 /etc/libvirt/qemu.conf 中添加 ‘security_driver = “none”  然後重新開機 libvirtd),然後 destroy/start 虛機後,它的 xml 配置中的 apparmor 就沒有了。這篇文章 詳細介紹了 apparmor。

3.3.2 當虛機是 boot from volume 時,live migration 失敗。

報錯:

Command: iscsiadm -m node -T iqn.2010-10.org.openstack:volume-26446902-5a56-4c79-b839-a8e13a66dc7a -p 10.0.2.41:3260 --rescan

Exit code: 21 Stdout: u'' Stderr: u'iscsiadm: No session found.\n' to caller
           

原因是 cinder 代碼中有 bug,導緻目的主機無法建立和 volume 的連接配接。fix 在這裡。

參考文檔:

https://www.mirantis.com/blog/tutorial-openstack-live-migration-with-kvm-hypervisor-and-nfs-shared-storage/

http://www.sebastien-han.fr/blog/2015/01/06/openstack-configure-vm-migrate-nova-ssh/

KVM 原理技術 實戰與原了解析 任永傑、單海濤著

OpenStack 官網