Files
cloudpods/pkg/hostman/hostmetrics/container_metrics.go

1002 lines
29 KiB
Go

// Copyright 2019 Yunion
//
// 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 hostmetrics
import (
"fmt"
"sort"
"strconv"
"strings"
"time"
"yunion.io/x/log"
apis "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/hostman/guestman"
"yunion.io/x/onecloud/pkg/hostman/options"
"yunion.io/x/onecloud/pkg/util/pod/stats"
)
const (
// Cumulative cpu time consumed by the container in core-seconds
CPU_USAGE_SECONDS_TOTAL = "usage_seconds_total"
// cpu usage rate
CPU_USAGE_RATE = "usage_rate"
// current working set of memory size in bytes
MEMORY_WORKING_SET_BYTES = "working_set_bytes"
MEMORY_USAGE_BYTES = "usage_bytes"
// memory usage rate
MEMORY_USAGE_RATE = "usage_rate"
MEMORY_WORKING_SET_RATE = "working_set_rate"
VOLUME_TOTAL = "total"
VOLUME_FREE = "free"
VOLUME_USED = "used"
VOLUME_USED_PERCENT = "used_percent"
VOLUME_INODES_TOTAL = "inodes_total"
VOLUME_INODES_FREE = "inodes_free"
VOLUME_INODES_USED = "inodes_used"
VOLUME_INODES_USED_PERCENT = "inodes_used_percent"
PROCESS_COUNT = "process_count"
FD_COUNT = "fd_count"
SOCKET_COUNT = "socket_count"
THREADS_CURRENT = "threads_current"
THREADS_MAX = "threads_max"
NVIDIA_GPU_MEMORY_TOTAL = "memory_total"
NVIDIA_GPU_INDEX = "index"
NVIDIA_GPU_PHYSICAL_INDEX = "physical_index"
NVIDIA_GPU_FRAME_BUFFER = "frame_buffer"
NVIDIA_GPU_CCPM = "ccpm"
NVIDIA_GPU_SM = "sm"
NVIDIA_GPU_MEM_UTIL = "mem_util"
NVIDIA_GPU_ENC = "enc"
NVIDIA_GPU_DEC = "dec"
NVIDIA_GPU_JPG = "jpg"
NVIDIA_GPU_OFA = "ofa"
VASTAITECH_GPU_DEV_ID = "dev_id"
VASTAITECH_GPU_ENC = "enc"
VASTAITECH_GPU_DEC = "dec"
VASTAITECH_GPU_GFX = "gfx"
VASTAITECH_GPU_MEM = "mem"
VASTAITECH_GPU_MEM_UTIL = "mem_util"
CPH_AMD_GPU_DEV_ID = "dev_id"
CPH_AMD_GPU_MEM = "mem"
CPH_AMD_GPU_MEM_UTIL = "mem_util"
)
type CadvisorProcessMetric struct {
// Number of processes
ProcessCount uint64 `json:"process_count"`
// Number of open file descriptors
FdCount uint64 `json:"fd_count,omitempty"`
// Number of sockets
SocketCount uint64 `json:"socket_count"`
// Number of threads currently in container
ThreadsCurrent uint64 `json:"threads_current,omitempty"`
// Maximum number of threads allowed in container
ThreadsMax uint64 `json:"threads_max,omitempty"`
}
func (m CadvisorProcessMetric) ToMap() map[string]interface{} {
return map[string]interface{}{
PROCESS_COUNT: m.ProcessCount,
FD_COUNT: m.FdCount,
SOCKET_COUNT: m.SocketCount,
THREADS_CURRENT: m.ThreadsCurrent,
THREADS_MAX: m.ThreadsMax,
}
}
type PodMetrics struct {
PodCpu *PodCpuMetric `json:"pod_cpu"`
PodMemory *PodMemoryMetric `json:"pod_memory"`
PodProcess *PodProcessMetric `json:"pod_process"`
PodVolumes []*PodVolumeMetric `json:"pod_volume"`
PodDiskIos PodDiskIoMetrics `json:"pod_disk_ios"`
PodNvidiaGpu []*PodNvidiaGpuMetrics `json:"pod_nvidia_gpu"`
PodVastaitechGpu []*PodVastaitechGpuMetrics `json:"pod_vastaitech_gpu"`
PodCphAmdGpu []*PodCphAmdGpuMetrics `json:"pod_cph_amd_gpu"`
Containers []*ContainerMetrics `json:"containers"`
}
type PodMetricMeta struct {
Time time.Time
}
func NewPodMetricMeta(time time.Time) PodMetricMeta {
return PodMetricMeta{Time: time}
}
func (m PodMetricMeta) GetTag() map[string]string {
return nil
}
type PodCphAmdGpuMetrics struct {
PodMetricMeta
DevId string
Mem float64 // MB
MemUtil float64
}
func (m PodCphAmdGpuMetrics) GetName() string {
return "pod_cph_amd_gpu"
}
func (m PodCphAmdGpuMetrics) GetUniformName() string {
return "pod_gpu"
}
func (m PodCphAmdGpuMetrics) GetTag() map[string]string {
return map[string]string{
"dev_id": m.DevId,
"dev_type": apis.GPU_TYPE,
}
}
func (m PodCphAmdGpuMetrics) ToMap() map[string]interface{} {
ret := map[string]interface{}{
CPH_AMD_GPU_DEV_ID: m.DevId,
CPH_AMD_GPU_MEM: m.Mem,
CPH_AMD_GPU_MEM_UTIL: m.MemUtil,
}
return ret
}
type PodVastaitechGpuMetrics struct {
PodMetricMeta
PciAddr string
DevId string
Mem float64 // MB
MemUtil float64
Gfx float64
DecUtil float64
EncUtil float64
}
func (m PodVastaitechGpuMetrics) GetName() string {
return "pod_vastaitech_gpu"
}
func (m PodVastaitechGpuMetrics) GetUniformName() string {
return "pod_gpu"
}
func (m PodVastaitechGpuMetrics) GetTag() map[string]string {
return map[string]string{
"dev_id": m.DevId,
"dev_type": apis.NPU_TYPE,
}
}
func (m PodVastaitechGpuMetrics) ToMap() map[string]interface{} {
ret := map[string]interface{}{
VASTAITECH_GPU_DEC: m.DecUtil,
VASTAITECH_GPU_DEV_ID: m.DevId,
VASTAITECH_GPU_ENC: m.EncUtil,
VASTAITECH_GPU_GFX: m.Gfx,
VASTAITECH_GPU_MEM: m.Mem,
VASTAITECH_GPU_MEM_UTIL: m.MemUtil,
}
return ret
}
type PodNvidiaGpuMetrics struct {
PodMetricMeta
Index int
PhysicalIndex int
MemTotal int
Framebuffer int // Framebuffer Memory Usage
Ccpm int // Current CUDA Contexts Per Measurement
SmUtil float64 // Streaming Multiprocessor Utilization
Mem int // Mem Usage
MemUtil float64 // Memory Utilization
EncUtil float64 // Encoder Utilization
DecUtil float64 // Decoder Utilization
JpgUtil float64 // JPEG Decoder Utilization
OfaUtil float64 // Other Feature Utilization
}
func (m PodNvidiaGpuMetrics) GetName() string {
return "pod_nvidia_gpu"
}
func (m PodNvidiaGpuMetrics) GetUniformName() string {
return "pod_gpu"
}
func (m PodNvidiaGpuMetrics) GetTag() map[string]string {
devType := apis.GPU_TYPE
sharingMode := apis.DEVICE_SHARING_MODE_UNLIMITED
if options.HostOptions.EnableCudaMPS {
sharingMode = apis.DEVICE_SHARING_MODE_MPS
} else if options.HostOptions.EnableCudaHAMI {
sharingMode = apis.DEVICE_SHARING_MODE_HAMI
}
return map[string]string{
"index": strconv.Itoa(m.Index),
"physical_index": strconv.Itoa(m.PhysicalIndex),
"dev_type": devType,
"sharing_mode": sharingMode,
}
}
func (m PodNvidiaGpuMetrics) ToMap() map[string]interface{} {
ret := map[string]interface{}{
NVIDIA_GPU_MEMORY_TOTAL: m.MemTotal,
NVIDIA_GPU_INDEX: m.Index,
NVIDIA_GPU_PHYSICAL_INDEX: m.PhysicalIndex,
NVIDIA_GPU_FRAME_BUFFER: m.Framebuffer,
NVIDIA_GPU_CCPM: m.Ccpm,
NVIDIA_GPU_SM: m.SmUtil,
NVIDIA_GPU_MEM_UTIL: m.MemUtil,
NVIDIA_GPU_ENC: m.EncUtil,
NVIDIA_GPU_DEC: m.DecUtil,
NVIDIA_GPU_JPG: m.JpgUtil,
NVIDIA_GPU_OFA: m.OfaUtil,
}
return ret
}
type PodCpuMetric struct {
PodMetricMeta
CpuUsageSecondsTotal float64 `json:"cpu_usage_seconds_total"`
CpuUsageRate *float64 `json:"cpu_usage_rate"`
}
func (m PodCpuMetric) GetName() string {
return "pod_cpu"
}
func (m PodCpuMetric) ToMap() map[string]interface{} {
ret := map[string]interface{}{
CPU_USAGE_SECONDS_TOTAL: m.CpuUsageSecondsTotal,
}
if m.CpuUsageRate != nil {
ret[CPU_USAGE_RATE] = *m.CpuUsageRate
}
return ret
}
type PodMemoryMetric struct {
PodMetricMeta
MemoryWorkingSetBytes float64 `json:"memory_working_set_bytes"`
MemoryWorkingSetRate float64 `json:"memory_working_set_rate"`
MemoryUsageBytes float64 `json:"memory_usage_bytes"`
MemoryUsageRate float64 `json:"memory_usage_rate"`
}
func (m PodMemoryMetric) GetName() string {
return "pod_mem"
}
func (m PodMemoryMetric) ToMap() map[string]interface{} {
return map[string]interface{}{
MEMORY_WORKING_SET_BYTES: m.MemoryWorkingSetBytes,
MEMORY_USAGE_BYTES: m.MemoryUsageBytes,
MEMORY_USAGE_RATE: m.MemoryUsageRate,
MEMORY_WORKING_SET_RATE: m.MemoryWorkingSetRate,
}
}
type PodProcessMetric struct {
PodMetricMeta
*CadvisorProcessMetric
}
func (m PodProcessMetric) GetName() string {
return "pod_process"
}
type PodVolumeMetric struct {
ContainerMetricMeta
// 容器内挂载路径
MountPath string `json:"mount_path"`
// 宿主机路径
HostPath string `json:"host_path"`
Type string `json:"type"`
Fstype string `json:"fstype"`
Total uint64 `json:"total"`
Free uint64 `json:"free"`
Used uint64 `json:"used"`
UsedPercent float64 `json:"used_percent"`
InodesTotal uint64 `json:"inodes_total"`
InodesUsed uint64 `json:"inodes_used"`
InodesFree uint64 `json:"inodes_free"`
InodesUsedPercent float64 `json:"inodes_used_percent"`
Tags map[string]string `json:"tags"`
}
type CadvisorDiskIoMetric struct {
Device string `json:"device"`
//AsyncBytes uint64 `json:"async_bytes"`
//DiscardBytes uint64 `json:"discard_bytes"`
ReadBytes uint64 `json:"read_bytes"`
WriteBytes uint64 `json:"write_bytes"`
//TotalBytes uint64 `json:"total_bytes"`
//AsyncCount uint64 `json:"async_count"`
//DiscardCount uint64 `json:"discard_count"`
ReadCount uint64 `json:"read_count"`
WriteCount uint64 `json:"write_count"`
//TotalCount uint64 `json:"total_count"`
ReadIOPS float64 `json:"read_iops"`
WriteIOPS float64 `json:"write_iops"`
ReadBPS float64 `json:"read_Bps"`
WriteBPS float64 `json:"write_Bps"`
}
func (m CadvisorDiskIoMetric) GetTag() map[string]string {
return map[string]string{
"device": m.Device,
}
}
func (m CadvisorDiskIoMetric) ToMap() map[string]interface{} {
return map[string]interface{}{
"read_bytes": m.ReadBytes,
"write_bytes": m.WriteBytes,
"read_Bps": m.ReadBPS,
"write_Bps": m.WriteBPS,
"read_count": m.ReadCount,
"write_count": m.WriteCount,
"read_iops": m.ReadIOPS,
"write_iops": m.WriteIOPS,
}
}
type PodDiskIoMetrics map[string]*PodDiskIoMetric
func newPodDiskIoMetrics(metrics map[string]CadvisorDiskIoMetric, meta PodMetricMeta) PodDiskIoMetrics {
ret := make(map[string]*PodDiskIoMetric)
for k, v := range metrics {
ret[k] = &PodDiskIoMetric{
PodMetricMeta: meta,
CadvisorDiskIoMetric: v,
}
}
return ret
}
func (m PodDiskIoMetrics) ToCadvisorDiskIoMetrics() map[string]CadvisorDiskIoMetric {
ret := make(map[string]CadvisorDiskIoMetric)
for k, v := range m {
ret[k] = v.CadvisorDiskIoMetric
}
return ret
}
func (m PodDiskIoMetrics) GetTime() time.Time {
for _, v := range m {
return v.Time
}
return time.Time{}
}
type PodDiskIoMetric struct {
PodMetricMeta
CadvisorDiskIoMetric
}
func (m PodDiskIoMetric) GetName() string {
return "pod_diskio"
}
func (m PodDiskIoMetric) GetTag() map[string]string {
return m.CadvisorDiskIoMetric.GetTag()
}
func (m PodVolumeMetric) GetName() string {
return "pod_volume"
}
func (m PodVolumeMetric) ToMap() map[string]interface{} {
r := map[string]interface{}{
VOLUME_TOTAL: m.Total,
VOLUME_FREE: m.Free,
VOLUME_USED: m.Used,
VOLUME_USED_PERCENT: m.UsedPercent,
VOLUME_INODES_TOTAL: m.InodesTotal,
VOLUME_INODES_FREE: m.InodesFree,
VOLUME_INODES_USED: m.InodesUsed,
VOLUME_INODES_USED_PERCENT: m.InodesUsedPercent,
}
return r
}
func (m PodVolumeMetric) GetTag() map[string]string {
baseTags := m.ContainerMetricMeta.GetTag()
curTags := map[string]string{
"mount_path": m.MountPath,
"host_path": m.HostPath,
"type": m.Type,
}
for k, v := range curTags {
baseTags[k] = v
}
for k, v := range m.Tags {
baseTags[k] = v
}
return baseTags
}
type ContainerDiskIoMetrics map[string]*ContainerDiskIoMetric
func newContainerDiskioMetrics(metrics map[string]CadvisorDiskIoMetric, meta ContainerMetricMeta) ContainerDiskIoMetrics {
ret := make(map[string]*ContainerDiskIoMetric)
for k, v := range metrics {
ret[k] = &ContainerDiskIoMetric{
ContainerMetricMeta: meta,
CadvisorDiskIoMetric: v,
}
}
return ret
}
func (m ContainerDiskIoMetrics) ToCadvisorDiskIoMetrics() map[string]CadvisorDiskIoMetric {
ret := make(map[string]CadvisorDiskIoMetric)
for k, v := range m {
ret[k] = v.CadvisorDiskIoMetric
}
return ret
}
type ContainerMetrics struct {
ContainerCpu *ContainerCpuMetric `json:"container_cpu"`
ContainerMemory *ContainerMemoryMetric `json:"container_memory"`
ContainerProcess *ContainerProcessMetric `json:"container_process"`
ContainerDiskIos ContainerDiskIoMetrics `json:"container_diskios"`
}
type ContainerMetricMeta struct {
PodMetricMeta
ContainerId string `json:"container_id"`
ContainerName string `json:"container_name"`
PodId string `json:"pod_id"`
}
func (m ContainerMetricMeta) GetTag() map[string]string {
ret := map[string]string{
"pod_id": m.PodId,
"container_name": strings.ReplaceAll(m.ContainerName, " ", "+"),
}
if m.ContainerId != "" {
ret["container_id"] = m.ContainerId
}
return ret
}
type ContainerMemoryMetric struct {
ContainerMetricMeta
MemoryWorkingSetBytes float64 `json:"memory_working_set_bytes"`
MemoryWorkingSetRate float64 `json:"memory_working_set_rate"`
MemoryUsageBytes float64 `json:"memory_usage_bytes"`
MemoryUsageRate float64 `json:"memory_usage_rate"`
}
func (m ContainerMemoryMetric) GetName() string {
return "container_mem"
}
func (m *ContainerMemoryMetric) ToMap() map[string]interface{} {
return map[string]interface{}{
MEMORY_WORKING_SET_BYTES: m.MemoryWorkingSetBytes,
MEMORY_USAGE_BYTES: m.MemoryUsageBytes,
MEMORY_USAGE_RATE: m.MemoryUsageRate,
MEMORY_WORKING_SET_RATE: m.MemoryWorkingSetRate,
}
}
type ContainerCpuMetric struct {
ContainerMetricMeta
CpuUsageSecondsTotal float64 `json:"cpu_usage_seconds_total"`
CpuUsageRate *float64 `json:"cpu_usage_rate"`
}
func (m ContainerCpuMetric) GetName() string {
return "container_cpu"
}
func (m *ContainerCpuMetric) ToMap() map[string]interface{} {
ret := map[string]interface{}{
CPU_USAGE_SECONDS_TOTAL: m.CpuUsageSecondsTotal,
}
if m.CpuUsageRate != nil {
ret[CPU_USAGE_RATE] = *m.CpuUsageRate
}
return ret
}
type ContainerProcessMetric struct {
ContainerMetricMeta
*CadvisorProcessMetric
}
func (m ContainerProcessMetric) GetName() string {
return "container_process"
}
type ContainerDiskIoMetric struct {
ContainerMetricMeta
CadvisorDiskIoMetric
}
func (m ContainerDiskIoMetric) GetName() string {
return "container_diskio"
}
func (m *ContainerDiskIoMetric) GetTag() map[string]string {
baseTags := m.ContainerMetricMeta.GetTag()
for k, v := range m.CadvisorDiskIoMetric.GetTag() {
baseTags[k] = v
}
return baseTags
}
func GetPodStatsById(ss []stats.PodStats, gpuPodProcs map[string]map[string]struct{}, podId string) (*stats.PodStats, map[string]struct{}) {
var podStat *stats.PodStats
for i := range ss {
if ss[i].PodRef.UID == podId {
podStat = &ss[i]
break
}
}
podProcs, _ := gpuPodProcs[podId]
return podStat, podProcs
}
func GetPodNvidiaGpuMetrics(metrics []NvidiaGpuProcessMetrics, podProcs map[string]struct{}) []NvidiaGpuProcessMetrics {
podMetrics := make([]NvidiaGpuProcessMetrics, 0)
for i := range metrics {
pid := metrics[i].Pid
if _, ok := podProcs[pid]; ok {
podMetrics = append(podMetrics, metrics[i])
}
}
return podMetrics
}
func GetPodVastaitechGpuMetrics(metrics []VastaitechGpuProcessMetrics, podProcs map[string]struct{}) []VastaitechGpuProcessMetrics {
podMetrics := make([]VastaitechGpuProcessMetrics, 0)
for i := range metrics {
pid := metrics[i].Pid
if _, ok := podProcs[pid]; ok {
podMetrics = append(podMetrics, metrics[i])
}
}
return podMetrics
}
func GetPodCphAmdGpuMetrics(metrics []CphAmdGpuProcessMetrics, podProcs map[string]struct{}) []CphAmdGpuProcessMetrics {
podMetrics := make([]CphAmdGpuProcessMetrics, 0)
for i := range metrics {
pid := metrics[i].Pid
if _, ok := podProcs[pid]; ok {
podMetrics = append(podMetrics, metrics[i])
}
}
return podMetrics
}
func (s *SGuestMonitorCollector) collectPodMetrics(gm *SGuestMonitor, prevUsage *GuestMetrics) *GuestMetrics {
gmData := new(GuestMetrics)
gmData.PodMetrics = gm.PodMetrics(prevUsage)
// netio
gmData.VmNetio = gm.Netio()
netio1 := gmData.VmNetio
netio2 := prevUsage.VmNetio
s.addNetio(netio1, netio2)
return gmData
}
func NewContainerMetricMeta(serverId string, containerId string, containerName string, time time.Time) ContainerMetricMeta {
return ContainerMetricMeta{
PodMetricMeta: NewPodMetricMeta(time),
ContainerId: containerId,
PodId: serverId,
ContainerName: containerName,
}
}
func (m *SGuestMonitor) HasPodMetrics() bool {
return m.podStat != nil
}
func (m *SGuestMonitor) getVolumeMetrics() []*PodVolumeMetric {
pi := m.instance.(guestman.PodInstance)
if !pi.IsRunning() {
return nil
}
vus, err := pi.GetVolumeMountUsages()
if err != nil {
log.Warningf("get volume mount usages: %v", err)
}
result := make([]*PodVolumeMetric, 0)
for i := range vus {
vu := vus[i]
ctr := pi.GetContainerById(vu.Id)
if ctr == nil {
log.Warningf("not found container by %s", vu.Id)
continue
}
meta := NewContainerMetricMeta(pi.GetId(), vu.Id, ctr.Name, time.Now())
result = append(result, &PodVolumeMetric{
ContainerMetricMeta: meta,
MountPath: vu.MountPath,
HostPath: vu.HostPath,
Type: vu.VolumeType,
Fstype: vu.Usage.Fstype,
Total: vu.Usage.Total,
Free: vu.Usage.Free,
Used: vu.Usage.Used,
UsedPercent: vu.Usage.UsedPercent,
InodesTotal: vu.Usage.InodesTotal,
InodesUsed: vu.Usage.InodesUsed,
InodesFree: vu.Usage.InodesFree,
InodesUsedPercent: vu.Usage.InodesUsedPercent,
Tags: vu.Tags,
})
}
return result
}
func (m *SGuestMonitor) getCadvisorProcessMetric(stat *stats.ProcessStats) *CadvisorProcessMetric {
if stat == nil {
return nil
}
return &CadvisorProcessMetric{
ProcessCount: stat.ProcessCount,
FdCount: stat.FdCount,
SocketCount: stat.SocketCount,
ThreadsCurrent: stat.ThreadsCurrent,
ThreadsMax: stat.ThreadsMax,
}
}
func (m *SGuestMonitor) getCadvisorDiskIoMetrics(cur stats.DiskIoStats, prev map[string]CadvisorDiskIoMetric, curTime, prevTime time.Time) map[string]CadvisorDiskIoMetric {
ret := make(map[string]CadvisorDiskIoMetric)
for devName, stat := range cur {
devR := CadvisorDiskIoMetric{
Device: stat.DeviceName,
ReadCount: stat.ReadCount,
WriteCount: stat.WriteCount,
ReadBytes: stat.ReadBytes,
WriteBytes: stat.WriteBytes,
}
diffTime := float64(curTime.Sub(prevTime) / time.Second)
if diffTime > 0 && prev != nil {
prevStat, ok := prev[devName]
if ok {
// 检查计数器是否回退(可能是容器重启导致)
if stat.ReadCount < prevStat.ReadCount || stat.WriteCount < prevStat.WriteCount ||
stat.ReadBytes < prevStat.ReadBytes || stat.WriteBytes < prevStat.WriteBytes {
log.Warningf("Disk IO counters decreased: guest=%s(%s), device=%s, ReadCount %d -> %d, WriteCount %d -> %d, ReadBytes %d -> %d, WriteBytes %d -> %d. Possible container restart, skipping rate calculation.",
m.Name, m.Id, devName,
prevStat.ReadCount, stat.ReadCount,
prevStat.WriteCount, stat.WriteCount,
prevStat.ReadBytes, stat.ReadBytes,
prevStat.WriteBytes, stat.WriteBytes)
// 跳过计算,避免产生负值
} else {
devR.ReadBPS = float64(stat.ReadBytes-prevStat.ReadBytes) / diffTime
devR.WriteBPS = float64(stat.WriteBytes-prevStat.WriteBytes) / diffTime
devR.ReadIOPS = float64(stat.ReadCount-prevStat.ReadCount) / diffTime
devR.WriteIOPS = float64(stat.WriteCount-prevStat.WriteCount) / diffTime
}
}
}
ret[devName] = devR
}
return ret
}
func isPodContainerStopped(prevUsage *GuestMetrics, stat *stats.PodStats) bool {
hasPrevUsage := prevUsage != nil && prevUsage.PodMetrics != nil
if !hasPrevUsage {
return false
}
curTime := stat.CPU.Time.Time
podCpu := &PodCpuMetric{
PodMetricMeta: NewPodMetricMeta(curTime),
CpuUsageSecondsTotal: float64(*stat.CPU.UsageCoreNanoSeconds) / float64(time.Second),
}
pmPodCpu := prevUsage.PodMetrics.PodCpu
if podCpu.CpuUsageSecondsTotal < pmPodCpu.CpuUsageSecondsTotal {
return true
}
return false
}
func (m *SGuestMonitor) PodMetrics(prevUsage *GuestMetrics) *PodMetrics {
stat := m.podStat
if stat == nil || stat.CPU == nil || stat.CPU.UsageCoreNanoSeconds == nil || stat.Memory == nil || stat.Memory.WorkingSetBytes == nil || stat.Memory.UsageBytes == nil {
log.Warningf("skip pod metrics for %s(%s): incomplete pod stats", m.Name, m.Id)
return nil
}
curTime := stat.CPU.Time.Time
podCpu := &PodCpuMetric{
PodMetricMeta: NewPodMetricMeta(curTime),
CpuUsageSecondsTotal: float64(*stat.CPU.UsageCoreNanoSeconds) / float64(time.Second),
}
hasPrevUsage := prevUsage != nil && prevUsage.PodMetrics != nil
if hasPrevUsage {
pmPodCpu := prevUsage.PodMetrics.PodCpu
val := (podCpu.CpuUsageSecondsTotal - pmPodCpu.CpuUsageSecondsTotal) / podCpu.Time.Sub(pmPodCpu.Time).Seconds() * 100
podCpu.CpuUsageRate = &val
}
podMemory := &PodMemoryMetric{
MemoryWorkingSetBytes: float64(*stat.Memory.WorkingSetBytes),
MemoryWorkingSetRate: (float64(*stat.Memory.WorkingSetBytes) / float64(m.MemMB*1024*1024)) * 100,
MemoryUsageBytes: float64(*stat.Memory.UsageBytes),
MemoryUsageRate: (float64(*stat.Memory.UsageBytes) / float64(m.MemMB*1024*1024)) * 100,
}
containers := make([]*ContainerMetrics, 0)
for _, ctr := range stat.Containers {
if ctr.CPU == nil || ctr.CPU.UsageCoreNanoSeconds == nil || ctr.Memory == nil || ctr.Memory.WorkingSetBytes == nil || ctr.Memory.UsageBytes == nil {
log.Warningf("skip incomplete container stats in pod %s(%s), container=%s", m.Name, m.Id, ctr.Name)
continue
}
ctrMeta := NewContainerMetricMeta(m.Id, "", ctr.Name, ctr.CPU.Time.Time)
cm := &ContainerMetrics{
ContainerCpu: &ContainerCpuMetric{
ContainerMetricMeta: ctrMeta,
CpuUsageSecondsTotal: float64(*ctr.CPU.UsageCoreNanoSeconds) / float64(time.Second),
},
ContainerMemory: &ContainerMemoryMetric{
ContainerMetricMeta: ctrMeta,
MemoryWorkingSetBytes: float64(*ctr.Memory.WorkingSetBytes),
MemoryWorkingSetRate: (float64(*ctr.Memory.WorkingSetBytes) / float64(m.MemMB*1024*1024)) * 100,
MemoryUsageBytes: float64(*ctr.Memory.UsageBytes),
MemoryUsageRate: (float64(*ctr.Memory.UsageBytes) / float64(m.MemMB*1024*1024)) * 100,
},
}
var prevCtrM *ContainerMetrics
if hasPrevUsage {
for _, prevCtr := range prevUsage.PodMetrics.Containers {
if prevCtr.ContainerCpu.ContainerName == ctr.Name {
prevCtrM = prevCtr
val := (cm.ContainerCpu.CpuUsageSecondsTotal - prevCtr.ContainerCpu.CpuUsageSecondsTotal) / cm.ContainerCpu.Time.Sub(prevCtr.ContainerCpu.Time).Seconds() * 100
cm.ContainerCpu.CpuUsageRate = &val
break
}
}
}
if ctr.ProcessStats != nil {
cm.ContainerProcess = &ContainerProcessMetric{
ContainerMetricMeta: ctrMeta,
CadvisorProcessMetric: m.getCadvisorProcessMetric(ctr.ProcessStats),
}
}
if ctr.DiskIo != nil {
var prevStat map[string]CadvisorDiskIoMetric
var prevTime = ctrMeta.Time
if prevCtrM != nil {
if prevCtrM.ContainerDiskIos != nil {
prevStat = prevCtrM.ContainerDiskIos.ToCadvisorDiskIoMetrics()
prevTime = prevCtrM.ContainerCpu.Time
}
}
cm.ContainerDiskIos = newContainerDiskioMetrics(m.getCadvisorDiskIoMetrics(ctr.DiskIo, prevStat, ctrMeta.Time, prevTime), ctrMeta)
}
containers = append(containers, cm)
}
var podProcess *PodProcessMetric
if stat.ProcessStats != nil {
podProcess = &PodProcessMetric{
CadvisorProcessMetric: m.getCadvisorProcessMetric(stat.ProcessStats),
}
}
pm := &PodMetrics{
PodCpu: podCpu,
PodMemory: podMemory,
PodProcess: podProcess,
PodVolumes: m.getVolumeMetrics(),
PodNvidiaGpu: m.getPodNvidiaGpuMetrics(),
PodVastaitechGpu: m.getPodVastaitechGpuMetrics(),
PodCphAmdGpu: m.getPodCphAmdGpuMetrics(),
Containers: containers,
}
if stat.DiskIo != nil {
var prevStat map[string]CadvisorDiskIoMetric
var prevTime = curTime
if hasPrevUsage {
pd := prevUsage.PodMetrics.PodDiskIos
if pd != nil && len(pd) != 0 {
prevStat = pd.ToCadvisorDiskIoMetrics()
prevTime = pd.GetTime()
}
}
podMeta := NewPodMetricMeta(curTime)
pm.PodDiskIos = newPodDiskIoMetrics(m.getCadvisorDiskIoMetrics(stat.DiskIo, prevStat, curTime, prevTime), podMeta)
}
return pm
}
func (m *SGuestMonitor) getPodCphAmdGpuMetrics() []*PodCphAmdGpuMetrics {
if len(m.cphAmdGpuMetrics) == 0 {
return nil
}
addrGpuMap := map[string]*PodCphAmdGpuMetrics{}
for i := range m.cphAmdGpuMetrics {
devId := m.cphAmdGpuMetrics[i].DevId
gms, ok := addrGpuMap[devId]
if !ok {
gms = new(PodCphAmdGpuMetrics)
gms.DevId = devId
}
gms.Mem += m.cphAmdGpuMetrics[i].Mem
gms.MemUtil += m.cphAmdGpuMetrics[i].MemUtil
addrGpuMap[devId] = gms
}
res := make([]*PodCphAmdGpuMetrics, 0)
for _, gms := range addrGpuMap {
res = append(res, gms)
}
return res
}
func (m *SGuestMonitor) getPodVastaitechGpuMetrics() []*PodVastaitechGpuMetrics {
if len(m.vastaitechGpuMetrics) == 0 {
return nil
}
addrGpuMap := map[string]*PodVastaitechGpuMetrics{}
for i := range m.vastaitechGpuMetrics {
pciAddr := m.vastaitechGpuMetrics[i].PciAddr
gms, ok := addrGpuMap[pciAddr]
if !ok {
gms = new(PodVastaitechGpuMetrics)
gms.DevId = m.vastaitechGpuMetrics[i].DevId
gms.PciAddr = m.vastaitechGpuMetrics[i].PciAddr
}
gms.Mem += m.vastaitechGpuMetrics[i].GfxMem
gms.MemUtil += m.vastaitechGpuMetrics[i].GfxMemUsage
gms.Gfx += m.vastaitechGpuMetrics[i].Gfx
gms.DecUtil += m.vastaitechGpuMetrics[i].Dec
gms.EncUtil += m.vastaitechGpuMetrics[i].Enc
addrGpuMap[pciAddr] = gms
}
res := make([]*PodVastaitechGpuMetrics, 0)
for _, gms := range addrGpuMap {
res = append(res, gms)
}
return res
}
func (m *SGuestMonitor) getPodNvidiaGpuMetrics() []*PodNvidiaGpuMetrics {
if len(m.nvidiaGpuMetrics) == 0 {
return nil
}
indexGpuMap := map[int]*PodNvidiaGpuMetrics{}
for i := range m.nvidiaGpuMetrics {
index := m.nvidiaGpuMetrics[i].Index
gms, ok := indexGpuMap[index]
if !ok {
gms = new(PodNvidiaGpuMetrics)
}
gms.Framebuffer += m.nvidiaGpuMetrics[i].FB
gms.Ccpm += m.nvidiaGpuMetrics[i].Ccpm
gms.SmUtil += m.nvidiaGpuMetrics[i].Sm
gms.EncUtil += m.nvidiaGpuMetrics[i].Enc
gms.DecUtil += m.nvidiaGpuMetrics[i].Dec
gms.JpgUtil += m.nvidiaGpuMetrics[i].Jpg
gms.OfaUtil += m.nvidiaGpuMetrics[i].Ofa
indexGpuMap[index] = gms
}
indexs := make([]int, 0)
for index, gms := range indexGpuMap {
indexs = append(indexs, index)
indexStr := strconv.Itoa(index)
memSizeTotal, ok := m.nvidiaGpuIndexMemoryMap[indexStr]
if !ok {
continue
}
gms.MemTotal = memSizeTotal
gms.Mem = gms.Framebuffer
gms.MemUtil = float64(gms.Framebuffer) / float64(gms.MemTotal)
}
sort.Ints(indexs)
res := make([]*PodNvidiaGpuMetrics, len(indexs))
for i := range indexs {
gms := indexGpuMap[indexs[i]]
gms.PhysicalIndex = gms.Index
gms.Index = i
res[i] = gms
}
return res
}
type iPodMetric interface {
GetName() string
GetTag() map[string]string
ToMap() map[string]interface{}
}
type iPodUniformName interface {
GetUniformName() string
}
func (d *GuestMetrics) toPodTelegrafData(tagStr string) []string {
m := d.PodMetrics
ims := []iPodMetric{m.PodCpu, m.PodMemory}
for i := range m.PodVolumes {
ims = append(ims, m.PodVolumes[i])
}
if m.PodProcess != nil {
ims = append(ims, m.PodProcess)
}
if m.PodDiskIos != nil {
for _, d := range m.PodDiskIos {
ims = append(ims, d)
}
}
for i := range m.PodNvidiaGpu {
ims = append(ims, m.PodNvidiaGpu[i])
}
for i := range m.PodVastaitechGpu {
ims = append(ims, m.PodVastaitechGpu[i])
}
for i := range m.PodCphAmdGpu {
ims = append(ims, m.PodCphAmdGpu[i])
}
for _, c := range m.Containers {
ims = append(ims, c.ContainerCpu)
ims = append(ims, c.ContainerMemory)
if c.ContainerProcess != nil {
ims = append(ims, c.ContainerProcess)
}
for _, cd := range c.ContainerDiskIos {
ims = append(ims, cd)
}
}
res := []string{}
for _, im := range ims {
tagMap := im.GetTag()
newTagStr := tagStr
if len(tagMap) != 0 {
var newTagArr []string
for k, v := range tagMap {
newTagArr = append(newTagArr, fmt.Sprintf("%s=%s", k, v))
}
newTagStr = strings.Join([]string{tagStr, strings.Join(newTagArr, ",")}, ",")
}
res = append(res, fmt.Sprintf("%s,%s %s", im.GetName(), newTagStr, d.mapToStatStr(im.ToMap())))
if imu, ok := im.(iPodUniformName); ok {
if un := imu.GetUniformName(); un != "" {
res = append(res, fmt.Sprintf("%s,%s %s", un, newTagStr, d.mapToStatStr(im.ToMap())))
}
}
}
res = append(res, d.netioToTelegrafData("pod_netio", tagStr)...)
return res
}