package docker import ( "context" "encoding/json" "fmt" "time" "github.com/docker/docker/api/types" ) // ContainerStats 容器统计数据结构 type ContainerStats struct { CPUPercentage float64 `json:"cpu_percent"` // CPU 使用百分比 MemoryUsage int64 `json:"memory_usage"` // 内存使用量(字节) MemoryLimit int64 `json:"memory_limit"` // 内存限制(字节) MemoryPercentage float64 `json:"memory_percent"` // 内存使用百分比 NetworkRx int64 `json:"network_rx"` // 网络接收(字节) NetworkTx int64 `json:"network_tx"` // 网络发送(字节) BlockRead int64 `json:"block_read"` // 块设备读取(字节) BlockWrite int64 `json:"block_write"` // 块设备写入(字节) PID int `json:"pid"` // 进程ID Status string `json:"status"` // 容器状态 Uptime string `json:"uptime"` // 运行时间 } // GetStats 获取容器的实时统计数据 func GetStats(ctx context.Context, containerID string) (*ContainerStats, error) { if containerID == "" { return nil, fmt.Errorf("容器ID不能为空") } cli := GetClient() if cli == nil { return nil, fmt.Errorf("Docker客户端未初始化") } // 获取容器详细信息 inspect, err := cli.ContainerInspect(ctx, containerID) if err != nil { return nil, fmt.Errorf("无法获取容器信息: %w", err) } // 获取容器状态 status := "stopped" if inspect.State.Running { status = "running" } else if inspect.State.Paused { status = "paused" } else if inspect.State.Restarting { status = "restarting" } // 计算运行时间 uptime := "0s" if inspect.State.Running && inspect.State.StartedAt != "" { startTime, err := time.Parse(time.RFC3339, inspect.State.StartedAt) if err == nil { duration := time.Since(startTime) uptime = formatDuration(duration) } } // 如果容器未运行,返回基本信息 if !inspect.State.Running { return &ContainerStats{ Status: status, Uptime: uptime, PID: inspect.State.Pid, }, nil } // 获取容器实时统计数据 stats, err := cli.ContainerStats(ctx, containerID, false) // 不需要持续流式数据 if err != nil { return nil, fmt.Errorf("获取容器统计数据失败: %w", err) } defer stats.Body.Close() // 解析统计数据JSON var statsJSON types.StatsJSON if err := json.NewDecoder(stats.Body).Decode(&statsJSON); err != nil { return nil, fmt.Errorf("解析容器统计数据失败: %w", err) } result := &ContainerStats{ Status: status, Uptime: uptime, PID: inspect.State.Pid, } // 计算CPU使用百分比 result.CPUPercentage = calculateCPUPercentage(&statsJSON) // 设置内存使用情况 result.MemoryUsage = getMemoryUsage(&statsJSON) result.MemoryLimit = getMemoryLimit(&statsJSON) result.MemoryPercentage = calculateMemoryPercentage(&statsJSON) // 获取网络数据 networkStats := getNetworkStats(&statsJSON) result.NetworkRx = networkStats.rx result.NetworkTx = networkStats.tx // 获取I/O数据 ioStats := getIOStats(&statsJSON) result.BlockRead = ioStats.read result.BlockWrite = ioStats.write return result, nil } // 计算CPU使用百分比 func calculateCPUPercentage(stats *types.StatsJSON) float64 { if stats == nil { return 0.0 } // CPU使用率计算公式 = (CPU使用时间 / CPU总时间) * 核心数 * 100% cpuDelta := float64(stats.CPUStats.CPUUsage.TotalUsage) - float64(stats.PreCPUStats.CPUUsage.TotalUsage) systemDelta := float64(stats.CPUStats.SystemUsage) - float64(stats.PreCPUStats.SystemUsage) numCPUs := uint32(1) // 默认为1个CPU if len(stats.CPUStats.CPUUsage.PercpuUsage) > 0 { numCPUs = uint32(len(stats.CPUStats.CPUUsage.PercpuUsage)) } if systemDelta > 0 && cpuDelta > 0 { cpuPercent := (cpuDelta / systemDelta) * float64(numCPUs) * 100.0 return roundToTwo(cpuPercent) } return 0.0 } // 获取内存使用量 func getMemoryUsage(stats *types.StatsJSON) int64 { if stats == nil || stats.MemoryStats.Usage == 0 { return 0 } // 某些环境下,需要减去缓存 if stats.MemoryStats.Stats != nil { if cache, ok := stats.MemoryStats.Stats["cache"]; ok { return int64(stats.MemoryStats.Usage - cache) // 转换为int64 } } return int64(stats.MemoryStats.Usage) // 转换为int64 } // 获取内存限制 func getMemoryLimit(stats *types.StatsJSON) int64 { if stats == nil || stats.MemoryStats.Limit == 0 { return 0 } return int64(stats.MemoryStats.Limit) // 转换为int64 } // 计算内存使用百分比 func calculateMemoryPercentage(stats *types.StatsJSON) float64 { if stats == nil { return 0.0 } usage := getMemoryUsage(stats) limit := getMemoryLimit(stats) if limit > 0 && usage > 0 { memPercent := float64(usage) / float64(limit) * 100.0 return roundToTwo(memPercent) } return 0.0 } // 网络统计结构 type networkStats struct { rx int64 tx int64 } // 获取网络统计数据 func getNetworkStats(stats *types.StatsJSON) networkStats { if stats == nil || len(stats.Networks) == 0 { return networkStats{} } var rx, tx int64 for _, network := range stats.Networks { rx += int64(network.RxBytes) // 转换为int64 tx += int64(network.TxBytes) // 转换为int64 } return networkStats{rx: rx, tx: tx} } // IO统计结构 type ioStats struct { read int64 write int64 } // 获取IO统计数据 func getIOStats(stats *types.StatsJSON) ioStats { if stats == nil { return ioStats{} } var read, write int64 if len(stats.BlkioStats.IoServiceBytesRecursive) > 0 { for _, blkio := range stats.BlkioStats.IoServiceBytesRecursive { switch blkio.Op { case "Read": read += int64(blkio.Value) // 转换为int64 case "Write": write += int64(blkio.Value) // 转换为int64 } } } return ioStats{read: read, write: write} } // 四舍五入到两位小数 func roundToTwo(num float64) float64 { if num < 0 { return 0.0 // 避免负值 } return float64(int(num*100)) / 100 } // 格式化时间持续 func formatDuration(d time.Duration) string { if d < 0 { d = 0 // 避免负值 } days := int(d.Hours() / 24) hours := int(d.Hours()) % 24 minutes := int(d.Minutes()) % 60 seconds := int(d.Seconds()) % 60 if days > 0 { return fmt.Sprintf("%dd %dh %dm", days, hours, minutes) } if hours > 0 { return fmt.Sprintf("%dh %dm", hours, minutes) } if minutes > 0 { return fmt.Sprintf("%dm %ds", minutes, seconds) } return fmt.Sprintf("%ds", seconds) }