From 8fdcc6d0ee52fffcb7aee2c66add06baae6d717a Mon Sep 17 00:00:00 2001 From: Daniel Dao Date: Thu, 25 Jun 2020 23:23:23 +0100 Subject: [PATCH] Allow raw container to retrieve process stats Raw containers ( such as systemd services ) don't have main PID, so before this change they weren't allowed to retrieve various stats from the cgroup such as networking stats. However, some process stats such as file descriptor counts or number of processes are still valuable to those containers and we should be able to retrieve them without depending on the main pid. For example, on my laptop: Before: ``` container_processes{id="/system.slice/containerd.service"} 0 1593123685900 ``` After: ``` container_processes{id="/system.slice/containerd.service"} 4 1593123707235 ``` --- container/libcontainer/handler.go | 92 ++++++++++++++++--------------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/container/libcontainer/handler.go b/container/libcontainer/handler.go index 0271eba8..436379b7 100644 --- a/container/libcontainer/handler.go +++ b/container/libcontainer/handler.go @@ -118,56 +118,58 @@ func (h *Handler) GetStats() (*info.ContainerStats, error) { } // If we know the pid then get network stats from /proc//net/dev - if h.pid == 0 { - return stats, nil - } - if h.includedMetrics.Has(container.NetworkUsageMetrics) { - netStats, err := networkStatsFromProc(h.rootFs, h.pid) - if err != nil { - klog.V(4).Infof("Unable to get network stats from pid %d: %v", h.pid, err) - } else { - stats.Network.Interfaces = append(stats.Network.Interfaces, netStats...) - } - } - if h.includedMetrics.Has(container.NetworkTcpUsageMetrics) { - t, err := tcpStatsFromProc(h.rootFs, h.pid, "net/tcp") - if err != nil { - klog.V(4).Infof("Unable to get tcp stats from pid %d: %v", h.pid, err) - } else { - stats.Network.Tcp = t + if h.pid > 0 { + if h.includedMetrics.Has(container.NetworkUsageMetrics) { + netStats, err := networkStatsFromProc(h.rootFs, h.pid) + if err != nil { + klog.V(4).Infof("Unable to get network stats from pid %d: %v", h.pid, err) + } else { + stats.Network.Interfaces = append(stats.Network.Interfaces, netStats...) + } } + if h.includedMetrics.Has(container.NetworkTcpUsageMetrics) { + t, err := tcpStatsFromProc(h.rootFs, h.pid, "net/tcp") + if err != nil { + klog.V(4).Infof("Unable to get tcp stats from pid %d: %v", h.pid, err) + } else { + stats.Network.Tcp = t + } - t6, err := tcpStatsFromProc(h.rootFs, h.pid, "net/tcp6") - if err != nil { - klog.V(4).Infof("Unable to get tcp6 stats from pid %d: %v", h.pid, err) - } else { - stats.Network.Tcp6 = t6 - } + t6, err := tcpStatsFromProc(h.rootFs, h.pid, "net/tcp6") + if err != nil { + klog.V(4).Infof("Unable to get tcp6 stats from pid %d: %v", h.pid, err) + } else { + stats.Network.Tcp6 = t6 + } - } - if h.includedMetrics.Has(container.NetworkAdvancedTcpUsageMetrics) { - ta, err := advancedTCPStatsFromProc(h.rootFs, h.pid, "net/netstat", "net/snmp") - if err != nil { - klog.V(4).Infof("Unable to get advanced tcp stats from pid %d: %v", h.pid, err) - } else { - stats.Network.TcpAdvanced = ta } - } - if h.includedMetrics.Has(container.NetworkUdpUsageMetrics) { - u, err := udpStatsFromProc(h.rootFs, h.pid, "net/udp") - if err != nil { - klog.V(4).Infof("Unable to get udp stats from pid %d: %v", h.pid, err) - } else { - stats.Network.Udp = u + if h.includedMetrics.Has(container.NetworkAdvancedTcpUsageMetrics) { + ta, err := advancedTCPStatsFromProc(h.rootFs, h.pid, "net/netstat", "net/snmp") + if err != nil { + klog.V(4).Infof("Unable to get advanced tcp stats from pid %d: %v", h.pid, err) + } else { + stats.Network.TcpAdvanced = ta + } } + if h.includedMetrics.Has(container.NetworkUdpUsageMetrics) { + u, err := udpStatsFromProc(h.rootFs, h.pid, "net/udp") + if err != nil { + klog.V(4).Infof("Unable to get udp stats from pid %d: %v", h.pid, err) + } else { + stats.Network.Udp = u + } - u6, err := udpStatsFromProc(h.rootFs, h.pid, "net/udp6") - if err != nil { - klog.V(4).Infof("Unable to get udp6 stats from pid %d: %v", h.pid, err) - } else { - stats.Network.Udp6 = u6 + u6, err := udpStatsFromProc(h.rootFs, h.pid, "net/udp6") + if err != nil { + klog.V(4).Infof("Unable to get udp6 stats from pid %d: %v", h.pid, err) + } else { + stats.Network.Udp6 = u6 + } } } + // some process metrics are per container ( number of processes, number of + // file descriptors etc.) and not required a proper container's + // root PID (systemd services don't have the root PID atm) if h.includedMetrics.Has(container.ProcessMetrics) { paths := h.cgroupManager.GetPaths() path, ok := paths["cpu"] @@ -298,13 +300,15 @@ func processStatsFromProcs(rootFs string, cgroupPath string, rootPid int) (info. } } } - ulimits := processRootProcUlimits(rootFs, rootPid) processStats := info.ProcessStats{ ProcessCount: uint64(len(pids)), FdCount: fdCount, SocketCount: socketCount, - Ulimits: ulimits, + } + + if rootPid > 0 { + processStats.Ulimits = processRootProcUlimits(rootFs, rootPid) } return processStats, nil