概要
CBT を有効にした VM に対して QueryChangedDiskAreas を実行することでディスクの変更箇所を確認することができます (参考)
今回は govmomi で変更箇所を取得し VDDK API の VixDiskLib_Read
と VixDiskLib_Write
を使ってディスクの変更箇所 (増分) だけをディスクに追記してみました
環境
- CentOS 7
- VCSA 6.5.0 9451637
- VDDK API 6.7.1
- golang 1.11.2
事前作業
全く同じ状態のサーバを 2 台用意しましょう
そして 1 台 (src) を変更してその変更をもう 1 台 (dest) に反映する処理をします
同じサーバは VixDiskLib_QueryAllocatedBlocks
を使ってコピーを作成しても OK ですしクローンでも OK です
src での作業
CBT を有効にする
src (vm-10) の VM に対して CBT を有効にしましょう
こちらの記事を参考に mob を使ってやるのが簡単だと思います
CBT を有効化したらスナップショットを作成し ChangeID を取得しておきます (52 54 eb 64 13 82 bc 71-41 42 a2 b3 ba f4 96 2e/102
)
ここで取得した ChangeID は src の大元の ChangeID になります
この ChangeID とこの後新たに作成するスナップショットを使って QueryChangedDiskAreas を実行し増分情報を取得します
ChangeID を取得したらスナップショットは削除して OK です
増分を取得する
サーバは起動するだけでもまだ増分が出るので起動するだけでも良いですがそれだとつまらないので適当にファイルでも作成しましょう
一旦 src サーバを起動して以下のファイルを作成しましょう
date > src_file.txt
ファイルを作成したら VM を一旦停止しスナップショットを新たに作成しましょう (snapshot-20)
そしてそのスナップショットと先ほどメモしておいた ChangeID を使って増分情報を取得します
package main
import (
"context"
"flag"
"fmt"
"net/url"
"os"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/types"
)
var envURL = "https://192.168.100.20/sdk"
var user = "administrator@vsphere.local"
var pass = "xxxxxxxxxxxx"
var vmname = "src"
var diskNum int32 = 2000
var changeID = "52 54 eb 64 13 82 bc 71-41 42 a2 b3 ba f4 96 2e/102"
var urlDescription = fmt.Sprintf("ESX or vCenter URL [%s]", envURL)
var urlFlag = flag.String("url", envURL, urlDescription)
var envInsecure = true
var insecureDescription = fmt.Sprintf("Don't verify the server's certificate chain [%s]", envInsecure)
var insecureFlag = flag.Bool("insecure", envInsecure, insecureDescription)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
flag.Parse()
u, err := url.Parse(*urlFlag)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
u.User = url.UserPassword(user, pass)
c, err := govmomi.NewClient(ctx, u, *insecureFlag)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
f := find.NewFinder(c.Client, true)
dc, err := f.DefaultDatacenter(ctx)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
f.SetDatacenter(dc)
ss := &types.ManagedObjectReference{Type: "VirtualMachineSnapshot", Value: "snapshot-20"}
// change disk areas
query := new(types.QueryChangedDiskAreas)
query.ChangeId = changeID
query.DeviceKey = diskNum
query.Snapshot = ss
query.StartOffset = 0
query.This.Type = "VirtualMachine"
query.This.Value = "vm-10"
res, err := methods.QueryChangedDiskAreas(ctx, c.RoundTripper, query)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
for _, area := range res.Returnval.ChangedArea {
fmt.Printf("%d,%d\n", area.Start, area.Length)
}
}
QueryChangedDiskAreas は vSphere API なので golang からコールします
しかし VDDK API は c++ から呼ぶので連携するために CSV ファイルを作成します
go build github.com/hawksnowlog/j
go install github.com/hawksnowlog/j
$GOPATH/bin/j > changed_areas.csv
この CSV 増分の offset と length が羅列されています
1048576,65536
135266304,131072
143654912,65536
155189248,65536
155713536,65536
511705088,1703936
...
dest での作業
dest VM は念の為停止しておきましょう
増分を書き込む
先程の CSV を使って VDDK API で増分のセクタ情報を書き込みます
まずコードは以下の通りです
#include <iostream>
#include <cstring>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include "vixDiskLib.h"
using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::ifstream;
using std::istringstream;
#define VIXDISKLIB_VERSION_MAJOR 6
#define VIXDISKLIB_VERSION_MINOR 7
static struct {
VixDiskLibConnection connection;
char *libdir;
char *cfgFile;
} params, wparams;
static void LogFunc(const char *fmt, va_list args) {
printf("Log: ");
vprintf(fmt, args);
}
static void WarnFunc(const char *fmt, va_list args) {
printf("Warning: ");
vprintf(fmt, args);
}
static void PanicFunc(const char *fmt, va_list args) {
printf("Panic: ");
vprintf(fmt, args);
exit(10);
}
static vector<string> split(string& input, char delimiter) {
istringstream stream(input);
string field;
vector<string> result;
while (getline(stream, field, delimiter)) {
result.push_back(field);
}
return result;
}
int main(int argc, char* argv[]) {
VixDiskLibHandle _handle = NULL;
VixDiskLibHandle _whandle = NULL;
try {
VixError err;
err = VixDiskLib_InitEx(VIXDISKLIB_VERSION_MAJOR, VIXDISKLIB_VERSION_MINOR, &LogFunc, &WarnFunc, &PanicFunc, params.libdir, "init.cfg");
printf("%lu\n", err);
err = VixDiskLib_InitEx(VIXDISKLIB_VERSION_MAJOR, VIXDISKLIB_VERSION_MINOR, &LogFunc, &WarnFunc, &PanicFunc, wparams.libdir, "init.cfg");
printf("%lu\n", err);
VixDiskLibConnectParams cnxParams = {0};
cnxParams.vmxSpec = {(char*)"moref=vm-10"};
cnxParams.specType = VIXDISKLIB_SPEC_VMX;
cnxParams.serverName = {(char*)"192.168.100.20"};
cnxParams.credType = VIXDISKLIB_CRED_UID;
cnxParams.creds.uid.userName = {(char*)"administrator@vsphere.local"};
cnxParams.creds.uid.password = {(char*)"xxxxxxxxxxxx"};
cnxParams.thumbPrint = {(char*)"96:09:d6:5b:e0:83:58:1b:ba:2b:cc:78:22:88:33:36:64:50:32:eb"};
err = VixDiskLib_ConnectEx(&cnxParams, 1, NULL, NULL, ¶ms.connection);
printf("%lu\n", err);
VixDiskLibConnectParams wcnxParams = {0};
wcnxParams.vmxSpec = {(char*)"moref=vm-20"};
wcnxParams.specType = VIXDISKLIB_SPEC_VMX;
wcnxParams.serverName = {(char*)"192.168.100.20"};
wcnxParams.credType = VIXDISKLIB_CRED_UID;
wcnxParams.creds.uid.userName = {(char*)"administrator@vsphere.local"};
wcnxParams.creds.uid.password = {(char*)"xxxxxxxxxxxx"};
wcnxParams.thumbPrint = {(char*)"96:09:d6:5b:e0:83:58:1b:ba:2b:cc:78:22:88:33:36:64:50:32:eb"};
err = VixDiskLib_ConnectEx(&wcnxParams, 1, NULL, NULL, &wparams.connection);
printf("%lu\n", err);
err = VixDiskLib_Open(params.connection, "[datastore2] src/src.vmdk", VIXDISKLIB_FLAG_OPEN_SINGLE_LINK, &_handle);
printf("%lu\n", err);
err = VixDiskLib_Open(wparams.connection, "[datastore2] dest/dest.vmdk", VIXDISKLIB_FLAG_OPEN_SINGLE_LINK, &_whandle);
printf("%lu\n", err);
ifstream ifs("changed_areas.csv");
string line;
while (getline(ifs, line)) {
vector<string> strvec = split(line, ',');
VixDiskLibSectorType offset = stoul(strvec.at(0)) / VIXDISKLIB_SECTOR_SIZE;
VixDiskLibSectorType length = stoul(strvec.at(1)) / VIXDISKLIB_SECTOR_SIZE;
printf("offset: %lu\n", offset);
printf("length: %lu\n", length);
VixDiskLibSectorType start = offset;
VixDiskLibSectorType bufSize = 128;
VixDiskLibSectorType count = length / bufSize;
for (VixDiskLibSectorType j = 0; j < count; j++) {
uint8 *buf = new uint8[bufSize * VIXDISKLIB_SECTOR_SIZE];
// read
err = VixDiskLib_Read(_handle, start + (j * bufSize), bufSize, buf);
if (err != VIX_OK) {
throw std::exception();
}
// write
err = VixDiskLib_Write(_whandle, start + (j * bufSize), bufSize, buf);
if (err != VIX_OK) {
throw std::exception();
}
delete[] buf;
}
}
VixDiskLib_Close(_whandle);
VixDiskLib_Close(_handle);
VixDiskLib_Disconnect(params.connection);
} catch (...) {
VixDiskLib_Close(_whandle);
VixDiskLib_Close(_handle);
VixDiskLib_Disconnect(params.connection);
}
return 0;
}
解説
CSV の読み込みは ifstream
を使います
必要になるヘッダファイルがあるので冒頭で追加しています
読み込んだ CSV の行数分ループさせます
実は CSV の offset と length の値はそのままでは使えません
VDDK API の世界で使える単位にしなければいけないので VIXDISKLIB_SECTOR_SIZE
(512) で割る必要があります
(どこにもこの情報がなく初めはそのまま使っていたのですがうまく行かずいろいろ試してたどり着きました、、)
VixDiskLibSectorType offset = stoul(strvec.at(0)) / VIXDISKLIB_SECTOR_SIZE;
VixDiskLibSectorType length = stoul(strvec.at(1)) / VIXDISKLIB_SECTOR_SIZE;
あとは基本的には[フルバックアップ時[()に行ったように書き込めば OK です
bufSize
を上げれば Read/Write の速度が上昇します
増分はそこまで多くないのであれば 128 で十分だと思います
動作確認
実際に VDDK API 側のコードを動かして dest VM を起動すると src VM 側に作成した src_file.txt
があるのが確認できると思います
この方法だと確かに増分のファイルだけ送ることができるようになります
ですが src 側の kernel ログなども書き込んでしまいます
dest があくまでも src 側のバックアップサーバなのであれば問題ないですが、そうでない場合は src と dest のログが混在するケースが発生するので注意が必要です
最後に
QueryChangedDiskAreas を使って増分バックアップを実装してみました
後で調べてみたのですが QueryChangedDiskAreas にバグのような挙動があるらしく vmdk のサイズを後から拡大するとおかしな offset と length を返すようです (参考1, 参考2)
今回使用した vSphere 6.5 環境でその現象になるかまでは試していません
最新版は 6.7 なので最新版では解決している可能性もあります
そもそもこのバグらしく挙動をどうやって再現するのかから考えないとダメそうですが、、
0 件のコメント:
コメントを投稿