Added StatefulSet metrics

This commit is contained in:
Stijn De Haes 2017-06-18 20:47:43 +02:00
parent 57c44db945
commit fe6a64feba
4 changed files with 204 additions and 0 deletions

View File

@ -0,0 +1,8 @@
# Service Metrics
| Metric name| Metric type | Labels/tags |
| ---------- | ----------- | ----------- |
| kube_statefulset_status_replicas | Gauge | `statefulset`=&lt;statefulset-name&gt; <br> `namespace`=&lt;statefulset-namespace&gt; |
| kube_statefulset_status_observed_generation | Gauge | `statefulset`=&lt;statefulset-name&gt; <br> `namespace`=&lt;statefulset-namespace&gt; |
| kube_statefulset_replicas | Gauge | `statefulset`=&lt;statefulset-name&gt; <br> `namespace`=&lt;statefulset-namespace&gt; |
| kube_statefulset_metadata_generation | Gauge | `statefulset`=&lt;statefulset-name&gt; <br> `namespace`=&lt;statefulset-namespace&gt; |

98
collectors/statefulset.go Normal file
View File

@ -0,0 +1,98 @@
package collectors
import (
"github.com/prometheus/client_golang/prometheus"
"k8s.io/client-go/pkg/apis/apps/v1beta1"
"github.com/golang/glog"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"golang.org/x/net/context"
"k8s.io/client-go/pkg/api"
)
var (
descStatefulSetStatusReplicas = prometheus.NewDesc(
"kube_statefulset_status_replicas",
"The number of replicas per StatefulSet.",
[]string{"namespace", "statefulset"}, nil,
)
descStatefulSetStatusObservedGeneration = prometheus.NewDesc(
"kube_statefulset_status_observed_generation",
"The generation observed by the StatefulSet controller.",
[]string{"namespace", "statefulset"}, nil,
)
descStatefulSetSpecReplicas = prometheus.NewDesc(
"kube_statefulset_replicas",
"Number of desired pods for a StatefulSet.",
[]string{"namespace", "statefulset"}, nil,
)
descStatefulSetMetadataGeneration = prometheus.NewDesc(
"kube_statefulset_metadata_generation",
"Sequence number representing a specific generation of the desired state for the StatefulSet.",
[]string{"namespace", "statefulset"}, nil,
)
)
type StatefulSetLister func() ([]v1beta1.StatefulSet, error)
func (l StatefulSetLister) List() ([]v1beta1.StatefulSet, error) {
return l()
}
func RegisterStatefulSetCollector(registry prometheus.Registerer, kubeClient kubernetes.Interface) {
client := kubeClient.AppsV1beta1().RESTClient()
dlw := cache.NewListWatchFromClient(client, "statefulsets", api.NamespaceAll, nil)
dinf := cache.NewSharedInformer(dlw, &v1beta1.StatefulSet{}, resyncPeriod)
statefulSetLister := StatefulSetLister(func() (statefulSets []v1beta1.StatefulSet, err error) {
for _, c := range dinf.GetStore().List() {
statefulSets = append(statefulSets, *(c.(*v1beta1.StatefulSet)))
}
return statefulSets, nil
})
registry.MustRegister(&statefulSetCollector{store: statefulSetLister})
go dinf.Run(context.Background().Done())
}
type statefulSetStore interface {
List() (statefulSets []v1beta1.StatefulSet, err error)
}
type statefulSetCollector struct {
store statefulSetStore
}
// Describe implements the prometheus.Collector interface.
func (dc *statefulSetCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- descStatefulSetStatusReplicas
ch <- descStatefulSetStatusObservedGeneration
ch <- descStatefulSetSpecReplicas
ch <- descStatefulSetMetadataGeneration
}
// Collect implements the prometheus.Collector interface.
func (sc *statefulSetCollector) Collect(ch chan<- prometheus.Metric) {
dpls, err := sc.store.List()
if err != nil {
glog.Errorf("listing statefulsets failed: %s", err)
return
}
for _, d := range dpls {
sc.collectStatefulSet(ch, d)
}
}
func (dc *statefulSetCollector) collectStatefulSet(ch chan<- prometheus.Metric, statefulSet v1beta1.StatefulSet) {
addGauge := func(desc *prometheus.Desc, v float64, lv ...string) {
lv = append([]string{statefulSet.Namespace, statefulSet.Name}, lv...)
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, v, lv...)
}
addGauge(descStatefulSetStatusReplicas, float64(statefulSet.Status.Replicas))
addGauge(descStatefulSetStatusObservedGeneration, float64(*statefulSet.Status.ObservedGeneration))
addGauge(descStatefulSetSpecReplicas, float64(*statefulSet.Spec.Replicas))
addGauge(descStatefulSetMetadataGeneration, float64(statefulSet.ObjectMeta.Generation))
}

View File

@ -0,0 +1,96 @@
package collectors
import (
"k8s.io/client-go/pkg/apis/apps/v1beta1"
"testing"
"k8s.io/client-go/pkg/api/v1"
)
var (
statefulSet1Replicas int32 = 3
statefulSet2Replicas int32 = 6
statefulSet1ObservedGeneration int64 = 1
statefulSet2ObservedGeneration int64 = 2
)
type mockStatefulSetStore struct {
f func() ([]v1beta1.StatefulSet, error)
}
func (ds mockStatefulSetStore) List() (deployments []v1beta1.StatefulSet, err error) {
return ds.f()
}
func TestStatefuleSetCollector(t *testing.T) {
// Fixed metadata on type and help text. We prepend this to every expected
// output so we only have to modify a single place when doing adjustments.
const metadata = `
# HELP kube_statefulset_status_replicas The number of replicas per StatefulSet.
# TYPE kube_statefulset_status_replicas gauge
# HELP kube_statefulset_status_observed_generation The generation observed by the StatefulSet controller.
# TYPE kube_statefulset_status_observed_generation gauge
# HELP kube_statefulset_replicas Number of desired pods for a StatefulSet.
# TYPE kube_statefulset_replicas gauge
# HELP kube_statefulset_metadata_generation Sequence number representing a specific generation of the desired state for the StatefulSet.
# TYPE kube_statefulset_metadata_generation gauge
`
cases := []struct {
depls []v1beta1.StatefulSet
want string
}{
{
depls: []v1beta1.StatefulSet{
{
ObjectMeta: v1.ObjectMeta{
Name: "statefulset1",
Namespace: "ns1",
Generation: 3,
},
Spec: v1beta1.StatefulSetSpec{
Replicas: &statefulSet1Replicas,
ServiceName: "statefulset1service",
},
Status: v1beta1.StatefulSetStatus{
ObservedGeneration: &statefulSet1ObservedGeneration,
Replicas: 2,
},
}, {
ObjectMeta: v1.ObjectMeta{
Name: "statefulset2",
Namespace: "ns2",
Generation: 21,
},
Spec: v1beta1.StatefulSetSpec{
Replicas: &statefulSet2Replicas,
ServiceName: "statefulset2service",
},
Status: v1beta1.StatefulSetStatus{
ObservedGeneration: &statefulSet2ObservedGeneration,
Replicas: 5,
},
},
},
want: metadata + `
kube_statefulset_status_replicas{namespace="ns1",statefulset="statefulset1"} 2
kube_statefulset_status_replicas{namespace="ns2",statefulset="statefulset2"} 5
kube_statefulset_status_observed_generation{namespace="ns1",statefulset="statefulset1"} 1
kube_statefulset_status_observed_generation{namespace="ns2",statefulset="statefulset2"} 2
kube_statefulset_replicas{namespace="ns1",statefulset="statefulset1"} 3
kube_statefulset_replicas{namespace="ns2",statefulset="statefulset2"} 6
kube_statefulset_metadata_generation{namespace="ns1",statefulset="statefulset1"} 3
kube_statefulset_metadata_generation{namespace="ns2",statefulset="statefulset2"} 21
`,
},
}
for _, c := range cases {
sc := &statefulSetCollector{
store: mockStatefulSetStore{
f: func() ([]v1beta1.StatefulSet, error) { return c.depls, nil },
},
}
if err := gatherAndCompare(sc, c.want, nil); err != nil {
t.Errorf("unexpected collecting result:\n%s", err)
}
}
}

View File

@ -54,6 +54,7 @@ var (
"services": struct{}{},
"jobs": struct{}{},
"cronjobs": struct{}{},
"statefulsets": struct{}{},
}
availableCollectors = map[string]func(registry prometheus.Registerer, kubeClient clientset.Interface){
"cronjobs": collectors.RegisterCronJobCollector,
@ -67,6 +68,7 @@ var (
"replicationcontrollers": collectors.RegisterReplicationControllerCollector,
"resourcequotas": collectors.RegisterResourceQuotaCollector,
"services": collectors.RegisterServiceCollector,
"statefulsets": collectors.RegisterStatefulSetCollector,
}
)