需求
用阿里云云监控服务:
阿里云云监控没有grafana服务端,生成不了PDF,再加上Prometheus不能生成exl表格数据,阿里云云监控企业版太贵,一个报表0.14元。我们可以自己调取阿里云的api接口,获取ECS主机cpu负载、内存使用率等信息,生成报表定时发送指定邮箱。
有人会说了,为啥不自己搭建一个Prometheus服务端,然后把所有主机全部添加到自己的服务器上。这个想法好,但是如果“二次开发”阿里云 云监控平台,我们公司就不用单独购买服务器,毕竟人家做的也不错,除了有些功能收费以外。其实自己搭建Prometheus服务端我们已经实现了,目前可以做到监控数据生成PDF格式定时发送,动态添加主机(自动增删被监控主机,还未整理,主要是懒🌚),只不过想节省资源,也能为公司每个月省下好几百。
目前进度
1、(完成) 已获取所有主机指定时间段内的各种指标(目前该项目以cpu使用率为例)
2、(进行中) 已获取所有主机主机名和ip信息
3、(未开始) 把接口一获取到的instanceid和接口二获得的instanceid对比,如果相等,把接口二获取的主机名和ip写到该表行的“主机名”与“IP”列
4、(未开始)获取整个周期时间段数据的百分比,例如7天内,在10-12点的cpu使用率
效果一:获取单台主机资源信息
获取一个主机的7天内的cpu使用率信息,生成报表
阿里云API
阿里云 云监控服务API地址:https://next.api.aliyun.com/home
本篇使用的API网址:https://next.api.aliyun.com/api/Cms/2019-01-01/DescribeMetricTop?params={}
代码示例
// This file is auto-generated, don't edit it. Thanks. package main import ( "encoding/json" "fmt" "github.com/xuri/excelize/v2" "os" cms20190101 "github.com/alibabacloud-go/cms-20190101/v2/client" openapi "github.com/alibabacloud-go/darabonba-openapi/client" "github.com/alibabacloud-go/tea/tea" "strings" ) type List struct { Order int `json:"order"` Timestamp int `json:"timestamp"` UserId string `json:"userId"` InstanceId string `json:"instanceId"` Minimum float32 `json:"Minimum"` Maximum float32 `json:"Maximum"` Average float32 `json:"Average"` Count float32 `json:"_count"` } /** * 使用AK&SK初始化账号Client * @param accessKeyId * @param accessKeySecret * @return Client * @throws Exception */ func CreateClient (accessKeyId *string, accessKeySecret *string) (_result *cms20190101.Client, _err error) { config := &openapi.Config{ // 您的AccessKey ID AccessKeyId: accessKeyId, // 您的AccessKey Secret AccessKeySecret: accessKeySecret, } // 访问的域名 config.Endpoint = tea.String("metrics.cn-hangzhou.aliyuncs.com") _result = &cms20190101.Client{} _result, _err = cms20190101.NewClient(config) return _result, _err } func _main (args []*string) (_err error) { client, _err := CreateClient(tea.String("LTxxxxxxxxx5dUc"), tea.String("G1WxxxxxxxxxxxxxxxxxxiF6")) if _err != nil { return _err } describeMetricListRequest := &cms20190101.DescribeMetricListRequest{ StartTime: tea.String("1636905600000"), EndTime: tea.String("1637424000000"), MetricName: tea.String("cpu_total"), Namespace: tea.String("acs_ecs_dashboard"), Dimensions: tea.String("{\"instanceId\":\"i-8vxxxxxxxxgh\"}"), Period: tea.String("604800"), } // 复制代码运行请自行打印 API 的返回值 M, _err := client.DescribeMetricList(describeMetricListRequest) N := *M.Body.Datapoints fmt.Println(N) N1 := strings.TrimLeft(N, "[") N2 := strings.TrimRight(N1, "]") //fmt.Printf("N类型:%T,N值:%v\n",N2,N2) fmt.Println(N2) var list List err1 := json.Unmarshal([]byte(N2), &list) if err1 != nil { fmt.Printf("序列化转化失败,%v",err1) }else { fmt.Println("序列化转化成功") } fmt.Println(list) f := excelize.NewFile() // 创建一个工作表 index := f.NewSheet("主机列表") err2 := f.SetColWidth("主机列表", "A", "H", 23) if err2 != nil { fmt.Printf("表格创建失败,%v",err2) }else { fmt.Println("表格创建成功") } // 设置单元格的值 f.SetCellValue("主机列表", "A1", "实例ID号") f.SetCellValue("主机列表", "A2", list.InstanceId) f.SetCellValue("主机列表","B1","7天CPU最小使用率") f.SetCellValue("主机列表","B2",list.Minimum) f.SetCellValue("主机列表","C1","7天CPU最大使用率") f.SetCellValue("主机列表","C2",list.Maximum) f.SetCellValue("主机列表","D1","7天CPU平均使用率") f.SetCellValue("主机列表","D2",list.Average) // 设置工作簿的默认工作表 f.SetActiveSheet(index) // 根据指定路径保存文件 if err := f.SaveAs("Book1.xlsx"); err != nil { fmt.Println(err) } if _err != nil { return _err } return _err } func main() { err := _main(tea.StringSlice(os.Args[1:])) if err != nil { panic(err) } }
效果二:获取所有主机资源信息
阿里云API
https://next.api.aliyun.com/api/Cms/2019-01-01/DescribeMetricList?params={}
https://next.api.aliyun.com/api/Cms/2019-01-01/DescribeMetricLast?params={}
说实话,不知道这两个有什么区别。都可以用,而且两个获得数据不准,乱七八糟!先讲究用吧
代码实例
获取所有主机监控数据
// This file is auto-generated, don't edit it. Thanks. package main import ( "encoding/json" "fmt" cms20190101 "github.com/alibabacloud-go/cms-20190101/v2/client" openapi "github.com/alibabacloud-go/darabonba-openapi/client" "github.com/alibabacloud-go/tea/tea" "github.com/xuri/excelize/v2" _ "github.com/xuri/excelize/v2" "os" "strconv" ) type List struct { Order int `json:"order"` Timestamp int `json:"timestamp"` UserId string `json:"userId"` InstanceId string `json:"instanceId"` Minimum float32 `json:"Minimum"` Maximum float32 `json:"Maximum"` Average float32 `json:"Average"` Count float32 `json:"_count"` } /** * 使用AK&SK初始化账号Client * @param accessKeyId * @param accessKeySecret * @return Client * @throws Exception */ func CreateClient(accessKeyId *string, accessKeySecret *string) (_result *cms20190101.Client, _err error) { config := &openapi.Config{ // 您的AccessKey ID AccessKeyId: accessKeyId, // 您的AccessKey Secret AccessKeySecret: accessKeySecret, } // 访问的域名 config.Endpoint = tea.String("metrics.cn-hangzhou.aliyuncs.com") _result = &cms20190101.Client{} _result, _err = cms20190101.NewClient(config) return _result, _err } func _main(args []*string) (_err error) { client, _err := CreateClient(tea.String("LTAIxxxxxxxxxxx"), tea.String("G1WBNxxxxxxxxxxxxxxxxxxxxxx")) if _err != nil { return _err } describeMetricListRequest := &cms20190101.DescribeMetricListRequest{ StartTime: tea.String("1636905600000"), EndTime: tea.String("1637424000000"), MetricName: tea.String("cpu_total"), Namespace: tea.String("acs_ecs_dashboard"), Dimensions: tea.String(""), Period: tea.String("604800"), } // 复制代码运行请自行打印 API 的返回值 M, _err := client.DescribeMetricList(describeMetricListRequest) N := *M.Body.Datapoints //fmt.Println("N值\n", N) var slice []map[string]interface{} //注意:反序列化map,不需要make,因为make操作被封装到Unmarshal函数 err := json.Unmarshal([]byte(N), &slice) if err != nil { fmt.Printf("unmarshal err=%v\n", err) } f := excelize.NewFile() index := f.NewSheet("主机列表") f.SetCellValue("主机列表", "A1", "实例ID号") f.SetCellValue("主机列表", "B1", "7天内的CPU最大使用率") f.SetCellValue("主机列表", "C1", "7天内的CPU最小使用率") f.SetCellValue("主机列表", "D1", "7天内的CPU平均使用率") f.SetColWidth("主机列表", "A", "H", 23) /* fmt.Printf("反序列化后:%v\n", slice) fmt.Println("第一个切片是:", slice[0]) fmt.Println("第二个切片是:", slice[1]) //获取第一个主机信息 Arr, err := json.Marshal(slice[0]) if err != nil { fmt.Println(err) return } var List1 List err = json.Unmarshal(Arr, &List1) if err != nil { fmt.Println(err) return } fmt.Println(List1) f.SetCellValue("主机列表", "A2", List1.InstanceId) f.SetCellValue("主机列表", "B2", List1.Maximum) f.SetCellValue("主机列表", "C2", List1.Minimum) f.SetCellValue("主机列表", "D2", List1.Average) //获取第二个主机信息 Arr1, err := json.Marshal(slice[1]) if err != nil { fmt.Println(err) return } var List2 List err = json.Unmarshal(Arr1, &List2) if err != nil { fmt.Println(err) return } fmt.Println(List2) f.SetCellValue("主机列表", "A3", List2.InstanceId) f.SetCellValue("主机列表", "B3", List2.Maximum) f.SetCellValue("主机列表", "C3", List2.Minimum) f.SetCellValue("主机列表", "D3", List2.Average) */ //把上面单个获取主机方式改成for循环 for i :=2 ;i < len(slice); i++ { A1 :="A" //strconv.Itoa函数 可以将int类型转string类型 A2 := fmt.Sprint(A1 + strconv.Itoa(i)) B1 :="B" B2 := fmt.Sprint(B1 + strconv.Itoa(i)) C1 :="C" C2 := fmt.Sprint(C1 + strconv.Itoa(i)) D1 :="D" D2 := fmt.Sprint(D1 + strconv.Itoa(i)) Arr, err := json.Marshal(slice[i-2]) if err != nil { fmt.Println(err) return } var List1 List err = json.Unmarshal(Arr, &List1) if err != nil { fmt.Println(err) return } //fmt.Println(List1) f.SetCellValue("主机列表", A2, List1.InstanceId) f.SetCellValue("主机列表", B2, List1.Maximum) f.SetCellValue("主机列表", C2, List1.Minimum) f.SetCellValue("主机列表", D2, List1.Average) } // 设置工作簿的默认工作表 f.SetActiveSheet(index) // 根据指定路径保存文件 if err := f.SaveAs("Book1.xlsx"); err != nil { fmt.Println(err) } if _err != nil { return _err } return _err } func main() { err := _main(tea.StringSlice(os.Args[1:])) if err != nil { panic(err) } }