基于Kubernetes的PaaS平台提供的监控服务
概述
我一直在负责维护的PaaS平台引入了Kubernetes作为底层支持,可以借助Kubernetes的生态做更多的事情,这篇博客主要介绍如何为普通用户提供图表监控服务(承接上一篇提供Dashboard支持)。默认读者有能力自己搭建Kubernetes
集群以及初级的监控系统,以及可以简单的使用PromQL
,因为博客主要想介绍的内容不在此。
我早于Dashboard就增加了这个功能,但是讲解起来实在复杂。一直拖着没写,今天有空,正好来分享一下吧。
方案拓扑
这里我懒一下,不想画图了。图中是整个方案的左半边,由采集工具采集机器以及容器信息,上传至
Prometheus
中,右下角的Grafana
通过PromQL
查询以及展示数据,下面两个部分会简单介绍Prometheus
以及Grafana
,但重点在后面哈。朴素的收集与展示工具Prometheus
这是一张Ingress的CPU利用率统计图:朴素的原因主要是这个工具做作用是收集数据,每次只能查询一条语句,而且没有什么历史记录功能,权限控制几乎没有,这样基础的工具肯定不能直接提供给用户。
漂亮的展示工具Grafana
相比上面简陋的图片,
Grafana
的功能就强大很多了,可以展示多张图表,而且有了权限控制,非常适合建立大盘监控,对于PaaS
平台中的每个用户来讲,看到自己应用的监控是多么美妙的一件事。-----------
如果仅仅是介绍到这里,大家在自己的Kubernetes集群中鼓捣一下应该很快就可以用了,我们既然作为PaaS平台的开发者,自然要考虑多用户以及多应用的组织管理方式,呈现给用户需要的资料的同时,也要保证用户以及应用间不会相互影响。没错这与上一篇Dashboard支持方案类似,也要进行权限设计以及控制。
权限同步策略
PaaS平台的权限系统
在PaaS平台中,假设我们有一个用户A,他拥有自己的一个或多个应用群组(G1,G2),每个群组中部署了一系列应用程序(a1,a2……)。Grafana中的权限系统
在Grafana权限系统中,有一些Teams,用户可以属于一个或多个Team。每个Folder可以拥有一个或多个Dashboard。
映射
考虑到上述的关系,我们的思路就很明确了,我的设计方案是:- 用户的每个群组对应一个Team,该Team拥有一个Folder的访问权限
- 每个应用拥有一个Dashboard
你也可以使用别的设计方案(比如说不要
Folder
,每个Team拥有一些Dashboard),上述方案只是我想方便管理,每个Team拥有自己的Folder。Grafana相关类库及其使用
我用的是Python
定时同步权限以及图表信息,当时也想过用Golang
,但我说真的,这种代码用Python
太适合了。下面的部分太过于偏了,建议不要去纠结,你大概率用不上,肯定要自己去读文档的。grafana-api==1.0.3 # 用来与Grafana API通信
grafanalib==0.5.7 # 创建Grafana图表
User,Team,Folder创建
# 下面是一个team的创建,其他类似
def ensure_team(g_api, team_name):
team_id = -1
try:
# {'message': 'Team created', 'teamId': 121}
g_team = g_api.teams.add_team({'name': team_name})
team_id = g_team['teamId']
except GrafanaClientError as e:
if e.status_code == 409:
# 'Client Error 409: Team name taken'
g_team = g_api.teams.get_team_by_name(team_name)[0]
team_id = g_team['id']
assert team_id != -1
return team_id
Folder与Team权限同步
# 下面是同步team以及其用户,因为用户有可能增减,需要对应的增减
def sync_user_team(g_api, team_id, user_ids):
orig_members = g_api.teams.get_team_members(team_id)
now_user_ids = set(user_ids)
orig_user_ids = set([u['userId'] for u in orig_members])
for user_id in now_user_ids - orig_user_ids:
try:
g_api.teams.add_team_member(team_id, user_id)
except GrafanaBadInputError:
pass
for user_id in orig_user_ids - now_user_ids:
g_api.teams.remove_team_member(team_id, user_id)
# 下面是同步folder以及team权限,所有普通用户只有只读权限
def sync_folder_permission(folder, team_id=None):
# https://grafana.com/docs/grafana/latest/http_api/folder_permissions/
g_api = get_grafana_api() # type: GrafanaFace
permissions = []
if team_id != None:
permissions.append({
"teamId": team_id,
"permission": 1 # 只有查看权限
})
g_api.folder.update_folder_permissions(
folder['uid'],
{
"items": permissions,
}
)
Dashboard页面设计以及同步
这部分其实挺难的,我想使用类似声名式的写法,类似kubectl apply -f xxx.yaml
,不过Grafana
的API太难用了,一种解决方案是:导入Dashboard图表数据,可以看到我这里的API都是手动加的。def sync_dashboard_data(dashboard_uid, folder_id, dashboard_json_data, inputs=[]):
"""同步dashboard的组信息以及dashboard的pannel数据信息
"""
g_api = get_grafana_api() # type: GrafanaFace
d = g_api.dashboard.get_dashboard(dashboard_uid)
data = {
'dashboard': dashboard_json_data,
'folderId': folder_id,
"inputs": inputs,
'overwrite': True
}
put_dashboard_path = "/dashboards/import"
r = g_api.api.POST(put_dashboard_path, json=data)
# 数据样式的生成请参考这里:
# https://github.com/weaveworks/grafanalib/blob/master/grafanalib/tests/examples/example-elasticsearch.dashboard.py
最终效果
针对我们系统中的每个应用,基本都有这么一张图表:常用的图表推荐
我平时作为管理员一般也不会看每个应用,可能看个大概吧。Ingress
https://github.com/kubernetes/ ... .jsonnode_exporter
https://github.com/rfrail3/gra ... .json总结
这篇文章我主要想介绍下我们的系统是如何设计的,希望能够在大家设计系统接入方案时能有所参考,也欢迎大家留言讨论。顺便吐槽一下
Grafana
,居然不能设置URL头像(为了改头像我都去读Grafana代码了,发现不能修改),真是令人生气!原文链接:https://corvo.myseu.cn/2021/01/06/2021-01-06-基于Kubernetes的PaaS平台提供的监控服务/,作者:corvofeng