Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support cgroup v2 #5

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,12 @@ module github.com/groove-x/cgroup-exporter
go 1.11

require (
github.com/Microsoft/go-winio v0.4.14 // indirect
github.com/containerd/cgroups v0.0.0-20181001140508-d2400726cfa7
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d // indirect
github.com/Microsoft/go-winio v0.4.16 // indirect
github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/docker v1.13.1
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.3.4-0.20181018102642-03db2b60b820 // indirect
github.com/godbus/dbus v4.1.1-0.20180905195443-5f1bd775722e+incompatible // indirect
github.com/gogo/protobuf v1.2.0 // indirect
github.com/moby/moby v1.13.1
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/runtime-spec v1.0.2-0.20180913141938-5806c3563733 // indirect
golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect
)
76 changes: 49 additions & 27 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/containerd/cgroups v0.0.0-20181001140508-d2400726cfa7 h1:HpTs4G3F32Jue3Hu3Tc4hD4d9AhALxYE4+vCO0SkSvw=
github.com/containerd/cgroups v0.0.0-20181001140508-d2400726cfa7/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d h1:t5Wuyh53qYyg9eqn4BbnlIT+vmhyww0TatL+zT3uWgI=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/cilium/ebpf v0.2.0 h1:Fv93L3KKckEcEHR3oApXVzyBTDA8WAm6VXhPE00N3f8=
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68 h1:hkGVFjz+plgr5UfxZUTPFbUFIF/Km6/s+RVRIRHLrrY=
github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
github.com/coreos/go-systemd/v22 v22.1.0 h1:kq/SbG2BCKLkDKkjQf5OWwKWUKj1lgs3lFI4PxnR5lg=
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
Expand All @@ -12,36 +17,53 @@ github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.3.4-0.20181018102642-03db2b60b820 h1:EQSiSOsNbDAIw+kdd4NPThBl+510eNrB58SRYcKwUK4=
github.com/docker/go-units v0.3.4-0.20181018102642-03db2b60b820/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/godbus/dbus v4.1.1-0.20180905195443-5f1bd775722e+incompatible h1:1EAOqHEDzmRZ+SWNSMZ3nnczBDhMT1jIfE3QZ2iHZ9s=
github.com/godbus/dbus v4.1.1-0.20180905195443-5f1bd775722e+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/moby/moby v1.13.1 h1:mC5WwQwCXt/dYxZ1cIrRsnJAWw7VdtcTZUIGr4tXzOM=
github.com/moby/moby v1.13.1/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/runtime-spec v1.0.2-0.20180913141938-5806c3563733 h1:M9SVV7xezQ8PC/0NPAek6TUr1IdwVx5wGkdojnXwalM=
github.com/opencontainers/runtime-spec v1.0.2-0.20180913141938-5806c3563733/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
149 changes: 118 additions & 31 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import (
"time"

"github.com/containerd/cgroups"
v1 "github.com/containerd/cgroups/stats/v1"
v2 "github.com/containerd/cgroups/v2"
"github.com/containerd/cgroups/v2/stats"
"github.com/docker/docker/api/types"
"github.com/moby/moby/client"
)
Expand All @@ -41,21 +44,7 @@ func main() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)

system, err := cgroups.Load(subsystem, cgroups.StaticPath(*cgroupPath))
if err != nil {
log.Fatalf("cgroups load: %s", err)
}

var dockerClient *client.Client
if *enableDocker {
dockerClient, err = client.NewEnvClient()
if err != nil {
log.Fatalf("%v", err)
}
defer dockerClient.Close()
}

http.HandleFunc("/metrics", exportMetrics(system, dockerClient))
http.HandleFunc("/metrics", exportMetrics(*enableDocker))

server := &http.Server{
Addr: *address,
Expand Down Expand Up @@ -113,18 +102,26 @@ func subsystem() ([]cgroups.Subsystem, error) {
return s, nil
}

type cgroupMetrics struct {
*cgroups.Metrics
type cgroupV1Metrics struct {
*v1.Metrics
Process *ProcessStats `json:"process_stats"`
}

func statsCgroups(ctx context.Context, system cgroups.Cgroup) (map[string]*cgroupMetrics, error) {
func statsCgroupsV1(ctx context.Context) (map[string]*cgroupV1Metrics, error) {
if cgroups.Mode() == cgroups.Unified {
return map[string]*cgroupV1Metrics{}, nil
}
system, err := cgroups.Load(subsystem, cgroups.StaticPath(*cgroupPath))
if err != nil {
log.Fatalf("cgroups load: %s", err)
}

processes, err := system.Processes(cgroups.Devices, true)
if err != nil {
return nil, fmt.Errorf("cgroups load: %s", err)
}

groups := make(map[string]*cgroupMetrics, len(processes))
groups := make(map[string]*cgroupV1Metrics, len(processes))
for _, p := range processes {
name := strings.TrimPrefix(p.Path, "/sys/fs/cgroup/devices")
name = strings.TrimSuffix(name, "/")
Expand All @@ -145,25 +142,72 @@ func statsCgroups(ctx context.Context, system cgroups.Cgroup) (map[string]*cgrou
continue
}
ps := processStats(p.Pid)
groups[name] = &cgroupMetrics{
groups[name] = &cgroupV1Metrics{
Metrics: stats,
Process: ps,
}
}
return groups, nil
}

func gatherAllDirs(basepath, path string) []string {
dirs := []string{path}

files, err := ioutil.ReadDir(basepath + "/" + path)

if err != nil {
log.Printf("gatherAllDirs ReadDir: %s", err)
return dirs
}

for _, f := range files {
if f.IsDir() {
dirs = append(dirs, gatherAllDirs(basepath, path+"/"+f.Name())...)
}
}
return dirs
}

func statsCgroupsV2(ctx context.Context) (map[string]*stats.Metrics, error) {
if cgroups.Mode() != cgroups.Unified {
return map[string]*stats.Metrics{}, nil
}
cgroupsMountpoint := "/sys/fs/cgroup"
allCgNames := gatherAllDirs(cgroupsMountpoint, *cgroupPath)
allCgNames = append(allCgNames, *cgroupPath)

groups := make(map[string]*stats.Metrics, len(allCgNames))

for _, cgName := range allCgNames {
manager, err := v2.LoadManager(cgroupsMountpoint, cgName)
if err != nil {
log.Printf("cgroupsv2 load manager %s: %s", cgName, err)
continue
}
stats, err := manager.Stat()
if err != nil {
log.Printf("cgroupsv2 stat %s: %s", cgName, err)
continue
}
groups[cgName] = stats
}
return groups, nil
}

type dockerStats struct {
CPU types.CPUStats `json:"cpu_stats,omitempty"`
PreCPU types.CPUStats `json:"precpu_stats,omitempty"` // "Pre"="Previous"
Memory types.MemoryStats `json:"memory_stats,omitempty"`
Process *ProcessStats `json:"process_stats"`
}

func statsDockerContainers(ctx context.Context, dockerClient *client.Client) (map[string]dockerStats, error) {
if dockerClient == nil {
return nil, nil
func statsDockerContainers(ctx context.Context) (map[string]dockerStats, error) {
dockerClient, err := client.NewEnvClient()
if err != nil {
log.Fatalf("%v", err)
}
defer dockerClient.Close()

containers, err := dockerClient.ContainerList(ctx, types.ContainerListOptions{
All: true,
Limit: 0,
Expand Down Expand Up @@ -198,47 +242,90 @@ func statsDockerContainers(ctx context.Context, dockerClient *client.Client) (ma
return dockerContainers, nil
}

func exportMetrics(system cgroups.Cgroup, dockerClient *client.Client) func(w http.ResponseWriter, r *http.Request) {
func exportMetrics(enableDocker bool) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

groups, err := statsCgroups(ctx, system)
groupsV1, err := statsCgroupsV1(ctx)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

dockerContainers, err := statsDockerContainers(ctx, dockerClient)
groupsV2, err := statsCgroupsV2(ctx)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

var dockerContainers = make(map[string]dockerStats)
if enableDocker {
dockerContainers, err = statsDockerContainers(ctx)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}

fmt.Fprintln(w, `# HELP container_cpu_user_seconds_total Cumulative user cpu time consumed in seconds.
# TYPE container_cpu_user_seconds_total counter`)
for name, stats := range groups {
for name, stats := range groupsV1 {
fmt.Fprintf(w, `container_cpu_user_seconds_total{id=%s} %.2f`, strconv.Quote(name), float64(stats.CPU.Usage.User)/1000000000.0)
fmt.Fprintln(w)
}
for name, stats := range groupsV2 {
fmt.Fprintf(w, `container_cpu_user_seconds_total{id=%s} %.2f`, strconv.Quote(name), float64(stats.CPU.UserUsec)/1000000.0)
fmt.Fprintln(w)
}
for name, stats := range dockerContainers {
fmt.Fprintf(w, `container_cpu_user_seconds_total{id=%s} %.2f`, strconv.Quote(name), float64(stats.CPU.CPUUsage.UsageInUsermode)/1000000000.0)
fmt.Fprintln(w)
}

fmt.Fprintln(w, `# HELP container_cpu_seconds_total Cumulative cpu time consumed in seconds.
# TYPE container_cpu_seconds_total counter`)
for name, stats := range groupsV1 {
fmt.Fprintf(w, `container_cpu_seconds_total{id=%s} %.2f`, strconv.Quote(name), float64(stats.CPU.Usage.Total)/1000000000.0)
fmt.Fprintln(w)
}
for name, stats := range groupsV2 {
fmt.Fprintf(w, `container_cpu_seconds_total{id=%s} %.2f`, strconv.Quote(name), float64(stats.CPU.UsageUsec)/1000000.0)
fmt.Fprintln(w)
}
for name, stats := range dockerContainers {
fmt.Fprintf(w, `container_cpu_seconds_total{id=%s} %.2f`, strconv.Quote(name), float64(stats.CPU.CPUUsage.TotalUsage)/1000000000.0)
fmt.Fprintln(w)
}

fmt.Fprintln(w, `# HELP container_memory_usage_bytes Current memory usage in bytes, including all memory regardless of when it was accessed
# TYPE container_memory_usage_bytes gauge`)
for name, stats := range groups {
for name, stats := range groupsV1 {
fmt.Fprintf(w, `container_memory_usage_bytes{id=%s} %d`, strconv.Quote(name), stats.Memory.Usage.Usage)
fmt.Fprintln(w)
}
for name, stats := range groupsV2 {
fmt.Fprintf(w, `container_memory_usage_bytes{id=%s} %d`, strconv.Quote(name), stats.Memory.Usage)
fmt.Fprintln(w)
}
for name, stats := range dockerContainers {
fmt.Fprintf(w, `container_memory_usage_bytes{id=%s} %d`, strconv.Quote(name), stats.Memory.Usage)
fmt.Fprintln(w)
}

fmt.Fprintln(w, `# HELP container_memsw_usage_bytes Current memory+swap usage in bytes
# TYPE container_memsw_usage_bytes gauge`)
for name, stats := range groupsV1 {
fmt.Fprintf(w, `container_memsw_usage_bytes{id=%s} %d`, strconv.Quote(name), stats.Memory.Swap.Usage)
fmt.Fprintln(w)
}
for name, stats := range groupsV2 {
fmt.Fprintf(w, `container_memsw_usage_bytes{id=%s} %d`, strconv.Quote(name), stats.Memory.Usage+stats.Memory.SwapUsage)
fmt.Fprintln(w)
}

fmt.Fprintln(w, `# HELP container_memory_rss Size of RSS in bytes.
# TYPE container_memory_rss gauge`)
for name, stats := range groups {
for name, stats := range groupsV1 {
fmt.Fprintf(w, `container_memory_rss{id=%s} %d`, strconv.Quote(name), stats.Memory.RSS)
fmt.Fprintln(w)
}
Expand All @@ -249,7 +336,7 @@ func exportMetrics(system cgroups.Cgroup, dockerClient *client.Client) func(w ht

fmt.Fprintln(w, `# HELP container_open_fds Number of open file descriptors
# TYPE container_open_fds gauge`)
for name, stats := range groups {
for name, stats := range groupsV1 {
if stats.Process == nil {
continue
}
Expand All @@ -266,7 +353,7 @@ func exportMetrics(system cgroups.Cgroup, dockerClient *client.Client) func(w ht

fmt.Fprintln(w, `# HELP container_open_sockets Number of open sockets
# TYPE container_open_sockets gauge`)
for name, stats := range groups {
for name, stats := range groupsV1 {
if stats.Process == nil {
continue
}
Expand Down