2015年7月6日 星期一

備份/搬家/還原/大量複製 openvz 虛擬機

備份/搬家/還原/大量複製 openvz 虛擬機 如果單純只是想要備份還原一兩部 openvz 虛擬機, 「布丁布丁吃什麼?」 有兩篇圖文並茂的詳盡說明: 採用 proxmox 網頁介面 採用命令列。 但如果你遇到跟我一樣的狀況, 需要幫學生或客戶一口氣建立幾十部 openvz 虛擬機, 那當然就要採用命令列的方式比較快。 本文精簡地重述如何用命令列備份還原, 並且把重點放在 「批次/大量複製」。

一、 備份/搬家/還原

Proxmox 預設把虛擬機的備份檔放在 /var/lib/vz/dump。 所以比方說想要手動備份 499 號虛擬機, 可以這樣下: vzdump 499 -dumpdir /var/lib/vz/dump -compress lzo

搬家很簡單: 直接把剛剛產生的 /var/lib/vz/dump/vzdump-openvz-499-2015_07_05-15_41_07.tar.lzo 用 scp 指令拷貝到新家的同一目錄, 那麼從新家的網頁介面 (https://...:8006) 也可以看見這個手動產生、 搬過來的備份。 (如果只想在原本的 host node 上面大量複製, 就省略此步驟。)

還原也很簡單: vzrestore /var/lib/vz/dump/vzdump-openvz-499-2015_07_05-15_41_07.tar.lzo 499 如果在新家並不需要 499 號機, 也可省略這步。 事實上可以還原成其他任意 vmid (例如 401 或 402 等等), 所以這同時也是複製指令。

二、 整齊畫一的大量生產

接下來要用 499 號機的備份檔來大量複製產生 401 到 406 共六部虛擬機。

行前提醒: 開著 ssh 服務掛在網路上是很危險的。 建議先確認 499 號機已做好 ssh 基本防護, 別像我一樣等到每小時被企圖入侵上千次才驚醒。

先產生一個文字檔 ids.txt, 內含 01 到 06: perl -e 'for $i (1..6) { printf "%02d\n", $i; }' > ids.txt 然後一口氣 「還原」 (其實是 「複製」) 產生這六部虛擬機: for i in $(cat ids.txt) ; do vzrestore /var/lib/vz/dump/vzdump-openvz-499-2015_07_05-15_41_07.tar.lzo 4$i ; done

三、 初步個性化

複製完之後, 每一部機器的主機名稱跟 ip 位址應該要改過來。 尤其是如果換了 host node, 網段可能會不一樣。

但是依據虛擬機所採用的網路介面不同, /etc/vz/conf/*.conf 會有不同的設法:

  1. 如果你採用的是比較簡單/安全/高效率的 venet, 那麼設定檔裡面會有一句 IP_ADDRESS="..."; 等一下要用 --ipdel 跟 --ipadd 來修改這個設定。
  2. 如果你採用的是功能較完整但較複雜的 veth, 那麼設定檔裡面會有一句 NETIF="..."

本節適用於第一種狀況。 看起來 官網的虛擬機複製教學 也只處理第一種狀況。 (事實上我覺得那一頁還漏了一些重要設定 -- 如果不採用備份還原而是採用手動複製根目錄, 那麼應該還要手動修改設定檔內的 VE_ROOT 跟 VE_PRIVATE。)

先手動修改一部機器做實驗 (就拿原來的 499 吧) 直到你滿意這個指令為止。 例如根據我 (自己任意規定的) 主機名稱及 IP 位址設定原則, 應該這樣修改第 499 號虛擬機:

vzctl set 499 --hostname dp499 --ipdel all --ipadd 10.167.49.91 --save

於是把這一句話貼到 fix499.sh 文字檔內, 然後下: for i in $(cat ids.txt) ; do perl -pe "s/49\.91/4991/; s/499/4$i/g" fix499.sh ; done | perl -pe 's/\.(4\d)(\d1)/21.$1.$2/; s/\.0(\d)/.$1/'

這會產生如下的輸出:

vzctl set 401 --hostname dp401 --ipdel all --ipadd 10.167.40.11 --save
vzctl set 402 --hostname dp402 --ipdel all --ipadd 10.167.40.21 --save
vzctl set 403 --hostname dp403 --ipdel all --ipadd 10.167.40.31 --save
vzctl set 404 --hostname dp404 --ipdel all --ipadd 10.167.40.41 --save
vzctl set 405 --hostname dp405 --ipdel all --ipadd 10.167.40.51 --save
vzctl set 406 --hostname dp406 --ipdel all --ipadd 10.167.40.61 --save

剪貼一兩句、 執行一下。 如果一切看來 OK, 就把上述結果再 pipe 給 bash (... | bash) 完成初步個性化, 讓每部機器有別於其他複製兄弟。 以下幾點說明:

  1. 以上兩段底線部分是為了處理我特殊指定 ip 方式: vmid (401、 402、 ... 406) 被句點拆成兩段了, 所以先刪掉句點, 等全面代換完畢, 再把句點塞回去。 如果你指定 ip 的方式比較簡單, 可以直接省略那兩段。
  2. 要記得把 IP 裡面的 .05 改成 .5 等等。
  3. 第一段 perl 後面要用雙引號, 這樣來自 shell 的 $i 才會正確代換。
  4. 第二段 perl 後面要用單引號, 這樣 (來自 perl 內部、 前半句比對結果的) $1 跟 $2 才會正確代換。

然後請略過下一節, 直接跳到第五節。

四、 veth 類型虛擬網卡的 mac address

如果你的虛擬機 (一開始就採用, 或是 後來才改) 採用 veth, 那麼上一節還是要照著做, 但其中的 --ipdel 跟 --ipadd 處理 ip address 的段落可以忽略。 這一節處理 veth 的狀況。

這個直接改設定檔比較快。 請先 grep -i mac /etc/vz/conf/499.conf 查看一下原來的 mac address 設定。 我看到的類似這樣:

NETIF="ifname=eth0,mac=12:34:56:78:9A:BC,host_ifname=veth499.0,host_mac=FE:DC:BA:98:76:54,bridge=vmbr79"

所以我這樣修改 499 號機的 mac address: perl -i.bak -pe "s/(mac=[\w:]{14}):\w\w/\$1:99/gi" /etc/vz/conf/499.conf 這會直接把 /etc/vz/conf/499.conf 設定檔裡的兩個 mac 的尾巴都改成 99, 並且產生一個備份檔 /etc/vz/conf/499.conf.bak。 以下幾點請注意:

  1. 備份檔僅一次有效。 後來再產生的新備份檔會蓋掉先前的舊備份檔!
  2. (我的錯誤經驗) 如果閒著無聊, 你也可以 用 md5sum 產生 Mac address, 但最好不要去動 mac address 的第一個 byte, 因為根據 這個問答, 第 2 位必須是 2 或 6 或 A 或 E。
  3. 這裡我很偷懶。 嚴格來講, 應該要先把十進位代號轉成十六進位, 再填入最後一個 byte。

成功之後, 再包上一層 (shell 的) for 迴圈: for i in $(cat ids.txt) ; do perl -i.bak -pe "s/(mac=[\w:]{14}):\w\w/\$1:$i/gi" /etc/vz/conf/4$i.conf

五、 啟動

不論你的網路介面是 venet 還是 veth, 至此, 已可啟動複製機大軍: for i in $(cat ids.txt) ; do vzctl start 4$i ; done

六、 sshd 的金鑰

在每一部機器的 /etc/ssh 底下, 放著 ssh 連線金鑰。 就像公寓裡的每個單元長像相同、 但鎖頭不同, 這些複製機的金鑰也不應該相同。 所以我們把每一部機器的金鑰刪掉, 重新再產生一次屬於它自己的金鑰: for i in $(cat ids.txt) ; do vzctl exec $i rm /etc/ssh/ssh_host*key* ; vzctl exec $i dpkg-reconfigure openssh-server ; done 當然, 前提是每部複製虛擬機都已啟動才能用 exec。

同理, 如果你採用自己認證的憑證來提供 https 服務, 那麼也應該幫每部機器重新產生它專屬的憑證。

七、 結論

當然, 你也可以試著把這些步驟寫成一個 shell script。 問題是:

  1. 每位 proxmox 系統管理員的命名/指定 IP 規則可能很不一樣。 不同的管理員終究還是需要去改程式碼。
  2. 系統管理遇到大量/批次處理的動作時, 最好先小量單獨測試比較保險。

基於這兩個理由, 我覺得還是照著本文略改、 逐步實作比較實在。 同時也會順便學到 shell 的 for 迴圈、 命令結果代換、 regexp、 以及... perl 萬歲! (暑假我想學 python, 本來想用 python 做, 不過最後還是選擇言簡意胲、 一語中的的 perl。)

沒有留言:

張貼留言

因為垃圾留言太多,現在改為審核後才發佈,請耐心等候一兩天。