Prometheus迷霧 - 無法獲取Pod的label

問題背景

Prometheus在抓取container的CPU/Mem等metric的時候,發(fā)現(xiàn)metric上沒有帶Pod的label,這導(dǎo)致一個問題,無法通過自定義的label查看其下的所有metric資源。
例如下面的一個a-custom-project, 帶了一個project: a-custom-project label

apiVersion: v1
kind: Pod
metadata:
  labels:
    project: a-custom-project 
  name: a-custom-project 
spec:
  containers:
    image: nginx
    imagePullPolicy: Always

希望project: a-custom-project label過濾出其下的所有metic指標(biāo)屁魏,下面是一個container_cpu_usage_seconds_total的metric

正常情況下是不會出現(xiàn)紅色框中的label,因此在grafana上也無法通過下面的方式顯示

問題分析

導(dǎo)致此問題的出現(xiàn)主要的原因是kubelet在調(diào)用kubeGenericRuntimeManager創(chuàng)建container的時候捉腥,過濾掉所有的自定義的label氓拼,只添加了固定的幾個指標(biāo)。

kubernetes/pkg/kubelet/kuberuntime/labels.go

// newContainerLabels creates container labels from v1.Container and v1.Pod.
func newContainerLabels(container *v1.Container, pod *v1.Pod) map[string]string {
  labels := map[string]string{}
  labels[types.KubernetesPodNameLabel] = pod.Name
  labels[types.KubernetesPodNamespaceLabel] = pod.Namespace
  labels[types.KubernetesPodUIDLabel] = string(pod.UID)
  labels[types.KubernetesContainerNameLabel] = container.Name

  return labels
}

并且kubelet會過濾容器上的label標(biāo)簽但狭,只保留規(guī)定的KubernetesPodNameLabel / KubernetesPodNameSpaceLabel / KubernetesContainerNameLabel 等label

kubernetes/pkg/kubelet/server/server.go

func containerPrometheusLabelsFunc(s stats.Provider) metrics.ContainerLabelsFunc {
  // containerPrometheusLabels maps cAdvisor labels to prometheus labels.
  return func(c *cadvisorapi.ContainerInfo) map[string]string {
    // Prometheus requires that all metrics in the same family have the same labels,
    // so we arrange to supply blank strings for missing labels
    var name, image, podName, namespace, containerName string
    if len(c.Aliases) > 0 {
      name = c.Aliases[0]
    }
    image = c.Spec.Image
    if v, ok := c.Spec.Labels[kubelettypes.KubernetesPodNameLabel]; ok {
      podName = v
    }
    if v, ok := c.Spec.Labels[kubelettypes.KubernetesPodNamespaceLabel]; ok {
      namespace = v
    }
    if v, ok := c.Spec.Labels[kubelettypes.KubernetesContainerNameLabel]; ok {
      containerName = v
    }

    // Associate pod cgroup with pod so we have an accurate accounting of sandbox
    if podName == "" && namespace == "" {
      if pod, found := s.GetPodByCgroupfs(c.Name); found {
        podName = pod.Name
        namespace = pod.Namespace
      }
    }
    set := map[string]string{
      metrics.LabelID:    c.Name,
      metrics.LabelName:  name,
      metrics.LabelImage: image,
      "pod":              podName,
      "namespace":        namespace,
      "container":        containerName,
    }
    return set
  }
}

此問題從2015年披诗,k8s 1.1版本起就在社區(qū)開始有反饋,期間也不斷有人issues立磁,但是一直沒有解決呈队,社區(qū)主要考慮的有2點:
1)abel是一個很易變的數(shù)據(jù),變化后會導(dǎo)致圖標(biāo)不連續(xù)(詳見:https://www.robustperception.io/how-to-have-labels-for-machine-roles

2)過多的label會導(dǎo)致prometheus存儲壓力的增加唱歧,每個label都會創(chuàng)建一個timeseries宪摧,(詳見:https://github.com/kubernetes/kubernetes/issues/79702)

image

references:https://github.com/kubernetes/kubernetes/issues/32326

影響范圍

目前只影響cadvisor上指標(biāo),所有的container_*指標(biāo)都存在此問題颅崩,例如fs / cpu / mem / net 等等容器指標(biāo)几于。

解決方案一:聯(lián)表聚合(社區(qū)方案)

社區(qū)的思想是減少容器的label,這塊大大減輕prometheus的存儲壓力沿后,并且認(rèn)為label應(yīng)該是恒定沿彭,在整個業(yè)務(wù)的生命周期中應(yīng)該固定的,并且認(rèn)為label是個易變尖滚,很容易導(dǎo)致業(yè)務(wù)的圖標(biāo)出現(xiàn)中斷:

references:

基于上面這個思想喉刘,社區(qū)提供一個聯(lián)表聚合的方案,這個方案也是社區(qū)提供的唯一方案(詳見:https://www.weave.works/blog/aggregating-pod-resource-cpu-memory-usage-arbitrary-labels-prometheus/

大概的解決思路是:kube_state_metrics exporter提供一個kube_pod_labels的metric漆弄,這個metric里面有pod自定義的pod labels睦裳,然后和沒有自定義label的metric進(jìn)行聯(lián)表聚合查詢,從而實現(xiàn)了通過自定義label來過濾沒有自定義label的metric的能力撼唾,由于是聯(lián)表聚合查詢廉邑,因此此能力是運行時的。

sum(irate(container_cpu_usage_seconds_total{image!=""}[1m])) by (namespace,pod)
    * on (namespace, pod) group_left(label_yfd_service)
kube_pod_labels{label_yfd_service="tutor-live-subjective-question"}

緩解方案

1)通過sharding方式,但是此方式無法按時間段存儲在不同的prometheus蛛蒙,因此這種方式對單個merric來說是無效的糙箍。在metric固定的情況下,增加prometheus是可以起到一些緩解作用
2)通過recording rule方式牵祟,此方式通過將聚合查詢的結(jié)果記錄為一張新表倍靡,可以實現(xiàn)和原生label相同的查詢速度,但是此方式對計算和存儲的資源消耗很大课舍,但是也可以通過增加prometheus來緩解。

解決方案二:修改kubelet源碼方案

在社區(qū)有人提出通過白名單的給cadvisor添加label他挎,但是社區(qū)沒有下文(詳見:https://github.com/google/cadvisor/issues/2380筝尾。在社區(qū)搜了一下有很多人都在提這個問題,最近還有一個人提了PR办桨,但是被拒絕了(詳見:https://github.com/kubernetes/kubernetes/pull/95210筹淫。

Demo實現(xiàn):直接通過硬編碼 project 關(guān)鍵字 label,檢查此 label 并將其傳遞到container 的 label 上呢撞。kubelet 暴露給 prometheus 的接口處也添加了一個檢查损姜,會將此 label 加入到返回的 set 集中,從而實現(xiàn) promethues 能獲取到pod 的 label殊霞。

更優(yōu)方案:通過設(shè)置一個白名單列表摧阅,通過命令參數(shù)傳入,這樣既可以避免過多的 label 影響prometheus的存儲绷蹲,也可以實現(xiàn)靈活的 label 配置棒卷。

kubernetes/pkg/kubelet/kuberuntime/labels.go

// newContainerLabels creates container labels from v1.Container and v1.Pod.
func newContainerLabels(container *v1.Container, pod *v1.Pod) map[string]string {
  labels := map[string]string{}
  labels[types.KubernetesPodNameLabel] = pod.Name
  labels[types.KubernetesPodNamespaceLabel] = pod.Namespace
  labels[types.KubernetesPodUIDLabel] = string(pod.UID)
  labels[types.KubernetesContainerNameLabel] = container.Name
  
  // patch by zhaixigui

  for labelName ,_ := range pod.ObjectMeta.GetLabels() {
    if labelName == "project" {
      labels[labelName] = pod.ObjectMeta.Labels[labelName]
    }
  }

  return labels

kubernetes/pkg/kubelet/server/server.go

func containerPrometheusLabelsFunc(s stats.Provider) metrics.ContainerLabelsFunc {
  // containerPrometheusLabels maps cAdvisor labels to prometheus labels.
  return func(c *cadvisorapi.ContainerInfo) map[string]string {
    // Prometheus requires that all metrics in the same family have the same labels,
    // so we arrange to supply blank strings for missing labels
    var name, image, podName, namespace, containerName string
    if len(c.Aliases) > 0 {
      name = c.Aliases[0]
    }
    image = c.Spec.Image
    if v, ok := c.Spec.Labels[kubelettypes.KubernetesPodNameLabel]; ok {
      podName = v
    }
    if v, ok := c.Spec.Labels[kubelettypes.KubernetesPodNamespaceLabel]; ok {
      namespace = v
    }
    if v, ok := c.Spec.Labels[kubelettypes.KubernetesContainerNameLabel]; ok {
      containerName = v
    }

    // Associate pod cgroup with pod so we have an accurate accounting of sandbox
    if podName == "" && namespace == "" {
      if pod, found := s.GetPodByCgroupfs(c.Name); found {
        podName = pod.Name
        namespace = pod.Namespace
      }
    }
    set := map[string]string{
      metrics.LabelID:    c.Name,
      metrics.LabelName:  name,
      metrics.LabelImage: image,
      "pod":              podName,
      "namespace":        namespace,
      "container":        containerName,
    }

    // patch by xigui

    for labelName,labelValue := range c.Spec.Labels {
      if labelName == "project" {
        set[labelName] = labelValue
      }
    }

    return set
  }
}

解決方案三:阿里云 - 大數(shù)據(jù)

阿里云將 Promsql 查詢時間小于2秒的用社區(qū)的 kube_pod_label 聯(lián)表聚合的方案,而大于2秒的查詢用大數(shù)據(jù)平臺處理(類似Hbase)祝钢,有大數(shù)據(jù)平臺保證查詢的響應(yīng)時間比规。

解決方案四:獨立安裝cAdvisor

社區(qū)有 member 想將 cAdvisor 獨立出來,為了解決 kubelet 的性能問題拦英,但是最后也不了了之蜒什。(詳解:https://github.com/kubernetes/kubernetes/issues/18770

另外有一個人將 cAdvisor 的部分統(tǒng)計信息放到了CRI里疤估,試圖將 kubelet 和 cadvisor 分離灾常,但是此PR并不是解決 Pod 的 label 傳遞到 container 上的問題(詳見:https://github.com/kubernetes/kubernetes/pull/45614),此方案沒有最終的實現(xiàn)做裙。

性能測試

prometheus查詢container_cpu_usage_seconds_total metric岗憋,實例數(shù)5個,最大查詢時間2天

1)聯(lián)表查詢(單位:毫秒)

sum(irate(container_cpu_usage_seconds_total{image!=""}[1m])) by (namespace,pod)
    * on (namespace, pod) group_left(label_yfd_service)
kube_pod_labels{label_yfd_service="tutor-live-subjective-question"}

2d: 1273, 1312, 1277, 1258, 1320
1h: 311, 396, 367, 365, 2882)原生查詢(單位:毫秒)

2)原生查詢

sum(irate(container_cpu_usage_seconds_total{image!="", yfd_service="tutor-live-subjective-question"}[1m])) by (namespace,pod)

2d: 71, 73, 78, 76, 85
1h: 39, 43, 45, 50, 40

對比:通過直接過濾label和kube_pod_labels連表查詢的返回響應(yīng)

2d: 平均相差1212毫秒锚贱,相差16倍
1h:平均相差302毫秒仔戈, 相差7倍

方案對比

修改源碼 聯(lián)表聚合 大數(shù)據(jù)
優(yōu)點 簡單,性能高 簡單,符合社區(qū)方向 在不改源碼的情況监徘,解決了「聯(lián)表聚合」性能問題
缺點 修改了kubelet源碼 響應(yīng)時間很慢晋修,數(shù)據(jù)量大了很難處理 需要一個大數(shù)據(jù)平臺,對維護(hù)和成本來說都是巨大的挑戰(zhàn)
響應(yīng)時間 很慢
架構(gòu)復(fù)雜性 簡單 簡單 非常復(fù)雜
開發(fā)成本
資源要求
符合社區(qū)方向 不符合 符合 -
穩(wěn)定性

其他公司的實踐

公司 規(guī)模 選用方案 說明
快手 超大 放棄prometheus 經(jīng)過幾次修改prometheus源碼后均不能解決其大數(shù)據(jù)量的問題
瓜子 聯(lián)表聚合 規(guī)模較小凰盔,提升性能用rule
馬蜂窩 聯(lián)表聚合 規(guī)模小墓卦,對基于自定義的label查詢需求不大

對kubernetes云原生方面的故障排查、解決方案户敬、優(yōu)化調(diào)優(yōu)感興趣的朋友可以關(guān)注喜貴的云原生

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末落剪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子尿庐,更是在濱河造成了極大的恐慌忠怖,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抄瑟,死亡現(xiàn)場離奇詭異凡泣,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)皮假,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門鞋拟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人惹资,你說我怎么就攤上這事贺纲。” “怎么了褪测?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵哮笆,是天一觀的道長。 經(jīng)常有香客問我汰扭,道長稠肘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任萝毛,我火速辦了婚禮项阴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘笆包。我一直安慰自己环揽,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布庵佣。 她就那樣靜靜地躺著歉胶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪巴粪。 梳的紋絲不亂的頭發(fā)上通今,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天粥谬,我揣著相機(jī)與錄音,去河邊找鬼辫塌。 笑死漏策,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的臼氨。 我是一名探鬼主播掺喻,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼储矩!你這毒婦竟也來了感耙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤持隧,失蹤者是張志新(化名)和其女友劉穎抑月,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舆蝴,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年题诵,在試婚紗的時候發(fā)現(xiàn)自己被綠了洁仗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡性锭,死狀恐怖赠潦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情草冈,我是刑警寧澤她奥,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站怎棱,受9級特大地震影響哩俭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拳恋,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一凡资、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧谬运,春花似錦隙赁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至轰驳,卻和暖如春厚掷,著一層夾襖步出監(jiān)牢的瞬間弟灼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工蝗肪, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留袜爪,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓薛闪,卻偏偏與公主長得像辛馆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子豁延,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354