Merge remote-tracking branch 'upstream/master'
Docker-DCO-1.1-Signed-off-by: Rohit Jnagal <jnagal@google.com> (github: rjnagal)
This commit is contained in:
commit
54fbc922a5
13
.travis.yml
13
.travis.yml
@ -1,9 +1,12 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.1
|
||||
- 1.2
|
||||
before_script:
|
||||
- go get github.com/stretchr/testify/mock
|
||||
- go get github.com/kr/pretty
|
||||
script:
|
||||
- go test -v -race github.com/google/cadvisor/container
|
||||
- go test -v -race github.com/google/cadvisor/info
|
||||
- go test -v -race github.com/google/cadvisor/client
|
||||
- go test -v -race github.com/google/cadvisor/sampling
|
||||
- go build -race github.com/google/cadvisor
|
||||
- go test -v github.com/google/cadvisor/info
|
||||
- go test -v github.com/google/cadvisor/client
|
||||
- go test -v github.com/google/cadvisor/sampling
|
||||
- go build github.com/google/cadvisor
|
||||
|
16
Dockerfile
Normal file
16
Dockerfile
Normal file
@ -0,0 +1,16 @@
|
||||
FROM google/golang-runtime
|
||||
MAINTAINER dengnan@google.com vmarmol@google.com proppy@google.com
|
||||
|
||||
# TODO(vmarmol): Build from source.
|
||||
# Get lmctfy and its dependencies.
|
||||
RUN apt-get update -y --force-yes && apt-get install -y --no-install-recommends --force-yes pkg-config libapparmor1
|
||||
ADD http://storage.googleapis.com/cadvisor-bin/lmctfy/libre2.so.0.0.0 /usr/lib/libre2.so.0
|
||||
ADD http://storage.googleapis.com/cadvisor-bin/lmctfy/lmctfy /usr/bin/lmctfy
|
||||
RUN chmod +x /usr/bin/lmctfy
|
||||
|
||||
# Install libprotobuf8.
|
||||
ADD http://storage.googleapis.com/cadvisor-bin/lmctfy/libprotobuf8_2.5.0-9_amd64.deb /tmp/libprotobuf8_2.5.0-9_amd64.deb
|
||||
ADD http://storage.googleapis.com/cadvisor-bin/lmctfy/libc6_2.19-1_amd64.deb /tmp/libc6_2.19-1_amd64.deb
|
||||
RUN dpkg -i /tmp/libc6_2.19-1_amd64.deb /tmp/libprotobuf8_2.5.0-9_amd64.deb
|
||||
|
||||
# The image builds the app and exposes it on 8080.
|
@ -25,6 +25,7 @@ import (
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/container/docker"
|
||||
"github.com/google/cadvisor/container/lmctfy"
|
||||
"github.com/google/cadvisor/info"
|
||||
"github.com/google/cadvisor/manager"
|
||||
"github.com/google/cadvisor/pages"
|
||||
"github.com/google/cadvisor/pages/static"
|
||||
@ -92,6 +93,7 @@ func main() {
|
||||
|
||||
go containerManager.Start()
|
||||
|
||||
log.Printf("Starting cAdvisor version: %q", info.VERSION)
|
||||
log.Print("About to serve on port ", *argPort)
|
||||
|
||||
addr := fmt.Sprintf(":%v", *argPort)
|
||||
|
@ -30,6 +30,7 @@ type ListType int
|
||||
|
||||
// Interface for container operation handlers.
|
||||
type ContainerHandler interface {
|
||||
ContainerReference() (info.ContainerReference, error)
|
||||
GetSpec() (*info.ContainerSpec, error)
|
||||
GetStats() (*info.ContainerStats, error)
|
||||
ListContainers(listType ListType) ([]info.ContainerReference, error)
|
||||
|
@ -40,11 +40,11 @@ func (self *dockerFactory) NewContainerHandler(name string) (handler container.C
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
handler = &dockerContainerHandler{
|
||||
client: client,
|
||||
name: name,
|
||||
machineInfoFactory: self.machineInfoFactory,
|
||||
}
|
||||
handler, err = newDockerContainerHandler(
|
||||
client,
|
||||
name,
|
||||
self.machineInfoFactory,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -35,10 +35,43 @@ import (
|
||||
type dockerContainerHandler struct {
|
||||
client *docker.Client
|
||||
name string
|
||||
aliases []string
|
||||
machineInfoFactory info.MachineInfoFactory
|
||||
container.NoStatsSummary
|
||||
}
|
||||
|
||||
func newDockerContainerHandler(
|
||||
client *docker.Client,
|
||||
name string,
|
||||
machineInfoFactory info.MachineInfoFactory,
|
||||
) (container.ContainerHandler, error) {
|
||||
handler := &dockerContainerHandler{
|
||||
client: client,
|
||||
name: name,
|
||||
machineInfoFactory: machineInfoFactory,
|
||||
}
|
||||
if !handler.isDockerContainer() {
|
||||
return handler, nil
|
||||
}
|
||||
_, id, err := handler.splitName()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid docker container %v: %v", name, err)
|
||||
}
|
||||
ctnr, err := client.InspectContainer(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to inspect container %v: %v", name, err)
|
||||
}
|
||||
handler.aliases = append(handler.aliases, path.Join("/docker", ctnr.Name))
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
func (self *dockerContainerHandler) ContainerReference() (info.ContainerReference, error) {
|
||||
return info.ContainerReference{
|
||||
Name: self.name,
|
||||
Aliases: self.aliases,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (self *dockerContainerHandler) splitName() (string, string, error) {
|
||||
parent, id := path.Split(self.name)
|
||||
cgroupSelf, err := os.Open("/proc/self/cgroup")
|
||||
|
@ -114,7 +114,7 @@ func (self *factoryManager) NewContainerHandler(path string) (ContainerHandler,
|
||||
err := fmt.Errorf("nil factory for container %v", path)
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("container handler factory for %v is %v\n", path, factory)
|
||||
log.Printf("Container handler factory for %v is %v\n", path, factory)
|
||||
return factory.NewContainerHandler(path)
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,10 @@ type containerListFilter struct {
|
||||
NoStatsSummary
|
||||
}
|
||||
|
||||
func (self *containerListFilter) ContainerReference() (info.ContainerReference, error) {
|
||||
return self.handler.ContainerReference()
|
||||
}
|
||||
|
||||
func (self *containerListFilter) GetSpec() (*info.ContainerSpec, error) {
|
||||
return self.handler.GetSpec()
|
||||
}
|
||||
|
@ -32,6 +32,11 @@ func (self *mockContainerHandler) GetSpec() (*info.ContainerSpec, error) {
|
||||
return args.Get(0).(*info.ContainerSpec), args.Error(1)
|
||||
}
|
||||
|
||||
func (self *mockContainerHandler) ContainerReference() (info.ContainerReference, error) {
|
||||
args := self.Called()
|
||||
return args.Get(0).(info.ContainerReference), args.Error(1)
|
||||
}
|
||||
|
||||
func (self *mockContainerHandler) GetStats() (*info.ContainerStats, error) {
|
||||
args := self.Called()
|
||||
return args.Get(0).(*info.ContainerStats), args.Error(1)
|
||||
|
@ -47,6 +47,10 @@ func New(name string) (container.ContainerHandler, error) {
|
||||
return el, nil
|
||||
}
|
||||
|
||||
func (self *lmctfyContainerHandler) ContainerReference() (info.ContainerReference, error) {
|
||||
return info.ContainerReference{Name: self.Name}, nil
|
||||
}
|
||||
|
||||
func getExitCode(err error) int {
|
||||
msg, ok := err.(*exec.ExitError)
|
||||
if ok {
|
||||
|
@ -1,3 +1,17 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package container
|
||||
|
||||
import (
|
||||
|
@ -36,6 +36,10 @@ func (self *percentilesContainerHandlerWrapper) GetSpec() (*info.ContainerSpec,
|
||||
return self.handler.GetSpec()
|
||||
}
|
||||
|
||||
func (self *percentilesContainerHandlerWrapper) ContainerReference() (info.ContainerReference, error) {
|
||||
return self.handler.ContainerReference()
|
||||
}
|
||||
|
||||
func (self *percentilesContainerHandlerWrapper) updatePrevStats(stats *info.ContainerStats) {
|
||||
if stats == nil || stats.Cpu == nil || stats.Memory == nil {
|
||||
// discard incomplete stats
|
||||
|
@ -94,6 +94,12 @@ func containerWithTrace(duration time.Duration, cpuUsages []uint64, memUsages []
|
||||
}
|
||||
}
|
||||
|
||||
func (self *replayTrace) ContainerReference() (info.ContainerReference, error) {
|
||||
return info.ContainerReference{
|
||||
Name: "replay",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (self *replayTrace) GetStats() (*info.ContainerStats, error) {
|
||||
stats := new(info.ContainerStats)
|
||||
stats.Cpu = new(info.CpuStats)
|
||||
|
18
info/version.go
Normal file
18
info/version.go
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package info
|
||||
|
||||
// Version of cAdvisor.
|
||||
const VERSION = "0.1.0"
|
@ -91,7 +91,12 @@ func NewContainerData(containerName string) (*containerData, error) {
|
||||
return nil, err
|
||||
}
|
||||
cont.handler = handler
|
||||
cont.info.Name = containerName
|
||||
ref, err := handler.ContainerReference()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cont.info.Name = ref.Name
|
||||
cont.info.Aliases = ref.Aliases
|
||||
cont.info.Stats = list.New()
|
||||
cont.stop = make(chan bool, 1)
|
||||
|
||||
|
@ -124,7 +124,8 @@ func (m *manager) GetContainerInfo(containerName string) (*info.ContainerInfo, e
|
||||
// Make a copy of the info for the user.
|
||||
ret := &info.ContainerInfo{
|
||||
ContainerReference: info.ContainerReference{
|
||||
Name: cinfo.Name,
|
||||
Name: cinfo.Name,
|
||||
Aliases: cinfo.Aliases,
|
||||
},
|
||||
Subcontainers: cinfo.Subcontainers,
|
||||
Spec: cinfo.Spec,
|
||||
@ -169,9 +170,13 @@ func (m *manager) createContainer(containerName string) (*containerData, error)
|
||||
m.containersLock.Lock()
|
||||
defer m.containersLock.Unlock()
|
||||
|
||||
log.Printf("Added container: %s", containerName)
|
||||
// Add the container name and all its aliases.
|
||||
m.containers[containerName] = cont
|
||||
for _, alias := range cont.info.Aliases {
|
||||
m.containers[alias] = cont
|
||||
}
|
||||
}()
|
||||
log.Printf("Added container: %s (aliases: %s)", containerName, cont.info.Aliases)
|
||||
|
||||
// Start the container's housekeeping.
|
||||
cont.Start()
|
||||
@ -193,9 +198,12 @@ func (m *manager) destroyContainer(containerName string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove the container from our records.
|
||||
// Remove the container from our records (and all its aliases).
|
||||
delete(m.containers, containerName)
|
||||
log.Printf("Destroyed container: %s", containerName)
|
||||
for _, alias := range cont.info.Aliases {
|
||||
delete(m.containers, alias)
|
||||
}
|
||||
log.Printf("Destroyed container: %s (aliases: %s)", containerName, cont.info.Aliases)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -219,7 +227,10 @@ func (m *manager) getContainersDiff() (added []info.ContainerReference, removed
|
||||
// Determine which were added and which were removed.
|
||||
allContainersSet := make(map[string]*containerData)
|
||||
for name, d := range m.containers {
|
||||
allContainersSet[name] = d
|
||||
// Only add the canonical name.
|
||||
if d.info.Name == name {
|
||||
allContainersSet[name] = d
|
||||
}
|
||||
}
|
||||
for _, c := range allContainers {
|
||||
delete(allContainersSet, c.Name)
|
||||
|
@ -37,7 +37,6 @@ var funcMap = template.FuncMap{
|
||||
"printMask": printMask,
|
||||
"printCores": printCores,
|
||||
"printMegabytes": printMegabytes,
|
||||
"containerNameEquals": containerNameEquals,
|
||||
"getMemoryUsage": getMemoryUsage,
|
||||
"getMemoryUsagePercent": getMemoryUsagePercent,
|
||||
"getHotMemoryPercent": getHotMemoryPercent,
|
||||
@ -85,10 +84,6 @@ func containerLink(container info.ContainerReference, basenameOnly bool, cssClas
|
||||
return template.HTML(fmt.Sprintf("<a class=\"%s\" href=\"%s%s\">%s</a>", cssClasses, ContainersPage[:len(ContainersPage)-1], containerName, displayName))
|
||||
}
|
||||
|
||||
func containerNameEquals(c1 string, c2 string) bool {
|
||||
return c1 == c2
|
||||
}
|
||||
|
||||
func printMask(mask *info.CpuSpecMask, numCores int) interface{} {
|
||||
// TODO(vmarmol): Detect this correctly.
|
||||
// TODO(vmarmol): Support more than 64 cores.
|
||||
@ -130,25 +125,31 @@ func printMegabytes(bytes uint64) string {
|
||||
return strconv.FormatFloat(megabytes, 'f', 3, 64)
|
||||
}
|
||||
|
||||
func toMemoryPercent(usage uint64, spec *info.ContainerSpec) int {
|
||||
return int((usage * 100) / (spec.Memory.Limit))
|
||||
func toMemoryPercent(usage uint64, spec *info.ContainerSpec, machine *info.MachineInfo) int {
|
||||
// Saturate limit to the machine size.
|
||||
limit := uint64(spec.Memory.Limit)
|
||||
if limit > uint64(machine.MemoryCapacity) {
|
||||
limit = uint64(machine.MemoryCapacity)
|
||||
}
|
||||
|
||||
return int((usage * 100) / limit)
|
||||
}
|
||||
|
||||
func getMemoryUsage(stats []*info.ContainerStats) string {
|
||||
return strconv.FormatFloat(toMegabytes((stats[len(stats)-1].Memory.Usage)), 'f', 2, 64)
|
||||
}
|
||||
|
||||
func getMemoryUsagePercent(spec *info.ContainerSpec, stats []*info.ContainerStats) int {
|
||||
return toMemoryPercent((stats[len(stats)-1].Memory.Usage), spec)
|
||||
func getMemoryUsagePercent(spec *info.ContainerSpec, stats []*info.ContainerStats, machine *info.MachineInfo) int {
|
||||
return toMemoryPercent((stats[len(stats)-1].Memory.Usage), spec, machine)
|
||||
}
|
||||
|
||||
func getHotMemoryPercent(spec *info.ContainerSpec, stats []*info.ContainerStats) int {
|
||||
return toMemoryPercent((stats[len(stats)-1].Memory.WorkingSet), spec)
|
||||
func getHotMemoryPercent(spec *info.ContainerSpec, stats []*info.ContainerStats, machine *info.MachineInfo) int {
|
||||
return toMemoryPercent((stats[len(stats)-1].Memory.WorkingSet), spec, machine)
|
||||
}
|
||||
|
||||
func getColdMemoryPercent(spec *info.ContainerSpec, stats []*info.ContainerStats) int {
|
||||
func getColdMemoryPercent(spec *info.ContainerSpec, stats []*info.ContainerStats, machine *info.MachineInfo) int {
|
||||
latestStats := stats[len(stats)-1].Memory
|
||||
return toMemoryPercent((latestStats.Usage)-(latestStats.WorkingSet), spec)
|
||||
return toMemoryPercent((latestStats.Usage)-(latestStats.WorkingSet), spec, machine)
|
||||
}
|
||||
|
||||
func ServerContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL) error {
|
||||
|
@ -132,16 +132,16 @@ const containersHtmlTemplate = `
|
||||
<h4>Usage Breakdown</h4>
|
||||
<div class="col-sm-9">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-danger" style="width: {{getHotMemoryPercent .Spec .Stats}}%">
|
||||
<div class="progress-bar progress-bar-danger" style="width: {{getHotMemoryPercent .Spec .Stats .MachineInfo}}%">
|
||||
<span class="sr-only">Hot Memory</span>
|
||||
</div>
|
||||
<div class="progress-bar progress-bar-info" style="width: {{getColdMemoryPercent .Spec .Stats}}%">
|
||||
<div class="progress-bar progress-bar-info" style="width: {{getColdMemoryPercent .Spec .Stats .MachineInfo}}%">
|
||||
<span class="sr-only">Cold Memory</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
{{ getMemoryUsage .Stats }} MB ({{ getMemoryUsagePercent .Spec .Stats }}%)
|
||||
{{ getMemoryUsage .Stats }} MB ({{ getMemoryUsagePercent .Spec .Stats .MachineInfo}}%)
|
||||
</div>
|
||||
</div>
|
||||
<h4>Page Faults</h4>
|
||||
|
@ -154,7 +154,7 @@ function drawCpuUsageBreakdown(elementId, containerInfo) {
|
||||
}
|
||||
|
||||
// Draw the gauges for overall resource usage.
|
||||
function drawOverallUsage(elementId, containerInfo) {
|
||||
function drawOverallUsage(elementId, machineInfo, containerInfo) {
|
||||
var cur = containerInfo.stats[containerInfo.stats.length - 1];
|
||||
|
||||
var cpuUsage = 0;
|
||||
@ -171,7 +171,13 @@ function drawOverallUsage(elementId, containerInfo) {
|
||||
|
||||
var memoryUsage = 0;
|
||||
if (containerInfo.spec.memory) {
|
||||
memoryUsage = Math.round((cur.memory.usage / containerInfo.spec.memory.limit) * 100);
|
||||
// Saturate to the machine size.
|
||||
var limit = containerInfo.spec.memory.limit;
|
||||
if (limit > machineInfo.memory_capacity) {
|
||||
limit = machineInfo.memory_capacity;
|
||||
}
|
||||
|
||||
memoryUsage = Math.round((cur.memory.usage / limit) * 100);
|
||||
}
|
||||
|
||||
drawGauge(elementId, cpuUsage, memoryUsage);
|
||||
@ -236,7 +242,7 @@ function drawCharts(machineInfo, containerInfo) {
|
||||
var steps = [];
|
||||
|
||||
steps.push(function() {
|
||||
drawOverallUsage("usage-gauge", containerInfo)
|
||||
drawOverallUsage("usage-gauge", machineInfo, containerInfo)
|
||||
});
|
||||
|
||||
// CPU.
|
||||
|
@ -1,3 +1,17 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sampling
|
||||
|
||||
import (
|
||||
|
@ -1,3 +1,17 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sampling
|
||||
|
||||
import "testing"
|
||||
|
Loading…
Reference in New Issue
Block a user