Grafana 其实是没什么好讲的,这里记录下也是因为这个星期在做这个Grafana的多租户设置以及Grafana 服务器的分发,所以也就简单的记录下。
基本概念
在Grafana 里面的基础的概念说多不多,说少不少。这里也不准备全部介绍,就简单的讲一下,笔者在使用的过程中所用到的几个功能模块。
Data Source
数据源,这是我们的Grafana 的一个很重要的东西,Grafana 本身只是一个页面的UI,并不能真的去存储数据。这里额数据源可以理解为我们的源数据,但是其实这里的数据源其实是一个中间结构,相当于一个连接池。简单的画一个图:
图中的虚线,其实就是我们感觉到的数据的走向。但是这个数据的走向其实是先经过了 DataSource , 在到 Prometheus 的。 这几天我跟踪了下后台的请求,我个人的理解是这样的。
当我们创建一个数据源的时候,其实是在后台帮我们创建一个数据源的配置,然后查询的时候,它查询的路径是: /api/datasources/proxy/13/api/v1/ , 这个路径其实就是我们的数据源里面的一个请求路径,这里的 13 是我们的数据源的 id , 我们看下这个数据源的配置是什么:
{ "id": 13, "orgId": 1, "name": "prometheus_datasource_test_grafana_30", "type": "prometheus", "typeLogoUrl": "", "access": "proxy", "url": "http://127.0.0.1:8888", "password": "", "user": "", "database": "", "basicAuth": true, "basicAuthUser": "test_grafana_30", "basicAuthPassword": "", "withCredentials": false, "isDefault": false, "jsonData": {}, "secureJsonFields": { "basicAuthPassword": true }, "version": 3, "readOnly": false}
可以看到的是,我们的数据源的真实地址是 http://127.0.0.1:8888 . 这个地址是笔者的一个中间服务器地址,那大家可能的就是 Prometheus / Cortex 的地址了。
Organization
我们来看下这个官方的介绍:
Each organization contains their own dashboards, data sources and configuration, and cannot be shared between orgs. While users may belong to more than one, multiple organization are most frequently used in multi-tenant deployments.
这句话什么意思呢?意思就是说 一个 Organization 拥有他们自己的 数据面板,数据源,以及配置,这些东西在两个 Organization 是不能共享的。当然我们的用户可以分属不同的 Organization 。简单的来说这就是一个多租户的管理。而一个 Organization 就是一个租户的组织。
可以为组织内的用户分配角色,也可以为这个角色分配权限,权限有: Admin / Viewer / Editer
Users
用户,这个大家就比较清楚了,就是登陆到 Grafana 的使用者。有管理员和普通用户的区别,管理员就是哪都有他,是 Grafana 的全局管理者,这个可以设置用户成为管理员。用户就是比较简单的用户。这里的用户的权限是针对Grafana 而言的,注意和 Organization 里的 角色权限做下区分。
小结
目前用到的概念比较多的就是这三个,在Grafana 里面也其他的几个概念,因为笔者没有用到,就不列了,大家可以查看 Grafana 的官网。
代码
这里用Go 写了一个操作 Grafana API 的简单的程序,可以供大家参考。头条上的显示不是很好:
package grafanaimport ( "context" "encoding/json" "fmt" "net/http" "net/url" "prometheus-front-server/pkg/api/client" "strconv" "strings")// Package: grafana// Version: 1.0 Created by SunYang on 2020/5/7 10:24const ( ErrBadData ErrorType = "bad_data" ErrTimeout ErrorType = "timeout" ErrCanceled ErrorType = "canceled" ErrExec ErrorType = "execution" ErrBadResponse ErrorType = "bad_response" ErrServer ErrorType = "server_error" ErrClient ErrorType = "client_error")type Error struct { Type ErrorType Msg string Detail string}type apiGrafanaClientImpl struct { client client.GrafanaClient}type httpAPI struct { client apiGrafanaClient}type apiGrafanaClient interface { URL(ep string, args map[string]string) *url.URL Do(context.Context, *http.Request) (*http.Response, []byte, Warnings, error) DoGetFallback(ctx context.Context, u *url.URL, args url.Values) (*http.Response, []byte, Warnings, error)}type apiResponse struct { Status string `json:"status"` Data json.RawMessage `json:"data"` ErrorType ErrorType `json:"errorType"` Error string `json:"error"` Warnings []string `json:"warnings,omitempty"`}type ErrorType stringtype Warnings []stringfunc (e *Error) Error() string { return fmt.Sprintf("%s: %s", e.Type, e.Msg)}const ( statusAPIError = 422 apiPrefix = "/api" epGrafanaAccount = apiPrefix + "/admin/users" epChangeAccountPermission = apiPrefix + "/admin/users/:id/permissions" epCreateOrganizations = apiPrefix + "/orgs" epAddAccountToOrganization = apiPrefix + "/orgs/:orgId/users" epChangeAccountOrgsRole = apiPrefix + "/orgs/:orgId/users/:userId" epDeleteAccountFromMainOrg = apiPrefix + "/orgs/:orgId/users/:userId" epAddGrafanaDataSource = apiPrefix + "/datasources" epChangeDataSourcePermission = apiPrefix + "/datasources/:id/permissions")const ( permission = `{"isGrafanaAdmin": true}` role = `{"role":"Admin"}` main_organization = 1)type GrafanaAPI interface { // 创建组织 CreateOrganizations(ctx context.Context, organization string) (OrganizationResult, error) // 创建用户 CreateGrafanaAcount(ctx context.Context, body string) (CreateGrafanaAcountResult, error) // 修改用户权限 ChangeAccountPermission(ctx context.Context, id int) (MessageResult, error) // 添加用户到组织 AddAccountToOrganization(ctx context.Context, orgId int, requestBody string) error // 修改用户在组织中的角色 ChangAccountOrgRole(ctx context.Context, orgId int, id int) (MessageResult, error) // 从组织中删除某个用户 DeleteAccountFromMainOrg(ctx context.Context, id int) error // 添加数据源 AddDataSource(ctx context.Context, requestBody string) (AddDataSourceResult, error) // 修改数据源的权限 ChangeDataSourcePermission(ctx context.Context, id int) error}func (h *httpAPI) CreateOrganizations(ctx context.Context, organization string) (OrganizationResult, error) { u := h.client.URL(epCreateOrganizations, nil) request, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(organization)) request.Header.Set("Content-Type", "application/json") if err != nil { return OrganizationResult{}, err } _, body, _, err := h.client.Do(ctx, request) if err != nil { return OrganizationResult{}, err } fmt.Println(string(body)) var res OrganizationResult return res, json.Unmarshal(body, &res)}func (h *httpAPI) CreateGrafanaAcount(ctx context.Context, form string) (CreateGrafanaAcountResult, error) { u := h.client.URL(epGrafanaAccount, nil) request, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(form)) request.Header.Set("Content-Type", "application/json") if err != nil { return CreateGrafanaAcountResult{}, err } _, body, _, err := h.client.Do(ctx, request) if err != nil { return CreateGrafanaAcountResult{}, err } var res CreateGrafanaAcountResult return res, json.Unmarshal(body, &res)}
头条不适合放大块的程序,这里就不放很多的那种程序了,其实大家都一样。
总结
这个 Grafana 第一个分享就到这里,后续有什么我再来补充。