学习 OpenStack (2):cloudkitty

Cloudkitty 计费使用小结

前提

1.环境: 双节点 OpenStack rocky 版
2.采样及存储: ceilometer、gnocchi
3.rating: hashmap   # cloudkitty 计费模型
4.storage backend: sqlalchemy
5.计费指标: flavor  

你需要一个可用的 OpenStack 平台(包含组件:keystone、nova 等),安装咱不提了,官网及其他博客均有介绍
ceilometer 11.1.0
gnocchi 4.3.2 (不同版本源码差异,可能和本文内容有些许出入)

PS:我不喜欢贴图(之前免费的图库忘了叫啥),凑合着看吧


先检查下采样是否正常
# 获取权限
$ source XXXrc
# 查看虚拟机
$ nova list
# 查看 gnocchi resource
$ gnocchi resource show <instance_id>
# 查看 gnocchi measures
$ gnocchi measures show <metric_id>

按 OpenStack 官网安装 cloudkitty
# 问题1
# 如果启动报 keystone 相关错
# 排查 /etc/cloudkitty/cloudkitty.conf [ks_auth]

...
[ks_auth]
auth_type = v3password
auth_protocol = http
auth_url = http://KEYSTONE_HOST:5000/
identity_uri = http://KEYSTONE_HOST:5000/
username = cloudkitty
password = CK_PASSWORD
project_name = service
user_domain_name = default
project_domain_name = default
debug = True
...
官网 auth_section 参数与本地冲突,参考本地可用组件的配置 [keystone_authtoken]
 

# 问题2
# /etc/cloudkitty/metrics.yml
# 使用原生 metrics.yml 配置文件进行采样是有问题的
# rocky
    https://github.com/openstack/cloudkitty/blob/stable/rocky/etc/cloudkitty/metrics.yml
# stein
    https://github.com/openstack/cloudkitty/blob/stable/stein/etc/cloudkitty/metrics.yml
比对两个版本的配置文件 metric:cpu ,以及ceilometer采样指标的metadata
你会发现 'flavor' 要替换成 'flavor_name'
PS: 这点很重要!如果你在 cloudkitty-hashmap 计费模式中,定义了 field 计费,且以metrics.yml中 flavor 与 field 做关联,你的相关计费数据会始终为空 

按官网部署&初始化以后
# 给需要计费的项目添加 rating 角色
    $ openstack role add --project XXX --user cloudkitty rating
# 查看项目下的所有用户&角色
    $ openstack role assignment list --project XXX --names

# 确认计费模型 hashmap:enable
    $ cloudkitty module list
# 按 cloudkitty 官网介绍定义 hashmap-mapping (本文 metric:cpu metadata:flavor_name)
    $ cloudkitty hashmap service create XXX
    $ cloudkitty hashmap group create XXX
    $ cloudkitty hashmap field create <service_id> flavor_name
    $ cloudkitty hashmap mapping create --field-id <field-id> -g <group_id> -p <tenant_id> -t <type> --value <field_value> <cost>

# 进数据库验证定义的数据
    > select * from hashmap_mappings;
# 顺便看看另外两张要用到的表
    > select * from cloudkitty_storage_states;
    > select * from rated_data_frames;

启动计费前先看下源码
# OpenStack 源码可以在 setup.cfg -> [entry_points] 找到相关定义
    cloudkitty-processor = cloudkitty.cli.processor:main
# 其他方式 "which cloudkitty-processor" 查看对应的二进制文件,也能找到
    from cloudkitty.cli.processor import main

cloudkitty/orchestrator.py
# 依据启动代码找到执行位置
# <class.Orchestrator> <def.process>
    ...
    self.tenants = self.fetcher.get_tenants()   # 获取需要计费的租户, 参考 setup.cfg 和 cloudkitty.conf 可以找到之前给 project 配置 rating 角色的原因
    ...
    worker.run()    # 周期计费的入口
    ...
    eventlet.sleep(CONF.collect.period)     # 周期计费的时间 间隔, 代码可以看出变量可以通过 cloudkitty.conf [collect] period 去调整
    ...

cloudkitty/orchestrator.py 获取采样数据
# run 函数(个人划分为三部分:采样、计费、存储)
# <class.Worker> <def.run>
    ...
    timestamp = self.check_state()  # 当前 fetcher 的项目, 上一次计费截止时间(cloudkitty_storage_states表内)
    ...
    metrics = list(self._conf['metrics'].keys())    # self._conf 来源为 /etc/cloudkitty/metrics.yml(CONF.collect.metrics_conf)
    ...
    data = self._collect(metric, timestamp)     # 建议 rpdb 断点看一次, from stevedore import driver 会根据配置文件动态加载
    ...

# 前面提到,本文 collect 来源为 gnocchi
    cloudkitty\collector\__init__.py -> retrieve
    cloudkitty\collector\gnocchi.py -> fetch_all

# 在rpdb堆栈信息里可以找到调用顺序
    ↓ self._fetch_metric
    ↓ self._conn.aggregates.fetch
    ↓ from gnocchiclient import client as gclient -> aggregates # 一个聚合的查询请求

gnocchi/rest/aggregates/api.py
# <class.AggregatesController> <def.post>
"""
    line:258
    "measures": self._get_measures_by_name
    因无异常捕获, 在无采样值、无采样指标时会报错报错, 间接导致 cloudkitty-processor 计费中断
    个人视情况决定是否修改这里
"""

cloudkitty/orchestrator.py 计费
# <class.Worker> <def.run>
"""
    # Rating
    for processor in self._processors:
        processor.obj.process(data)
"""

# 如果你选用的计费模型是 hashmap
# cloudkitty\rating\hash\__init__.py
# <class.HashMap> <def.process>
"""
   def process(self, data):
        for cur_data in data:
            cur_usage = cur_data['usage']
            for service_name, service_data in cur_usage.items():
                for item in service_data:
                    self._res = {}
                    self.process_services(service_name, item)
                    self.process_fields(service_name, item)
                    self.add_rating_informations(item)
        return data
"""

参考
posted on 2021-08-04 17:49  〆灬丶  阅读(453)  评论(0编辑  收藏  举报