自定义监控
功能概述
自定义监控的功能支持,可满足云平台已有用户、非云平台用户对基础资源的指标监控需求,可与 CloudSat 服务内的其他功能结合使用,形成完整的立体化监控告警服务。
如果您需要使用自定义相关功能,请按照下述步骤进行。
操作步骤
步骤一:创建自定义监控
-
通过 Web 浏览器登录{product_name}的 Console。
-
在顶部菜单栏中选择产品与服务 > 运维与监控 > 云监控 CloudSat > 自定义监控,进入自定义监控页面。
-
点击创建自定义监控,弹出自定义监控设置对话框。
-
用户按照弹框内的提示进行信息填写,点击提交,完成操作。
步骤二:上报监控数据
延续前文步骤,按照初始页面的提示,进行数据上报工作,上报数据请参照自定义监控上报数据规范进行上传。
自定义页面监控列表中涉及到一些时间区间内的监控数据计算,最小统计周期为 5 分钟,因此需连续发送不小于 5 分钟时间段的数据,页面才有数据显示。
如持续发送超过 5 分钟仍未正常显示,请检查上传数据的 meter
字段是否和在自定义监控页面下该命名空间下创建的监控指标名称一致。
步骤三:管理自定义监控
-
延续前文步骤,进入自定义监控列表页面。该页面汇总用户自主创建的命名空间,各个命名空间之间相互独立。
-
点击目标命名空间,进入具体的空间内,对该空间内的、自主上报的指标进行统一的管理。对异常的监控指标,进行直观的显示。
-
若需对异常情况进行下钻与细查,可以点击指标所在列,进入该这指标的监控详情视图页面,如下图所示。
-
平台也支持对自定义监控数据的配置修改,可按照实际业务需求灵活调整统计方式、命名等信息,如下图所示。
步骤四:查看告警信息
延续前文步骤,进入命名空间详情页面后,点击监控列表 > 告警,可查看告警规则汇总。如下图所示。

自定义监控上报数据
目前上报自定义监控数据,是通过 CloudSat 中 UploadMonitorData
接口进行上报,然后进行监控数据分析和告警。
详细操作如下。
-
步骤一:参数升序排列
将所需参数进行按参数名进行升序排列,排序后的参数示例如下:
{ "access_key_id":"QYACCESSKEYIDEXAMPLE", "action":"DescribeUsers", "signature_method":"HmacSHA256", "signature_version":1, "time_stamp":"2013-08-27T14:30:10Z", "version":1, "zone":"sh1" }
-
步骤二:对参数进行 URL 编码
对参数名称和参数值进行 URL 编码,编码后的参数示例如下:
{ "access_key_id":"QYACCESSKEYIDEXAMPLE", "action":"DescribeUsers", "signature_method":"HmacSHA256", "signature_version":1, "time_stamp":"2013-08-27T14%3A30%3A10Z", "version":1, "zone":"sh1" }
注意 -
编码时,空格要转换成
%20
,而不是+
。 -
转码部分的字符要用大写,如
:
应转成%3A
,而不是%3a
。
-
-
步骤三:构造 URL 请求
参数名和参数值之间使用
=
号进行连接,参数和参数之间使用&
号连接。构造后的 URL 请求示例如下。{ access_key_id=QYACCESSKEYIDEXAMPLE&action=DescribeUsers&signature_method=HmacSHA256&signature_version=1&time_stamp=2013-08-27T14%3A30%3A10Z&version=1&zone=sh1 }
-
步骤四:构造被签名串
被签名串的构造规则为: 被签名串 = HTTP 请求方式 +
\n
+ URI +\n
+ 上述请求串注意 \n
是换行符,不要将\
转义。假设 HTTP 请求方法为
GET
,请求的 URI 路径为/iaas/
, 则构造的被签名串示例如下。{ GET\n/iaas/\naccess_key_id=QYACCESSKEYIDEXAMPLE&action=DescribeUsers&signature_method=HmacSHA256&signature_version=1&time_stamp=2013-08-27T14%3A30%3A10Z&version=1&zone=sh1 }
-
步骤五:计算签名
计算被签名串的签名。
-
将 API 密钥的私钥
secret_access_key
作为 Key,生成被签名串的 HMAC-SHA256 或者 HMAC-SHA1 签名,更多信息可参见 RFC2104 文档。 -
将签名进行 Base64 编码。
-
将 Base64 编码后的结果进行 URL 编码。
-
当 Base64 编码后存在空格时,不要对空格进行 URL 编码,而要直接将空格转为
+
,以 Python 版本 2.7 代码为例,结果如下。{ import base64 import hmac import urllib from hashlib import sha256 # 前面生成的被签名串 string_to_sign = 'GET\n/iaas/\naccess_key_id=QYACCESSKEYIDEXAMPLE&action=DescribeUsers&signature_method=HmacSHA256&signature_version=1&time_stamp=2013-08-27T14%3A30%3A10Z&version=1&zone=sh1' h = hmac.new(secret_access_key, digestmod=sha256) h.update(string_to_sign) sign = base64.b64encode(h.digest()).strip() signature = urllib.quote_plus(sign) }
-
-
步骤六:添加签名
将签名参数附在原有请求串的最后面,最终的 HTTP 请求串示例如下。这里为了查看方便,我们人为地将参数之间用
回车键
分隔开。{ access_key_id=QYACCESSKEYIDEXAMPLE &action=DescribeUsers &signature_method=HmacSHA256 &signature_version=1 &time_stamp=2013-08-27T14%3A30%3A10Z &version=1 &zone=sh1 &signature=bOQMI8wJ4ikFnadNXc%2BpnVMcUyf83C7b9JO5%2FAvkGyk%3D }
附录
UploadMonitorData 接口说明
-
描述
上传监控数据。
-
请求类型
POST。
-
请求 URL
{ http://cloudsat.qingcloud.com/api/:zone/v1/custom/UploadMonitorData?access_key_id=QEJMCFROGCAPHUOAJMRN&action=DescribeUsers&signature_method=HmacSHA256&signature_version=1&time_stamp=2020-10-13T10%3A28%3A33Z&version=1&zone=test&signature=SO9ZufFb69Om21bK%2BH7Gs6f%2FuuDljHh41STgIX%3D }
注意 请仔细阅读上文说明,根据实际情况构造验证请求串,拼接成最终的 API 请求 URL 并替换
:zone
信息。 -
Headers
参数 参数值 是否必须 示例 备注 Content-Type
application/json
是
'Content-Type':'application/json'
不可缺少
-
请求 Body
{ "user_id": "usr-123456", "namespace": "namespace-1", "data": [ { "source": "test", "user_id": "usr-KJ8DrfQT", "tags": "role=master,interface=eth10", "group_id": "group10", "resource_id": "i-instance-10" , "resource_name": "name10", "resource_type": "instance", "root_user_id": "usr-KJ8DrfQ", "meter": "disk_ri'", "region": "sh1", "value": 99, "value_type": "percent", "time_stamp":"2020-11-03T09:58:44Z" },{ "source": "test", "user_id": "usr-KJ8DrfQT", "tags": "role=master,interface=eth10", "group_id": "group10" , "resource_id": "i-instance-10", "resource_name": "name10" , "resource_type": "instance", "root_user_id": "usr-KJ8DrfQ", "meter": "diskio", "region": "sh1", "value": 88, "value_type": "percent", "time_stamp":"2020-11-03T09:58:44Z" } ] }
-
字段说明
字段 类型 举例 必填 说明 namespace
string
namespace-1
是
命名空间。
region
string
sh1
是
Region ID
source
string
custom
是
监控数据的来源。
group_id
string
group_1
否
监控数据的 Group,或者分组标记。
resource_id
string
i-12345678
是
监控数据关联的资源 ID。
resource_name
string
roger-test
否
监控数据关联的资源名称。
resource_type
string
instance
是
资源类型。
user_id
string
usr-123456
是
监控资源资源对应用户的 ID。
root_user_id
string
usr-123456
否
主账户 ID。
meter
string
cpu
是
监控指标,请于该命名空间下监控配置中的监控指标名称保持一致
value_type
string
raw
是
指标值的类型。
raw
表示原格式,percent
表示百分比。value
int
80
是
监控的数据,整型的数据
time_stamp
string
2019-12-16T11:14:32Z
是
监控数据时间(UTC)
tags
string
role=master,cln-node=node-1,interface=eth0
否
数据的 tags,用于归类,key-value 的形式,用于
like
查询。 -
返回结果
{ "data": { "upload_count": 2 }, "ret_code": 0 }
-
结果说明
ret_code
为0
表示上传数据成功,data
中upload_count
表示上传数据条数。
UploadMonitorData 接口 URL 构造规范
该接口 URL 格式为:构造前 URL + ?
+ 构造验证请求串。
-
构造前 URL 示例
http://cloudsat.qingcloud.com/api/:zone/v1/custom/UploadMonitorData
说明 URL 里面
:zone
,请根据具体分区信息填写。 -
申请 API 密钥
需先在 Console 创建 API 密钥,获得
accesss_key_id
和secret_access_key
,这里假设:access_key_id = 'QYACCESSKEYIDEXAMPLE' secret_access_key = 'SECRETACCESSKEY'
-
构造验证请求串示例
access_key_id=CCDJRDKCCKZYTEXANZJD&action=DescribeUsers&signature_method=HmacSHA256&signature_version=1&time_stamp=2020-12-23T13%3A32%3A34Z&version=1&zone=sh1&signature=sOdokWwvYJ80mM%2FxYbBTsgTgQl3iu%2F2WDXWjgKFPNNs%3D
说明 这部分请根据实际参数进行构造。
-
API 请求中签名
由于上面构造前 URL 上传监控数据需要对请求进行验证,这里采用类似 API 请求中签名的生成方法,构造验证请求串,拼接在上面构造前 URL 后面。由于需要去 iaas 对用户信息进行确认,所以这里选择
DescribeUsers
这个 action 去构造签名信息。构造完成后请求 URL 示例为
http://cloudsat.qingcloud.com/api/:zone/v1/custom/UploadMonitorData?access_key_id=CCDJRDKCCKZYTEXANZJD&action=DescribeUsers&signature_method=HmacSHA256&signature_version=1&time_stamp=2020-12-23T13%3A32%3A34Z&version=1&zone=sh1&signature=sOdokWwvYJ80mM%2FxYbBTsgTgQl3iu%2F2WDXWjgKFPNNs%3D
示例代码
示例代码以 Golang、Python 语言为例,其他语言类似。
-
Golang 示例
-
构造验证请求串代码示例,请用实际数据替换
AccessKeyID
、SecretAccessKey
、Zone
等信息。package main import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/json" "fmt" "net/url" "sort" "strconv" "strings" "time" ) type verifyInfo struct { httpMothod string path string secretAccessKey string describeUsers *describeUsers } type describeUsers struct { AccessKeyID string `json:"access_key_id"` Action string `json:"action"` SignatureMethod string `json:"signature_method"` SignatureVersion int `json:"signature_version"` Version int `json:"version"` Zone string `json:"zone"` TimeStamp string `json:"time_stamp"` } func hmacSha256(data string, secret string) []byte { h := hmac.New(sha256.New, []byte(secret)) h.Write([]byte(data)) return h.Sum(nil) } func formatParams(v interface{}) (map[string]interface{}, error) { paramMap := make(map[string]interface{}) jsonBuf, err := json.Marshal(v) if err != nil { return nil, err } err = json.Unmarshal(jsonBuf, ¶mMap) if err != nil { return nil, err } return paramMap, nil } func sortParams(params map[string]interface{}) string { var keys []string for k := range params { keys = append(keys, k) } sort.Strings(keys) var parts []string for _, key := range keys { value := url.QueryEscape(strVal(params[key])) value = strings.Replace(strings.Replace(value, ":", "%3A", -1), " ", "%20", -1) parts = append(parts, key+"="+value) } return strings.Join(parts, "&") } func constructSignature(httpMothod, reqUrl, requestStr, key string) string { signaturePre := httpMothod + "\n" + reqUrl + "\n" + requestStr hmacShaStr := hmacSha256(signaturePre, key) base64Str := base64.StdEncoding.EncodeToString(hmacShaStr) signature := strings.TrimSpace(base64Str) signature = strings.Replace(signature, " ", "+", -1) signature = url.QueryEscape(signature) return signature } func strVal(value interface{}) string { var key string if value == nil { return key } switch value.(type) { case int: it := value.(int) key = strconv.Itoa(it) case string: key = value.(string) default: newValue, _ := json.Marshal(value) key = string(newValue) } return key } func (v *verifyInfo) buildVerifyStr() (string, error) { params, err := formatParams(v.describeUsers) if err != nil { return "", err } sortParams := sortParams(params) signature := constructSignature(v.httpMothod, v.path, sortParams, v.secretAccessKey) rsqParams := sortParams + "&signature=" + signature return rsqParams, nil } func main() { vi := &verifyInfo{ httpMothod: "GET", path: "/iaas/", secretAccessKey: "SECRETACCESSKEY", describeUsers: &describeUsers{ AccessKeyID: "QYACCESSKEYIDEXAMPLE", Action: "DescribeUsers", SignatureMethod: "HmacSHA256", SignatureVersion: 1, Version: 1, Zone: "sh1", TimeStamp: time.Now().Format("2006-01-02T15:04:05Z"), }, } result, _ := vi.buildVerifyStr() fmt.Println(result) }
代码中的
result
结果即构造的验证请求串,由于验证信息有时效性,如中断数据发送后重新发送时间间隔大于 5 分钟,请重新构造验证请求串。 -
验证所构造的验证请求串是否正确,请使用自己构造的验证请求串替换下面代码中
result
,执行下面代码。打印结果为1
,验证请求串构造错误;打印结果为0
,验证请求串构造正确。type successRespJson struct { Message interface{} `json:"message,required"` RetCode int `json:"ret_code,required"` } func Verify(url string) int { req, err := http.NewRequest("GET", url, nil) if err != nil { return 1 } tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true, Renegotiation: tls.RenegotiateOnceAsClient}, } cli := http.Client{ Transport: tr, CheckRedirect: nil, Jar: nil, Timeout: 0, } response, _:= cli.Do(req) defer response.Body.Close() body, _:= ioutil.ReadAll(response.Body) var vr successRespJson if response.StatusCode == http.StatusOK { if err := json.Unmarshal(body, &vr); err != nil { return 1 } } return vr.RetCode } func main() { result := "access_key_id=CCDJRDKCCKZYTEXANZJD&action=DescribeUsers&signature_method=HmacSHA256&signature_version=1&time_stamp=2020-12-23T14%3A03%3A44Z&version=1&zone=sh1&signature=d6eMFDgO3E6wBqbbn2AydX%2BxQws7iD%2BtSgfGF6Lq7Uo%3D" url := "https://api.qingcloud.com/iaas/" + "?" + result fmt.Println(Verify(url)) }
-
上传自定义数据代码示例
上传自定义数据需要预先在 CloudSat 自定义监控中创建命名空间和监控配置,并根据实际情况替换下面代码中一些数据,如
UserId
、Namespace
、Region
、Meter
等。Meter
请与对应命名空间上监控配置中的监控指标保持一致。时间使用UTC 时间
,所有上传数据字段严格遵守接口数据规范中字段说明规范。package main import ( "bytes" "encoding/json" "fmt" "math/rand" "net/http" "strconv" "time" ) type monitorData struct { UserId string `json:"user_id"` Namespace string `json:"namespace"` Data []meters `json:"data"` } type meters struct { Source string `json:"source"` UserId string `json:"user_id"` Tags string `json:"tags"` GroupId string `json:"group_id"` ResourceId string `json:"resource_id"` ResourceName string `json:"resource_name"` ResourceType string `json:"resource_type"` RootUserId string `json:"root_user_id"` Meter string `json:"meter"` Region string `json:"region"` Value int `json:"value"` ValueType string `json:"value_type"` TimeStamp string `json:"time_stamp"` } type date struct { UploadCount int `json:"upload_count"` } type ret struct { Data date `json:"data"` RetCode int `json:"ret_code"` } var client = http.Client{ Timeout: 10 * time.Second, } func HttpPostJson(url string, data interface{}, result interface{}) error { buf := bytes.NewBuffer(nil) encoder := json.NewEncoder(buf) if err := encoder.Encode(data); err != nil { return err } request, err := http.NewRequest(http.MethodPost, url, buf) if err != nil { return err } request.Header.Add("Content-Type", "application/json") request.Header.Add("Content-Type", "charset=UTF-8") response, err := client.Do(request) if err != nil { return err } defer response.Body.Close() decoder := json.NewDecoder(response.Body) if err = decoder.Decode(&result); err != nil { return err } fmt.Println(result) return nil } func main() { for { timeStamp := time.Now().UTC().Format("2006-01-02T15:04:05Z") rand.Seed(time.Now().UnixNano()) flag := strconv.Itoa(rand.Intn(16)) p := monitorData{ UserId: "usr-12345678", Namespace: "cloudsat-test", Data: []meters{ { Source: "dylan-test", UserId: "usr-12345678", Tags: "role=master,interface=eth10", GroupId: "group1", ResourceId: "i-instance-"+flag , ResourceName: "name"+flag, ResourceType: "instance", RootUserId: "usr-12345678", Meter: "cpu", Region: "sh1", Value: rand.Intn(100), ValueType: "percent", TimeStamp:timeStamp, },{ Source: "dylan-test", UserId: "usr-12345678", Tags: "role=master,interface=eth10", GroupId: "group1" , ResourceId: "i-instance-"+flag , ResourceName: "name"+flag, ResourceType: "instance", RootUserId: "usr-12345678", Meter: "memory", Region: "sh1", Value: rand.Intn(100), ValueType: "percent", TimeStamp:timeStamp, }, }, } var r ret url := `http://cloudsat.qingcloud.com/api/sh1/v1/custom/UploadMonitorData?access_key_id=CCDJRDKCCKZYTEXANZJD&action=DescribeUsers&signature_method=HmacSHA256&signature_version=1&time_stamp=2020-12-23T09%3A52%3A10Z&version=1&zone=sh1&signature=vorfODyQzKD0v4En7y5mdwkvUe4OZGQcDdQU5xBSjio%3D` err := HttpPostJson(url,p,r) if err != nil { fmt.Println(err) } time.Sleep(time.Duration(1)*time.Second) } }
-
-
python 完整上传自定义数据示例
请根据实际,使用自己的
access_key_id
、secret_access_key
、user_id
、namespace
、region
、meter
替换代码中相应变量,这里以上海1区(sh1)
为例,实际应用中需要同步更新self.CloudSatUrl
中对应的区间信息,详情参见上文。import hmac import json import base64 import datetime import time from hashlib import sha256 from collections import OrderedDict try: import urllib.parse as urllib except: import urllib import requests def sort_value(old_dict): items = sorted(old_dict.items()) new_dict = OrderedDict() for item in items: new_dict[item[0]] = old_dict[item[0]] return new_dict class UploadMonitorData(object): def __init__(self, access_key_id, secret_access_key): self.access_key_id = access_key_id self.secret_access_key = secret_access_key nowtime = datetime.datetime.now() self.signature_time = datetime.datetime.strftime(nowtime, "%Y-%m-%dT%H:%M:%SZ") self.time_stamp = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") self.time = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ") self.url = "https://api.qingcloud.com/iaas/" self.CloudSatUrl = "http://cloudsat.qingcloud.com/api/sh1/v1/custom/UploadMonitorData" self.url_path = '/iaas/' self.methods = 'GET' def post_request(self, post_url): headers = {"Content-Type": "application/json; charset=UTF-8"} body = { "user_id": "usr-8Ljvov6I", "namespace": "pt-test", "data": [ { "source": "custom", "user_id": "usr-8Ljvov6I", "resource_id": "i-ets7af6q", "resource_type": "instance", "root_user_id": "usr-8Ljvov6I", "meter": "cpu", "region": "sh1", "value": 99, "value_type": "percent", "time_stamp": self.time_stamp }, { "source": "custom", "user_id": "usr-8Ljvov6I", "resource_id": "i-ets7af6q", "resource_type": "instance", "root_user_id": "usr-8Ljvov6I", "meter": "memory", "region": "sh1", "value": 96, "value_type": "percent", "time_stamp": self.time_stamp } ] } result = requests.post(url=post_url, headers=headers, data=json.dumps(body)) print(result.status_code) print(result.reason) print(result.text) def post_monitor_data(self): od = OrderedDict() od['access_key_id'] = self.access_key_id od['action'] = "DescribeUsers" od['signature_method'] = "HmacSHA256" od['signature_version'] = 1 od['time_stamp'] = self.signature_time od['version'] = 1 od['zone'] = "sh1" od_sort = sort_value(od) data = urllib.urlencode(od_sort) string_to_sign = self.methods + "\n" + self.url_path + "\n" + data print(string_to_sign) h = hmac.new(self.secret_access_key.encode(), digestmod=sha256) h.update(string_to_sign.encode()) sign = base64.b64encode(h.digest()).strip() signature = urllib.quote_plus(sign) post_url = self.CloudSatUrl + "?" + data + "&signature=" + signature self.post_request(post_url) if __name__ == '__main__': access_key_id = 'ACCESSKEYID' secret_access_key = 'SECRETACCESSKEY' while True: upload_struct_object = UploadMonitorData(access_key_id, secret_access_key) upload_struct_object.post_monitor_data() time.sleep(3)