摘要
需求
用阿里云云监控服务:
 阿里云云监控没有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"
"/xuri/excelize/v2"
"os"
cms20190101 "/alibabacloud-go/cms-20190101/v2/client"
openapi "/alibabacloud-go/darabonba-openapi/client"
"/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 "/alibabacloud-go/cms-20190101/v2/client"
openapi "/alibabacloud-go/darabonba-openapi/client"
"/alibabacloud-go/tea/tea"
"/xuri/excelize/v2"
_ "/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)
}
}
                
 
 
                     
            
        













 
                    

 
                 
                    