Add cgroup info and links to the process list on root page.

This commit is contained in:
Rohit Jnagal 2015-05-19 22:41:23 +00:00
parent f54e10b7e8
commit d8fb3c802f
4 changed files with 44 additions and 8 deletions

View File

@ -181,5 +181,6 @@ type ProcessInfo struct {
VirtualSize uint64 `json:"virtual_size"` VirtualSize uint64 `json:"virtual_size"`
Status string `json:"status"` Status string `json:"status"`
RunningTime string `json:"running_time"` RunningTime string `json:"running_time"`
CgroupPath string `json:"cgroup_path"`
Cmd string `json:"cmd"` Cmd string `json:"cmd"`
} }

View File

@ -19,6 +19,7 @@ import (
"fmt" "fmt"
"math" "math"
"os/exec" "os/exec"
"regexp"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -41,6 +42,8 @@ var HousekeepingInterval = flag.Duration("housekeeping_interval", 1*time.Second,
var maxHousekeepingInterval = flag.Duration("max_housekeeping_interval", 60*time.Second, "Largest interval to allow between container housekeepings") var maxHousekeepingInterval = flag.Duration("max_housekeeping_interval", 60*time.Second, "Largest interval to allow between container housekeepings")
var allowDynamicHousekeeping = flag.Bool("allow_dynamic_housekeeping", true, "Whether to allow the housekeeping interval to be dynamic") var allowDynamicHousekeeping = flag.Bool("allow_dynamic_housekeeping", true, "Whether to allow the housekeeping interval to be dynamic")
var cgroupPathRegExp = regexp.MustCompile(".*:devices:(.*?),.*")
// Decay value used for load average smoothing. Interval length of 10 seconds is used. // Decay value used for load average smoothing. Interval length of 10 seconds is used.
var loadDecay = math.Exp(float64(-1 * (*HousekeepingInterval).Seconds() / 10)) var loadDecay = math.Exp(float64(-1 * (*HousekeepingInterval).Seconds() / 10))
@ -116,6 +119,19 @@ func (c *containerData) DerivedStats() (v2.DerivedStats, error) {
return c.summaryReader.DerivedStats() return c.summaryReader.DerivedStats()
} }
func (c *containerData) getCgroupPath(cgroups string) (string, error) {
if cgroups == "-" {
return "/", nil
}
matches := cgroupPathRegExp.FindSubmatch([]byte(cgroups))
if len(matches) != 2 {
glog.V(3).Infof("failed to get devices cgroup path from %q", cgroups)
// return root in case of failures - devices hierarchy might not be enabled.
return "/", nil
}
return string(matches[1]), nil
}
func (c *containerData) GetProcessList() ([]v2.ProcessInfo, error) { func (c *containerData) GetProcessList() ([]v2.ProcessInfo, error) {
// report all processes for root. // report all processes for root.
isRoot := c.info.Name == "/" isRoot := c.info.Name == "/"
@ -130,9 +146,9 @@ func (c *containerData) GetProcessList() ([]v2.ProcessInfo, error) {
} }
} }
// TODO(rjnagal): Take format as an option? // TODO(rjnagal): Take format as an option?
format := "user,pid,ppid,stime,pcpu,pmem,rss,vsz,stat,time,comm" format := "user,pid,ppid,stime,pcpu,pmem,rss,vsz,stat,time,comm,cgroup"
args := []string{"-e", "-o", format} args := []string{"-e", "-o", format}
expectedFields := 11 expectedFields := 12
out, err := exec.Command("ps", args...).Output() out, err := exec.Command("ps", args...).Output()
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to execute ps command: %v", err) return nil, fmt.Errorf("failed to execute ps command: %v", err)
@ -171,6 +187,14 @@ func (c *containerData) GetProcessList() ([]v2.ProcessInfo, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid virtual size %q: %v", fields[7], err) return nil, fmt.Errorf("invalid virtual size %q: %v", fields[7], err)
} }
var cgroupPath string
if isRoot {
cgroupPath, err = c.getCgroupPath(fields[11])
if err != nil {
return nil, fmt.Errorf("could not parse cgroup path from %q: %v", fields[10], err)
}
}
if isRoot || pidMap[pid] == true { if isRoot || pidMap[pid] == true {
processes = append(processes, v2.ProcessInfo{ processes = append(processes, v2.ProcessInfo{
User: fields[0], User: fields[0],
@ -183,7 +207,8 @@ func (c *containerData) GetProcessList() ([]v2.ProcessInfo, error) {
VirtualSize: vs, VirtualSize: vs,
Status: fields[8], Status: fields[8],
RunningTime: fields[9], RunningTime: fields[9],
Cmd: strings.Join(fields[10:], " "), Cmd: fields[10],
CgroupPath: cgroupPath,
}) })
} }
} }

View File

@ -215,7 +215,7 @@ const containersHtmlTemplate = `
{{end}} {{end}}
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
startPage({{.ContainerName}}, {{.CpuAvailable}}, {{.MemoryAvailable}}, {{.Root}}); startPage({{.ContainerName}}, {{.CpuAvailable}}, {{.MemoryAvailable}}, {{.Root}}, {{.IsRoot}});
drawImages({{.DockerImages}}); drawImages({{.DockerImages}});
</script> </script>
</body> </body>

View File

@ -462,12 +462,16 @@ function drawImages(images) {
drawTable(titles, titleTypes, data, "docker-images", 30); drawTable(titles, titleTypes, data, "docker-images", 30);
} }
function drawProcesses(processInfo) { function drawProcesses(isRoot, rootDir, processInfo) {
if (processInfo.length == 0) { if (processInfo.length == 0) {
return; return;
} }
var titles = ["User", "PID", "PPID", "Start Time", "CPU %", "MEM %", "RSS", "Virtual Size", "Status", "Running Time", "Command"]; var titles = ["User", "PID", "PPID", "Start Time", "CPU %", "MEM %", "RSS", "Virtual Size", "Status", "Running Time", "Command"];
var titleTypes = ['string', 'number', 'number', 'string', 'number', 'number', 'number', 'number', 'string', 'string', 'string']; var titleTypes = ['string', 'number', 'number', 'string', 'number', 'number', 'number', 'number', 'string', 'string', 'string'];
if (isRoot) {
titles.push("Cgroup");
titleTypes.push('string');
}
var data = [] var data = []
for (var i = 0; i < processInfo.length; i++) { for (var i = 0; i < processInfo.length; i++) {
var elements = []; var elements = [];
@ -482,6 +486,12 @@ function drawProcesses(processInfo) {
elements.push(processInfo[i].status); elements.push(processInfo[i].status);
elements.push(processInfo[i].running_time); elements.push(processInfo[i].running_time);
elements.push(processInfo[i].cmd); elements.push(processInfo[i].cmd);
if (isRoot) {
var cgroup = processInfo[i].cgroup_path
// Use the raw cgroup link as it works for all containers.
var cgroupLink = '<a href="' + rootDir + 'containers/' + cgroup +'">' + cgroup.substr(0,30) + ' </a>';
elements.push({v:cgroup, f:cgroupLink});
}
data.push(elements); data.push(elements);
} }
drawTable(titles, titleTypes, data, "processes-top", 25); drawTable(titles, titleTypes, data, "processes-top", 25);
@ -600,7 +610,7 @@ function drawCharts(machineInfo, containerInfo) {
} }
// Executed when the page finishes loading. // Executed when the page finishes loading.
function startPage(containerName, hasCpu, hasMemory, rootDir) { function startPage(containerName, hasCpu, hasMemory, rootDir, isRoot) {
// Don't fetch data if we don't have any resource. // Don't fetch data if we don't have any resource.
if (!hasCpu && !hasMemory) { if (!hasCpu && !hasMemory) {
return; return;
@ -612,11 +622,11 @@ function startPage(containerName, hasCpu, hasMemory, rootDir) {
// Draw process information at start and refresh every 60s. // Draw process information at start and refresh every 60s.
getProcessInfo(rootDir, containerName, function(processInfo) { getProcessInfo(rootDir, containerName, function(processInfo) {
drawProcesses(processInfo) drawProcesses(isRoot, rootDir, processInfo)
}); });
setInterval(function() { setInterval(function() {
getProcessInfo(rootDir, containerName, function(processInfo) { getProcessInfo(rootDir, containerName, function(processInfo) {
drawProcesses(processInfo) drawProcesses(isRoot, rootDir, processInfo)
}); });
}, 60000); }, 60000);