在Grafana 里面的基础的概念说多不多,说少不少。这里也不准备全部介绍,就简单的讲一下,笔者在使用的过程中所用到的几个功能模块。
数据源,这是我们的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 的地址了。
我们来看下这个官方的介绍:
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
用户,这个大家就比较清楚了,就是登陆到 Grafana 的使用者。有管理员和普通用户的区别,管理员就是哪都有他,是 Grafana 的全局管理者,这个可以设置用户成为管理员。用户就是比较简单的用户。这里的用户的权限是针对Grafana 而言的,注意和 Organization 里的 角色权限做下区分。
目前用到的概念比较多的就是这三个,在Grafana 里面也其他的几个概念,因为笔者没有用到,就不列了,大家可以查看 Grafana 的官网。
这里用Go 写了一个操作 Grafana API 的简单的程序,可以供大家参考。头条上的显示不是很好:
package grafana
import (
"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:24
const (
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 string
type Warnings []string
func (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)
}
func (h *httpAPI) AddDataSource(ctx context.Context, requestBody string) (AddDataSourceResult, error) {
u := h.client.URL(epAddGrafanaDataSource, nil)
request, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(requestBody))
request.Header.Set("Content-Type", "application/json")
if err != nil {
return AddDataSourceResult{}, err
}
_, body, _, err := h.client.Do(ctx, request)
if err != nil {
return AddDataSourceResult{}, err
}
var res AddDataSourceResult
return res, json.Unmarshal(body, &res)
}
func (h *httpAPI) ChangeAccountPermission(ctx context.Context, id int) (MessageResult, error) {
u := h.client.URL(epChangeAccountPermission, map[string]string{"id": strconv.Itoa(id)})
request, err := http.NewRequest(http.MethodPut, u.String(), strings.NewReader(permission))
if err != nil {
return MessageResult{}, err
}
request.Header.Set("Content-Type", "application/json")
_, body, _, err := h.client.Do(ctx, request)
if err != nil {
return MessageResult{}, err
}
var res MessageResult
return res, json.Unmarshal(body, &res)
}
func (h *httpAPI) AddAccountToOrganization(ctx context.Context, orgId int, requestBody string) error {
u := h.client.URL(epAddAccountToOrganization, map[string]string{"orgId": strconv.Itoa(orgId)})
request, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(requestBody))
if err != nil {
return err
}
request.Header.Set("Content-Type", "application/json")
_, _, _, err = h.client.Do(ctx, request)
if err != nil {
return err
}
return nil
}
func (h *httpAPI) DeleteAccountFromMainOrg(ctx context.Context, id int) error {
u := h.client.URL(epDeleteAccountFromMainOrg, map[string]string{"orgId": strconv.Itoa(main_organization), "userId": strconv.Itoa(id)})
request, err := http.NewRequest(http.MethodDelete, u.String(), nil)
if err != nil {
return err
}
request.Header.Set("Content-Type", "application/json")
_, _, _, err = h.client.Do(ctx, request)
if err != nil {
return err
}
return nil
}
func (h *httpAPI) ChangAccountOrgRole(ctx context.Context, orgId int, id int) (MessageResult, error) {
u := h.client.URL(epChangeAccountOrgsRole, map[string]string{"orgId": strconv.Itoa(orgId), "userId": strconv.Itoa(id)})
request, err := http.NewRequest(http.MethodPatch, u.String(), strings.NewReader(role))
if err != nil {
return MessageResult{}, err
}
request.Header.Set("Content-Type", "application/json")
_, body, _, err := h.client.Do(ctx, request)
if err != nil {
return MessageResult{}, err
}
fmt.Println(string(body))
var res MessageResult
err = json.Unmarshal(body, &res)
return res, err
}
func (h *httpAPI) ChangeDataSourcePermission(ctx context.Context, id int) error {
panic("修改数据源的权限")
}
func NewGrafanaAPI(c client.GrafanaClient) GrafanaAPI {
return &httpAPI{
client: &apiGrafanaClientImpl{
client: c,
},
}
}
func apiError(code int) bool {
return code == statusAPIError || code == http.StatusBadRequest
}
func (h *apiGrafanaClientImpl) DoGetFallback(ctx context.Context, u *url.URL, args url.Values) (*http.Response, []byte, Warnings, error) {
panic("implement me")
}
func (h *apiGrafanaClientImpl) URL(ep string, args map[string]string) *url.URL {
return h.client.URL(ep, args)
}
func (h *apiGrafanaClientImpl) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, Warnings, error) {
resp, body, err := h.client.Do(ctx, req)
if err != nil {
return resp, body, nil, err
}
code := resp.StatusCode
if code/100 != 2 && !apiError(code) {
errorType, errorMsg := errorTypeAndMsgFor(resp)
return resp, body, nil, &Error{
Type: errorType,
Msg: errorMsg,
Detail: string(body),
}
}
return resp, body, nil, err
}
func errorTypeAndMsgFor(resp *http.Response) (ErrorType, string) {
switch resp.StatusCode / 100 {
case 4:
return ErrClient, fmt.Sprintf("client error: %d", resp.StatusCode)
case 5:
return ErrServer, fmt.Sprintf("server error: %d", resp.StatusCode)
}
return ErrBadResponse, fmt.Sprintf("bad response code %d", resp.StatusCode)
}
type MessageResult struct {
Message string `json:"message"`
}
type OrganizationResult struct {
OrgId int `json:"orgId"`
Message string `json:"message"`
}
type CreateGrafanaAcountResult struct {
Id int `json:"id"`
Message string `json:"message"`
}
type AddDataSourceResult struct {
DataSource DataSource `json:"datasource"`
Id int `json:"id"`
Message string `json:"message"`
Name string `json:"name"`
}
type DataSource struct {
Id int `json:"id"`
OrgId int `json:"orgId"`
Name string `json:"name"`
Type string `json:"type"`
TypeLogoUrl string `json:"typeLogoUrl"`
Access string `json:"access"`
Url string `json:"url"`
Password string `json:"password"`
User string `json:"user"`
DataBase string `json:"database"`
BasicAuth bool `json:"basicAuth"`
BasicAuthUser string `json:"basicAuthUser"`
BasicAuthPassword string `json:"basicAuthPassword"`
WithCredentials bool `json:"withCredentials"`
IsDefault bool `json:"isDefault"`
JsonData interface{} `json:"jsonData"`
SecureJsonFields interface{} `json:"secureJsonFields"'`
Version int64 `json:"version"`
ReadOnly bool `json:"readOnly"`
}
type Client struct {
GrafanaClient GrafanaAPI
}
func NewGrafanaQueryClient(address string) (Client, error) {
grafanaClient, err := client.NewGrafanaClient(client.Config{
Address: address,
})
if err != nil {
return Client{}, err
}
c := Client{GrafanaClient: NewGrafanaAPI(grafanaClient)}
return c, nil
}
这个 Grafana 的一个分享就到这里,后续有什么我再来补充。