Add cgroup info and links to the process list on root page.
This commit is contained in:
parent
f54e10b7e8
commit
d8fb3c802f
@ -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"`
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user