使用 pyVmomi 采集 vSphere 监控指标

作者: freedomkk_qfeng | 来源:发表于2017-10-24 11:09 被阅读0次
    image.png

    前言

    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 atexit
    from pyVmomi import vim, vmodl
    from 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 -> Folder
    hostFolder.childEntity -> list(Folder or ClusterComputeResource or ComputeResource)
    ClusterComputeResource.host -> list(HostSystem)
    ComputeResource.host -> list(HostSystem)
    

    Folder 即我们建立的文件夹,ClusterComputeResource 是主机集群,如果没有建立集群,Datacenter 下直接建立主机的话,则是 ComputeResource
    FolderchildEntity 是一个 list,里面可能是 Folder, ClusterComputeResourceComputeResource
    ClusterComputeResourceComputeResource 下的 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 拿到所有的 computeResourceClusterComputeResource

    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.cpuInfo, host.hardware.memorySizehost.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.perfCounter
    for counter in perfList:
        counter_full = "{}.{}.{}".format(counter.groupInfo.key, counter.nameInfo.key, counter.rollupType)
        perf_dict[counter_full] = counter.key
    print 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 去获取主机信息,主要是为了能够在遍历过程中,拿到主机上级的 DatacenterCluster 等信息,作为数据的 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.averagenet.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.git
    yum install -y python-virtualenv
    cd vsphere-monitor
    virtualenv ./env
    ./env/bin/pip install -r requirement.txt
    
    配置

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

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

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

    /opt/vsphere-monitor/env/bin/python /opt/vsphere-monitor/vsphere-monitor.py
    

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

    crontab -e
    0-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)

    以上

    转载授权

    CC BY-SA

    相关文章

      网友评论

        本文标题:使用 pyVmomi 采集 vSphere 监控指标

        本文链接:https://www.haomeiwen.com/subject/lgwjuxtx.html