dcgm.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. package scrape
  2. import (
  3. "fmt"
  4. "regexp"
  5. "github.com/opencost/opencost/core/pkg/clustercache"
  6. "github.com/opencost/opencost/core/pkg/log"
  7. "github.com/opencost/opencost/core/pkg/source"
  8. "github.com/opencost/opencost/modules/collector-source/pkg/event"
  9. "github.com/opencost/opencost/modules/collector-source/pkg/metric"
  10. "github.com/opencost/opencost/modules/collector-source/pkg/scrape/target"
  11. v1 "k8s.io/api/core/v1"
  12. )
  13. var dcgmRegex = regexp.MustCompile("(?i)(.*dcgm-exporter.*)")
  14. func newDCGMScrapper(clusterCache clustercache.ClusterCache) Scraper {
  15. tp := newDCGMTargetProvider(clusterCache)
  16. return newDCGMTargetScraper(tp, podUIDEnricher(clusterCache))
  17. }
  18. func newDCGMTargetScraper(provider target.TargetProvider, enrich UpdateEnricher) *TargetScraper {
  19. return newTargetScrapper(
  20. event.DCGMScraperName,
  21. provider,
  22. []string{
  23. metric.DCGMFIPROFGRENGINEACTIVE,
  24. metric.DCGMFIDEVDECUTIL,
  25. },
  26. true,
  27. enrich)
  28. }
  29. // podUIDEnricher backfills pod_uid on a DCGM update using its own namespace/pod name labels,
  30. // resolved against a freshly built index of the cluster's current pods. Left unset if pod_uid
  31. // is already present, or if namespace/pod can't be resolved to a known pod.
  32. func podUIDEnricher(clusterCache clustercache.ClusterCache) UpdateEnricher {
  33. return func(updates []metric.Update) {
  34. index := buildPodIndex(clusterCache.GetAllPods())
  35. for _, update := range updates {
  36. if update.Labels[source.PodUIDLabel] != "" {
  37. continue
  38. }
  39. namespace, pod := update.Labels[source.NamespaceLabel], update.Labels[source.PodLabel]
  40. if namespace == "" || pod == "" {
  41. continue
  42. }
  43. if uid, ok := index[podKey{namespace: namespace, name: pod}]; ok {
  44. update.Labels[source.PodUIDLabel] = string(uid)
  45. }
  46. }
  47. }
  48. }
  49. type DCGMTargetProvider struct {
  50. clusterCache clustercache.ClusterCache
  51. port int
  52. }
  53. func newDCGMTargetProvider(clusterCache clustercache.ClusterCache) *DCGMTargetProvider {
  54. return &DCGMTargetProvider{
  55. clusterCache: clusterCache,
  56. port: 9400,
  57. }
  58. }
  59. func (p *DCGMTargetProvider) GetTargets() []target.ScrapeTarget {
  60. // NOTE: The proper way to discover these targets is to first identify a Service that
  61. // NOTE: matches a specific selector. Then, locate the Endpoints kubernetes resource associated
  62. // NOTE: with that Service. This Endpoints resource has a list of all the targetted pods and their
  63. // NOTE: addresses. We do _not_ have the Endpoints resource on our cluster cache at the moment,
  64. // NOTE: so we'll perform this lookup ourselves.
  65. pods := p.clusterCache.GetAllPods()
  66. var targets []target.ScrapeTarget
  67. for _, pod := range pods {
  68. if pod.Status.Phase == v1.PodRunning && isDCGM(pod.Labels) {
  69. log.Debugf("DCGM: found target: http://%s:%d/metrics", pod.Status.PodIP, p.port)
  70. t := target.NewUrlTarget(fmt.Sprintf("http://%s:%d/metrics", pod.Status.PodIP, p.port))
  71. targets = append(targets, t)
  72. }
  73. }
  74. return targets
  75. }
  76. func isDCGM(labels map[string]string) bool {
  77. keys := []string{
  78. "app",
  79. "app.kubernetes.io/name",
  80. "app.kubernetes.io/component",
  81. }
  82. for _, key := range keys {
  83. if value, ok := labels[key]; ok {
  84. if dcgmRegex.MatchString(value) {
  85. return true
  86. }
  87. }
  88. }
  89. return false
  90. }