Skip to content

Instantly share code, notes, and snippets.

@maurorappa
Created February 26, 2020 10:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maurorappa/47c917f945a9cb25b047e097c2b98f30 to your computer and use it in GitHub Desktop.
Save maurorappa/47c917f945a9cb25b047e097c2b98f30 to your computer and use it in GitHub Desktop.
docker-stats: get memory stats from cgroups
package main
import (
"context"
"flag"
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
//"github.com/guptarohit/asciigraph"
"io/ioutil"
"log"
"os"
"sort"
"strconv"
"strings"
"time"
)
const cgroupPath = "/sys/fs/cgroup/memory/docker/"
const logPath = "/tmp"
var (
stats = map[string]int{}
runningContainers = map[string]int{}
deadContainers = map[string]int{}
verbose bool
ancestor string
)
func main() {
interval := flag.Int("i", 5, "update interval")
flag.StringVar(&ancestor, "a", "", "coma separated image to filter")
flag.BoolVar(&verbose, "v", false, "verbose")
//saveStatFile := flag.Bool("s",true, "save container stats in /tmp")
flag.Parse()
if verbose {
log.Printf("\nScanning Docker containers every %d seconds\n", *interval)
}
if *interval > 0 {
//set a x seconds ticker
ticker := time.NewTicker(time.Duration(*interval) * time.Second)
go func() {
for _ = range ticker.C {
dockerStats()
//generateGraphs()
}
}()
}
select {}
}
func dockerStats() {
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
panic(err)
}
filters := filters.NewArgs()
filters.Add("status","running")
if ancestor != "" {
images := strings.Split(string(ancestor), ",")
if len(images) == 0 && verbose {
fmt.Printf("no images specified! separate by a comma, please\n")
}
for _, img := range images {
filters.Add("ancestor", img)
}
}
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{
Size: true,
All: false,
Since: "container",
Filters: filters,
})
if err != nil {
panic(err)
}
cli.Close()
if len(containers)==0 && verbose {
fmt.Printf("no container based on the requested image\n")
}
for _, container := range containers {
//fmt.Printf("%s %s\n", container.ID, container.Image)
if _, fresh := runningContainers[container.ID]; !fresh {
runningContainers[container.ID] = 1
memContainer(container.ID, true)
} else {
memContainer(container.ID, false)
}
}
var found bool
//find stopped containers
for id, _ := range runningContainers {
found = false
if verbose { fmt.Printf("checking %s\n", id[:10]) }
for _, containerID := range containers {
if id == containerID.ID {
found = true
break
}
}
if !found {
fmt.Printf("dead!\n")
delete(runningContainers, id)
}
}
}
func memContainer(id string, header bool) {
if verbose {
log.Printf("\n\nStats for %s", id)
}
content, err := ioutil.ReadFile(cgroupPath + id + "/memory.stat")
// see https://crate.io/a/analyzing-docker-container-performance-native-tools
if err != nil {
//Do something
}
lines := strings.Split(string(content), "\n")
keys := make([]string, 0, 20)
// fmt.Printf("%q\n", lines)
for _, line := range lines {
info := strings.Split(string(line), " ")
// check for useful lines
if len(info) < 2 {
continue
}
// we only care about total container stats
if len(info[0]) > 7 && info[0][:6] == "total_" {
num, _ := strconv.Atoi(info[1])
stats[info[0][6:]] = num
keys = append(keys, info[0][6:])
}
}
sort.Strings(keys)
filename := logPath + "/" + id[:10] + ".csv"
csv, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
now := time.Now().Unix()
defer csv.Close()
if err != nil {
panic(err)
}
allInfo := ""
line := ""
if header {
head := "time,"
for _, k := range keys {
line = fmt.Sprintf("%s,", k)
head = head + line
}
head = head + "\n"
_, err = csv.WriteString(head)
if err != nil {
panic(err)
}
//fmt.Println(head+"\n")
}
allInfo = strconv.FormatInt(now, 10) + ","
for _, k := range keys {
//line := fmt.Sprintf("%s: %d\n",k,stats[k])
line = fmt.Sprintf("%d,", stats[k])
allInfo = allInfo + line
}
allInfo = allInfo + "\n"
if verbose { fmt.Println(allInfo) }
_, err = csv.WriteString(allInfo)
if err != nil {
panic(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment