kubernetes+ prometheus自动伸缩的设计与实现(二)

上一篇介绍了kubernetes+ prometheus中k8s的hpa的部分,但是没有介绍转化的部分,下面接着介绍
如果每次hpa调用查询metrics的指标我就去调用prometheus,但指标在prometheus里面是否存在,我们先要本地缓存一下,免给prometheus带来很大压力,所以本地缓存还有应该有的,主要缓存prometheus里面存在的指标,定义如下:

metrics []provider.MetricInfo

上面metrics保存prometheus的指标。当然本地缓存指标数据还有定期更新。

func (l *cachingMetricsLister) Run() {
    l.RunUntil(wait.NeverStop)
}

func (l *cachingMetricsLister) RunUntil(stopChan <-chan struct{}) {
    go wait.Until(func() {
        if err := l.updateMetrics(); err != nil {
            utilruntime.HandleError(err)
        }
    }, l.updateInterval, stopChan)
}

通过updateMetrics更新本地数据,这个方法首先是查询数据

func (l *cachingMetricsLister) updateMetrics() error {
    startTime := pmodel.Now().Add(-1 * l.updateInterval)

    // container-specific metrics from cAdvsior have their own form, and need special handling
    containerSel := prom.MatchSeries("", prom.NameMatches("^container_.*"), prom.LabelNeq("container_name", "POD"), prom.LabelNeq("namespace", ""), prom.LabelNeq("pod_name", ""))
    namespacedSel := prom.MatchSeries("", prom.LabelNeq("namespace", ""), prom.NameNotMatches("^container_.*"))

    series, err := l.promClient.Series(context.Background(), pmodel.Interval{startTime, 0}, containerSel, namespacedSel)
    if err != nil {
        return fmt.Errorf("unable to update list of all available metrics: %v", err)
    }

    glog.V(10).Infof("Set available metric list from Prometheus to: %v", series)
    if len(series)>0 {
        l.SetSeries(series)
    }
    return nil
}

先是通过Series获取指标序列,一个是容器的container,另一个是命名空间的namespace的。

func (h *queryClient) Series(ctx context.Context, interval model.Interval, selectors ...Selector) ([]Series, error) {
    vals := url.Values{}
    if interval.Start != 0 {
        vals.Set("start", interval.Start.String())
    }
    if interval.End != 0 {
        vals.Set("end", interval.End.String())
    }

    for _, selector := range selectors {
        vals.Add("match[]", string(selector))
    }

    res, err := h.api.Do(ctx, "GET", seriesURL, vals)
    if err != nil {
        return nil, err
    }

    var seriesRes []Series
    err = json.Unmarshal(res.Data, &seriesRes)
    return seriesRes, err
}

这里通过prometheus的series接口获取指标序列,从而更新本地数据

func (r *basicSeriesRegistry) SetSeries(newSeries []prom.Series) error {
    newInfo := make(map[provider.MetricInfo]seriesInfo)
    for _, series := range newSeries {
        if strings.HasPrefix(series.Name, "container_") {
            r.namer.processContainerSeries(series, newInfo)
        } else if namespaceLabel, hasNamespaceLabel := series.Labels["namespace"]; hasNamespaceLabel && namespaceLabel != "" {
            // we also handle namespaced metrics here as part of the resource-association logic
            if err := r.namer.processNamespacedSeries(series, newInfo); err != nil {
                glog.Errorf("Unable to process namespaced series %q: %v", series.Name, err)
                continue
            }
        } else {
            if err := r.namer.processRootScopedSeries(series, newInfo); err != nil {
                glog.Errorf("Unable to process root-scoped series %q: %v", series.Name, err)
                continue
            }
        }
    }

    newMetrics := make([]provider.MetricInfo, 0, len(newInfo))
    for info := range newInfo {
        newMetrics = append(newMetrics, info)
    }

    r.mu.Lock()
    defer r.mu.Unlock()

    r.info = newInfo
    r.metrics = newMetrics

    return nil
}

这里,如果指标是container_开头、包含namespace指标的和其它三种情况处理,先看processContainerSeries里面

func (n *metricNamer) processContainerSeries(series prom.Series, infos map[provider.MetricInfo]seriesInfo) {

    originalName := series.Name

    var name string
    metricKind := GaugeSeries
    if override, hasOverride := n.overrides[series.Name]; hasOverride {
        name = override.metricName
        metricKind = override.kind
    } else {
        // 这里会将指标前缀 "container_" 去掉
        series.Name = series.Name[10:]
        name, metricKind = n.metricNameFromSeries(series)
    }

    info := provider.MetricInfo{
        GroupResource: schema.GroupResource{Resource: "pods"},
        Namespaced:    true,
        Metric:        name,
    }
    //infos 这个map的key类似pods/memory_swap(namespaced)这样
    infos[info] = seriesInfo{
        kind:        metricKind,
        baseSeries:  prom.Series{Name: originalName},
        isContainer: true,
    }
}

因为上面重写了String方法

func (i MetricInfo) String() string {
    if i.Namespaced {
        return fmt.Sprintf("%s/%s(namespaced)", i.GroupResource.String(), i.Metric)
    } else {
        return fmt.Sprintf("%s/%s", i.GroupResource.String(), i.Metric)
    }
}

所以key是pods/memory_swap(namespaced)。

如果是processNamespacedSeries或者processRootScopedSeries都是类似的,不过指标组不再是pod,譬如ingress_http_hits_total{pod=”foo”,service=”bar”,ingress=”baz”,namespace=”ns”}这样的一个指标
会返回三个GroupResources: “pods”, “services”, and “ingresses”.
这样指标就会变成services/ingress_http_hits_total。
补充一下,series 接口是prometheus查询数据序列的接口eg:

curl -g 'http://localhost:9090/api/v1/series?match[]=up&match[]=process_start_time_seconds{job="prometheus"}'
{
   "status" : "success",
   "data" : [
      {
         "__name__" : "up",
         "job" : "prometheus",
         "instance" : "localhost:9090"
      },
      {
         "__name__" : "up",
         "job" : "node",
         "instance" : "localhost:9091"
      },
      {
         "__name__" : "process_start_time_seconds",
         "job" : "prometheus",
         "instance" : "localhost:9090"
      }
   ]
}

这样就能获取到指标序列了并且指标的label。这样prometheus本地指标缓存功能有了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳清风09

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值