[zz]OpenStack中虛擬機器的監控
整個方案的基本思想是由host負責執行程式,採集資料,額外一臺伺服器作為server收集每臺host的資料進行分析。本文涉及的程式程式碼均可以從Github上下載,虛擬化使用kvm,使用libvirt作為C API。
AD:
本文涉及的程式程式碼均可以從我的github上下載, 並且持續更新程式碼
虛擬化使用kvm,使用libvirt作為C API
基本思想:host負責執行程式,採集資料,額外一臺伺服器作為server收集每臺host的資料進行分析
程式介紹:
首先我們需要開啟一個和hypervisor的連線,需要一個virConnectPtr的指標
virConnectOpenReadOnly(char *) 返回的就是這麼一個指標。初始化程式例如:
void conn_init(char *ip, virConnectPtr *conn) { *conn = NULL; /* the hypervisor connection */ char *p; p = (char *)malloc(35*sizeof(char)); *conn = virConnectOpenReadOnly(p); free(p); if (*conn == NULL) { fprintf(stderr, "Failed to connect to hypervisor\n"); } }
第二個引數是一個指向virConnectPtr變數的指標,這裡的p指向的是類似“qemu+ssh://10.0.0.1/system”的字串,10.0.0.1是你的host ip
關閉連線的函式
void conn_close(virConnectPtr *conn) { if (*conn != NULL) virConnectClose(*conn); }
現在我們有了一個指向host的hypervisor的連線,我們可以用他來獲得host上跑的虛擬機器的情況
void list_id_domain(virConnectPtr conn) { int ids[10]; int maxids=10; int num, i; num = virConnectListDomains(conn, ids, maxids); for(i = 0;i < num;i++) { printf("%d\n",ids[i]); } }
這個函式使用上面得到的conn這個指標,列出host上跑的例項的id號
有了id號我們就可以獲取每個例項的詳細資訊,假設我有一個id為7的虛擬機器例項:
virDomainPtr dom = NULL; dom = virDomainLookupByID(conn, 7);
dom這個變數就是以後我們要一直用到的,釋放函式:
virDomainFree(dom);
cpu監控程式:
void list_info_domain(virDomainPtr domain) { virDomainInfo info; int interval = 2; struct timeval startTime; struct timeval endTime; int realTime; int cpuTime; double cpuUsage; virDomainGetInfo(domain, &info); unsigned long long startCpuTime = info.cpuTime; if (gettimeofday(&startTime, NULL) == -1) { printf("Failed to get start time\n"); } sleep(interval); virDomainGetInfo(domain, &info); unsigned long long endCpuTime = info.cpuTime; if (gettimeofday(&endTime, NULL) == -1) { printf("Failed to get end time\n"); } cpuTime = (endCpuTime - startCpuTime)/1000; realTime = 1000000 * (endTime.tv_sec - startTime.tv_sec) + (endTime.tv_usec - startTime.tv_usec); cpuUsage = cpuTime / (double)(realTime); printf("\t\tstate is %d\n", info.state); printf("\t\tvCPU is %d\n", info.nrVirtCpu); printf("\t\tMAXmemory is %ld\n", info.maxMem/1024); printf("\t\tmemory is %ld\n", info.memory/1024); printf("\t\tcpuUsage is %.2f%\n", cpuUsage*100); }
解釋一下程式,首先virDomainGetInfo函式,傳入剛才我們得到的domain,另外一個引數是要返回的virDomainInfo的結構體變數,其中包含了cpu個數,分配的時間,和分配的mem資訊。我們分別取了間隔為2妙的info資訊,使用裡面的info.cpuTime執行時間,把後一次減去前一次,然後再除以實際的gettimeofday函式得到host的cpu執行時間,得到一個近似的百分比,反應的是此虛擬機器例項的cpu使用情況在整個host的cpu使用情況中的百分比。
磁碟監控:
void list_disk_domain(virDomainPtr domain) { virDomainBlockStatsStruct stats; size_t size; const char *disk = "vda"; size = sizeof(stats); int interval = 2; virDomainBlockStats(domain, disk, &stats, size); long long start_rd_bytes = stats.rd_bytes; long long start_wr_bytes = stats.wr_bytes; sleep(interval); virDomainBlockStats(domain, disk, &stats, size); long long end_rd_bytes = stats.rd_bytes; long long end_wr_bytes = stats.wr_bytes; long rd_bytes = end_rd_bytes - start_rd_bytes; long wr_bytes = end_wr_bytes - start_wr_bytes; int rd_usage = rd_bytes/interval; int wr_usage= wr_bytes/interval; // printf("%s:\n", virDomainGetName(domain)); printf("\t\tread: %dbytes/s\n", rd_usage); printf("\t\twrite: %dbytes/s\n", wr_usage); printf("\t\trd_req: %lld\n", stats.rd_req); printf("\t\trd_bytes: %lld\n", stats.rd_bytes); printf("\t\twr_req: %lld\n", stats.wr_req); printf("\t\twr_bytes: %lld\n", stats.wr_bytes); }
磁碟使用情況的方法和cpu類似,這裡用到的是virDomainBlockStats(domain, disk, &stats, size)這個函式,disk指向的字串這裡為“vda”,實際使用甚麼你要根據xml裡面的資訊
網路部份這裡我們要用到libvirt中的Network Filters
openstack例項的libvirt.xml在nova.conf中定義的例項目錄下,裡面有
... <interface type='bridge'> <source bridge='br100'/> <mac address='02:16:3e:23:f3:7d'/> <model type='virtio'/> <filterref filter="nova-instance-instance-00000007-02163e23f37d"> <parameter name="IP" value="10.200.200.56" /> <parameter name="DHCPSERVER" value="10.200.200.54" /> </filterref> </interface> ...
filterref中包括了其他的filters,預設在/etc/libvirt/nwfilter目錄下
你也可以使用virsh管理工具檢視具體filter內容
# virsh nwfilter-dumpxml nova-instance-instance-00000007-02163e23f37d Filtering chains
Filtering chains就是你在目錄下看到的許多filters的檔案。譬如有arp, 有dhcp, mac等
在程式中使用libvirt
int list_network_domain(virDomainPtr domain) { const char *path; virDomainInterfaceStatsStruct stats; size_t size; size = sizeof(stats); path = "vnet1"; int interval = 2; if( virDomainInterfaceStats(domain, path, &stats, size) ) return FALSE; long long start_rx_bytes = stats.rx_bytes; long long start_tx_bytes = stats.tx_bytes; sleep(interval); if( virDomainInterfaceStats(domain, path, &stats, size) ) return FALSE; long long end_rx_bytes = stats.rx_bytes; long long end_tx_bytes = stats.tx_bytes; int rx_usage = (end_rx_bytes - start_rx_bytes)/interval; int tx_usage = (end_tx_bytes - start_tx_bytes)/interval; printf("\t\trx usage: %d bytes/s", rx_usage); printf("\trx bytes: %lld bytes", stats.rx_bytes); printf("\t\trx packets: %lld", stats.rx_packets); printf("\trx errs: %lld\n", stats.rx_errs); printf("\t\ttx usage: %d bytes/s", tx_usage); printf("\ttx bytes: %lld bytes", stats.tx_bytes); printf("\t\ttx packets: %lld", stats.tx_packets); printf("\ttx errs: %lld\n", stats.tx_errs); }
這個函式很重要,其中將返回stats指標所指向的內容便是domain中各個網口的資訊。這裡有個問題,就是path的值,他是由domain中網絡卡的名字,不是eth0也不是em0等,而是要通過獲取domain的xml中<device>網絡卡的interface部分中<target dev="vnet0">這一部分中的vnet0,同理前面說得disk裡面的“vda”也是從這裡獲取,
所以你需要執行一下這個程式
char *xmldesc; xmldesc = virDomainGetXMLDesc(dom, 0); if ((fp = fopen(virDomainGetName(dom), "w")) == NULL) { printf("Cannot open file test\n"); } fprintf(fp,xmldesc); fclose(fp); free(xmldesc);
返回的是字串指標指向了xml的內容,記住這個程式執行好需要free指標。