fix perf issue with resource metrics
parent
2d2c6b06b6
commit
c996ab314b
|
|
@ -6,6 +6,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
|
@ -112,10 +113,10 @@ func (c Container) Render(o interface{}, name string, r *Row) error {
|
|||
boolToStr(co.IsInit),
|
||||
restarts,
|
||||
probe(co.Container.LivenessProbe) + ":" + probe(co.Container.ReadinessProbe),
|
||||
toMc(cur.rCPU().MilliValue()),
|
||||
toMi(cur.rMEM().Value()),
|
||||
toMc(res[requestCPU].MilliValue()) + ":" + toMc(res[limitCPU].MilliValue()),
|
||||
toMi(res[requestMEM].Value()) + ":" + toMi(res[limitMEM].Value()),
|
||||
toMc(cur.cpu),
|
||||
toMi(cur.mem),
|
||||
toMc(res.cpu) + ":" + toMc(res.lcpu),
|
||||
toMi(res.mem) + ":" + toMi(res.lmem),
|
||||
strconv.Itoa(perc.rCPU()),
|
||||
strconv.Itoa(perc.lCPU()),
|
||||
strconv.Itoa(perc.rMEM()),
|
||||
|
|
@ -143,26 +144,39 @@ func (Container) diagnose(state, ready string) error {
|
|||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func gatherMetrics(co *v1.Container, mx *mv1beta1.ContainerMetrics) (resources, percentages, resources) {
|
||||
func gatherMetrics(co *v1.Container, mx *mv1beta1.ContainerMetrics) (metric, percentages, metric) {
|
||||
rList, lList := containerRequests(co), co.Resources.Limits
|
||||
c, p, r := newResources(nil, nil), newPercentages(), newResources(rList, lList)
|
||||
var c metric
|
||||
p := newPercentages()
|
||||
|
||||
var r metric
|
||||
if rList.Cpu() != nil {
|
||||
r.cpu = rList.Cpu().MilliValue()
|
||||
}
|
||||
if lList.Cpu() != nil {
|
||||
r.lcpu = lList.Cpu().MilliValue()
|
||||
}
|
||||
if rList.Memory() != nil {
|
||||
r.mem = rList.Memory().Value()
|
||||
}
|
||||
if lList.Memory() != nil {
|
||||
r.lmem = lList.Memory().Value()
|
||||
}
|
||||
|
||||
if mx == nil {
|
||||
return c, p, r
|
||||
}
|
||||
|
||||
c[requestCPU], c[requestMEM] = mx.Usage.Cpu(), mx.Usage.Memory()
|
||||
if rList.Cpu() != nil {
|
||||
p[requestCPU] = percentMc(c.rCPU(), rList.Cpu())
|
||||
if mx.Usage.Cpu() != nil {
|
||||
c.cpu = mx.Usage.Cpu().MilliValue()
|
||||
}
|
||||
if lList.Cpu() != nil {
|
||||
p[limitCPU] = percentMc(c.rCPU(), lList.Cpu())
|
||||
}
|
||||
if rList.Memory() != nil {
|
||||
p[requestMEM] = percentMi(c.rMEM(), rList.Memory())
|
||||
}
|
||||
if lList.Memory() != nil {
|
||||
p[limitMEM] = percentMi(c.rMEM(), lList.Memory())
|
||||
if mx.Usage.Memory() != nil {
|
||||
c.mem = mx.Usage.Memory().Value()
|
||||
}
|
||||
p[requestCPU] = client.ToPercentage(c.cpu, r.cpu)
|
||||
p[limitCPU] = client.ToPercentage(c.cpu, r.lcpu)
|
||||
p[requestMEM] = client.ToPercentage(c.mem, r.mem)
|
||||
p[limitMEM] = client.ToPercentage(c.mem, r.lmem)
|
||||
|
||||
return c, p, r
|
||||
}
|
||||
|
|
@ -204,11 +218,16 @@ func ToContainerState(s v1.ContainerState) string {
|
|||
}
|
||||
}
|
||||
|
||||
const (
|
||||
on string = "on"
|
||||
off = "off"
|
||||
)
|
||||
|
||||
func probe(p *v1.Probe) string {
|
||||
if p == nil {
|
||||
return "off"
|
||||
return off
|
||||
}
|
||||
return "on"
|
||||
return on
|
||||
}
|
||||
|
||||
// ContainerRes represents a container and its metrics.
|
||||
|
|
|
|||
|
|
@ -50,6 +50,25 @@ func TestContainer(t *testing.T) {
|
|||
)
|
||||
}
|
||||
|
||||
func BenchmarkContainerRender(b *testing.B) {
|
||||
var c render.Container
|
||||
|
||||
cres := render.ContainerRes{
|
||||
Container: makeContainer(),
|
||||
Status: makeContainerStatus(),
|
||||
MX: makeContainerMetrics(),
|
||||
IsInit: false,
|
||||
Age: makeAge(),
|
||||
}
|
||||
var r render.Row
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
c.Render(cres, "blee", &r)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/message"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/duration"
|
||||
)
|
||||
|
|
@ -82,14 +81,6 @@ func asSelector(s *metav1.LabelSelector) string {
|
|||
return sel.String()
|
||||
}
|
||||
|
||||
func percentMc(v1, v2 *resource.Quantity) int {
|
||||
return client.ToPercentage(v1.MilliValue(), v2.MilliValue())
|
||||
}
|
||||
|
||||
func percentMi(v1, v2 *resource.Quantity) int {
|
||||
return client.ToPercentage(client.ToMB(v1.Value()), client.ToMB(v2.Value()))
|
||||
}
|
||||
|
||||
// ToSelector flattens a map selector to a string selector.
|
||||
func toSelector(m map[string]string) string {
|
||||
s := make([]string, 0, len(m))
|
||||
|
|
|
|||
|
|
@ -80,20 +80,13 @@ func (n Node) Render(o interface{}, ns string, r *Row) error {
|
|||
c, p, a := gatherNodeMX(&no, oo.MX)
|
||||
trc, trm, tlc, tlm := new(resource.Quantity), new(resource.Quantity), new(resource.Quantity), new(resource.Quantity)
|
||||
for _, p := range oo.Pods {
|
||||
rList := podRequests(p.Spec)
|
||||
if rList.Cpu() != nil {
|
||||
trc.Add(*rList.Cpu())
|
||||
}
|
||||
if rList.Memory() != nil {
|
||||
trm.Add(*rList.Memory())
|
||||
}
|
||||
lList := podLimits(p.Spec)
|
||||
if lList.Cpu() != nil {
|
||||
tlc.Add(*lList.Cpu())
|
||||
}
|
||||
if lList.Memory() != nil {
|
||||
tlm.Add(*lList.Memory())
|
||||
}
|
||||
rcpu, rmem := podRequests(p.Spec)
|
||||
trc.Add(rcpu)
|
||||
trm.Add(rmem)
|
||||
|
||||
lcpu, lmem := podLimits(p.Spec)
|
||||
tlc.Add(lcpu)
|
||||
tlm.Add(lmem)
|
||||
}
|
||||
statuses := make(sort.StringSlice, 10)
|
||||
status(no.Status.Conditions, no.Spec.Unschedulable, statuses)
|
||||
|
|
@ -176,11 +169,12 @@ func (n *NodeWithMetrics) DeepCopyObject() runtime.Object {
|
|||
}
|
||||
|
||||
type metric struct {
|
||||
cpu, mem int64
|
||||
cpu, mem int64
|
||||
lcpu, lmem int64
|
||||
}
|
||||
|
||||
func gatherNodeMX(no *v1.Node, mx *mv1beta1.NodeMetrics) (metric, percentages, metric) {
|
||||
c := metric{cpu: 0, mem: 0}
|
||||
var c metric
|
||||
p := newPercentages()
|
||||
a := metric{
|
||||
cpu: no.Status.Allocatable.Cpu().MilliValue(),
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ const (
|
|||
|
||||
type (
|
||||
qualifiedResource string
|
||||
resources map[qualifiedResource]*resource.Quantity
|
||||
percentages map[qualifiedResource]int
|
||||
)
|
||||
|
||||
|
|
@ -45,31 +44,6 @@ func (p percentages) lMEM() int {
|
|||
return p[limitMEM]
|
||||
}
|
||||
|
||||
func newResources(req, lim v1.ResourceList) resources {
|
||||
if lim == nil {
|
||||
lim = v1.ResourceList{}
|
||||
}
|
||||
return resources{
|
||||
requestCPU: req.Cpu(),
|
||||
requestMEM: req.Memory(),
|
||||
limitCPU: lim.Cpu(),
|
||||
limitMEM: lim.Memory(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r resources) rCPU() *resource.Quantity {
|
||||
return r[requestCPU]
|
||||
}
|
||||
func (r resources) rMEM() *resource.Quantity {
|
||||
return r[requestMEM]
|
||||
}
|
||||
func (r resources) lCPU() *resource.Quantity {
|
||||
return r[limitCPU]
|
||||
}
|
||||
func (r resources) lMEM() *resource.Quantity {
|
||||
return r[limitMEM]
|
||||
}
|
||||
|
||||
// Pod renders a K8s Pod to screen.
|
||||
type Pod struct{}
|
||||
|
||||
|
|
@ -158,10 +132,10 @@ func (p Pod) Render(o interface{}, ns string, r *Row) error {
|
|||
strconv.Itoa(cr) + "/" + strconv.Itoa(len(ss)),
|
||||
strconv.Itoa(rc),
|
||||
phase,
|
||||
toMc(c.rCPU().MilliValue()),
|
||||
toMi(c.rMEM().Value()),
|
||||
toMc(res[requestCPU].MilliValue()) + ":" + toMc(res[limitCPU].MilliValue()),
|
||||
toMi(res[requestMEM].Value()) + ":" + toMi(res[limitMEM].Value()),
|
||||
toMc(c.cpu),
|
||||
toMi(c.mem),
|
||||
toMc(res.cpu) + ":" + toMc(res.lcpu),
|
||||
toMi(res.mem) + ":" + toMi(res.lmem),
|
||||
strconv.Itoa(perc.rCPU()),
|
||||
strconv.Itoa(perc.lCPU()),
|
||||
strconv.Itoa(perc.rMEM()),
|
||||
|
|
@ -207,26 +181,25 @@ func (p *PodWithMetrics) DeepCopyObject() runtime.Object {
|
|||
return p
|
||||
}
|
||||
|
||||
func (*Pod) gatherPodMX(pod *v1.Pod, mx *mv1beta1.PodMetrics) (resources, percentages, resources) {
|
||||
rList, lList := podRequests(pod.Spec), podLimits(pod.Spec)
|
||||
r, p := newResources(rList, lList), newPercentages()
|
||||
func (*Pod) gatherPodMX(pod *v1.Pod, mx *mv1beta1.PodMetrics) (metric, percentages, metric) {
|
||||
var c, r metric
|
||||
p := newPercentages()
|
||||
|
||||
rcpu, rmem := podRequests(pod.Spec)
|
||||
lcpu, lmem := podLimits(pod.Spec)
|
||||
r.cpu, r.lcpu = rcpu.MilliValue(), lcpu.MilliValue()
|
||||
r.mem, r.lmem = rmem.Value(), lmem.Value()
|
||||
|
||||
if mx == nil {
|
||||
return newResources(nil, nil), p, r
|
||||
return c, p, r
|
||||
}
|
||||
|
||||
c := newResources(currentRes(mx), nil)
|
||||
if rList.Cpu() != nil {
|
||||
p[requestCPU] = percentMc(c.rCPU(), rList.Cpu())
|
||||
}
|
||||
if lList.Cpu() != nil {
|
||||
p[limitCPU] = percentMc(c.rCPU(), lList.Cpu())
|
||||
}
|
||||
if rList.Memory() != nil {
|
||||
p[requestMEM] = percentMi(c.rMEM(), rList.Memory())
|
||||
}
|
||||
if lList.Memory() != nil {
|
||||
p[limitMEM] = percentMi(c.rMEM(), lList.Memory())
|
||||
}
|
||||
ccpu, cmem := currentRes(mx)
|
||||
c.cpu = ccpu.MilliValue()
|
||||
c.mem = cmem.Value()
|
||||
|
||||
p[requestCPU], p[limitCPU] = client.ToPercentage(c.cpu, r.cpu), client.ToPercentage(c.cpu, r.lcpu)
|
||||
p[requestMEM], p[limitMEM] = client.ToPercentage(c.mem, r.mem), client.ToPercentage(c.mem, r.lmem)
|
||||
|
||||
return c, p, r
|
||||
}
|
||||
|
|
@ -241,10 +214,10 @@ func containerRequests(co *v1.Container) v1.ResourceList {
|
|||
return lim
|
||||
}
|
||||
|
||||
return newResourceList(nil, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func podLimits(spec v1.PodSpec) v1.ResourceList {
|
||||
func podLimits(spec v1.PodSpec) (resource.Quantity, resource.Quantity) {
|
||||
cpu, mem := new(resource.Quantity), new(resource.Quantity)
|
||||
for _, co := range spec.Containers {
|
||||
limit := co.Resources.Limits
|
||||
|
|
@ -255,23 +228,10 @@ func podLimits(spec v1.PodSpec) v1.ResourceList {
|
|||
mem.Add(*limit.Memory())
|
||||
}
|
||||
}
|
||||
return newResourceList(cpu, mem)
|
||||
return *cpu, *mem
|
||||
}
|
||||
|
||||
func newResourceList(cpu, mem *resource.Quantity) v1.ResourceList {
|
||||
if cpu == nil {
|
||||
cpu = new(resource.Quantity)
|
||||
}
|
||||
if mem == nil {
|
||||
mem = new(resource.Quantity)
|
||||
}
|
||||
return v1.ResourceList{
|
||||
v1.ResourceCPU: *cpu,
|
||||
v1.ResourceMemory: *mem,
|
||||
}
|
||||
}
|
||||
|
||||
func podRequests(spec v1.PodSpec) v1.ResourceList {
|
||||
func podRequests(spec v1.PodSpec) (resource.Quantity, resource.Quantity) {
|
||||
cpu, mem := new(resource.Quantity), new(resource.Quantity)
|
||||
for i := range spec.Containers {
|
||||
rl := containerRequests(&spec.Containers[i])
|
||||
|
|
@ -282,13 +242,13 @@ func podRequests(spec v1.PodSpec) v1.ResourceList {
|
|||
mem.Add(*rl.Memory())
|
||||
}
|
||||
}
|
||||
return newResourceList(cpu, mem)
|
||||
return *cpu, *mem
|
||||
}
|
||||
|
||||
func currentRes(mx *mv1beta1.PodMetrics) v1.ResourceList {
|
||||
func currentRes(mx *mv1beta1.PodMetrics) (resource.Quantity, resource.Quantity) {
|
||||
cpu, mem := new(resource.Quantity), new(resource.Quantity)
|
||||
if mx == nil {
|
||||
return newResourceList(nil, nil)
|
||||
return *cpu, *mem
|
||||
}
|
||||
for _, co := range mx.Containers {
|
||||
c, m := co.Usage.Cpu(), co.Usage.Memory()
|
||||
|
|
@ -296,7 +256,7 @@ func currentRes(mx *mv1beta1.PodMetrics) v1.ResourceList {
|
|||
mem.Add(*m)
|
||||
}
|
||||
|
||||
return newResourceList(cpu, mem)
|
||||
return *cpu, *mem
|
||||
}
|
||||
|
||||
func (*Pod) mapQOS(class v1.PodQOSClass) string {
|
||||
|
|
|
|||
Loading…
Reference in New Issue