写点什么

使用 pyVmomi 采集 vSphere 监控指标

用户头像
冯骐
关注
发布于: 2021 年 03 月 01 日

前言


VMware 在中小型的企业/单位里几乎是标配了。毕竟规模不大的情况下,性价比还是相当好的。对于 VMware 平台上各项指标,我们当然也要去做监控了。


我们使用了 pyVmomi 去连接 vCenter ,获取 ESXi 主机,datastore,VM 等性能指标,并推送给 Open-Falcon


需求


我们需要监控的内容包含:


  1. ESXi —— 主机的状态当然是要监控的

  2. datastore —— 所有挂载的存储,要监控他们的使用情况,剩余空间什么的

  3. vm —— 在提供租户服务时,在租户的服务器里直接塞 agent 肯定是不合适的。得通过 vSphere 来获取租户服务器的相关指标。有些虚拟机,比如思科语音的 CUCM 这种,也没法给他装 agent,只能通过 vSphere 来获取监控指标。


这些数据其实 vCenter 里全部都有,我们在 vCenter 内都能看得到。但是如果我们希望能够统一监控的平台,要把这些监控数据从 vCenter 里取出来,怎么做呢?


pyVmomi 是 VMware vSphere API 的一个 python sdk,我们可以利用它来与 vCenter 交互,获取我们需要的信息.


使用 pyVmomi 连接 vCenter


pyVmomi 说老实话,蛮复杂的。好在 VMware 官方提供了一大堆 example——pyvmomi-community-samples 可以参考。


安装 pyVmomi 也非常容易,pip install pyVmomi 就好了。


首先我们来连接一下 vCenter,与 example 不同的是,我们使用 SmartConnectNoSSL 来连接 vCenter,这样不会由于证书原因报错。


#!/usr/bin/env python#coding=utf-8 
import atexitfrom pyVmomi import vim, vmodlfrom pyVim.connect import SmartConnectNoSSL, Disconnect
def run(host,user,pwd,port): try: si = SmartConnectNoSSL(host=host, user=user, pwd=pwd, port=port) atexit.register(Disconnect, si) content = si.RetrieveContent() print "Hellow World!" except vmodl.MethodFault as error: print "Caught vmodl fault : " + error.msg return False, error.msg return True, "ok"
if __name__ == "__main__": host = "vcenter.host" user = "administrator@vpshere.local" pwd = "password" port = 443
print run(host,user,pwd,port)
复制代码

Hello World !


Hellow World!(True, 'ok')
复制代码

获取 Datastore 信息


在连接上 vCenter 之后,我们就可以开始获取各项指标了。我们从 content 下的根目录逐级开始遍历,他的第一个 childEntity 就是我们的 datacenter


print content.rootFolder.childEntity(ManagedObject) [   'vim.Datacenter:datacenter-2']
复制代码

datacenter 下面的 datastore 属性,即存储的相关信息


for datacenter in content.rootFolder.childEntity:    print "datacenter =",datacenter.name    print datacenter.datastore    datastores = datacenter.datastore    for ds in datastores:        print ds.summary
复制代码

我们可以通过 datacenter.name,获取 datacenter 的名字,在组织数据上报的时候,可以作为 tag 打在 datastore 上,可以区分 datastore 来自哪个 datacenter 。

datastore 的容量,类型等数据,则都在 datastore.summary 之中


datacenter = Datacenter(ManagedObject) [   'vim.Datastore:datastore-52',   'vim.Datastore:datastore-63',   'vim.Datastore:datastore-53',   'vim.Datastore:datastore-64',   'vim.Datastore:datastore-10'](vim.Datastore.Summary) {   dynamicType = <unset>,   dynamicProperty = (vmodl.DynamicProperty) [],   datastore = 'vim.Datastore:datastore-52',   name = 'datastore1',   url = 'ds:///vmfs/volumes/56c67e14-26d12426-719b-e02f6dbbf15a/',   capacity = 1189705940992L,   freeSpace = 1025420296192L,   uncommitted = 1005186974305L,   accessible = true,   multipleHostAccess = false,   type = 'VMFS',   maintenanceMode = 'normal'}(vim.Datastore.Summary) {   dynamicType = <unset>,   dynamicProperty = (vmodl.DynamicProperty) [],   datastore = 'vim.Datastore:datastore-63',   name = 'datastore1 (1)',   url = 'ds:///vmfs/volumes/56c6891b-a7873bd4-c83e-e86549552d90/',   capacity = 1189705940992L,   freeSpace = 1188680433664L,   uncommitted = <unset>,   accessible = true,   multipleHostAccess = false,   type = 'VMFS',   maintenanceMode = 'normal'}(vim.Datastore.Summary) {   dynamicType = <unset>,   dynamicProperty = (vmodl.DynamicProperty) [],   datastore = 'vim.Datastore:datastore-53',   name = 'datastore2',   url = 'ds:///vmfs/volumes/56c6a783-08a37b38-cd79-e02f6dbbf15a/',   capacity = 1197490569216L,   freeSpace = 302907392000L,   uncommitted = 589L,   accessible = true,   multipleHostAccess = false,   type = 'VMFS',   maintenanceMode = 'normal'}(vim.Datastore.Summary) {   dynamicType = <unset>,   dynamicProperty = (vmodl.DynamicProperty) [],   datastore = 'vim.Datastore:datastore-64',   name = 'datastore2 (1)',   url = 'ds:///vmfs/volumes/56c6a879-8b734ca2-dddd-e86549552d90/',   capacity = 1197490569216L,   freeSpace = 24554504192L,   uncommitted = 487L,   accessible = true,   multipleHostAccess = false,   type = 'VMFS',   maintenanceMode = 'normal'}(vim.Datastore.Summary) {   dynamicType = <unset>,   dynamicProperty = (vmodl.DynamicProperty) [],   datastore = 'vim.Datastore:datastore-10',   name = 'datastore1 (32)',   url = 'ds:///vmfs/volumes/59c3b795-2762392c-3cb9-3440b5d64c30/',   capacity = 590021132288L,   freeSpace = 232418967552L,   uncommitted = 2355216338L,   accessible = true,   multipleHostAccess = false,   type = 'VMFS',   maintenanceMode = 'normal'}
复制代码

获取 ESXi 信息


ESXi , 即我们 vSphere 集群中的主机信息,他在 pyVmomi 的结构是这样的:


Datacenter.hostFolder -> FolderhostFolder.childEntity -> list(Folder or ClusterComputeResource or ComputeResource)ClusterComputeResource.host -> list(HostSystem)ComputeResource.host -> list(HostSystem)
复制代码

Folder 即我们建立的文件夹,ClusterComputeResource 是主机集群,如果没有建立集群,Datacenter 下直接建立主机的话,则是 ComputeResource

Folder 的 childEntity 是一个 list,里面可能是 FolderClusterComputeResource 或 ComputeResource

ClusterComputeResource 和 ComputeResource 下的 host 也是一个 list,里面是主机的列表。


这里我们面临一个问题,因为 Folder 下面还可能嵌套 Folder,所以我们得写个递归来遍历。


def getComputeResource(Folder,computeResourceList):    if hasattr(Folder, 'childEntity'):        for computeResource in Folder.childEntity:           getComputeResource(computeResource,computeResourceList)    else:        computeResourceList.append(Folder)    return computeResourceList
复制代码

这样,我们就可以遍历所有的 Folder 拿到所有的 computeResource 和 ClusterComputeResource 了


for datacenter in content.rootFolder.childEntity:    print "datacenter =",datacenter.name    if hasattr(datacenter.hostFolder, 'childEntity'):        hostFolder = datacenter.hostFolder        computeResourceList = []        computeResourceList = getComputeResource(hostFolder,computeResourceList)        for computeResource in computeResourceList:            print computeResource.name            print computeResource.host
复制代码

类似的,主机的相关基本信息都在 host.summary 下面。其中 summary.quickStats 有一些基本的监控信息。cpu,mem 的总量则需要去 host.hardware 中去找。以下是某台主机的host.name host.hardware.cpuInfohost.hardware.memorySize 和 host.summary.quickStats


esxi = 192.168.179.120(vim.host.CpuInfo) {   dynamicType = <unset>,   dynamicProperty = (vmodl.DynamicProperty) [],   numCpuPackages = 2,   numCpuCores = 8,   numCpuThreads = 16,   hz = 2666761067L}memorySize = 34347229184(vim.host.Summary.QuickStats) {   dynamicType = <unset>,   dynamicProperty = (vmodl.DynamicProperty) [],   overallCpuUsage = 209,   overallMemoryUsage = 13881,   distributedCpuFairness = 10000,   distributedMemoryFairness = 574,   uptime = 2018927}
复制代码

perfManager


现在我们还有主机的网络数据没有拿到,这些在 quickStats 中是没有的。此时就需要通过 perfManager 来获取性能数据了。vSphere 中内置了大量的性能指标,可以从perfManager.perfCounter 中获取


perf_dict = {}perfList = content.perfManager.perfCounterfor counter in perfList:    counter_full = "{}.{}.{}".format(counter.groupInfo.key, counter.nameInfo.key, counter.rollupType)    perf_dict[counter_full] = counter.keyprint perf_dict
复制代码

通过 content.perfManager.perfCounter 我们可以拿到所有的指标 counter 和对应的 counter key,后面我们查询的时候会用到这个 counter key


我们先构造一个性能查询的函数


from datetime import timedelta
def BuildQuery(content, vchtime, counterId, instance, entity, interval): perfManager = content.perfManager metricId = vim.PerformanceManager.MetricId(counterId=counterId, instance=instance) startTime = vchtime - timedelta(seconds=(interval + 60)) endTime = vchtime - timedelta(seconds=60) query = vim.PerformanceManager.QuerySpec(intervalId=20, entity=entity, metricId=[metricId], startTime=startTime, endTime=endTime) perfResults = perfManager.QueryPerf(querySpec=[query]) if perfResults: return perfResults else: return False
复制代码

几个参数说明


  1. content —— 就是连接 vCenter后返回的那个 content

  2. vchtime —— 当前的时间,用来计算查询的时间范围,可以通过 si.CurrentTime() 去拿 vCenter 的当前时间,规避时间同步问题

  3. counterId —— 需要查询的 counter 所对应的 ID 号

  4. instance —— 需要返回的实例,某些指标会有多个实例。比如网络指标就对应有多张网卡。* 则表示全部返回

  5. entity —— 查询的对象,比如 ESXi 主机或者某个 虚拟机

  6. interval —— 查询的间隔,用来产生查询的时间范围。和我们上报监控的 step 保持一致。


使用 perfManger 查询性能时,需要给出查询的时间范围和查询的颗粒度。我这里使用的颗粒度是 intervalId = 20,也就是 20 秒一个点。提供的时间范围提前了 1 分钟,避免太接近当前时间的点上查不到数据。


一个返回的示例


(vim.PerformanceManager.EntityMetricBase) [   (vim.PerformanceManager.EntityMetric) {      dynamicType = <unset>,      dynamicProperty = (vmodl.DynamicProperty) [],      entity = 'vim.HostSystem:host-9',      sampleInfo = (vim.PerformanceManager.SampleInfo) [         (vim.PerformanceManager.SampleInfo) {            dynamicType = <unset>,            dynamicProperty = (vmodl.DynamicProperty) [],            timestamp = 2017-10-20T15:37:20Z,            interval = 20         },         (vim.PerformanceManager.SampleInfo) {            dynamicType = <unset>,            dynamicProperty = (vmodl.DynamicProperty) [],            timestamp = 2017-10-20T15:37:40Z,            interval = 20         },         (vim.PerformanceManager.SampleInfo) {            dynamicType = <unset>,            dynamicProperty = (vmodl.DynamicProperty) [],            timestamp = 2017-10-20T15:38:00Z,            interval = 20         }      ],      value = (vim.PerformanceManager.MetricSeries) [         (vim.PerformanceManager.IntSeries) {            dynamicType = <unset>,            dynamicProperty = (vmodl.DynamicProperty) [],            id = (vim.PerformanceManager.MetricId) {               dynamicType = <unset>,               dynamicProperty = (vmodl.DynamicProperty) [],               counterId = 148,               instance = ''            },            value = (long) [               10L,               4L,               5L            ]         },         (vim.PerformanceManager.IntSeries) {            dynamicType = <unset>,            dynamicProperty = (vmodl.DynamicProperty) [],            id = (vim.PerformanceManager.MetricId) {               dynamicType = <unset>,               dynamicProperty = (vmodl.DynamicProperty) [],               counterId = 148,               instance = 'vmnic3'            },            value = (long) [               0L,               0L,               0L            ]         },         (vim.PerformanceManager.IntSeries) {            dynamicType = <unset>,            dynamicProperty = (vmodl.DynamicProperty) [],            id = (vim.PerformanceManager.MetricId) {               dynamicType = <unset>,               dynamicProperty = (vmodl.DynamicProperty) [],               counterId = 148,               instance = 'vmnic2'            },            value = (long) [               0L,               0L,               0L            ]         },         (vim.PerformanceManager.IntSeries) {            dynamicType = <unset>,            dynamicProperty = (vmodl.DynamicProperty) [],            id = (vim.PerformanceManager.MetricId) {               dynamicType = <unset>,               dynamicProperty = (vmodl.DynamicProperty) [],               counterId = 148,               instance = 'vmnic1'            },            value = (long) [               10L,               4L,               5L            ]         },         (vim.PerformanceManager.IntSeries) {            dynamicType = <unset>,            dynamicProperty = (vmodl.DynamicProperty) [],            id = (vim.PerformanceManager.MetricId) {               dynamicType = <unset>,               dynamicProperty = (vmodl.DynamicProperty) [],               counterId = 148,               instance = 'vusb0'            },            value = (long) [               0L,               0L,               0L            ]         },         (vim.PerformanceManager.IntSeries) {            dynamicType = <unset>,            dynamicProperty = (vmodl.DynamicProperty) [],            id = (vim.PerformanceManager.MetricId) {               dynamicType = <unset>,               dynamicProperty = (vmodl.DynamicProperty) [],               counterId = 148,               instance = 'vmnic0'            },            value = (long) [               0L,               0L,               0L            ]         }      ]   }]
复制代码

起始的 sampleInfo 表示的是性能数据所在的时间点。我们查的 interval 是 60 秒,颗粒度是 20 秒。因此返回了 3 个点。value 是每个点上的值,instance 对应了不同的网卡,如vmnic0vmnic1 等,其中 instance = '' 表示这台主机所有的网卡流量总和,也就是这台机器总体的网络流量。


获取 VM 信息


类似的 , vm 的数据都在 Datacenter.vmFolder 下面,当然也会碰到 Folder 嵌套的问题。不过我们之所以遍历 Folder 去获取主机信息,主要是为了能够在遍历过程中,拿到主机上级的 Datacenter 和 Cluster 等信息,作为数据的 tag 一同报送给监控系统。


这些 tag 对于经常飘来飘去的 vm 而言就没太大的意义了,而且对于 Open-Falcon 这样的监控系统而言, tag 的变动意味着 counter 发生变更,数据会落入新的 rrd 当中,无疑是给自己找麻烦。


我们可以通过更简单的办法拿到所有的 vm 清单


obj = content.viewManager.CreateContainerView(content.rootFolder, [vim.VirtualMachine], True)for vm in obj.view    print vm.summary.quickStats
复制代码

与主机类似的,vm 的基本状态信息也在 summary.quickStats 内。而诸如网络流量,磁盘 IO 等信息也需要通过 perfManager 来获取。对应的 counter ID 也依然可以从 perfManager.perfCounter 中获取。


此时可能会发现部分 vm 是拿不到网络信息,也就是net.transmitted.average 和 net.received.average 这两个 counter 的性能数据。我们可以打开 vCenter 内虚拟机的性能图表,会发现网络图表也会显示 未指定衡量指标 或者 No Metric Specified


这是 VMware 的一个已知 bug,在 vSphere 6.0 Update 1 以上版本被修复。详见官方 Knowledge —— 在 VMware vSphere Client 6.0 中查看虚拟机网络的实时性能图表时显示错误:未指定衡量指标 (2125021)


轮子


如果你使用的监控系统是 Open-Falcon 的话,那么轮子已经造好了。可以直接使用 vsphere-monitor 进行监控。


vsphere-monitor 需要 python2.7 以上版本


安装


git clone https://github.com/freedomkk-qfeng/vsphere-monitor.gityum install -y python-virtualenvcd vsphere-monitorvirtualenv ./env./env/bin/pip install -r requirement.txt
复制代码

配置


配置文件是 config.py,修改它就可以了


# falconendpoint = "vcenter" # 上报给 open-falcon 的 endpointpush_api = "http://127.0.0.1:6060/api/push" # 上报的 http api 接口interval = 60 # 上报的 step 间隔
# vcenterhost = "vcenter.host" # vcenter 的地址user = "administrator@vsphere.local" # vcenter 的用户名pwd = "password" # vcenter 的密码port = 443 # vcenter 的端口
# esxiesxi_names = [] # 需要采集的 esxi ,留空则全部采集
# datastoredatastore_names = [] # 需要采集的 datastore ,留空则全部采集
# vmvm_enable = True # 是否要采集虚拟机信息vm_names = [ # 需要采集的虚拟机,留空则全部采集 "vm1", "vm2", "vm3" ]
复制代码

运行


先尝试跑一下,假定 vsphere-monitor 放在 /opt 下


/opt/vsphere-monitor/env/bin/python /opt/vsphere-monitor/vsphere-monitor.py
复制代码

没有问题的话,将他放入定时任务


crontab -e0-59/1 * * * * /opt/vsphere-monitor/env/bin/python /opt/vsphere-monitor/vsphere-monitor.py
复制代码

运行


ESXi 主机监控


image.png

VM 运行监控


image.png

参考文档


pyvmomi-community-samples

pyvmomi

在 VMware vSphere Client 6.0 中查看虚拟机网络的实时性能图表时显示错误:未指定衡量指标 (2125021)


以上


原文于 2017 年 10 月首发于简书,搬家存档。

行文有微调。


用户头像

冯骐

关注

教育行业码农 2020.06.19 加入

一个教育行业的码农

评论

发布
暂无评论
使用 pyVmomi 采集 vSphere 监控指标