基于Consul完成腾讯云主机监控

发布时间 2023-06-14 19:43:46作者: 元气少女郭德纲!!

基于Consul完成腾讯云主机监控

背景

  • 腾讯云提供tencent-exporter支持获取CVM主机列表及监控信息。但碍于CVM主机过多,使用Tencent-exporter将导致频繁调用腾讯云API,导致额外费用支持。因此在监控CVM云主机使用Consul自动注册监控方式。但Consul在使用中只是一个注册中心,并不会自动获取到腾讯云CVM实例列表,需要自行调用腾讯云API获取CVM实例列表注册至Consul。

构成

  • 监控构成:

NodeExporter + 自开发脚本 + Consul + Prometheus

流程

  1. 获取腾讯云信息存储至数据库

  2. 所有CVM主机通过自主化助手统一部署Node-Exporter

  3. 获取数据库中CVM信息POST至Consul

  4. Prometheus抓取Consul信息

  5. 完成主机自动发现

  • 步骤0和1这里不做展开,主要对步骤2、3、4展开说明
  • 因为Prometheus环境有两套,所以分别部署了两套Conusl,一套测试环境,一套生产环境。根据CVM打标分为dev与prod环境,将归属于不同环境的CVM主机根据标签分别注册至不同环境Consul。

数据POST至Conusl

package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/hashicorp/consul/api"
	"log"
	"strings"
)

var (
	instanceid string
	Mapping_Ip string
)

func ConnMysql() (db *sql.DB) {
	// mysql 连接信息
	username := "!!!!"
	password := "!!!!"
	host := "!!!!"
	port := 3306
	Dbname := "!!!!"

	// 连接mysql
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", username, password, host, port, Dbname)
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		log.Fatal(err)
	}

	return db
}

func ConnConsul(env string) (client *api.Client) {
	// consul 连接信息
	consulAddress := fmt.Sprintf("consul-%s.aaa.com", env)
	// 连接consul
	client, err := api.NewClient(&api.Config{Address: consulAddress})
	if err != nil {
		log.Fatal(err)
	}
	return client
}

func ReadCvmDataForConsul(db *sql.DB, client *api.Client, env string) {
	rows, err := db.Query(fmt.Sprintf("SELECT InstanceId,Mapping_Ip FROM `tencent_cvm` WHERE `Env` = '%s' AND `delete` = '0' AND `InstanceName` NOT LIKE '%%emr%%' AND `instancestatus` = 'Running' ", env))
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()
	dbinstanceid := make(map[string]bool)
	for rows.Next() {
		rows.Scan(&instanceid, &Mapping_Ip)
		Mapping_Ip = strings.Trim(Mapping_Ip, "[]")
		entry := &api.AgentServiceRegistration{
			ID:      instanceid,
			Name:    "tencent-cvm",
			Tags:    []string{"consul-cvm", "test"},
			Port:    9100,
			Address: Mapping_Ip,
		}

		dbinstanceid[instanceid] = true
		// 将ServiceEntry注册到Consul的Agent中
		agent := client.Agent()
		if err := agent.ServiceRegister(entry); err != nil {
			log.Fatal(err)
		}
	}

	// 查询consul services中所有的instance
	services, _, err := client.Catalog().Service("tencent-cvm", "", nil)
	if err != nil {
		log.Fatal(err)
	}

	// 对比consul中存在但数据库不存在的instance,对销毁主机下线监控
	for _, service := range services {
		if !dbinstanceid[service.ServiceID] {
			err = client.Agent().ServiceDeregister(service.ServiceID)
			if err != nil {
				log.Fatal(err)
			}
			fmt.Printf("Service在consul中存在,但是在mysql中已被删除:%s\n", service.ServiceID)
		}
	}

}

func main() {
	db := ConnMysql()
	defer db.Close()

	TencentEnv := []string{"dev", "prod"}
	for _, ch := range TencentEnv {
		client := ConnConsul(ch)
		ReadCvmDataForConsul(db, client, ch)
	}

}

Prometheus抓取Consul注册主机

    - job_name: "consul-tencent-cvm" # 此处考虑未来如果多云?
      scrape_interval: 15s
      consul_sd_configs:
      - server: 'consul.aaa.com:8500'
        refresh_interval: 1m
        services: ["tencent-cvm"]
      relabel_configs:
        - source_labels: [__meta_consul_service_id]
          target_label:  "instanceId"
        - source_labels: [__address__]
          regex: ([^:]+)(?::\d+)?
          replacement: "$1"
          target_label: instance
          action: replace