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:
Rohit Jnagal 2014-06-16 17:22:48 +00:00
commit 54fbc922a5
21 changed files with 197 additions and 36 deletions

View File

@ -1,9 +1,12 @@
language: go language: go
go: go:
- 1.1 - 1.2
before_script:
- go get github.com/stretchr/testify/mock
- go get github.com/kr/pretty
script: script:
- go test -v -race github.com/google/cadvisor/container - go test -v -race github.com/google/cadvisor/container
- go test -v -race github.com/google/cadvisor/info - go test -v github.com/google/cadvisor/info
- go test -v -race github.com/google/cadvisor/client - go test -v github.com/google/cadvisor/client
- go test -v -race github.com/google/cadvisor/sampling - go test -v github.com/google/cadvisor/sampling
- go build -race github.com/google/cadvisor - go build github.com/google/cadvisor

16
Dockerfile Normal file
View 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.

View File

@ -25,6 +25,7 @@ import (
"github.com/google/cadvisor/container" "github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/docker" "github.com/google/cadvisor/container/docker"
"github.com/google/cadvisor/container/lmctfy" "github.com/google/cadvisor/container/lmctfy"
"github.com/google/cadvisor/info"
"github.com/google/cadvisor/manager" "github.com/google/cadvisor/manager"
"github.com/google/cadvisor/pages" "github.com/google/cadvisor/pages"
"github.com/google/cadvisor/pages/static" "github.com/google/cadvisor/pages/static"
@ -92,6 +93,7 @@ func main() {
go containerManager.Start() go containerManager.Start()
log.Printf("Starting cAdvisor version: %q", info.VERSION)
log.Print("About to serve on port ", *argPort) log.Print("About to serve on port ", *argPort)
addr := fmt.Sprintf(":%v", *argPort) addr := fmt.Sprintf(":%v", *argPort)

View File

@ -30,6 +30,7 @@ type ListType int
// Interface for container operation handlers. // Interface for container operation handlers.
type ContainerHandler interface { type ContainerHandler interface {
ContainerReference() (info.ContainerReference, error)
GetSpec() (*info.ContainerSpec, error) GetSpec() (*info.ContainerSpec, error)
GetStats() (*info.ContainerStats, error) GetStats() (*info.ContainerStats, error)
ListContainers(listType ListType) ([]info.ContainerReference, error) ListContainers(listType ListType) ([]info.ContainerReference, error)

View File

@ -40,11 +40,11 @@ func (self *dockerFactory) NewContainerHandler(name string) (handler container.C
if err != nil { if err != nil {
return return
} }
handler = &dockerContainerHandler{ handler, err = newDockerContainerHandler(
client: client, client,
name: name, name,
machineInfoFactory: self.machineInfoFactory, self.machineInfoFactory,
} )
return return
} }

View File

@ -35,10 +35,43 @@ import (
type dockerContainerHandler struct { type dockerContainerHandler struct {
client *docker.Client client *docker.Client
name string name string
aliases []string
machineInfoFactory info.MachineInfoFactory machineInfoFactory info.MachineInfoFactory
container.NoStatsSummary 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) { func (self *dockerContainerHandler) splitName() (string, string, error) {
parent, id := path.Split(self.name) parent, id := path.Split(self.name)
cgroupSelf, err := os.Open("/proc/self/cgroup") cgroupSelf, err := os.Open("/proc/self/cgroup")

View File

@ -114,7 +114,7 @@ func (self *factoryManager) NewContainerHandler(path string) (ContainerHandler,
err := fmt.Errorf("nil factory for container %v", path) err := fmt.Errorf("nil factory for container %v", path)
return nil, err 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) return factory.NewContainerHandler(path)
} }

View File

@ -26,6 +26,10 @@ type containerListFilter struct {
NoStatsSummary NoStatsSummary
} }
func (self *containerListFilter) ContainerReference() (info.ContainerReference, error) {
return self.handler.ContainerReference()
}
func (self *containerListFilter) GetSpec() (*info.ContainerSpec, error) { func (self *containerListFilter) GetSpec() (*info.ContainerSpec, error) {
return self.handler.GetSpec() return self.handler.GetSpec()
} }

View File

@ -32,6 +32,11 @@ func (self *mockContainerHandler) GetSpec() (*info.ContainerSpec, error) {
return args.Get(0).(*info.ContainerSpec), args.Error(1) 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) { func (self *mockContainerHandler) GetStats() (*info.ContainerStats, error) {
args := self.Called() args := self.Called()
return args.Get(0).(*info.ContainerStats), args.Error(1) return args.Get(0).(*info.ContainerStats), args.Error(1)

View File

@ -47,6 +47,10 @@ func New(name string) (container.ContainerHandler, error) {
return el, nil return el, nil
} }
func (self *lmctfyContainerHandler) ContainerReference() (info.ContainerReference, error) {
return info.ContainerReference{Name: self.Name}, nil
}
func getExitCode(err error) int { func getExitCode(err error) int {
msg, ok := err.(*exec.ExitError) msg, ok := err.(*exec.ExitError)
if ok { if ok {

View File

@ -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 package container
import ( import (

View File

@ -36,6 +36,10 @@ func (self *percentilesContainerHandlerWrapper) GetSpec() (*info.ContainerSpec,
return self.handler.GetSpec() return self.handler.GetSpec()
} }
func (self *percentilesContainerHandlerWrapper) ContainerReference() (info.ContainerReference, error) {
return self.handler.ContainerReference()
}
func (self *percentilesContainerHandlerWrapper) updatePrevStats(stats *info.ContainerStats) { func (self *percentilesContainerHandlerWrapper) updatePrevStats(stats *info.ContainerStats) {
if stats == nil || stats.Cpu == nil || stats.Memory == nil { if stats == nil || stats.Cpu == nil || stats.Memory == nil {
// discard incomplete stats // discard incomplete stats

View File

@ -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) { func (self *replayTrace) GetStats() (*info.ContainerStats, error) {
stats := new(info.ContainerStats) stats := new(info.ContainerStats)
stats.Cpu = new(info.CpuStats) stats.Cpu = new(info.CpuStats)

18
info/version.go Normal file
View 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"

View File

@ -91,7 +91,12 @@ func NewContainerData(containerName string) (*containerData, error) {
return nil, err return nil, err
} }
cont.handler = handler 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.info.Stats = list.New()
cont.stop = make(chan bool, 1) cont.stop = make(chan bool, 1)

View File

@ -125,6 +125,7 @@ func (m *manager) GetContainerInfo(containerName string) (*info.ContainerInfo, e
ret := &info.ContainerInfo{ ret := &info.ContainerInfo{
ContainerReference: info.ContainerReference{ ContainerReference: info.ContainerReference{
Name: cinfo.Name, Name: cinfo.Name,
Aliases: cinfo.Aliases,
}, },
Subcontainers: cinfo.Subcontainers, Subcontainers: cinfo.Subcontainers,
Spec: cinfo.Spec, Spec: cinfo.Spec,
@ -169,9 +170,13 @@ func (m *manager) createContainer(containerName string) (*containerData, error)
m.containersLock.Lock() m.containersLock.Lock()
defer m.containersLock.Unlock() defer m.containersLock.Unlock()
log.Printf("Added container: %s", containerName) // Add the container name and all its aliases.
m.containers[containerName] = cont 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. // Start the container's housekeeping.
cont.Start() cont.Start()
@ -193,9 +198,12 @@ func (m *manager) destroyContainer(containerName string) error {
return err return err
} }
// Remove the container from our records. // Remove the container from our records (and all its aliases).
delete(m.containers, containerName) 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 return nil
} }
@ -219,8 +227,11 @@ func (m *manager) getContainersDiff() (added []info.ContainerReference, removed
// Determine which were added and which were removed. // Determine which were added and which were removed.
allContainersSet := make(map[string]*containerData) allContainersSet := make(map[string]*containerData)
for name, d := range m.containers { for name, d := range m.containers {
// Only add the canonical name.
if d.info.Name == name {
allContainersSet[name] = d allContainersSet[name] = d
} }
}
for _, c := range allContainers { for _, c := range allContainers {
delete(allContainersSet, c.Name) delete(allContainersSet, c.Name)
_, ok := m.containers[c.Name] _, ok := m.containers[c.Name]

View File

@ -37,7 +37,6 @@ var funcMap = template.FuncMap{
"printMask": printMask, "printMask": printMask,
"printCores": printCores, "printCores": printCores,
"printMegabytes": printMegabytes, "printMegabytes": printMegabytes,
"containerNameEquals": containerNameEquals,
"getMemoryUsage": getMemoryUsage, "getMemoryUsage": getMemoryUsage,
"getMemoryUsagePercent": getMemoryUsagePercent, "getMemoryUsagePercent": getMemoryUsagePercent,
"getHotMemoryPercent": getHotMemoryPercent, "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)) 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{} { func printMask(mask *info.CpuSpecMask, numCores int) interface{} {
// TODO(vmarmol): Detect this correctly. // TODO(vmarmol): Detect this correctly.
// TODO(vmarmol): Support more than 64 cores. // TODO(vmarmol): Support more than 64 cores.
@ -130,25 +125,31 @@ func printMegabytes(bytes uint64) string {
return strconv.FormatFloat(megabytes, 'f', 3, 64) return strconv.FormatFloat(megabytes, 'f', 3, 64)
} }
func toMemoryPercent(usage uint64, spec *info.ContainerSpec) int { func toMemoryPercent(usage uint64, spec *info.ContainerSpec, machine *info.MachineInfo) int {
return int((usage * 100) / (spec.Memory.Limit)) // 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 { func getMemoryUsage(stats []*info.ContainerStats) string {
return strconv.FormatFloat(toMegabytes((stats[len(stats)-1].Memory.Usage)), 'f', 2, 64) return strconv.FormatFloat(toMegabytes((stats[len(stats)-1].Memory.Usage)), 'f', 2, 64)
} }
func getMemoryUsagePercent(spec *info.ContainerSpec, stats []*info.ContainerStats) int { func getMemoryUsagePercent(spec *info.ContainerSpec, stats []*info.ContainerStats, machine *info.MachineInfo) int {
return toMemoryPercent((stats[len(stats)-1].Memory.Usage), spec) return toMemoryPercent((stats[len(stats)-1].Memory.Usage), spec, machine)
} }
func getHotMemoryPercent(spec *info.ContainerSpec, stats []*info.ContainerStats) int { func getHotMemoryPercent(spec *info.ContainerSpec, stats []*info.ContainerStats, machine *info.MachineInfo) int {
return toMemoryPercent((stats[len(stats)-1].Memory.WorkingSet), spec) 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 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 { func ServerContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL) error {

View File

@ -132,16 +132,16 @@ const containersHtmlTemplate = `
<h4>Usage Breakdown</h4> <h4>Usage Breakdown</h4>
<div class="col-sm-9"> <div class="col-sm-9">
<div class="progress"> <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> <span class="sr-only">Hot Memory</span>
</div> </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> <span class="sr-only">Cold Memory</span>
</div> </div>
</div> </div>
</div> </div>
<div class="col-sm-3"> <div class="col-sm-3">
{{ getMemoryUsage .Stats }} MB ({{ getMemoryUsagePercent .Spec .Stats }}%) {{ getMemoryUsage .Stats }} MB ({{ getMemoryUsagePercent .Spec .Stats .MachineInfo}}%)
</div> </div>
</div> </div>
<h4>Page Faults</h4> <h4>Page Faults</h4>

View File

@ -154,7 +154,7 @@ function drawCpuUsageBreakdown(elementId, containerInfo) {
} }
// Draw the gauges for overall resource usage. // 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 cur = containerInfo.stats[containerInfo.stats.length - 1];
var cpuUsage = 0; var cpuUsage = 0;
@ -171,7 +171,13 @@ function drawOverallUsage(elementId, containerInfo) {
var memoryUsage = 0; var memoryUsage = 0;
if (containerInfo.spec.memory) { 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); drawGauge(elementId, cpuUsage, memoryUsage);
@ -236,7 +242,7 @@ function drawCharts(machineInfo, containerInfo) {
var steps = []; var steps = [];
steps.push(function() { steps.push(function() {
drawOverallUsage("usage-gauge", containerInfo) drawOverallUsage("usage-gauge", machineInfo, containerInfo)
}); });
// CPU. // CPU.

View File

@ -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 package sampling
import ( import (

View File

@ -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 package sampling
import "testing" import "testing"