Ceilometer項目源碼分析----ceilometer-agent-compute服務的初始化和啓動

感謝朋友支持本博客,歡迎共同探討交流,由於能力和時間有限,錯誤之處在所難免,歡迎指正!

如果轉載,請保留作者信息。
博客地址:http://blog.csdn.net/gaoxingnengjisuan
郵箱地址:[email protected]

PS:最近沒有登錄博客,很多朋友的留言沒有看見,這裏道歉!還有就是本人較少上QQ,可以郵件交流。


ceilometer-agent-compute服務的初始化和啓動

    本篇博客將解析服務組件ceilometer-agent-compute的初始化和啓動操作,ceilometer-agent-compute服務組件主要用來收集計算節點上的虛擬機實例的監控數據,在每一個計算節點上都要運行這個服務組件,該agent通過Stevedore管理了一組pollster插件,分別用來獲取計算節點上虛擬機的CPU,Disk IO,Network IO,Instance這些信息,這些信息大部分是通過調用Hypervisor的API來獲取的,需要定期Poll輪詢收集信息。
    來看方法/ceilometer/cli.py----def agent_compute,這個方法即實現了ceilometer-agent-compute服務的初始化和啓動操作。

def agent_compute():
    """
    加載並啓動指定的服務compute_manager.AgentManager();
    ceilometer.poll.compute:
    該組件用來收集計算節點上的信息,在每一個計算節點上都要運行一個Compute Agent,
    該Agent通過Stevedore管理了一組pollster插件,分別用來獲取虛擬機的CPU,Disk IO,
    Network IO,Instance這些信息,值得一提的是這些信息大部分是通過調用Hypervisor的API
    來獲取的,目前,Ceilometer提供了Libvirt的API和Hyper-V的API。
    """
    service.prepare_service()
    os_service.launch(compute_manager.AgentManager()).wait()

1 服務ceilometer-agent-compute的初始化操作

服務ceilometer-agent-compute的初始化操作主要實現了以下內容的操作:

(1)根據指定參數獲取命名空間ceilometer.poll.compute,獲取與ceilometer.poll.compute相匹配的所有插件,並加載;ceilometer.poll.compute所指定的插件描述瞭如何獲取收集所監控的虛擬機實例相關的監控採樣數據;
(2)根據指定參數獲取命名空間ceilometer.discover,獲取與ceilometer.discover相匹配的所有插件,並加載;ceilometer.discover所指定的插件描述瞭如何發現主機上的監控的虛擬機。
ceilometer.discover = local_instances = ceilometer.compute.discovery:InstanceDiscovery
(3)獲取管理員操作的上下文環境類的初始化對象;
(4)建立線程池,用於後續服務中若干操作的運行;

(5)加載命名空間ceilometer.compute.virt,描述了獲取虛擬機實例的信息(實例數據,CPU數據,網卡數據和磁盤數據等)的方式;

class AgentManager(agent.AgentManager)----def __init__

class AgentManager(agent.AgentManager):
    def __init__(self):
       
        super(AgentManager, self).__init__('compute', ['local_instances'])      
        """
        定義獲取虛擬機實例的信息(實例數據,CPU數據,網卡數據和磁盤數據等)的方式;
        加載命名空間ceilometer.compute.virt;
        ceilometer.compute.virt =
            libvirt = ceilometer.compute.virt.libvirt.inspector:LibvirtInspector
            hyperv = ceilometer.compute.virt.hyperv.inspector:HyperVInspector
            vsphere = ceilometer.compute.virt.vmware.inspector:VsphereInspector
        """
        self._inspector = virt_inspector.get_hypervisor_inspector()
class AgentManager(os_service.Service)----def __init__
class AgentManager(os_service.Service):

    def __init__(self, namespace, default_discovery=[]):
        super(AgentManager, self).__init__()
        self.default_discovery = default_discovery
        """
        加載命名空間ceilometer.poll.compute的所有插件:
        ceilometer.poll.compute =
        disk.read.requests = ceilometer.compute.pollsters.disk:ReadRequestsPollster
        disk.write.requests = ceilometer.compute.pollsters.disk:WriteRequestsPollster
        disk.read.bytes = ceilometer.compute.pollsters.disk:ReadBytesPollster
        disk.write.bytes = ceilometer.compute.pollsters.disk:WriteBytesPollster
        disk.read.requests.rate = ceilometer.compute.pollsters.disk:ReadRequestsRatePollster
        disk.write.requests.rate = ceilometer.compute.pollsters.disk:WriteRequestsRatePollster
        disk.read.bytes.rate = ceilometer.compute.pollsters.disk:ReadBytesRatePollster
        disk.write.bytes.rate = ceilometer.compute.pollsters.disk:WriteBytesRatePollster
        cpu = ceilometer.compute.pollsters.cpu:CPUPollster
        cpu_util = ceilometer.compute.pollsters.cpu:CPUUtilPollster
        network.incoming.bytes = ceilometer.compute.pollsters.net:IncomingBytesPollster
        network.incoming.packets = ceilometer.compute.pollsters.net:IncomingPacketsPollster
        network.outgoing.bytes = ceilometer.compute.pollsters.net:OutgoingBytesPollster
        network.outgoing.packets = ceilometer.compute.pollsters.net:OutgoingPacketsPollster
        network.incoming.bytes.rate = ceilometer.compute.pollsters.net:IncomingBytesRatePollster
        network.outgoing.bytes.rate = ceilometer.compute.pollsters.net:OutgoingBytesRatePollster
        instance = ceilometer.compute.pollsters.instance:InstancePollster
        instance_flavor = ceilometer.compute.pollsters.instance:InstanceFlavorPollster
        memory.usage = ceilometer.compute.pollsters.memory:MemoryUsagePollster
        """
        self.pollster_manager = self._extensions('poll', namespace)

        """
        加載命名空間所有插件ceilometer.discover:
        ceilometer.discover =
        local_instances = ceilometer.compute.discovery:InstanceDiscovery
        """
        self.discovery_manager = self._extensions('discover')
        self.context = context.RequestContext('admin', 'admin', is_admin=True)
class AgentManager(os_service.Service)----def _extensions
    def _extensions(category, agent_ns=None):  
        """ 
        根據指定參數獲取命名空間namespace, 
        獲取與namespace相匹配的所有插件,並加載; 
        """  
        namespace = ('ceilometer.%s.%s' % (category, agent_ns) if agent_ns else 'ceilometer.%s' % category)  
              
        """ 
        獲取與namespace相匹配的所有插件,並加載; 
        """  
        return extension.ExtensionManager(  
            namespace=namespace,  
            invoke_on_load=True,  
        )  
class Service(object)----def __init__
    class Service(object):  
        """Service object for binaries running on hosts."""  
        def __init__(self, threads=1000):  
            self.tg = threadgroup.ThreadGroup(threads)  
      
            # signal that the service is done shutting itself down:  
            self._done = event.Event()  


2 服務ceilometer-agent-compute的啓動操作

這裏服務ceilometer-agent-compute服務ceilometer-agent-central的啓動操作的實現代碼是完全一致的,只不過所獲取的監控任務不同而已,所調用的採集監控項採樣數據的方法不同而已;

服務ceilometer-agent-compute的啓動操作週期性地實現以下任務:
(1)遍歷任務(通道),獲取每個任務指定獲取的監控項的採樣數據;
(2)針對每個監控項的採樣數據,實現發佈監控項採樣數據樣本到消息隊列,其中實現採樣數據發佈的方式有三種,即RPC/UDP/FILE;

     其中,RPC將會發布相關消息到消息隊列,後續的collector組件服務將會監聽相應的消息隊列來獲取這些數據信息;UDP將會建立socket建立一個信息通道,實現發送相關消息數據,而後續的collector組件服務將會通過這個信息通道接收相關的消息數據;FILE將會直接保存相關消息數據到指定的日誌文件中。

class AgentManager(os_service.Service)----def start
class AgentManager(os_service.Service):  
    def start(self):  
        self.pipeline_manager = pipeline.setup_pipeline()  
        for interval,task in self.setup_polling_tasks().iteritems():  
            self.tg.add_timer(interval,  
                              self.interval_task,  
                              task=task)  
class AgentManager(os_service.Service)----def interval_task
def interval_task(task):  
    task.poll_and_publish()  
class PollingTask(object)----def poll_and_publish

class AgentManager(os_service.Service):
    def poll_and_publish(self):
        """
        創建輪徇的任務;
        任務以一定時間間隔週期性地進行;
        
        遍歷任務(通道),獲取每個任務指定獲取的監控項的採樣數據;
        針對每個監控項的採樣數據,實現發佈監控項採樣數據樣本到消息隊列;
        """
        """
        通過nova客戶端訪問nova數據庫,獲取本地主機上所有的虛擬機實例;
        ""
        agent_resources = self.manager.discover()
        with self.publish_context as publisher:
            cache = {}    
            """
            遍歷任務(通道);
            獲取每個任務指定獲取的監控項的採樣數據;
            """
            for pollster in self.pollsters:
                key = pollster.name
                LOG.info(_("Polling pollster %s"), key)
                source_resources = list(self.resources[key].resources)
                try:
                    """
                    get_samples:獲取nova管理下虛擬機實例的某一監控項的採樣數據;
                    注:這些信息大部分是通過調用Hypervisor的API來獲取的,Ceilometer提供了Libvirt的API和Hyper-V的API。
                    class CPUPollster(plugin.ComputePollster)----def get_samples(self, manager, cache, resources)
                    class CPUUtilPollster(plugin.ComputePollster)----def get_samples(self, manager, cache, resources)
                    class _Base(plugin.ComputePollster)----def get_samples(self, manager, cache, resources)
                    class _DiskRatesPollsterBase(plugin.ComputePollster)----def get_samples(self, manager, cache, resources)
                    class InstancePollster(plugin.ComputePollster)----def get_samples(manager, cache, resources)
                    class InstanceFlavorPollster(plugin.ComputePollster)----def get_samples(manager, cache, resources)
                    class MemoryUsagePollster(plugin.ComputePollster)----def get_samples(self, manager, cache, resources)
                    class _Base(plugin.ComputePollster)----def get_samples(self, manager, cache, resources)
                    """
                    samples = list(pollster.obj.get_samples(
                        self.manager,
                        cache,
                        resources=source_resources or agent_resources,
                    ))
                    
                    """
                    實現發佈監控項採樣數據樣本;
                    """
                    publisher(samples)
                except Exception as err:
                    LOG.warning(_(
                        'Continue after error from %(name)s: %(error)s')
                        % ({'name': pollster.name, 'error': err}),
                        exc_info=True)

方法小結:
本方法週期性地執行以下操作:
(1)遍歷任務(通道),獲取每個任務指定獲取的監控項的採樣數據;
(2)針對每個監控項的採樣數據,實現發佈監控項採樣數據樣本到消息隊列;

class PublishContext----def __enter__

在上述代碼中,發佈監控項採樣樣本數據是由這個方法實現的,這個方法主要實現了以下內容:

實現發佈監控項採樣數據樣本,其中實現採樣數據發佈的方式有三種,即RPC/UDP/FILE;
(1)class FilePublisher(publisher.PublisherBase)----def publish_samples(self, context, samples);
實現發佈採樣數據到一個日誌文件;
(2)class RPCPublisher(publisher.PublisherBase)----def publish_samples(self, context, samples);
實現通過RPC發佈採樣數據;
* 從若干採樣數據信息samples中獲取提取數據形成信息格式meters,爲信息的發佈或存儲做準備;
* 將之前從採樣數據中提取的信息meters包裝成msg;
* 將匹配的topic,msg添加到本地隊列local_queue中,topic默認爲metering;
* 實現發佈本地隊列local_queue中的所有數據信息到隊列metering中;
* 其中,消息msg中封裝的'method'方法爲'record_metering_data',即當消息被消費時,將會執行方法record_metering_data,實現存儲到數據存儲系統中(數據庫);
(3)class UDPPublisher(publisher.PublisherBase)----def publish_samples(self, context, samples)
通過UDP發佈採樣數據;

    class PublishContext(object):  
        def __enter__(self):  
            """ 
            實現發佈監控項採樣數據樣本; 
            publish_samples: 
            1.class FilePublisher(publisher.PublisherBase)----def publish_samples(self, context, samples); 
            實現發佈採樣數據到一個日誌文件; 
            2.class RPCPublisher(publisher.PublisherBase)----def publish_samples(self, context, samples); 
            通過RPC發佈採樣數據; 
            * 從若干採樣數據信息samples中獲取提取數據形成信息格式meters,爲信息的發佈或存儲做準備; 
            * 將之前從採樣數據中提取的信息meters包裝成msg; 
            * 將匹配的topic,msg添加到本地隊列local_queue中,topic默認爲metering; 
            * 實現發佈本地隊列local_queue中的所有數據信息到隊列metering中; 
            * 其中,消息msg中封裝的'method'方法爲'record_metering_data',即當消息被消費時,將會 
              執行方法record_metering_data,實現存儲到數據存儲系統中(數據庫); 
            3.class UDPPublisher(publisher.PublisherBase)----def publish_samples(self, context, samples) 
            通過UDP發佈採樣數據; 
            """  
            def p(samples):  
                for p in self.pipelines:  
                    p.publish_samples(self.context,  
                                      samples)  
            return p

2.1 實現發佈採樣數據到一個日誌文件

    class FilePublisher(publisher.PublisherBase):  
        def publish_samples(self, context, samples):  
            if self.publisher_logger:  
                for sample in samples:  
                    self.publisher_logger.info(sample.as_dict())  
2.2 通過RPC發佈採樣數據(具體見代碼註釋)

    class RPCPublisher(publisher.PublisherBase):  
        def publish_samples(self, context, samples):  
            """ 
            通過RPC發佈信息; 
            1.從若干採樣數據信息samples中獲取提取數據形成信息格式meters,爲信息的發佈或存儲做準備; 
            2.將之前從採樣數據中提取的信息meters包裝成msg; 
            3.將匹配的topic,msg添加到本地隊列local_queue中,topic默認爲metering; 
            4.實現發佈本地隊列local_queue中的所有數據信息到隊列metering中; 
            5.其中,消息msg中封裝的'method'方法爲'record_metering_data',即當消息被消費時,將會 
              執行方法record_metering_data,實現存儲到數據存儲系統中(數據庫); 
            """  
      
            # 從若干採樣數據信息中獲取提取數據形成信息格式,爲信息的發佈或存儲做準備;  
            meters = [  
                # meter_message_from_counter:  
                # 爲一個監控採樣數據做好準備被髮布或存儲;  
                # 從一個採樣數據信息中獲取提取信息形成msg;  
                utils.meter_message_from_counter(  
                    sample,  
                    cfg.CONF.publisher.metering_secret)  
                for sample in samples  
            ]  
      
            # cfg.CONF.publisher_rpc.metering_topic:metering messages所使用的主題,默認爲metering;  
            topic = cfg.CONF.publisher_rpc.metering_topic  
              
            # 將之前從採樣數據中提取的信息meters包裝成msg;  
            msg = {  
                'method': self.target,  
                'version': '1.0',  
                'args': {'data': meters},  
            }  
              
            # 將匹配的topic,msg添加到本地隊列local_queue中,topic默認爲metering;  
            self.local_queue.append((context, topic, msg))  
      
            if self.per_meter_topic:  
                for meter_name, meter_list in itertools.groupby(  
                        sorted(meters, key=operator.itemgetter('counter_name')),  
                        operator.itemgetter('counter_name')):  
                    msg = {  
                        'method': self.target,  
                        'version': '1.0',  
                        'args': {'data': list(meter_list)},  
                    }  
                    topic_name = topic + '.' + meter_name  
                    LOG.audit(_('Publishing %(m)d samples on %(n)s') % (  
                              {'m': len(msg['args']['data']), 'n': topic_name}))  
                    self.local_queue.append((context, topic_name, msg))  
      
            # 實現發佈本地隊列local_queue中的所有數據信息;  
            self.flush()  
    def flush(self):  
        """ 
        實現發佈本地隊列中的所有數據信息; 
        """          
        # 獲取本地隊列的數據信息;  
        queue = self.local_queue  
        self.local_queue = []  
              
        # 實現循環發佈隊列queue中的信息數據;  
        self.local_queue = self._process_queue(queue, self.policy) + \self.local_queue  
              
        if self.policy == 'queue':  
            self._check_queue_length()  
    @staticmethod  
    def _process_queue(queue, policy):  
        """ 
        實現循環發佈隊列queue中的信息數據; 
        """  
        while queue:  
            # 取出第一位的topic、msg等數據;  
            context, topic, msg = queue[0]  
            try:  
                # 實現遠程發佈信息,不返回任何值;  
                rpc.cast(context, topic, msg)  
            except (SystemExit, rpc.common.RPCException):  
                samples = sum([len(m['args']['data']) for n, n, m in queue])  
                if policy == 'queue':  
                    LOG.warn(_("Failed to publish %d samples, queue them"),samples)  
                    return queue  
                elif policy == 'drop':  
                    LOG.warn(_("Failed to publish %d samples, dropping them"),samples)  
                    return []  
                # default, occur only if rabbit_max_retries > 0  
                raise  
            else:  
                # 從隊列中刪除發佈後的信息;  
                queue.pop(0)  
        return []  
2.3 通過UDP發佈採樣數據(具體見代碼註釋)

    class UDPPublisher(publisher.PublisherBase):  
        def publish_samples(self, context, samples):  
            """ 
            通過UDP協議發送meter信息到服務器端,實現監控信息的發佈; 
            """  
            for sample in samples:  
                """ 
                爲一個監控採樣數據做好準備被髮布或存儲; 
                從一個採樣數據信息中獲取提取信息形成msg; 
                """  
                msg = utils.meter_message_from_counter(  
                    sample,  
                    cfg.CONF.publisher.metering_secret)  
                host = self.host  
                port = self.port  
                LOG.debug(_("Publishing sample %(msg)s over UDP to "  
                            "%(host)s:%(port)d") % {'msg': msg, 'host': host,'port': port})  
                """ 
                通過UDP協議發送meter信息到服務器端,實現監控信息的發佈; 
                """  
                try:  
                    self.socket.sendto(msgpack.dumps(msg),(self.host, self.port))  
                except Exception as e:  
                    LOG.warn(_("Unable to send sample over UDP"))  
                    LOG.exception(e)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章