Skip to content

Commit

Permalink
Added metrics describing the cpu and mem requests per container . (ku…
Browse files Browse the repository at this point in the history
…bernetes#35)

These metrics are retrieved using the v1 api directly.
No additional accumulation or computation is done.

The state is maintained per container in a pod in kubernetes.

The metrics are also tagged by the node, where  the containers are residing.
Our use case for these metrics are as follows:

It is useful to accumulate these per node and understand how much
remaining un-requested resourced are available in each node.

This would give an indication of the "utilization" of the cluster.
  • Loading branch information
ahakanbaba committed Oct 13, 2016
1 parent cc22286 commit bf2e8db
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ additional metrics!
| kube_pod_container_status_terminated | Gauge | `container`=&lt;container-name&gt; <br> `pod`=&lt;pod-name&gt; <br> `namespace`=&lt;pod-namespace&gt; |
| kube_pod_container_status_ready | Gauge | `container`=&lt;container-name&gt; <br> `pod`=&lt;pod-name&gt; <br> `namespace`=&lt;pod-namespace&gt; |
| kube_pod_container_status_restarts | Counter | `container`=&lt;container-name&gt; <br> `namespace`=&lt;pod-namespace&gt; <br> `pod`=&lt;pod-name&gt; |
| kube_pod_container_requested_cpu_millicores | Gauge | `container`=&lt;container-name&gt; <br> `pod`=&lt;pod-name&gt; <br> `namespace`=&lt;pod-namespace&gt; <br> `node`=&lt; node-address&gt; |
| kube_pod_container_requested_memory_bytes | Gauge | `container`=&lt;container-name&gt; <br> `pod`=&lt;pod-name&gt; <br> `namespace`=&lt;pod-namespace&gt; <br> `node`=&lt; node-address&gt; |

## kube-state-metrics vs. Heapster

Expand Down
28 changes: 28 additions & 0 deletions pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ var (
"The number of container restarts per container.",
[]string{"namespace", "pod", "container"}, nil,
)

descPodContainerRequestedCpuMilliCores = prometheus.NewDesc(
"kube_pod_container_requested_cpu_millicores",
"The number of requested cpu millicores by a container.",
[]string{"namespace", "pod", "container", "node"}, nil,
)

descPodContainerRequestedMemoryBytes = prometheus.NewDesc(
"kube_pod_container_requested_memory_bytes",
"The number of requested memory bytes by a container.",
[]string{"namespace", "pod", "container", "node"}, nil,
)
)

type podStore interface {
Expand All @@ -97,6 +109,8 @@ func (pc *podCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- descPodContainerStatusTerminated
ch <- descPodContainerStatusReady
ch <- descPodContainerStatusRestarts
ch <- descPodContainerRequestedCpuMilliCores
ch <- descPodContainerRequestedMemoryBytes
}

// Collect implements the prometheus.Collector interface.
Expand Down Expand Up @@ -145,4 +159,18 @@ func (pc *podCollector) collectPod(ch chan<- prometheus.Metric, p v1.Pod) {
addGauge(descPodContainerStatusReady, boolFloat64(cs.Ready), cs.Name)
addCounter(descPodContainerStatusRestarts, float64(cs.RestartCount), cs.Name)
}

nodeName := p.Spec.NodeName
for _, c := range p.Spec.Containers {
req := c.Resources.Requests
if cpu, ok := req[v1.ResourceCPU]; ok {
addGauge(descPodContainerRequestedCpuMilliCores, float64(cpu.MilliValue()),
c.Name, nodeName)
}
if mem, ok := req[v1.ResourceMemory]; ok {
addGauge(descPodContainerRequestedMemoryBytes, float64(mem.Value()),
c.Name, nodeName)
}

}
}
83 changes: 83 additions & 0 deletions pod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package main
import (
"testing"

"k8s.io/client-go/1.4/pkg/api/resource"
"k8s.io/client-go/1.4/pkg/api/v1"
)

Expand Down Expand Up @@ -54,6 +55,10 @@ func TestPodCollector(t *testing.T) {
# TYPE kube_pod_status_ready gauge
# HELP kube_pod_status_scheduled Describes the status of the scheduling process for the pod.
# TYPE kube_pod_status_scheduled gauge
# HELP kube_pod_container_requested_cpu_millicores The number of requested cpu millicores by a container.
# TYPE kube_pod_container_requested_cpu_millicores gauge
# HELP kube_pod_container_requested_memory_bytes The number of requested memory bytes by a container.
# TYPE kube_pod_container_requested_memory_bytes gauge
`
cases := []struct {
pods []v1.Pod
Expand Down Expand Up @@ -372,6 +377,84 @@ func TestPodCollector(t *testing.T) {
kube_pod_status_scheduled{condition="unknown",namespace="ns2",pod="pod2"} 0
`,
metrics: []string{"kube_pod_status_scheduled"},
}, {
pods: []v1.Pod{
{
ObjectMeta: v1.ObjectMeta{
Name: "pod1",
Namespace: "ns1",
},
Spec: v1.PodSpec{
NodeName: "node1",
Containers: []v1.Container{
v1.Container{
Name: "pod1_con1",
Resources: v1.ResourceRequirements{
Requests: map[v1.ResourceName]resource.Quantity{
v1.ResourceCPU: *resource.NewMilliQuantity(int64(200), resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(100000000, resource.DecimalSI),
},
},
},
v1.Container{
Name: "pod1_con2",
Resources: v1.ResourceRequirements{
Requests: map[v1.ResourceName]resource.Quantity{
v1.ResourceCPU: *resource.NewMilliQuantity(int64(300), resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(200000000, resource.DecimalSI),
},
},
},
},
},
}, {
ObjectMeta: v1.ObjectMeta{
Name: "pod2",
Namespace: "ns2",
},
Spec: v1.PodSpec{
NodeName: "node2",
Containers: []v1.Container{
v1.Container{
Name: "pod2_con1",
Resources: v1.ResourceRequirements{
Requests: map[v1.ResourceName]resource.Quantity{
v1.ResourceCPU: *resource.NewMilliQuantity(int64(400), resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(300000000, resource.DecimalSI),
},
},
},
v1.Container{
Name: "pod2_con2",
Resources: v1.ResourceRequirements{
Requests: map[v1.ResourceName]resource.Quantity{
v1.ResourceCPU: *resource.NewMilliQuantity(int64(500), resource.DecimalSI),
v1.ResourceMemory: *resource.NewQuantity(400000000, resource.DecimalSI),
},
},
},
// A container without a resource specicication. No metrics will be emitted for that.
v1.Container{
Name: "pod2_con3",
},
},
},
},
},
want: metadata + `
kube_pod_container_requested_cpu_millicores{container="pod1_con1",namespace="ns1",node="node1",pod="pod1"} 200
kube_pod_container_requested_cpu_millicores{container="pod1_con2",namespace="ns1",node="node1",pod="pod1"} 300
kube_pod_container_requested_cpu_millicores{container="pod2_con1",namespace="ns2",node="node2",pod="pod2"} 400
kube_pod_container_requested_cpu_millicores{container="pod2_con2",namespace="ns2",node="node2",pod="pod2"} 500
kube_pod_container_requested_memory_bytes{container="pod1_con1",namespace="ns1",node="node1",pod="pod1"} 1e+08
kube_pod_container_requested_memory_bytes{container="pod1_con2",namespace="ns1",node="node1",pod="pod1"} 2e+08
kube_pod_container_requested_memory_bytes{container="pod2_con1",namespace="ns2",node="node2",pod="pod2"} 3e+08
kube_pod_container_requested_memory_bytes{container="pod2_con2",namespace="ns2",node="node2",pod="pod2"} 4e+08
`,
metrics: []string{
"kube_pod_container_requested_cpu_millicores",
"kube_pod_container_requested_memory_bytes",
},
},
}
for _, c := range cases {
Expand Down

0 comments on commit bf2e8db

Please sign in to comment.