56f77d5ade
containers may specify these cgroup values at runtime. This will allow processes to change their priority to real-time within the container when CONFIG_RT_GROUP_SCHED is enabled in the kernel. See #22380. Also added sanity checks for the new --cpu-rt-runtime and --cpu-rt-period flags to ensure that that the kernel supports these features and that runtime is not greater than period. Daemon will support a --cpu-rt-runtime flag to initialize the parent cgroup on startup, this prevents the administrator from alotting runtime to docker after each restart. There are additional checks that could be added but maybe too far? Check parent cgroups to ensure values are <= parent, inspecting rtprio ulimit and issuing a warning. Signed-off-by: Erik St. Martin <alakriti@gmail.com>
259 lines
7.8 KiB
Go
259 lines
7.8 KiB
Go
package sysinfo
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
"syscall"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
|
)
|
|
|
|
const (
|
|
// SeccompModeFilter refers to the syscall argument SECCOMP_MODE_FILTER.
|
|
SeccompModeFilter = uintptr(2)
|
|
)
|
|
|
|
func findCgroupMountpoints() (map[string]string, error) {
|
|
cgMounts, err := cgroups.GetCgroupMounts(false)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to parse cgroup information: %v", err)
|
|
}
|
|
mps := make(map[string]string)
|
|
for _, m := range cgMounts {
|
|
for _, ss := range m.Subsystems {
|
|
mps[ss] = m.Mountpoint
|
|
}
|
|
}
|
|
return mps, nil
|
|
}
|
|
|
|
// New returns a new SysInfo, using the filesystem to detect which features
|
|
// the kernel supports. If `quiet` is `false` warnings are printed in logs
|
|
// whenever an error occurs or misconfigurations are present.
|
|
func New(quiet bool) *SysInfo {
|
|
sysInfo := &SysInfo{}
|
|
cgMounts, err := findCgroupMountpoints()
|
|
if err != nil {
|
|
logrus.Warnf("Failed to parse cgroup information: %v", err)
|
|
} else {
|
|
sysInfo.cgroupMemInfo = checkCgroupMem(cgMounts, quiet)
|
|
sysInfo.cgroupCPUInfo = checkCgroupCPU(cgMounts, quiet)
|
|
sysInfo.cgroupBlkioInfo = checkCgroupBlkioInfo(cgMounts, quiet)
|
|
sysInfo.cgroupCpusetInfo = checkCgroupCpusetInfo(cgMounts, quiet)
|
|
sysInfo.cgroupPids = checkCgroupPids(quiet)
|
|
}
|
|
|
|
_, ok := cgMounts["devices"]
|
|
sysInfo.CgroupDevicesEnabled = ok
|
|
|
|
sysInfo.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward")
|
|
sysInfo.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables")
|
|
sysInfo.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
|
|
|
|
// Check if AppArmor is supported.
|
|
if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
|
|
sysInfo.AppArmor = true
|
|
}
|
|
|
|
// Check if Seccomp is supported, via CONFIG_SECCOMP.
|
|
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_GET_SECCOMP, 0, 0); err != syscall.EINVAL {
|
|
// Make sure the kernel has CONFIG_SECCOMP_FILTER.
|
|
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_SECCOMP, SeccompModeFilter, 0); err != syscall.EINVAL {
|
|
sysInfo.Seccomp = true
|
|
}
|
|
}
|
|
|
|
return sysInfo
|
|
}
|
|
|
|
// checkCgroupMem reads the memory information from the memory cgroup mount point.
|
|
func checkCgroupMem(cgMounts map[string]string, quiet bool) cgroupMemInfo {
|
|
mountPoint, ok := cgMounts["memory"]
|
|
if !ok {
|
|
if !quiet {
|
|
logrus.Warn("Your kernel does not support cgroup memory limit")
|
|
}
|
|
return cgroupMemInfo{}
|
|
}
|
|
|
|
swapLimit := cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes")
|
|
if !quiet && !swapLimit {
|
|
logrus.Warn("Your kernel does not support swap memory limit.")
|
|
}
|
|
memoryReservation := cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes")
|
|
if !quiet && !memoryReservation {
|
|
logrus.Warn("Your kernel does not support memory reservation.")
|
|
}
|
|
oomKillDisable := cgroupEnabled(mountPoint, "memory.oom_control")
|
|
if !quiet && !oomKillDisable {
|
|
logrus.Warn("Your kernel does not support oom control.")
|
|
}
|
|
memorySwappiness := cgroupEnabled(mountPoint, "memory.swappiness")
|
|
if !quiet && !memorySwappiness {
|
|
logrus.Warn("Your kernel does not support memory swappiness.")
|
|
}
|
|
kernelMemory := cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes")
|
|
if !quiet && !kernelMemory {
|
|
logrus.Warn("Your kernel does not support kernel memory limit.")
|
|
}
|
|
|
|
return cgroupMemInfo{
|
|
MemoryLimit: true,
|
|
SwapLimit: swapLimit,
|
|
MemoryReservation: memoryReservation,
|
|
OomKillDisable: oomKillDisable,
|
|
MemorySwappiness: memorySwappiness,
|
|
KernelMemory: kernelMemory,
|
|
}
|
|
}
|
|
|
|
// checkCgroupCPU reads the cpu information from the cpu cgroup mount point.
|
|
func checkCgroupCPU(cgMounts map[string]string, quiet bool) cgroupCPUInfo {
|
|
mountPoint, ok := cgMounts["cpu"]
|
|
if !ok {
|
|
if !quiet {
|
|
logrus.Warn("Unable to find cpu cgroup in mounts")
|
|
}
|
|
return cgroupCPUInfo{}
|
|
}
|
|
|
|
cpuShares := cgroupEnabled(mountPoint, "cpu.shares")
|
|
if !quiet && !cpuShares {
|
|
logrus.Warn("Your kernel does not support cgroup cpu shares")
|
|
}
|
|
|
|
cpuCfsPeriod := cgroupEnabled(mountPoint, "cpu.cfs_period_us")
|
|
if !quiet && !cpuCfsPeriod {
|
|
logrus.Warn("Your kernel does not support cgroup cfs period")
|
|
}
|
|
|
|
cpuCfsQuota := cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
|
|
if !quiet && !cpuCfsQuota {
|
|
logrus.Warn("Your kernel does not support cgroup cfs quotas")
|
|
}
|
|
|
|
cpuRealtimePeriod := cgroupEnabled(mountPoint, "cpu.rt_period_us")
|
|
if !quiet && !cpuRealtimePeriod {
|
|
logrus.Warn("Your kernel does not support cgroup rt period")
|
|
}
|
|
|
|
cpuRealtimeRuntime := cgroupEnabled(mountPoint, "cpu.rt_runtime_us")
|
|
if !quiet && !cpuRealtimeRuntime {
|
|
logrus.Warn("Your kernel does not support cgroup rt runtime")
|
|
}
|
|
|
|
return cgroupCPUInfo{
|
|
CPUShares: cpuShares,
|
|
CPUCfsPeriod: cpuCfsPeriod,
|
|
CPUCfsQuota: cpuCfsQuota,
|
|
CPURealtimePeriod: cpuRealtimePeriod,
|
|
CPURealtimeRuntime: cpuRealtimeRuntime,
|
|
}
|
|
}
|
|
|
|
// checkCgroupBlkioInfo reads the blkio information from the blkio cgroup mount point.
|
|
func checkCgroupBlkioInfo(cgMounts map[string]string, quiet bool) cgroupBlkioInfo {
|
|
mountPoint, ok := cgMounts["blkio"]
|
|
if !ok {
|
|
if !quiet {
|
|
logrus.Warn("Unable to find blkio cgroup in mounts")
|
|
}
|
|
return cgroupBlkioInfo{}
|
|
}
|
|
|
|
weight := cgroupEnabled(mountPoint, "blkio.weight")
|
|
if !quiet && !weight {
|
|
logrus.Warn("Your kernel does not support cgroup blkio weight")
|
|
}
|
|
|
|
weightDevice := cgroupEnabled(mountPoint, "blkio.weight_device")
|
|
if !quiet && !weightDevice {
|
|
logrus.Warn("Your kernel does not support cgroup blkio weight_device")
|
|
}
|
|
|
|
readBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device")
|
|
if !quiet && !readBpsDevice {
|
|
logrus.Warn("Your kernel does not support cgroup blkio throttle.read_bps_device")
|
|
}
|
|
|
|
writeBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device")
|
|
if !quiet && !writeBpsDevice {
|
|
logrus.Warn("Your kernel does not support cgroup blkio throttle.write_bps_device")
|
|
}
|
|
readIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device")
|
|
if !quiet && !readIOpsDevice {
|
|
logrus.Warn("Your kernel does not support cgroup blkio throttle.read_iops_device")
|
|
}
|
|
|
|
writeIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device")
|
|
if !quiet && !writeIOpsDevice {
|
|
logrus.Warn("Your kernel does not support cgroup blkio throttle.write_iops_device")
|
|
}
|
|
return cgroupBlkioInfo{
|
|
BlkioWeight: weight,
|
|
BlkioWeightDevice: weightDevice,
|
|
BlkioReadBpsDevice: readBpsDevice,
|
|
BlkioWriteBpsDevice: writeBpsDevice,
|
|
BlkioReadIOpsDevice: readIOpsDevice,
|
|
BlkioWriteIOpsDevice: writeIOpsDevice,
|
|
}
|
|
}
|
|
|
|
// checkCgroupCpusetInfo reads the cpuset information from the cpuset cgroup mount point.
|
|
func checkCgroupCpusetInfo(cgMounts map[string]string, quiet bool) cgroupCpusetInfo {
|
|
mountPoint, ok := cgMounts["cpuset"]
|
|
if !ok {
|
|
if !quiet {
|
|
logrus.Warn("Unable to find cpuset cgroup in mounts")
|
|
}
|
|
return cgroupCpusetInfo{}
|
|
}
|
|
|
|
cpus, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.cpus"))
|
|
if err != nil {
|
|
return cgroupCpusetInfo{}
|
|
}
|
|
|
|
mems, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.mems"))
|
|
if err != nil {
|
|
return cgroupCpusetInfo{}
|
|
}
|
|
|
|
return cgroupCpusetInfo{
|
|
Cpuset: true,
|
|
Cpus: strings.TrimSpace(string(cpus)),
|
|
Mems: strings.TrimSpace(string(mems)),
|
|
}
|
|
}
|
|
|
|
// checkCgroupPids reads the pids information from the pids cgroup mount point.
|
|
func checkCgroupPids(quiet bool) cgroupPids {
|
|
_, err := cgroups.FindCgroupMountpoint("pids")
|
|
if err != nil {
|
|
if !quiet {
|
|
logrus.Warn(err)
|
|
}
|
|
return cgroupPids{}
|
|
}
|
|
|
|
return cgroupPids{
|
|
PidsLimit: true,
|
|
}
|
|
}
|
|
|
|
func cgroupEnabled(mountPoint, name string) bool {
|
|
_, err := os.Stat(path.Join(mountPoint, name))
|
|
return err == nil
|
|
}
|
|
|
|
func readProcBool(path string) bool {
|
|
val, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return strings.TrimSpace(string(val)) == "1"
|
|
}
|