metering简介
metering是l3层流量监控的插件,通过统计iptables规则的流量计数达到流量监控的目的。
与iptables类似,创建metering label即创建一条自定义链,这条链被Forward表引用,针对某个ip创建metering label rule则生成相应的规则,进出方向的流量会留下它的痕迹。
metering配置
- 路径/etc/neutron,新增配置文件metering_agent.ini
# metering_agent.ini
interface_driver =neutron.agent.linux.interface.OVSInterfaceDriver
driver = neutron.services.metering.drivers.iptables.iptables_driver.IptablesMeteringDriver
- 文件/etc/neutron/neutron.conf,新增services_plugins
- 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
- API路径在neutron_lib/api/definitions
- neutron-server接收请求后交给plugin处理,路径neutron/services/metering/metering_plugin.py
- MeteringPlugin类存在以下方法:
def start_rpc_listeners
def create_metering_label
def delete_metering_label
def create_metering_label_rule
def delete_metering_label_rule
- 这些方法都按照一定的格式执行,先调用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
- 以
create_metering_label
为例,MeteringPlugin调用父类metering_db.MeteringDbMixin的方法create_metering_label
创建label的db,get_sync_data_metering
调用MeteringDbMixin的方法同步router数据,拿到的data是一个字典,对象是routers,每个router都带上了_metering_label
的标签 add_metering_label
则通过rpc消息队列发送给metering_agent端,让agent端执行实际的操作,rpc的接口定义在neutron/api/rpc/agentnotifiers/metering_rpc_agent_api.py
metering_agent
agent端可以分为三块主要内容:
- 同步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')
- 接受来自metering_plugin的请求,创建/删除labels、rules
- metering_agent调用driver获取iptables的流量监控信息,并发送给上层应用。原版的openstack通过ceilometer获取metering收集的流量信息并对外展示,但各大厂商可以设计自己的流量监控方案,例如通过消息队列kafka、rabbitmq等将监控信息转发到自己的流量监控平台上
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
- neutron(R版本)中暂时只实现了iptables driver,路径在neutron/services/metering/drivers/iptables
- neutron metering其实存在第二种noop driver,以我浅薄的认知目前还不知道noop具体是个啥玩意,源码中也没有实现具体的方法,但是给我们新增driver打了个样
- 以添加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)
get_external_device_names
是获取所需监控的qrouter内的网络设备,这一点需要注意,legacy router、ha router以及dvr router中相应的qrouter网关前缀未必是相同的,虽然源码中对qrouter内的网络设备设置唯一前缀“qr-”,但这一点的确应该注意。如果网络设备前缀并非都是“qr-”,可能会导致rule无法收集到监控数据- dvr router与ha router应该分别区分
- 获取到网络设备名字后,调用
_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执行命令行
- 收集数据的方法是
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
get_traffic_counters
通过参数zero开启每次计数后归零的功能,因此metering生成的规则流量计数会定时清零