崔廣章 杜程 劉睿
摘 要:研究旨在通過修改Collectd源碼來驗證其現(xiàn)有的內(nèi)存監(jiān)控項目數(shù)據(jù)的準確性,完善內(nèi)存相關(guān)監(jiān)控項。文章首先將Collectd進行部署,觀察其對KVM虛擬機內(nèi)存監(jiān)控的效果,然后通過對KVM虛擬機內(nèi)存監(jiān)控的源碼分析,驗證監(jiān)控圖上展示的相關(guān)內(nèi)存監(jiān)控項目的數(shù)據(jù)準確性,并根據(jù)其結(jié)果決定修改源碼來完善相關(guān)內(nèi)存監(jiān)控項的可行性。通過文中的分析得出結(jié)論:只有在Libvirt對KVM虛擬機內(nèi)存監(jiān)控相關(guān)功能進行API優(yōu)化之后,Collectd對KVM虛擬機的內(nèi)存監(jiān)控才能夠得到完善。從而為不用在KVM虛擬機里安裝任何東西的情況下實現(xiàn)對其內(nèi)存的監(jiān)控找到了一種切實可行的方法。
關(guān)鍵詞:Collectd;KVM;內(nèi)存監(jiān)控;準確性
中圖分類號:TP319 文獻標識碼:A 文章編號:2095-1302(2016)05-00-05
0 引 言
隨著互聯(lián)網(wǎng)技術(shù)的快速發(fā)展,各種云計算平臺也應(yīng)運而生。國外有AbiCloud、Hadoop(Apache基金會)、Eucalyptus 項目(加利福尼亞大學)、MongoDB(10gen)、Enomalism彈性計算平臺等,國內(nèi)有阿里巴巴的阿里云、中國移動的BigCloude-大云平臺、盛大云等。虛擬化是云計算平臺的基礎(chǔ),虛擬機的穩(wěn)定性直接影響到云計算平臺的性能,這就迫切需要一套能夠?qū)μ摂M機進行實時監(jiān)控的系統(tǒng)。但是,目前還沒有一套針對該問題成熟的監(jiān)控系統(tǒng)方案,本文用Collectd對KVM(Kernel-based Virtual Machine,KVM)虛擬機進行監(jiān)控。
1 Collectd對KVM虛擬機監(jiān)控的測試
1.1 關(guān)于Collectd
Collectd是一個守護(daemon)進程,通過調(diào)用Libvirt-API來周期性地收集系統(tǒng)的各項數(shù)據(jù),然后以一定的格式(比如以RRD形式)將數(shù)據(jù)進行存儲,這些數(shù)據(jù)可以用來找到當前系統(tǒng)性能的瓶頸。
Libvirt是一套免費、開源的支持Linux主流虛擬化工具的C函數(shù)庫,其旨在為包括Xen在內(nèi)的各種虛擬化工具提供一套方便、可靠的編程接口,支持與C、C++、Ruby、Python等多種主流開發(fā)語言的綁定。當前主流Linux平臺上默認的虛擬化管理工具virt-manager(圖形化)、virt-install(命令行模式)等均基于Libvirt開發(fā)而成。Libvirt庫是一種實現(xiàn)Linux虛擬化功能的Linux? API,它支持各種虛擬機監(jiān)控程序,包括Xen和KVM以及QEMU和用于其他操作系統(tǒng)的一些虛擬產(chǎn)品。Collectd通過Libvirt監(jiān)控虛擬機的原理圖如圖1所示。
圖1 Collectd通過Libvirt監(jiān)控虛擬機原理圖
通過Collectd對KVM虛擬機進行監(jiān)控的優(yōu)勢是:首先,Collectd只需要安裝在宿主機(Host)的底層,不必在虛擬機內(nèi)部安裝代理(Agent),這樣就避免了監(jiān)控程序?qū)μ摂M機的污染,使監(jiān)測到的虛擬機的各項數(shù)據(jù)更加精確;其次,由于監(jiān)控程序是安裝在宿主機的底層,用戶(虛擬機的使用者)沒有權(quán)限來關(guān)閉該監(jiān)控程序,這樣就避免了用戶將監(jiān)控程序關(guān)閉而造成的監(jiān)控系統(tǒng)監(jiān)控不到虛擬機問題的發(fā)生。
1.2 測試環(huán)境
本文通過兩臺DELL服務(wù)器部署Collectd進行測試,一臺服務(wù)器上部署Collectd計算節(jié)點,另一臺服務(wù)器部署Collectd控制節(jié)點。部署計算節(jié)點的服務(wù)器同時安裝KVM,并通過KVM在該服務(wù)器上創(chuàng)建10臺虛擬機。測試環(huán)境的詳細說明如表1所列。
控制節(jié)點與計算節(jié)點配置文件如表2所列。
表2 配置文件詳解
IP 節(jié)點類型 關(guān)鍵配置項
10.10.10.20 計算節(jié)點 Hostname "compute1"
FQDNLookup true
Interval 10
Timeout 2
ReadThreads 5
LoadPlugin logfile
LoadPlugin syslog
LogLevel info
LoadPlugin cpu
LoadPlugin disk
LoadPlugin libvirt
LoadPlugin load
LoadPlugin memory
LoadPlugin network
LoadPlugin ping
LoadPlugin swap
PrintSeverity false
value_t values[1]; //創(chuàng)建只有一個元素的value_t結(jié)構(gòu)體數(shù)組
value_list_t vl = VALUE_LIST_INIT; //創(chuàng)建并初始化一個value_list_t鏈表
init_value_list (&vl, dom); 用虛擬機dom給鏈表vl賦值
values[0].gauge = memory; //用內(nèi)存memory給結(jié)構(gòu)體數(shù)組values的0號元素的gauge賦值
vl.values = values; //用結(jié)構(gòu)體數(shù)組給鏈表vl的values賦值
vl.values_len = 1; //給鏈表vl的values_len賦值為1
sstrncpy (vl.type, “memory”, sizeof (vl.type)); //表明監(jiān)控指標類型為內(nèi)存
//表明監(jiān)控指標為tags指明的監(jiān)控項
sstrncpy (vl.type_instance, tags[tag_index], sizeof (vl.type_instance));
plugin_dispatch_values (&vl); //將vl的數(shù)據(jù)傳給系統(tǒng)
}
場景函數(shù)詳解如下:
for (i = 0; i < nr_domains; ++i) {
virDomainInfo info;//定義一個虛擬機信息結(jié)構(gòu)體
virVcpuInfoPtr vinfo = NULL;
virDomainMemoryStatPtr minfo = NULL;//定義一個虛擬機內(nèi)存結(jié)構(gòu)體指針
int status;
int j;
//調(diào)用virDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info)給info賦值
status = virDomainGetInfo (domains[i], &info);
……
cpu_submit (info.cpuTime, domains[i], “virt_cpu_total”);
//將虛擬機地址以及虛擬機的總內(nèi)存信息傳給memory_submit (gauge_t memory(內(nèi)存值), virDomainPtr dom(虛擬機地址))
memory_submit ((gauge_t) info.memory * 1024, domains[i]);
vinfo = malloc (info.nrVirtCpu * sizeof (vinfo[0]));
……
//給虛擬機內(nèi)存狀態(tài)結(jié)構(gòu)體指針minfo分配內(nèi)存
minfo = malloc (VIR_DOMAIN_MEMORY_STAT_NR * sizeof (virDomainMemoryStatStruct));
if (minfo == NULL) {
ERROR (“virt plugin: malloc failed.”);
continue;
}
//調(diào)用函數(shù)virDomainMemoryStats (virDomainPtr dom, virDomainMemoryStatPtr stats,
unsigned int nr_stats, unsigned int flags)來給虛擬機內(nèi)存狀態(tài)指針minfo賦值并返回對應(yīng)的內(nèi)存狀態(tài)個數(shù)
status =virDomainMemoryStats (domains[i], minfo, VIR_DOMAIN_MEMORY_STAT_NR, 0);for (j = 0; j < status; j++) {
//將從函數(shù)virDomainMemoryStats(…)得到的狀態(tài)標簽及對應(yīng)的內(nèi)存值傳給函數(shù)memory_stats_submit(…)
memory_stats_submit ((gauge_t) minfo[j].val * 1024, domains[i], minfo[j].tag);
}
sfree (minfo);
}
虛擬機信息結(jié)構(gòu)體詳解如下所示:
struct virDomainInfo {
unsigned charstate;//虛擬機的運行狀態(tài)
unsigned longmaxMem;//虛擬機的最大內(nèi)存
unsigned longmemory;//虛擬機的已用內(nèi)存
unsigned shortnrVirtCpu;//虛擬機的虛擬CPU個數(shù)
unsigned long longcpuTime;//虛擬機的納秒級時間片
}
虛擬機內(nèi)存狀態(tài)結(jié)構(gòu)體及結(jié)構(gòu)體指針詳解如下:
typedef virDomainMemoryStatStruct * virDomainMemoryStatPtr
struct virDomainMemoryStatStruct {
int tag;//內(nèi)存項標簽
unsigned long long val;//對應(yīng)tag的內(nèi)存值
}
虛擬機內(nèi)存狀態(tài)函數(shù)詳解如下:
int virDomainMemoryStats (virDomainPtr dom(虛擬機指針),
virDomainMemoryStatPtr stats(虛擬機內(nèi)存狀態(tài)指針),
unsigned int nr_stats(虛擬機狀態(tài)數(shù)),
unsigned int flags(官方推薦填0))
nr_stats個虛擬機內(nèi)存狀態(tài)會被對應(yīng)的虛擬機賦值,但該函數(shù)只返回目前Libvirt版本支持的數(shù)據(jù)。
2.2 內(nèi)存監(jiān)控源碼優(yōu)化
由對源碼的分析可知,在collectd/src/virt.c的源碼中,對內(nèi)存(總內(nèi)存除外)進行監(jiān)控的關(guān)鍵函數(shù)見表3所列。
表3 內(nèi)存監(jiān)控關(guān)鍵函數(shù)詳解
函數(shù)原形 函數(shù)作用
Int virDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info) 用指定的虛擬機給虛擬機信息結(jié)構(gòu)體指針賦值,從而得到虛擬機的總內(nèi)存
Static void memory_submit (gauge_t memory(內(nèi)存值), virDomainPtr dom(虛擬機地址)) 將由函數(shù)Int virDomainGetInfo(…)得到的虛擬機總內(nèi)存及對應(yīng)虛擬機傳遞給系統(tǒng)(Collectd)
Int virDomainMemoryStats (virDomainPtr dom, virDomainMemoryStatPtr stats,
unsigned int nr_stats, unsigned int flags) 用虛擬機指針對應(yīng)的虛擬機給虛擬機內(nèi)存狀態(tài)結(jié)構(gòu)體指針對應(yīng)的結(jié)構(gòu)體賦值,并返回對應(yīng)虛擬機的內(nèi)存狀態(tài)個數(shù)
static void memory_stats_submit (gauge_t memory, virDomainPtr dom, int tag_index)
將調(diào)用函數(shù)Int virDomainMemoryStats(…)得到的函數(shù)虛擬機內(nèi)存項目標簽及對應(yīng)的內(nèi)存值傳遞給系統(tǒng)(Collectd)
基于相關(guān)源碼分析,結(jié)合優(yōu)化目標,通過如下步驟進行優(yōu)化:
(1) 對collectd/src/virt.c中調(diào)用Lbvirt-API的int virDomainMemoryStats (virDomainPtr dom, virDomainMemoryStatPtr stats, unsigned int nr_stats, unsigned int flags)函數(shù)返回了幾個tag及判斷tag對應(yīng)的內(nèi)存鍵控制是否正確;
(2) 在步驟(1)得到解決的前提下,本文再通過在collectd/src/virt.c中加入想要監(jiān)控和展示的相關(guān)內(nèi)存項的代碼來實現(xiàn)增加相關(guān)監(jiān)控項的優(yōu)化目標。
通過在表5場景函數(shù)詳解中增加打印函數(shù)將調(diào)用Libvirt-API的int virDomainMemoryStats (virDomainPtr dom, virDomainMemoryStatPtr stats, unsigned int nr_stats, unsigned int flags)函數(shù)返回的tag值并將對應(yīng)的內(nèi)存值打印出來,具體修改如下:
for (i = 0; i < nr_domains; ++i) {
virDomainInfo info;
virVcpuInfoPtr vinfo = NULL;
virDomainMemoryStatPtr minfo = NULL;
int status;
int j;
status = virDomainGetInfo (domains[i], &info);
……
status =virDomainMemoryStats (domains[i], minfo, VIR_DOMAIN_MEMORY_STAT_NR, 0);
if (status < 0) {
ERROR (“virt plugin: virDomainMemoryStats failed with status %i.”, status);
sfree (minfo);
continue;
}
//在下面的for循環(huán)中添加文件打印函數(shù)來調(diào)用Libvirt-API的int virDomainMemoryStats (virDomainPtr dom, virDomainMemoryStatPtr stats, unsigned int nr_stats, unsigned int flags)函數(shù)返回的tag值并將對應(yīng)的內(nèi)存值打印出來
for (j = 0; j < status; j++) {
FILE *fp;//定義一個文件描述符
fp=fopen(“/root/test.txt”,”a”);//在/root下打開一個可進行內(nèi)容追加的test.txt文件
//將tag及對應(yīng)內(nèi)存值寫入test.txt文件
fprintf(fp,”tag:%d\t的memory值:%d\n”,minfo[j].tag,minfo[j].val);
fclose(fp);//將打開的文件關(guān)閉
memory_stats_submit ((gauge_t) minfo[j].val * 1024, domains[i], minfo[j].tag);
}
sfree (minfo);
}
除了添加數(shù)值打印函數(shù)外,還要在collectd/src/virt.c的頭文件中添加:#include
圖3 打印輸出的tag值及對應(yīng)的內(nèi)存值
圖3只是截取了test.txt中的一部分,因為tag:0、tag:7、tag:6是一個循環(huán),本文所示的圖3中只截取了眾多循環(huán)當中的5個循環(huán)。
分析圖3可知,調(diào)用Libvirt-API的int virDomainMemoryStats (virDomainPtr dom, virDomainMemoryStatPtr stats, unsigned int nr_stats, unsigned int flags)函數(shù)返回的tag值只有0、7、6這三個,傳遞給函數(shù)static void memory_stats_submit (gauge_t memory, virDomainPtr dom, int tag_index)對應(yīng)的tag是swap_in、rss、actual_balloon,與虛擬機內(nèi)存監(jiān)控圖上列出的一致,但是這三個tag值對應(yīng)的內(nèi)存值一直不變,這是虛擬機內(nèi)存監(jiān)控圖上相關(guān)監(jiān)控項目一直是直線的根本原因。
3 結(jié) 語
根據(jù)內(nèi)存監(jiān)控源碼優(yōu)化的分析,得出如下結(jié)果:
(1)內(nèi)存監(jiān)控圖上的相關(guān)曲線之所以一直是直線,是因為調(diào)用Libvirt-API的int virDomainMemoryStats (virDomainPtr dom, virDomainMemoryStatPtr stats, unsigned int nr_stats, unsigned int flags)函數(shù)取出的值都是一直不變的,并非因為圖形展示或其他原因造成的;
(2)調(diào)用Libvirt-API的int virDomainMemoryStats (virDomainPtr dom, virDomainMemoryStatPtr stats, unsigned int nr_stats, unsigned int flags)函數(shù)得到的tag值只有0、7、6三個而且都已經(jīng)在內(nèi)存監(jiān)控圖上得到展示,所以目前無法修改源碼添加其他內(nèi)存監(jiān)控項。
通過以上分析,本文得出結(jié)論:在Libvirt對KVM虛擬機內(nèi)存監(jiān)控的相關(guān)API功能得到實現(xiàn)與優(yōu)化之后,Collectd對KVM虛擬機的內(nèi)存監(jiān)控項目就能補全,監(jiān)控結(jié)果的準確性就能得到保障。也就是說本文為不用在KVM虛擬機里安裝任何東西的情況下實現(xiàn)對其內(nèi)存進行監(jiān)控找到了一種切實可行的方法。
參考文獻
[1]湯儒,李秦偉.Openstack云環(huán)境中KVM虛擬機性能分析[J].微型機與應(yīng)用,2013,32(23):94-96,100.
[2]鄧文洋.虛擬化環(huán)境下的多機內(nèi)存優(yōu)化技術(shù)研究與實現(xiàn)[D].北京:首都師范大學,2013.
[3]杜煒.KVM客戶機主動共享的內(nèi)存超量使用策略研究[D].杭州:杭州電子科技大學,2013.
[4]時衛(wèi)東.基于內(nèi)核的虛擬機的研究[D].長春:吉林大學,2011.
[5]王春光.虛擬操作系統(tǒng)行為監(jiān)控技術(shù)的研究與實現(xiàn)[D].長沙:國防科學技術(shù)大學,2009.
[6] libvirt部分API介紹[EB/OL].2015.06, http://www.bubuko.com/infodetail-903462.html