From 22a794cc22a0d5c9ada25508edda31633763afc9 Mon Sep 17 00:00:00 2001 From: Janet Kuo Date: Tue, 1 Sep 2015 16:35:32 -0700 Subject: [PATCH] List resource QoS tier of each container when describing pods; Re-order resource table --- pkg/kubectl/describe.go | 21 ++++++++--- pkg/kubelet/qos/util/qos.go | 75 +++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 pkg/kubelet/qos/util/qos.go diff --git a/pkg/kubectl/describe.go b/pkg/kubectl/describe.go index 02cbec59b0c..33701ed2d41 100644 --- a/pkg/kubectl/describe.go +++ b/pkg/kubectl/describe.go @@ -31,6 +31,7 @@ import ( client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/fieldpath" "k8s.io/kubernetes/pkg/fields" + qosutil "k8s.io/kubernetes/pkg/kubelet/qos/util" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/sets" @@ -714,6 +715,14 @@ func describeContainers(pod *api.Pod, out io.Writer) { fmt.Fprintf(out, " Image:\t%s\n", container.Image) fmt.Fprintf(out, " Image ID:\t%s\n", status.ImageID) + resourceToQoS := qosutil.GetQoS(&container) + if len(resourceToQoS) > 0 { + fmt.Fprintf(out, " QoS Tier:\n") + } + for resource, qos := range resourceToQoS { + fmt.Fprintf(out, " %s:\t%s\n", resource, qos) + } + if len(container.Resources.Limits) > 0 { fmt.Fprintf(out, " Limits:\n") } @@ -1180,8 +1189,8 @@ func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string) (str func describeNodeResource(pods []*api.Pod, node *api.Node, out io.Writer) { nonTerminatedPods := filterTerminatedPods(pods) fmt.Fprintf(out, "Non-terminated Pods:\t(%d in total)\n", len(nonTerminatedPods)) - fmt.Fprint(out, " Namespace\tName\t\tCPU Requests\tMemory Requests\tCPU Limits\tMemory Limits\n") - fmt.Fprint(out, " ─────────\t────\t\t────────────\t───────────────\t──────────\t─────────────\n") + fmt.Fprint(out, " Namespace\tName\t\tCPU Requests\tCPU Limits\tMemory Requests\tMemory Limits\n") + fmt.Fprint(out, " ─────────\t────\t\t────────────\t──────────\t───────────────\t─────────────\n") totalMilliCPUReq := int64(0) totalMemoryReq := int64(0) fractionPodCPUReq := float64(0) @@ -1216,13 +1225,13 @@ func describeNodeResource(pods []*api.Pod, node *api.Node, out io.Writer) { fractionPodCPULimit = float64(podTotalMilliCPULimit) / float64(node.Status.Capacity.Cpu().MilliValue()) * 100 fractionPodMemoryLimit = float64(podTotalMemoryLimit) / float64(node.Status.Capacity.Memory().Value()) * 100 fmt.Fprintf(out, " %s\t%s\t\t%dm (%d%%)\t%dKi (%d%%)\t%dm (%d%%)\t%dKi (%d%%)\n", pod.Namespace, pod.Name, - podTotalMilliCPUReq, int64(fractionPodCPUReq), podTotalMemoryReq/1000, int64(fractionPodMemoryReq), podTotalMilliCPULimit, int64(fractionPodCPULimit), podTotalMemoryLimit/1000, int64(fractionPodMemoryLimit)) + podTotalMilliCPUReq, int64(fractionPodCPUReq), podTotalMilliCPULimit, int64(fractionPodCPULimit), podTotalMemoryReq/1000, int64(fractionPodMemoryReq), podTotalMemoryLimit/1000, int64(fractionPodMemoryLimit)) } - fmt.Fprint(out, "Allocated resources:\n CPU Requests\tMemory Requests\tCPU Limits\tMemory Limits\n") - fmt.Fprint(out, " ────────────\t───────────────\t──────────\t─────────────\n") + fmt.Fprint(out, "Allocated resources:\n CPU Requests\tCPU Limits\tMemory Requests\tMemory Limits\n") + fmt.Fprint(out, " ────────────\t──────────\t───────────────\t─────────────\n") fractionTotalCPUReq = float64(totalMilliCPUReq) / float64(node.Status.Capacity.Cpu().MilliValue()) * 100 fractionTotalMemoryReq = float64(totalMemoryReq) / float64(node.Status.Capacity.Memory().Value()) * 100 - fmt.Fprintf(out, " %dm (%d%%)\t%dKi (%d%%)\t%dm\t%dKi\n", totalMilliCPUReq, int64(fractionTotalCPUReq), totalMemoryReq/1000, int64(fractionTotalMemoryReq), totalMilliCPULimit, totalMemoryLimit/1000) + fmt.Fprintf(out, " %dm (%d%%)\t%dm\t%dKi (%d%%)\t%dKi\n", totalMilliCPUReq, int64(fractionTotalCPUReq), totalMilliCPULimit, totalMemoryReq/1000, int64(fractionTotalMemoryReq), totalMemoryLimit/1000) } func filterTerminatedPods(pods []*api.Pod) []*api.Pod { diff --git a/pkg/kubelet/qos/util/qos.go b/pkg/kubelet/qos/util/qos.go new file mode 100644 index 00000000000..d7a1e5c88d2 --- /dev/null +++ b/pkg/kubelet/qos/util/qos.go @@ -0,0 +1,75 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "k8s.io/kubernetes/pkg/api" +) + +const ( + Guaranteed = "Guaranteed" + Burstable = "Burstable" + BestEffort = "Best-Effort" +) + +// isResourceGuaranteed returns true if the container's resource requirements are Guaranteed. +func isResourceGuaranteed(container *api.Container, resource api.ResourceName) bool { + // A container resource is guaranteed if its request == limit. + // If request == limit, the user is very confident of resource consumption. + req, hasReq := container.Resources.Requests[resource] + limit, hasLimit := container.Resources.Limits[resource] + if !hasReq || !hasLimit { + return false + } + return req.Value() == limit.Value() && req.Value() != 0 +} + +// isResourceBestEffort returns true if the container's resource requirements are best-effort. +func isResourceBestEffort(container *api.Container, resource api.ResourceName) bool { + // A container resource is best-effort if its request is unspecified or 0. + // If a request is specified, then the user expects some kind of resource guarantee. + req, hasReq := container.Resources.Requests[resource] + return !hasReq || req.Value() == 0 +} + +// GetQos returns a mapping of resource name to QoS class of a container +func GetQoS(container *api.Container) map[api.ResourceName]string { + resourceToQoS := map[api.ResourceName]string{} + for resource := range allResources(container) { + switch { + case isResourceGuaranteed(container, resource): + resourceToQoS[resource] = Guaranteed + case isResourceBestEffort(container, resource): + resourceToQoS[resource] = BestEffort + default: + resourceToQoS[resource] = Burstable + } + } + return resourceToQoS +} + +// allResources returns a set of resources the container has +func allResources(container *api.Container) map[api.ResourceName]bool { + resources := map[api.ResourceName]bool{} + for resource := range container.Resources.Requests { + resources[resource] = true + } + for resource := range container.Resources.Limits { + resources[resource] = true + } + return resources +}