k9s/internal/k8s/metrics.go

194 lines
4.7 KiB
Go

package k8s
import (
"fmt"
"math"
"path"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
metricsapi "k8s.io/metrics/pkg/apis/metrics"
metricsV1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1"
)
type (
// MetricsServer serves cluster metrics for nodes and pods.
MetricsServer struct{}
// Metric tracks resource metrics.
Metric struct {
CPU string
Mem string
AvailCPU string
AvailMem string
}
)
// NewMetricsServer return a metric server instance.
func NewMetricsServer() *MetricsServer {
return &MetricsServer{}
}
// NodeMetrics retrieves all nodes metrics
func (m *MetricsServer) NodeMetrics() (Metric, error) {
var mx Metric
opts := metav1.ListOptions{}
nn, err := conn.dialOrDie().CoreV1().Nodes().List(opts)
if err != nil {
return mx, err
}
nods := make([]string, len(nn.Items))
var maxCPU, maxMem float64
for i, n := range nn.Items {
nods[i] = n.Name
c := n.Status.Allocatable["cpu"]
maxCPU += float64(c.MilliValue())
m := n.Status.Allocatable["memory"]
maxMem += float64(m.Value() / (1024 * 1024))
}
mm, err := m.getNodeMetrics()
if err != nil {
return mx, err
}
var cpu, mem float64
for _, n := range nods {
for _, m := range mm.Items {
if m.Name == n {
cpu += float64(m.Usage.Cpu().MilliValue())
mem += float64(m.Usage.Memory().Value() / (1024 * 1024))
}
}
}
mx = Metric{
CPU: fmt.Sprintf("%0.f%%", math.Round((cpu/maxCPU)*100)),
Mem: fmt.Sprintf("%0.f%%", math.Round((mem/maxMem)*100)),
}
return mx, nil
}
// PodMetrics retrieves all pods metrics
func (m *MetricsServer) PodMetrics() (map[string]Metric, error) {
mx := map[string]Metric{}
mm, err := m.getPodMetrics()
if err != nil {
return mx, err
}
for _, m := range mm.Items {
var cpu, mem int64
for _, c := range m.Containers {
cpu += c.Usage.Cpu().MilliValue()
mem += c.Usage.Memory().Value() / (1024 * 1024)
}
pa := path.Join(m.Namespace, m.Name)
mx[pa] = Metric{CPU: fmt.Sprintf("%dm", cpu), Mem: fmt.Sprintf("%dMi", mem)}
}
return mx, nil
}
// PerNodeMetrics retrieves all nodes metrics
func (m *MetricsServer) PerNodeMetrics(nn []v1.Node) (map[string]Metric, error) {
mx := map[string]Metric{}
mm, err := m.getNodeMetrics()
if err != nil {
return mx, err
}
for _, n := range nn {
acpu := n.Status.Allocatable["cpu"]
amem := n.Status.Allocatable["memory"]
var cpu, mem int64
for _, m := range mm.Items {
if m.Name == n.Name {
cpu += m.Usage.Cpu().MilliValue()
mem += m.Usage.Memory().Value() / (1024 * 1024)
}
}
mx[n.Name] = Metric{
CPU: fmt.Sprintf("%dm", cpu),
Mem: fmt.Sprintf("%dMi", mem),
AvailCPU: fmt.Sprintf("%dm", acpu.MilliValue()),
AvailMem: fmt.Sprintf("%dMi", amem.Value()/(1024*1024)),
}
}
return mx, nil
}
func (m *MetricsServer) getPodMetrics() (*metricsapi.PodMetricsList, error) {
if conn.hasMetricsServer() {
return m.podMetricsViaService()
}
selector := labels.Everything()
var mx *metricsapi.PodMetricsList
conn, err := conn.heapsterDial()
if err != nil {
return mx, err
}
return conn.GetPodMetrics("", "", true, selector)
}
func (m *MetricsServer) getNodeMetrics() (*metricsapi.NodeMetricsList, error) {
if conn.hasMetricsServer() {
return m.nodeMetricsViaService()
}
selector := labels.Everything()
var mx *metricsapi.NodeMetricsList
conn, err := conn.heapsterDial()
if err != nil {
return mx, err
}
return conn.GetNodeMetrics("", selector.String())
}
func (*MetricsServer) nodeMetricsViaService() (*metricsapi.NodeMetricsList, error) {
var mx *metricsapi.NodeMetricsList
clt, err := conn.mxsDial()
if err != nil {
return mx, err
}
selector := labels.Everything()
var versionedMetrics *metricsV1beta1api.NodeMetricsList
mc := clt.Metrics()
nm := mc.NodeMetricses()
versionedMetrics, err = nm.List(metav1.ListOptions{LabelSelector: selector.String()})
if err != nil {
return nil, err
}
metrics := &metricsapi.NodeMetricsList{}
err = metricsV1beta1api.Convert_v1beta1_NodeMetricsList_To_metrics_NodeMetricsList(versionedMetrics, metrics, nil)
if err != nil {
return nil, err
}
return metrics, nil
}
func (*MetricsServer) podMetricsViaService() (*metricsapi.PodMetricsList, error) {
var mx *metricsapi.PodMetricsList
clt, err := conn.mxsDial()
if err != nil {
return mx, err
}
selector := labels.Everything()
var versionedMetrics *metricsV1beta1api.PodMetricsList
mc := clt.Metrics()
nm := mc.PodMetricses("")
versionedMetrics, err = nm.List(metav1.ListOptions{LabelSelector: selector.String()})
if err != nil {
return nil, err
}
metrics := &metricsapi.PodMetricsList{}
err = metricsV1beta1api.Convert_v1beta1_PodMetricsList_To_metrics_PodMetricsList(versionedMetrics, metrics, nil)
if err != nil {
return nil, err
}
return metrics, nil
}