Merge pull request #95719 from tsmetana/add-pv_collector-provisioner-metric

PV Controller: Add plugin name and volume mode to PV metrics
This commit is contained in:
Kubernetes Prow Robot 2020-11-11 01:49:49 -08:00 committed by GitHub
commit 423f8731ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 72 additions and 7 deletions

View File

@ -6,6 +6,7 @@ go_library(
importpath = "k8s.io/kubernetes/pkg/controller/volume/persistentvolume/metrics",
visibility = ["//visibility:public"],
deps = [
"//pkg/volume:go_default_library",
"//pkg/volume/util:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/component-base/metrics:go_default_library",

View File

@ -23,6 +23,7 @@ import (
v1 "k8s.io/api/core/v1"
"k8s.io/component-base/metrics"
"k8s.io/component-base/metrics/legacyregistry"
"k8s.io/kubernetes/pkg/volume"
metricutil "k8s.io/kubernetes/pkg/volume/util"
)
@ -31,6 +32,7 @@ const (
pvControllerSubsystem = "pv_collector"
// Metric names.
totalPVKey = "total_pv_count"
boundPVKey = "bound_pv_count"
unboundPVKey = "unbound_pv_count"
boundPVCKey = "bound_pvc_count"
@ -39,6 +41,11 @@ const (
// Label names.
namespaceLabel = "namespace"
storageClassLabel = "storage_class"
pluginNameLabel = "plugin_name"
volumeModeLabel = "volume_mode"
// String to use when plugin name cannot be determined
pluginNameNotAvailable = "N/A"
)
var registerMetrics sync.Once
@ -54,15 +61,15 @@ type PVCLister interface {
}
// Register all metrics for pv controller.
func Register(pvLister PVLister, pvcLister PVCLister) {
func Register(pvLister PVLister, pvcLister PVCLister, pluginMgr *volume.VolumePluginMgr) {
registerMetrics.Do(func() {
legacyregistry.CustomMustRegister(newPVAndPVCCountCollector(pvLister, pvcLister))
legacyregistry.CustomMustRegister(newPVAndPVCCountCollector(pvLister, pvcLister, pluginMgr))
legacyregistry.MustRegister(volumeOperationErrorsMetric)
})
}
func newPVAndPVCCountCollector(pvLister PVLister, pvcLister PVCLister) *pvAndPVCCountCollector {
return &pvAndPVCCountCollector{pvLister: pvLister, pvcLister: pvcLister}
func newPVAndPVCCountCollector(pvLister PVLister, pvcLister PVCLister, pluginMgr *volume.VolumePluginMgr) *pvAndPVCCountCollector {
return &pvAndPVCCountCollector{pvLister: pvLister, pvcLister: pvcLister, pluginMgr: pluginMgr}
}
// Custom collector for current pod and container counts.
@ -73,12 +80,19 @@ type pvAndPVCCountCollector struct {
pvLister PVLister
// Cache for accessing information about PersistentVolumeClaims.
pvcLister PVCLister
// Volume plugin manager
pluginMgr *volume.VolumePluginMgr
}
// Check if our collector implements necessary collector interface
var _ metrics.StableCollector = &pvAndPVCCountCollector{}
var (
totalPVCountDesc = metrics.NewDesc(
metrics.BuildFQName("", pvControllerSubsystem, totalPVKey),
"Gauge measuring total number of persistent volumes",
[]string{pluginNameLabel, volumeModeLabel}, nil,
metrics.ALPHA, "")
boundPVCountDesc = metrics.NewDesc(
metrics.BuildFQName("", pvControllerSubsystem, boundPVKey),
"Gauge measuring number of persistent volume currently bound",
@ -110,7 +124,20 @@ var (
[]string{"plugin_name", "operation_name"})
)
// volumeCount counts by PluginName and VolumeMode.
type volumeCount map[string]map[string]int
func (v volumeCount) add(pluginName string, volumeMode string) {
count, ok := v[pluginName]
if !ok {
count = map[string]int{}
}
count[volumeMode]++
v[pluginName] = count
}
func (collector *pvAndPVCCountCollector) DescribeWithStability(ch chan<- *metrics.Desc) {
ch <- totalPVCountDesc
ch <- boundPVCountDesc
ch <- unboundPVCountDesc
ch <- boundPVCCountDesc
@ -122,14 +149,26 @@ func (collector *pvAndPVCCountCollector) CollectWithStability(ch chan<- metrics.
collector.pvcCollect(ch)
}
func (collector *pvAndPVCCountCollector) getPVPluginName(pv *v1.PersistentVolume) string {
spec := volume.NewSpecFromPersistentVolume(pv, true)
fullPluginName := pluginNameNotAvailable
if plugin, err := collector.pluginMgr.FindPluginBySpec(spec); err == nil {
fullPluginName = metricutil.GetFullQualifiedPluginNameForVolume(plugin.GetPluginName(), spec)
}
return fullPluginName
}
func (collector *pvAndPVCCountCollector) pvCollect(ch chan<- metrics.Metric) {
boundNumberByStorageClass := make(map[string]int)
unboundNumberByStorageClass := make(map[string]int)
totalCount := make(volumeCount)
for _, pvObj := range collector.pvLister.List() {
pv, ok := pvObj.(*v1.PersistentVolume)
if !ok {
continue
}
pluginName := collector.getPVPluginName(pv)
totalCount.add(pluginName, string(*pv.Spec.VolumeMode))
if pv.Status.Phase == v1.VolumeBound {
boundNumberByStorageClass[pv.Spec.StorageClassName]++
} else {
@ -150,6 +189,16 @@ func (collector *pvAndPVCCountCollector) pvCollect(ch chan<- metrics.Metric) {
float64(number),
storageClassName)
}
for pluginName, volumeModeCount := range totalCount {
for volumeMode, number := range volumeModeCount {
ch <- metrics.NewLazyConstMetric(
totalPVCountDesc,
metrics.GaugeValue,
float64(number),
pluginName,
volumeMode)
}
}
}
func (collector *pvAndPVCCountCollector) pvcCollect(ch chan<- metrics.Metric) {

View File

@ -317,7 +317,7 @@ func (ctrl *PersistentVolumeController) Run(stopCh <-chan struct{}) {
go wait.Until(ctrl.volumeWorker, time.Second, stopCh)
go wait.Until(ctrl.claimWorker, time.Second, stopCh)
metrics.Register(ctrl.volumes.store, ctrl.claims)
metrics.Register(ctrl.volumes.store, ctrl.claims, &ctrl.volumePluginMgr)
<-stopCh
}

View File

@ -381,9 +381,12 @@ var _ = utils.SIGDescribe("[Serial] Volume metrics", func() {
// Test for pv controller metrics, concretely: bound/unbound pv/pvc count.
ginkgo.Describe("PVController", func() {
const (
classKey = "storage_class"
namespaceKey = "namespace"
classKey = "storage_class"
namespaceKey = "namespace"
pluginNameKey = "plugin_name"
volumeModeKey = "volume_mode"
totalPVKey = "pv_collector_total_pv_count"
boundPVKey = "pv_collector_bound_pv_count"
unboundPVKey = "pv_collector_unbound_pv_count"
boundPVCKey = "pv_collector_bound_pvc_count"
@ -506,6 +509,18 @@ var _ = utils.SIGDescribe("[Serial] Volume metrics", func() {
validator([]map[string]int64{{className: 1}, nil, {ns: 1}, nil})
})
ginkgo.It("should create total pv count metrics for with plugin and volume mode labels after creating pv",
func() {
var err error
dimensions := []string{pluginNameKey, volumeModeKey}
pv, err = e2epv.CreatePV(c, pv)
framework.ExpectNoError(err, "Error creating pv: %v", err)
waitForPVControllerSync(metricsGrabber, totalPVKey, pluginNameKey)
controllerMetrics, err := metricsGrabber.GrabFromControllerManager()
framework.ExpectNoError(err, "Error getting c-m metricValues: %v", err)
err = testutil.ValidateMetrics(testutil.Metrics(controllerMetrics), totalPVKey, dimensions...)
framework.ExpectNoError(err, "Invalid metric in Controller Manager metrics: %q", totalPVKey)
})
})
})