first commit
This commit is contained in:
146
apis/docker/docker.go
Normal file
146
apis/docker/docker.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"ArinDash/util"
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
type ContainerMetrics struct {
|
||||
ID string
|
||||
Name string
|
||||
Image string
|
||||
Status string
|
||||
CPUPercent float64
|
||||
MemUsage uint64
|
||||
MemLimit uint64
|
||||
MemPercent float64
|
||||
NetRx uint64
|
||||
NetTx uint64
|
||||
BlockRead uint64
|
||||
BlockWrite uint64
|
||||
PidsCurrent uint64
|
||||
}
|
||||
|
||||
func newDockerClient() (*client.Client, error) {
|
||||
return client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||
}
|
||||
|
||||
func FetchDockerMetrics(ctx context.Context) ([]ContainerMetrics, error) {
|
||||
cli, err := newDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
_, err = cli.Ping(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
containers, err := cli.ContainerList(ctx, containertypes.ListOptions{All: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make([]ContainerMetrics, 0, len(containers))
|
||||
for _, c := range containers {
|
||||
m := ContainerMetrics{
|
||||
ID: c.ID,
|
||||
Image: c.Image,
|
||||
Name: containerName(c.Names),
|
||||
Status: c.Status,
|
||||
}
|
||||
|
||||
stats, err := cli.ContainerStats(ctx, c.ID, false)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
func() {
|
||||
defer stats.Body.Close()
|
||||
var sj containertypes.StatsResponse
|
||||
if err := util.DecodeJSON(stats.Body, &sj); err != nil {
|
||||
return
|
||||
}
|
||||
m.CPUPercent = cpuPercentFromStats(sj)
|
||||
m.MemUsage, m.MemLimit, m.MemPercent = memoryFromStats(sj)
|
||||
m.NetRx, m.NetTx = networkTotals(sj)
|
||||
m.BlockRead, m.BlockWrite = blockIOTotals(sj)
|
||||
m.PidsCurrent = sj.PidsStats.Current
|
||||
}()
|
||||
|
||||
out = append(out, m)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func containerName(names []string) string {
|
||||
for _, n := range names {
|
||||
n = strings.TrimSpace(n)
|
||||
n = strings.TrimPrefix(n, "/")
|
||||
if n != "" {
|
||||
return n
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func cpuPercentFromStats(s containertypes.StatsResponse) float64 {
|
||||
// Protect against zero or missing data.
|
||||
if s.PreCPUStats.SystemUsage == 0 || s.CPUStats.SystemUsage == 0 {
|
||||
return 0
|
||||
}
|
||||
cpuDelta := float64(s.CPUStats.CPUUsage.TotalUsage - s.PreCPUStats.CPUUsage.TotalUsage)
|
||||
systemDelta := float64(s.CPUStats.SystemUsage - s.PreCPUStats.SystemUsage)
|
||||
if systemDelta <= 0 || cpuDelta < 0 {
|
||||
return 0
|
||||
}
|
||||
// Number of CPUs available to the container.
|
||||
numCPU := float64(len(s.CPUStats.CPUUsage.PercpuUsage))
|
||||
if numCPU == 0 {
|
||||
numCPU = 1
|
||||
}
|
||||
return (cpuDelta / systemDelta) * numCPU * 100.0
|
||||
}
|
||||
|
||||
func memoryFromStats(s containertypes.StatsResponse) (usage, limit uint64, percent float64) {
|
||||
usage = s.MemoryStats.Usage
|
||||
limit = s.MemoryStats.Limit
|
||||
// Optionally discount cached memory if stats present.
|
||||
if stats := s.MemoryStats.Stats; stats != nil {
|
||||
// The common Linux approach: usage - cache
|
||||
if cache, ok := stats["cache"]; ok && cache <= usage {
|
||||
usage -= cache
|
||||
}
|
||||
}
|
||||
if limit > 0 {
|
||||
percent = (float64(usage) / float64(limit)) * 100
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func networkTotals(s containertypes.StatsResponse) (rx, tx uint64) {
|
||||
if s.Networks == nil {
|
||||
return 0, 0
|
||||
}
|
||||
for _, v := range s.Networks {
|
||||
rx += v.RxBytes
|
||||
tx += v.TxBytes
|
||||
}
|
||||
return rx, tx
|
||||
}
|
||||
|
||||
func blockIOTotals(s containertypes.StatsResponse) (read, write uint64) {
|
||||
for _, e := range s.BlkioStats.IoServiceBytesRecursive {
|
||||
switch strings.ToLower(e.Op) {
|
||||
case "read":
|
||||
read += e.Value
|
||||
case "write":
|
||||
write += e.Value
|
||||
}
|
||||
}
|
||||
return read, write
|
||||
}
|
||||
Reference in New Issue
Block a user