1
0
mirror of https://github.com/bingohuang/docker-labs.git synced 2025-07-15 02:37:27 +08:00

Show cpu and memory stats of every node

This commit is contained in:
Jonathan Leibiusky @xetorthio 2016-11-18 13:52:47 -03:00
parent bc7dbead33
commit 8b0749a9ba
6 changed files with 121 additions and 7 deletions

View File

@ -1,6 +1,7 @@
package services package services
import ( import (
"io"
"log" "log"
"strings" "strings"
@ -28,6 +29,12 @@ func init() {
} }
func GetContainerStats(id string) (io.ReadCloser, error) {
stats, err := c.ContainerStats(context.Background(), id, true)
return stats.Body, err
}
func GetContainerInfo(id string) (types.ContainerJSON, error) { func GetContainerInfo(id string) (types.ContainerJSON, error) {
return c.ContainerInspect(context.Background(), id) return c.ContainerInspect(context.Background(), id)
} }

View File

@ -2,6 +2,8 @@ package services
import ( import (
"context" "context"
"encoding/json"
"fmt"
"io" "io"
"log" "log"
"os" "os"
@ -10,6 +12,7 @@ import (
"golang.org/x/text/encoding" "golang.org/x/text/encoding"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
units "github.com/docker/go-units"
) )
var rw sync.Mutex var rw sync.Mutex
@ -21,6 +24,7 @@ type Instance struct {
IP string `json:"ip"` IP string `json:"ip"`
conn *types.HijackedResponse `json:"-"` conn *types.HijackedResponse `json:"-"`
ctx context.Context `json:"-"` ctx context.Context `json:"-"`
statsReader io.ReadCloser `json:"-"`
} }
func (i *Instance) IsConnected() bool { func (i *Instance) IsConnected() bool {
@ -70,6 +74,9 @@ func NewInstance(session *Session) (*Instance, error) {
wsServer.BroadcastTo(session.Id, "new instance", instance.Name, instance.IP, instance.Hostname) wsServer.BroadcastTo(session.Id, "new instance", instance.Name, instance.IP, instance.Hostname)
// Start collecting stats
go instance.CollectStats()
return instance, nil return instance, nil
} }
@ -82,6 +89,67 @@ func (s *sessionWriter) Write(p []byte) (n int, err error) {
return len(p), nil return len(p), nil
} }
func (o *Instance) CollectStats() {
reader, err := GetContainerStats(o.Hostname)
if err != nil {
log.Println("Error while trying to collect instance stats", err)
return
}
o.statsReader = reader
dec := json.NewDecoder(o.statsReader)
var (
mem = 0.0
memLimit = 0.0
memPercent = 0.0
v *types.StatsJSON
memFormatted = ""
cpuPercent = 0.0
previousCPU uint64
previousSystem uint64
cpuFormatted = ""
)
for {
e := dec.Decode(&v)
if e != nil {
break
}
// Memory
if v.MemoryStats.Limit != 0 {
memPercent = float64(v.MemoryStats.Usage) / float64(v.MemoryStats.Limit) * 100.0
}
mem = float64(v.MemoryStats.Usage)
memLimit = float64(v.MemoryStats.Limit)
memFormatted = fmt.Sprintf("%.2f%% (%s / %s)", memPercent, units.BytesSize(mem), units.BytesSize(memLimit))
// cpu
previousCPU = v.PreCPUStats.CPUUsage.TotalUsage
previousSystem = v.PreCPUStats.SystemUsage
cpuPercent = calculateCPUPercentUnix(previousCPU, previousSystem, v)
cpuFormatted = fmt.Sprintf("%.2f%%", cpuPercent)
wsServer.BroadcastTo(o.session.Id, "instance stats", o.Name, memFormatted, cpuFormatted)
}
}
func calculateCPUPercentUnix(previousCPU, previousSystem uint64, v *types.StatsJSON) float64 {
var (
cpuPercent = 0.0
// calculate the change for the cpu usage of the container in between readings
cpuDelta = float64(v.CPUStats.CPUUsage.TotalUsage) - float64(previousCPU)
// calculate the change for the entire system between readings
systemDelta = float64(v.CPUStats.SystemUsage) - float64(previousSystem)
)
if systemDelta > 0.0 && cpuDelta > 0.0 {
cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CPUStats.CPUUsage.PercpuUsage)) * 100.0
}
return cpuPercent
}
func (i *Instance) ResizeTerminal(cols, rows uint) error { func (i *Instance) ResizeTerminal(cols, rows uint) error {
return ResizeConnection(i.Name, cols, rows) return ResizeConnection(i.Name, cols, rows)
} }
@ -111,6 +179,11 @@ func GetInstance(session *Session, name string) *Instance {
return session.Instances[name] return session.Instances[name]
} }
func DeleteInstance(session *Session, instance *Instance) error { func DeleteInstance(session *Session, instance *Instance) error {
// stop collecting stats
if instance.statsReader != nil {
instance.statsReader.Close()
}
//TODO: Use redis //TODO: Use redis
delete(session.Instances, instance.Name) delete(session.Instances, instance.Name)
err := DeleteContainer(instance.Name) err := DeleteContainer(instance.Name)

View File

@ -144,6 +144,13 @@ func LoadSessionsFromDisk() error {
for _, s := range sessions { for _, s := range sessions {
timeLeft := s.ExpiresAt.Sub(time.Now()) timeLeft := s.ExpiresAt.Sub(time.Now())
CloseSessionAfter(s, timeLeft) CloseSessionAfter(s, timeLeft)
// start collecting stats for every instance
for _, i := range s.Instances {
// wire the session back to the instance
i.session = s
go i.CollectStats()
}
} }
} }
file.Close() file.Close()

View File

@ -132,6 +132,12 @@
$scope.connected = true; $scope.connected = true;
}); });
socket.on('instance stats', function(name, mem, cpu) {
$scope.idx[name].mem = mem;
$scope.idx[name].cpu = cpu;
$scope.$apply();
});
$scope.socket = socket; $scope.socket = socket;
var i = response.data; var i = response.data;

View File

@ -46,3 +46,10 @@ md-card-content.terminal {
.disconnected { .disconnected {
background-color: #FDF4B6; background-color: #FDF4B6;
} }
md-input-container {
margin-bottom: 0;
}
md-input-container .md-errors-spacer {
height: 0;
min-height: 0;
}

View File

@ -2,6 +2,7 @@
<html ng-app="DockerPlay" ng-controller="PlayController"> <html ng-app="DockerPlay" ng-controller="PlayController">
<head> <head>
<title>Docker Playground</title> <title>Docker Playground</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic" />
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/angular_material/1.1.0/angular-material.min.css"> <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/angular_material/1.1.0/angular-material.min.css">
<link rel="stylesheet" href="/assets/xterm.css" /> <link rel="stylesheet" href="/assets/xterm.css" />
<link rel="stylesheet" href="/assets/style.css" /> <link rel="stylesheet" href="/assets/style.css" />
@ -64,7 +65,20 @@
<md-card-title> <md-card-title>
<md-card-title-text> <md-card-title-text>
<span class="md-headline">{{instance.name}}</span> <span class="md-headline">{{instance.name}}</span>
<span class="md-subhead">{{instance.ip}}</span> <md-input-container class="md-icon-float md-block">
<label>IP</label>
<input ng-model="instance.ip" type="text" readonly="readonly">
</md-input-container>
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm>
<label>Memory</label>
<input ng-model="instance.mem" type="text" readonly="readonly">
</md-input-container>
<md-input-container class="md-block" flex-gt-sm>
<label>CPU</label>
<input ng-model="instance.cpu" type="text" readonly="readonly">
</md-input-container>
</div>
<md-card-title-text> <md-card-title-text>
</md-card-title> </md-card-title>
<md-card-actions> <md-card-actions>