天天看點

neutron-metering解讀分析

metering簡介

metering是l3層流量監控的插件,通過統計iptables規則的流量計數達到流量監控的目的。

neutron-metering解讀分析

與iptables類似,建立metering label即建立一條自定義鍊,這條鍊被Forward表引用,針對某個ip建立metering label rule則生成相應的規則,進出方向的流量會留下它的痕迹。

metering配置

  1. 路徑/etc/neutron,新增配置檔案metering_agent.ini
# metering_agent.ini
interface_driver =neutron.agent.linux.interface.OVSInterfaceDriver
driver = neutron.services.metering.drivers.iptables.iptables_driver.IptablesMeteringDriver
           
  1. 檔案/etc/neutron/neutron.conf,新增services_plugins
    neutron-metering解讀分析
  2. metering_agnet是一個單獨的程序,負責與qrouter互動,建立iptables規則,執行指令:
/var/lib/openstack/bin/python /var/lib/openstack/bin/neutron-metering-agent --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/metering_agent.ini
           

metering_plugin

  1. API路徑在neutron_lib/api/definitions
  2. neutron-server接收請求後交給plugin處理,路徑neutron/services/metering/metering_plugin.py
  3. MeteringPlugin類存在以下方法:
def start_rpc_listeners
def create_metering_label
def delete_metering_label
def create_metering_label_rule
def delete_metering_label_rule
           
  1. 這些方法都按照一定的格式執行,先調用metering_plugin操作db,在通過rpc發送消息讓agent端處理
def create_metering_label(self, context, metering_label):
    label = super(MeteringPlugin, self).create_metering_label(
        context, metering_label)

    data = self.get_sync_data_metering(context)
    self.meter_rpc.add_metering_label(context, data)

    return label
           
  1. create_metering_label

    為例,MeteringPlugin調用父類metering_db.MeteringDbMixin的方法

    create_metering_label

    建立label的db,

    get_sync_data_metering

    調用MeteringDbMixin的方法同步router資料,拿到的data是一個字典,對象是routers,每個router都帶上了

    _metering_label

    的标簽
  2. add_metering_label

    則通過rpc消息隊列發送給metering_agent端,讓agent端執行實際的操作,rpc的接口定義在neutron/api/rpc/agentnotifiers/metering_rpc_agent_api.py

metering_agent

agent端可以分為三塊主要内容:

  1. 同步router、labels,labelrules的内容,程序拉起時會同步一次qrouter iptables的自定義鍊和規則,往後每隔一段時間會再次擷取routers的内容,更新緩存
def _sync_router_namespaces(self, context, routers):
   LOG.debug("Sync router namespaces")
   return self._invoke_driver(context, routers, 'sync_router_namespaces')
           
  1. 接受來自metering_plugin的請求,建立/删除labels、rules
    neutron-metering解讀分析
  2. metering_agent調用driver擷取iptables的流量監控資訊,并發送給上層應用。原版的openstack通過ceilometer擷取metering收集的流量資訊并對外展示,但各大廠商可以設計自己的流量監控方案,例如通過消息隊列kafka、rabbitmq等将監控資訊轉發到自己的流量監控平台上
    neutron-metering解讀分析
    metering_agent的代碼都遵循同一個範式,即收到上層的消息後,經過處理或者不處理,去調用

    _invoke_driver

    的函數,這其實是個調用driver的通用方法,使用者可以定義自己的driver,并在配置檔案中指定,agent拉起時會自動加載相應的driver,而

    _invoke_driver

    的調用則會拉起對應driver中的方法
def _invoke_driver(self, context, meterings, func_name):
    try:
        return getattr(self.metering_driver, func_name)(context, meterings)
    except AttributeError:
        LOG.exception("Driver %(driver)s does not implement %(func)s",
                      {'driver': self.conf.driver,
                       'func': func_name})
    except RuntimeError:
        LOG.exception("Driver %(driver)s:%(func)s runtime error",
                      {'driver': self.conf.driver,
                       'func': func_name})
           

self.metering_driver

是已經加載好的驅動,通過擷取對應的

func_name

映射到相應的函數體,并執行

metering driver

  1. neutron(R版本)中暫時隻實作了iptables driver,路徑在neutron/services/metering/drivers/iptables
  2. neutron metering其實存在第二種noop driver,以我淺薄的認知目前還不知道noop具體是個啥玩意,源碼中也沒有實作具體的方法,但是給我們新增driver打了個樣
  3. 以添加rule為例,

    _add_metering_label_rule

    直接調用方法

    _process_metering_rule_action

def _process_metering_rule_action(self, router, action):
   rm = self.routers.get(router['id'])
   if not rm:
       return

   ext_dev, ext_snat_dev = self.get_external_device_names(rm)
   for (im, dev) in [(rm.iptables_manager, ext_dev),
                     (rm.snat_iptables_manager, ext_snat_dev)]:
       if im and dev:
           self._process_metering_rule_action_based_on_ns(
               router, action, dev, im)
           
  1. get_external_device_names

    是擷取所需監控的qrouter内的網絡裝置,這一點需要注意,legacy router、ha router以及dvr router中相應的qrouter網關字首未必是相同的,雖然源碼中對qrouter内的網絡裝置設定唯一字首“qr-”,但這一點的确應該注意。如果網絡裝置字首并非都是“qr-”,可能會導緻rule無法收集到監控資料
  2. dvr router與ha router應該分别區分
  3. 擷取到網絡裝置名字後,調用

    _process_metering_rule_action_based_on_ns

    方法,首先分析router帶下來的

    _metering_labels

    字段,分析label是否發生變化,如果發生變化則同步一次,并更新緩存,如果rule字段存在,則調用具體的方法更新rule
def _add_rule_to_chain(self, ext_dev, rule, im,
                       label_chain, rules_chain):
    ipt_rule = self._prepare_rule(ext_dev, rule, label_chain)
    if rule['excluded']:
        im.ipv4['filter'].add_rule(rules_chain, ipt_rule,
                                   wrap=False, top=True)
    else:
        im.ipv4['filter'].add_rule(rules_chain, ipt_rule,
                                   wrap=False, top=False)

def _prepare_rule(self, ext_dev, rule, label_chain):
    remote_ip = rule['remote_ip_prefix']
    if rule['direction'] == 'egress':
        dir_opt = '-s %s -o %s' % (remote_ip, ext_dev)
    else:
        dir_opt = '-d %s -i %s' % (remote_ip, ext_dev)

    if rule['excluded']:
        ipt_rule = '%s -j RETURN' % dir_opt
    else:
        ipt_rule = '%s -j %s' % (dir_opt, label_chain)
    return ipt_rule
           

新增規則是以字元串的方式生成,兩個方向:出方向egress,入方向ingress,不同的方向生成的規則也有差異,最終調用neutron/agent/linux/iptables_mamager.py執行指令行

  1. 收集資料的方法是

    get_traffic_counters

    ,這個方法調用iptables_manager類的

    get_traffic_counters

    執行以下指令
sudo ip netns exec qrouter-namespace-id iptables -t filter -L chain-name -n -v -x -w xlock_wait_time -Z
           
  1. get_traffic_counters

    通過參數zero開啟每次計數後歸零的功能,是以metering生成的規則流量計數會定時清零

繼續閱讀