Skip to content

Commit edd182e

Browse files
committed
add replicaset metrics
1 parent ac919d9 commit edd182e

4 files changed

Lines changed: 261 additions & 0 deletions

File tree

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,19 @@ additional metrics!
8888
| ---------- | ----------- | ----------- |
8989
| kube_resource_quota | Gauge | `resourcequota`=&lt;quota-name&gt; <br> `namespace`=&lt;namespace&gt; <br> `resource`=&lt;ResourceName&gt; <br> `type`=&lt;quota-type&gt; |
9090

91+
### ReplicaSet metrics
92+
93+
| Metric name| Metric type | Labels/tags |
94+
| ---------- | ----------- | ----------- |
95+
| kube_replicaset_status_replicas | Gauge | `replicaset`=&lt;replicaset-name&gt; <br> `namespace`=&lt;replicaset-namespace&gt; |
96+
| kube_replicaset_status_replicas_available | Gauge | `replicaset`=&lt;replicaset-name&gt; <br> `namespace`=&lt;replicaset-namespace&gt; |
97+
| kube_replicaset_status_replicas_unavailable | Gauge | `replicaset`=&lt;replicaset-name&gt; <br> `namespace`=&lt;replicaset-namespace&gt; |
98+
| kube_replicaset_status_replicas_updated | Gauge | `replicaset`=&lt;replicaset-name&gt; <br> `namespace`=&lt;replicaset-namespace&gt; |
99+
| kube_replicaset_status_replicas_observed_generation | Gauge | `replicaset`=&lt;replicaset-name&gt; <br> `namespace`=&lt;replicaset-namespace&gt; |
100+
| kube_replicaset_spec_replicas | Gauge | `replicaset`=&lt;replicaset-name&gt; <br> `namespace`=&lt;replicaset-namespace&gt; |
101+
| kube_replicaset_spec_paused | Gauge | `replicaset`=&lt;replicaset-name&gt; <br> `namespace`=&lt;replicaset-namespace&gt; |
102+
| kube_replicaset_metadata_generation | Gauge | `replicaset`=&lt;replicaset-name&gt; <br> `namespace`=&lt;replicaset-namespace&gt; |
103+
91104
## kube-state-metrics vs. Heapster
92105

93106
[Heapster](https://github.com/kubernetes/heapster) is a project which fetches

main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,15 @@ var (
4747
"pods": struct{}{},
4848
"nodes": struct{}{},
4949
"resourcequotas": struct{}{},
50+
"replicasets": struct{}{},
5051
}
5152
availableCollectors = map[string]func(registry prometheus.Registerer, kubeClient clientset.Interface){
5253
"daemonsets": RegisterDaemonSetCollector,
5354
"deployments": RegisterDeploymentCollector,
5455
"pods": RegisterPodCollector,
5556
"nodes": RegisterNodeCollector,
5657
"resourcequotas": RegisterResourceQuotaCollector,
58+
"replicasets": RegisterReplicaSetCollector,
5759
}
5860
)
5961

replicaset.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
Copyright 2016 The Kubernetes Authors All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"github.com/golang/glog"
21+
"github.com/prometheus/client_golang/prometheus"
22+
"golang.org/x/net/context"
23+
"k8s.io/client-go/kubernetes"
24+
"k8s.io/client-go/pkg/api"
25+
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
26+
"k8s.io/client-go/tools/cache"
27+
)
28+
29+
var (
30+
descReplicaSetStatusReplicas = prometheus.NewDesc(
31+
"kube_replicaset_status_replicas",
32+
"The number of replicas per ReplicaSet.",
33+
[]string{"namespace", "replicaset"}, nil,
34+
)
35+
descReplicaSetStatusFullyLabeledReplicas = prometheus.NewDesc(
36+
"kube_replicaset_status_fully_labeled_replicas",
37+
"The number of fully labeled replicas per ReplicaSet.",
38+
[]string{"namespace", "replicaset"}, nil,
39+
)
40+
descReplicaSetStatusReadyReplicas = prometheus.NewDesc(
41+
"kube_replicaset_status_ready_replicas",
42+
"The number of ready replicas per ReplicaSet.",
43+
[]string{"namespace", "replicaset"}, nil,
44+
)
45+
descReplicaSetStatusObservedGeneration = prometheus.NewDesc(
46+
"kube_replicaset_status_observed_generation",
47+
"The generation observed by the ReplicaSet controller.",
48+
[]string{"namespace", "replicaset"}, nil,
49+
)
50+
descReplicaSetSpecReplicas = prometheus.NewDesc(
51+
"kube_replicaset_spec_replicas",
52+
"Number of desired pods for a ReplicaSet.",
53+
[]string{"namespace", "replicaset"}, nil,
54+
)
55+
descReplicaSetMetadataGeneration = prometheus.NewDesc(
56+
"kube_replicaset_metadata_generation",
57+
"Sequence number representing a specific generation of the desired state.",
58+
[]string{"namespace", "replicaset"}, nil,
59+
)
60+
)
61+
62+
type ReplicaSetLister func() ([]v1beta1.ReplicaSet, error)
63+
64+
func (l ReplicaSetLister) List() ([]v1beta1.ReplicaSet, error) {
65+
return l()
66+
}
67+
68+
func RegisterReplicaSetCollector(registry prometheus.Registerer, kubeClient kubernetes.Interface) {
69+
client := kubeClient.Extensions().RESTClient()
70+
rslw := cache.NewListWatchFromClient(client, "replicasets", api.NamespaceAll, nil)
71+
rsinf := cache.NewSharedInformer(rslw, &v1beta1.ReplicaSet{}, resyncPeriod)
72+
73+
replicaSetLister := ReplicaSetLister(func() (replicasets []v1beta1.ReplicaSet, err error) {
74+
for _, c := range rsinf.GetStore().List() {
75+
replicasets = append(replicasets, *(c.(*v1beta1.ReplicaSet)))
76+
}
77+
return replicasets, nil
78+
})
79+
80+
registry.MustRegister(&replicasetCollector{store: replicaSetLister})
81+
go rsinf.Run(context.Background().Done())
82+
}
83+
84+
type replicasetStore interface {
85+
List() (replicasets []v1beta1.ReplicaSet, err error)
86+
}
87+
88+
// replicasetCollector collects metrics about all replicasets in the cluster.
89+
type replicasetCollector struct {
90+
store replicasetStore
91+
}
92+
93+
// Describe implements the prometheus.Collector interface.
94+
func (dc *replicasetCollector) Describe(ch chan<- *prometheus.Desc) {
95+
ch <- descReplicaSetStatusReplicas
96+
ch <- descReplicaSetStatusFullyLabeledReplicas
97+
ch <- descReplicaSetStatusReadyReplicas
98+
ch <- descReplicaSetStatusObservedGeneration
99+
ch <- descReplicaSetSpecReplicas
100+
ch <- descReplicaSetMetadataGeneration
101+
}
102+
103+
// Collect implements the prometheus.Collector interface.
104+
func (dc *replicasetCollector) Collect(ch chan<- prometheus.Metric) {
105+
dpls, err := dc.store.List()
106+
if err != nil {
107+
glog.Errorf("listing replicasets failed: %s", err)
108+
return
109+
}
110+
for _, d := range dpls {
111+
dc.collectReplicaSet(ch, d)
112+
}
113+
}
114+
115+
func (dc *replicasetCollector) collectReplicaSet(ch chan<- prometheus.Metric, d v1beta1.ReplicaSet) {
116+
addGauge := func(desc *prometheus.Desc, v float64, lv ...string) {
117+
lv = append([]string{d.Namespace, d.Name}, lv...)
118+
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, v, lv...)
119+
}
120+
addGauge(descReplicaSetStatusReplicas, float64(d.Status.Replicas))
121+
addGauge(descReplicaSetStatusFullyLabeledReplicas, float64(d.Status.FullyLabeledReplicas))
122+
addGauge(descReplicaSetStatusReadyReplicas, float64(d.Status.ReadyReplicas))
123+
addGauge(descReplicaSetStatusObservedGeneration, float64(d.Status.ObservedGeneration))
124+
addGauge(descReplicaSetSpecReplicas, float64(*d.Spec.Replicas))
125+
addGauge(descReplicaSetMetadataGeneration, float64(d.ObjectMeta.Generation))
126+
}

replicaset_test.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
Copyright 2016 The Kubernetes Authors All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"testing"
21+
22+
"k8s.io/client-go/pkg/api/v1"
23+
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
24+
)
25+
26+
var (
27+
rs1Replicas int32 = 5
28+
rs2Replicas int32 = 0
29+
)
30+
31+
type mockReplicaSetStore struct {
32+
f func() ([]v1beta1.ReplicaSet, error)
33+
}
34+
35+
func (rs mockReplicaSetStore) List() (replicasets []v1beta1.ReplicaSet, err error) {
36+
return rs.f()
37+
}
38+
39+
func TestReplicaSetCollector(t *testing.T) {
40+
// Fixed metadata on type and help text. We prepend this to every expected
41+
// output so we only have to modify a single place when doing adjustments.
42+
const metadata = `
43+
# HELP kube_replicaset_metadata_generation Sequence number representing a specific generation of the desired state.
44+
# TYPE kube_replicaset_metadata_generation gauge
45+
# HELP kube_replicaset_status_replicas The number of replicas per ReplicaSet.
46+
# TYPE kube_replicaset_status_replicas gauge
47+
# HELP kube_replicaset_status_fully_labeled_replicas The number of fully labeled replicas per ReplicaSet.
48+
# TYPE kube_replicaset_status_fully_labeled_replicas gauge
49+
# HELP kube_replicaset_status_ready_replicas The number of ready replicas per ReplicaSet.
50+
# TYPE kube_replicaset_status_ready_replicas gauge
51+
# HELP kube_replicaset_status_observed_generation The generation observed by the ReplicaSet controller.
52+
# TYPE kube_replicaset_status_observed_generation gauge
53+
# HELP kube_replicaset_spec_replicas Number of desired pods for a ReplicaSet.
54+
# TYPE kube_replicaset_spec_replicas gauge
55+
`
56+
cases := []struct {
57+
rss []v1beta1.ReplicaSet
58+
want string
59+
}{
60+
{
61+
rss: []v1beta1.ReplicaSet{
62+
{
63+
ObjectMeta: v1.ObjectMeta{
64+
Name: "rs1",
65+
Namespace: "ns1",
66+
Generation: 21,
67+
},
68+
Status: v1beta1.ReplicaSetStatus{
69+
Replicas: 5,
70+
FullyLabeledReplicas: 10,
71+
ReadyReplicas: 5,
72+
ObservedGeneration: 1,
73+
},
74+
Spec: v1beta1.ReplicaSetSpec{
75+
Replicas: &rs1Replicas,
76+
},
77+
}, {
78+
ObjectMeta: v1.ObjectMeta{
79+
Name: "rs2",
80+
Namespace: "ns2",
81+
Generation: 14,
82+
},
83+
Status: v1beta1.ReplicaSetStatus{
84+
Replicas: 0,
85+
FullyLabeledReplicas: 5,
86+
ReadyReplicas: 0,
87+
ObservedGeneration: 5,
88+
},
89+
Spec: v1beta1.ReplicaSetSpec{
90+
Replicas: &rs2Replicas,
91+
},
92+
},
93+
},
94+
want: metadata + `
95+
kube_replicaset_metadata_generation{namespace="ns1",replicaset="rs1"} 21
96+
kube_replicaset_metadata_generation{namespace="ns2",replicaset="rs2"} 14
97+
kube_replicaset_status_replicas{namespace="ns1",replicaset="rs1"} 5
98+
kube_replicaset_status_replicas{namespace="ns2",replicaset="rs2"} 0
99+
kube_replicaset_status_observed_generation{namespace="ns1",replicaset="rs1"} 1
100+
kube_replicaset_status_observed_generation{namespace="ns2",replicaset="rs2"} 5
101+
kube_replicaset_status_fully_labeled_replicas{namespace="ns1",replicaset="rs1"} 10
102+
kube_replicaset_status_fully_labeled_replicas{namespace="ns2",replicaset="rs2"} 5
103+
kube_replicaset_status_ready_replicas{namespace="ns1",replicaset="rs1"} 5
104+
kube_replicaset_status_ready_replicas{namespace="ns2",replicaset="rs2"} 0
105+
kube_replicaset_spec_replicas{namespace="ns1",replicaset="rs1"} 5
106+
kube_replicaset_spec_replicas{namespace="ns2",replicaset="rs2"} 0
107+
`,
108+
},
109+
}
110+
for _, c := range cases {
111+
dc := &replicasetCollector{
112+
store: mockReplicaSetStore{
113+
f: func() ([]v1beta1.ReplicaSet, error) { return c.rss, nil },
114+
},
115+
}
116+
if err := gatherAndCompare(dc, c.want, nil); err != nil {
117+
t.Errorf("unexpected collecting result:\n%s", err)
118+
}
119+
}
120+
}

0 commit comments

Comments
 (0)