阿里云Prometheus监控的Remote Write功能支持作为远程数据库存储Prometheus监控数据。本文将介绍如何创建Prometheus实例 for Remote WriteRemote Write类型的Prometheus实例),即如何使用阿里云Prometheus监控的Remote Write对接自建Prometheus,构建监控数据的高效存储方案。

(可选)步骤一:为RAM用户授予GetPrometheusApiToken接口调用权限

自建Prometheus写入阿里云Prometheus监控时需要调用GetPrometheusApiToken接口。阿里云账号(主账号)默认支持调用GetPrometheusApiToken接口。如果您是RAM用户(子账号),则需要先使用阿里云账号为RAM用户授予GetPrometheusApiToken接口的调用权限。

  1. 在左侧导航栏,选择权限管理 > 权限策略
  2. 权限策略页面,单击创建权限策略
  3. 创建权限策略页面,单击脚本编辑页签。
  4. 输入以下权限策略内容,然后单击下一步:编辑基本信息
    {
        "Version": "1",
        "Statement": [
            {
                "Action": [
                    "arms:GetPrometheusApiToken"
                ],
                "Resource": [
                    "*"
                ],
                "Effect": "Allow"
            }
        ]
    }
    ert关于权限策略语法结构的详情,请参见权限策略语法和结构
  5. 输入权限策略名称备注
  6. 检查并优化权限策略内容。
    • 基础权限策略优化

      系统会对您添加的权限策略语句自动进行基础优化。基础权限策略优化会完成以下任务:

      • 删除不必要的条件。
      • 删除不必要的数组。
    • 可选:高级权限策略优化

      您可以将鼠标悬浮在可选:高级策略优化上,单击执行,对权限策略内容进行高级优化。高级权限策略优化功能会完成以下任务:

      • 拆分不兼容操作的资源或条件。
      • 收缩资源到更小范围。
      • 去重或合并语句。
  7. 单击确定
  8. 在左侧导航栏,选择身份管理 > 用户
  9. 用户页面,单击目标RAM用户操作列的添加权限
  10. 添加权限面板,为RAM用户添加权限。
    1. 选择授权应用范围。
      • 整个云账号:权限在当前阿里云账号内生效。
      • 指定资源组:权限在指定的资源组内生效。
        说明 指定资源组授权生效的前提是该云服务已支持资源组。更多信息,请参见支持资源组的云服务
    2. 输入授权主体。
      授权主体即需要授权的RAM用户,系统会自动填入当前的RAM用户,您也可以添加其他RAM用户。
    3. 选择权限策略。
      添加权限面板的选择权限区域,单击自定义策略,然后通过搜索查找步骤5中创建的权限策略,单击权限策略名称将权限策略添加至右侧已选择区域。为用户添加权限策略
  11. 单击确定
  12. 单击完成

步骤二:创建Remote Write并获取读写URL

  1. 登录Prometheus控制台
  2. 在左侧导航栏单击监控列表,然后在Prometheus监控页面的顶部菜单栏,选择地域。
  3. 单击新建Prometheus实例,然后在弹出的面板中单击Prometheus实例 for Remote Write区域。
  4. 自定义Prometheus实例的名称,然后单击新建
    Prometheus监控页面将会显示实例类型为Prometheus for Remote WritePrometheus实例
    说明 单击新建后,若控制台显示报错信息,是因为您设置的实例名称已存在,请重新设置实例名称即可。
  5. 复制并保存生成的Remote Read地址Remote Write地址

步骤三:配置Prometheus

  1. 安装Prometheus,安装方法可以参考官方文档
  2. 打开Prometheus.yaml配置文件,并在文件末尾增加以下内容,将remote_writeremote_read链接替换为步骤二:创建Remote Write并获取读写URL中获取的URL,然后保存文件。
    global:
      scrape_interval:     15s
      evaluation_interval: 15s
    scrape_configs:
      - job_name: 'prometheus'
        static_configs:
        - targets: ['localhost:9090']
    remote_write:
      - url: "http://ts-xxxxxxxxxxxx.hitsdb.rds.aliyuncs.com:3242/api/prom_write"
        basic_auth:   
          //Username和Password需要遵循AK/SK的限制,且AK/SK需要能够调用GetPrometheusApiToken。
          username: access-key-id
          password: access-key-secret
    remote_read:
      - url: "http://ts-xxxxxxxxxxxx.hitsdb.rds.aliyuncs.com:3242/api/prom_read"
        read_recent: true
    说明 自建Prometheus写入阿里云Prometheus监控时需要调用GetPrometheusApiToken接口。阿里云账号(主账号)默认支持调用GetPrometheusApiToken接口。如果您是RAM用户(子账号),则需要先使用阿里云账号为RAM用户授予GetPrometheusApiToken接口的调用权限。具体操作,请参见(可选)步骤一:为RAM用户授予GetPrometheusApiToken接口调用权限

使用远程存储对接OpenTelemetry

阿里云Prometheus监控的远程存储对接OpenTelemetry之后,您可以使用Prometheus监控的远程存储功能存储OpenTelemetry的数据。

  1. 业务代码进行OpenTelemetry埋点。
    package stat
    
    import (
        "context"
        "fmt"
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/metric"
        "time"
    )
    
    var buyCounter metric.Int64Counter
    
    func init() {
        fmt.Println(time.Now(), " - initMetrics start......")
        meter := otel.GetMeterProvider().Meter("github.com/liguozhong/prometheus-arms-aliyun-go-demo")
        buyCounter = metric.Must(meter).NewInt64Counter(
            "buy_total",
            metric.WithDescription("Measures  buy"),
        )
    }
    
    func DoBuy() (string, error) {
        buyCounter.Add(context.Background(), 1)
        return "buy success", nil
    }
  2. OpenTelemetry对Meter进行初始化并和连接Prometheus。
    package stat
    
    import (
        "context"
        prometheusPushExporter "go.opentelemetry.io/contrib/exporters/metric/cortex"
        prometheusExporter "go.opentelemetry.io/otel/exporters/metric/prometheus"
    
        "errors"
        "fmt"
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/exporters/otlp"
        "go.opentelemetry.io/otel/label"
        "go.opentelemetry.io/otel/sdk/metric/controller/pull"
        "go.opentelemetry.io/otel/sdk/metric/controller/push"
        "go.opentelemetry.io/otel/sdk/metric/processor/basic"
        "go.opentelemetry.io/otel/sdk/metric/selector/simple"
        "go.opentelemetry.io/otel/sdk/resource"
        "net/http"
        "time"
    
        _ "net/http/pprof"
    )
    
    func InitMeter(app string, push bool) error {
        fmt.Println(time.Now(), " - initMeter start......")
        if push {
            fmt.Println(time.Now(), " - initMeter opentelemetry push......")
            remoteUrl := "http://region.arms.aliyuncs.com/prometheus/../../../../api/v3/write"
            ak := "ak"
            sk := "sk"
            return initPushMeter(app, remoteUrl, ak, sk)
        }
        fmt.Println(time.Now(), " - initMeter opentelemetry pull......")
        return initPullMeter(app)
    }
    
    func initPushMeter(regionId string, remoteWriteUrl string, ak string, sk string) error {
        fmt.Println(time.Now(), " - initPushMeter start......")
        var validatedStandardConfig = prometheusPushExporter.Config{
            Endpoint:      remoteWriteUrl,
            Name:          "AliyunConfig",
            RemoteTimeout: 30 * time.Second,
            PushInterval:  10 * time.Second,
            Quantiles:     []float64{0.5, 0.9, 0.95, 0.99},
            BasicAuth: map[string]string{
                "username": ak,
                "password": sk,
            },
        }
        if validatedStandardConfig.Endpoint == "" {
            return errors.New(" validatedStandardConfig.Endpoint==empty.regionId:" + regionId)
        }
        fmt.Println("Success: Created Config struct")
        r, err := resource.New(context.Background(),
            resource.WithAttributes(
                label.String("cluster", "test-otel"),
                label.String("app", "buy")))
        if err != nil {
            fmt.Println("resource Error:", err)
        }
        pusher, err := prometheusPushExporter.InstallNewPipeline(validatedStandardConfig,
            push.WithPeriod(30*time.Second), push.WithResource(r))
        if err != nil {
            fmt.Println("InstallNewPipeline Error:", err)
        }
        otel.SetMeterProvider(pusher.MeterProvider())
        return nil
    }
    
    func initPullMeter(app string) error {
        fmt.Println(time.Now(), " - initPullMeter start......")
        r, err := resource.New(context.Background(),
            resource.WithAttributes(
                label.String("cluster", "test-otel"),
                label.String("app", app)))
        if err != nil {
            fmt.Println("resource Error:", err)
        }
        exporter, err := prometheusExporter.NewExportPipeline(
            prometheusExporter.Config{
                DefaultHistogramBoundaries: []float64{-0.5, 1},
            },
            pull.WithCachePeriod(0),
            pull.WithResource(r),
        )
        if err != nil {
            return err
        }
        http.HandleFunc("/opentelemetry", exporter.ServeHTTP)
        otel.SetMeterProvider(exporter.MeterProvider())
        return nil
    }
    
    func initOtlpProvider(regionId string) (*push.Controller, error) {
        exporter, err := otlp.NewExporter(
            context.Background(),
            otlp.WithInsecure(),
            otlp.WithAddress(regionId+"-intranet.arms.aliyuncs.com:8000"),
        )
        if err != nil {
            return nil, err
        }
    
        pusher := push.New(
            basic.New(
                simple.NewWithExactDistribution(),
                exporter,
            ),
            exporter,
            push.WithPeriod(30*time.Second),
        )
    
        otel.SetMeterProvider(pusher.MeterProvider())
        pusher.Start()
    
        return pusher, err
    }
  3. 业务入口初始化,并启动OpenTelemetry功能。
    package pkg
    
    import (
        "fmt"
        stat "github.com/liguozhong/prometheus-arms-aliyun-go-demo/pkg/opentelemetry"
        "github.com/prometheus/client_golang/prometheus/promhttp"
        "io"
        "net/http"
        "strconv"
    )
    
    type Server struct {
        port int
    }
    
    func NewServer(port int) *Server {
        return &Server{
            port: port,
        }
    }
    
    func (s *Server) Run() error {
        port := ":" + strconv.Itoa(s.port)
        path := "/metrics"
        service := "/buy"
        http.Handle(path, promhttp.Handler()) //初始一个http handler
        http.HandleFunc(service, func(writer http.ResponseWriter, request *http.Request) {
            content, err := stat.DoBuy()
            if err != nil {
                io.WriteString(writer, err.Error())
                return
            }
            io.WriteString(writer, content)
        })
        stat.InitMeter("buy2", true)
        fmt.Println("http.url: http://localhost" + port + path)
        fmt.Println("service.url: http://localhost" + port + service)
        err := http.ListenAndServe(port, nil)
        if err != nil {
            return err
        }
        return nil
    }
  4. 查看Go module的依赖列表。
    module github.com/liguozhong/prometheus-arms-aliyun-go-demo
    
    go 1.12
    
    require (
        github.com/go-kit/kit v0.9.0
        github.com/prometheus/client_golang v1.7.1
        go.opentelemetry.io/contrib/exporters/metric/cortex v0.15.0
        go.opentelemetry.io/otel v0.15.0
        go.opentelemetry.io/otel/exporters/metric/prometheus v0.15.0
        go.opentelemetry.io/otel/exporters/otlp v0.15.0
        go.opentelemetry.io/otel/sdk v0.15.0
        golang.org/x/text v0.3.3 // indirect
    )
  5. 在Grafana大盘中查看数据。OpenTelemetry展示数据

停止远程存储Prometheus监控数据

如需停止远程数据库存储Prometheus监控数据,请按照以下步骤卸载插件。

  1. 登录Prometheus控制台
  2. 在左侧导航栏单击监控列表,然后在Prometheus监控页面的顶部菜单栏,选择地域。
  3. Prometheus监控页面选中目标Prometheus实例,然后在其右侧操作列单击卸载,并在弹出的对话框中单击确认
    卸载完成后,Prometheus监控页面不再显示该Prometheus实例