97 lines
2.5 KiB
Go
97 lines
2.5 KiB
Go
package docker
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"time"
|
||
|
||
"github.com/docker/docker/api/types/container"
|
||
)
|
||
|
||
// Stats — snapshot metrik kontainer (kumulatif untuk net/disk; app menghitung
|
||
// laju dari selisih antar snapshot).
|
||
type Stats struct {
|
||
CPUPercent float64
|
||
MemUsed uint64 // usage − cache (seperti `docker stats`)
|
||
MemLimit uint64
|
||
MemPercent float64
|
||
NetRx uint64 // kumulatif
|
||
NetTx uint64 // kumulatif
|
||
BlkRead uint64 // kumulatif
|
||
BlkWrite uint64 // kumulatif
|
||
PIDs int
|
||
At time.Time
|
||
}
|
||
|
||
// StatsOnce mengambil satu snapshot statistik (one-shot, tanpa stream).
|
||
func (c *Client) StatsOnce(ctx context.Context, id string) (Stats, error) {
|
||
resp, err := c.cli.ContainerStatsOneShot(ctx, id)
|
||
if err != nil {
|
||
return Stats{}, fmt.Errorf("docker: stats: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
var raw container.StatsResponse
|
||
if err := json.NewDecoder(resp.Body).Decode(&raw); err != nil {
|
||
return Stats{}, fmt.Errorf("docker: decode stats: %w", err)
|
||
}
|
||
return computeStats(&raw), nil
|
||
}
|
||
|
||
// computeStats — rumus yang sama dengan `docker stats` (CPU dari delta
|
||
// pre/cpu-stats, memori usage − cache), dipisah agar bisa diuji tanpa daemon.
|
||
func computeStats(s *container.StatsResponse) Stats {
|
||
// CPU% = (Δcpu / Δsystem) × onlineCPU × 100
|
||
cpuDelta := float64(s.CPUStats.CPUUsage.TotalUsage) - float64(s.PreCPUStats.CPUUsage.TotalUsage)
|
||
sysDelta := float64(s.CPUStats.SystemUsage) - float64(s.PreCPUStats.SystemUsage)
|
||
online := s.CPUStats.OnlineCPUs
|
||
if online == 0 {
|
||
online = uint32(len(s.CPUStats.CPUUsage.PercpuUsage))
|
||
}
|
||
cpuPct := 0.0
|
||
if sysDelta > 0 && cpuDelta > 0 {
|
||
cpuPct = cpuDelta / sysDelta * float64(online) * 100
|
||
}
|
||
|
||
memUsed := s.MemoryStats.Usage
|
||
if cache, ok := s.MemoryStats.Stats["cache"]; ok && cache < memUsed {
|
||
memUsed -= cache
|
||
}
|
||
memPct := 0.0
|
||
if s.MemoryStats.Limit > 0 {
|
||
memPct = float64(memUsed) / float64(s.MemoryStats.Limit) * 100
|
||
}
|
||
|
||
// Net: jumlahkan semua interface (kumulatif).
|
||
var rx, tx uint64
|
||
for _, n := range s.Networks {
|
||
rx += n.RxBytes
|
||
tx += n.TxBytes
|
||
}
|
||
|
||
// Blok I/O: jumlahkan per operasi (kumulatif).
|
||
var rd, wr uint64
|
||
for _, b := range s.BlkioStats.IoServiceBytesRecursive {
|
||
switch b.Op {
|
||
case "Read", "read":
|
||
rd += b.Value
|
||
case "Write", "write":
|
||
wr += b.Value
|
||
}
|
||
}
|
||
|
||
return Stats{
|
||
CPUPercent: cpuPct,
|
||
MemUsed: memUsed,
|
||
MemLimit: s.MemoryStats.Limit,
|
||
MemPercent: memPct,
|
||
NetRx: rx,
|
||
NetTx: tx,
|
||
BlkRead: rd,
|
||
BlkWrite: wr,
|
||
PIDs: int(s.PidsStats.Current),
|
||
At: time.Now(),
|
||
}
|
||
}
|