Merge pull request #41016 from kolyshkin/cgroup-init

This commit is contained in:
Brian Goff 2020-07-16 11:26:52 -07:00 committed by GitHub
commit 260c26b7be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 92 additions and 114 deletions

View file

@ -500,8 +500,8 @@ func verifyPlatformContainerResources(resources *containertypes.Resources, sysIn
if resources.NanoCPUs > 0 && resources.CPUQuota > 0 {
return warnings, fmt.Errorf("Conflicting options: Nano CPUs and CPU Quota cannot both be set")
}
if resources.NanoCPUs > 0 && (!sysInfo.CPUCfsPeriod || !sysInfo.CPUCfsQuota) {
return warnings, fmt.Errorf("NanoCPUs can not be set, as your kernel does not support CPU cfs period/quota or the cgroup is not mounted")
if resources.NanoCPUs > 0 && !sysInfo.CPUCfs {
return warnings, fmt.Errorf("NanoCPUs can not be set, as your kernel does not support CPU CFS scheduler or the cgroup is not mounted")
}
// The highest precision we could get on Linux is 0.001, by setting
// cpu.cfs_period_us=1000ms
@ -518,17 +518,14 @@ func verifyPlatformContainerResources(resources *containertypes.Resources, sysIn
warnings = append(warnings, "Your kernel does not support CPU shares or the cgroup is not mounted. Shares discarded.")
resources.CPUShares = 0
}
if resources.CPUPeriod > 0 && !sysInfo.CPUCfsPeriod {
warnings = append(warnings, "Your kernel does not support CPU cfs period or the cgroup is not mounted. Period discarded.")
if (resources.CPUPeriod != 0 || resources.CPUQuota != 0) && !sysInfo.CPUCfs {
warnings = append(warnings, "Your kernel does not support CPU CFS scheduler. CPU period/quota discarded.")
resources.CPUPeriod = 0
resources.CPUQuota = 0
}
if resources.CPUPeriod != 0 && (resources.CPUPeriod < 1000 || resources.CPUPeriod > 1000000) {
return warnings, fmt.Errorf("CPU cfs period can not be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)")
}
if resources.CPUQuota > 0 && !sysInfo.CPUCfsQuota {
warnings = append(warnings, "Your kernel does not support CPU cfs quota or the cgroup is not mounted. Quota discarded.")
resources.CPUQuota = 0
}
if resources.CPUQuota > 0 && resources.CPUQuota < 1000 {
return warnings, fmt.Errorf("CPU cfs quota can not be less than 1ms (i.e. 1000)")
}
@ -1649,51 +1646,32 @@ func setupOOMScoreAdj(score int) error {
return err
}
func (daemon *Daemon) initCgroupsPath(path string) error {
func (daemon *Daemon) initCPURtController(mnt, path string) error {
if path == "/" || path == "." {
return nil
}
if daemon.configStore.CPURealtimePeriod == 0 && daemon.configStore.CPURealtimeRuntime == 0 {
return nil
}
if cgroups.IsCgroup2UnifiedMode() {
return fmt.Errorf("daemon-scoped cpu-rt-period and cpu-rt-runtime are not implemented for cgroup v2")
}
// Recursively create cgroup to ensure that the system and all parent cgroups have values set
// for the period and runtime as this limits what the children can be set to.
daemon.initCgroupsPath(filepath.Dir(path))
mnt, root, err := cgroups.FindCgroupMountpointAndRoot("", "cpu")
if err != nil {
if err := daemon.initCPURtController(mnt, filepath.Dir(path)); err != nil {
return err
}
// When docker is run inside docker, the root is based of the host cgroup.
// Should this be handled in runc/libcontainer/cgroups ?
if strings.HasPrefix(root, "/docker/") {
root = "/"
}
path = filepath.Join(mnt, root, path)
sysInfo := daemon.RawSysInfo(true)
if err := maybeCreateCPURealTimeFile(sysInfo.CPURealtimePeriod, daemon.configStore.CPURealtimePeriod, "cpu.rt_period_us", path); err != nil {
path = filepath.Join(mnt, path)
if err := os.MkdirAll(path, 0755); err != nil {
return err
}
return maybeCreateCPURealTimeFile(sysInfo.CPURealtimeRuntime, daemon.configStore.CPURealtimeRuntime, "cpu.rt_runtime_us", path)
if err := maybeCreateCPURealTimeFile(daemon.configStore.CPURealtimePeriod, "cpu.rt_period_us", path); err != nil {
return err
}
return maybeCreateCPURealTimeFile(daemon.configStore.CPURealtimeRuntime, "cpu.rt_runtime_us", path)
}
func maybeCreateCPURealTimeFile(sysinfoPresent bool, configValue int64, file string, path string) error {
if sysinfoPresent && configValue != 0 {
if err := os.MkdirAll(path, 0755); err != nil {
return err
}
if err := ioutil.WriteFile(filepath.Join(path, file), []byte(strconv.FormatInt(configValue, 10)), 0700); err != nil {
return err
}
func maybeCreateCPURealTimeFile(configValue int64, file string, path string) error {
if configValue == 0 {
return nil
}
return nil
return ioutil.WriteFile(filepath.Join(path, file), []byte(strconv.FormatInt(configValue, 10)), 0700)
}
func (daemon *Daemon) setupSeccompProfile() error {

View file

@ -30,8 +30,8 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo)
v.KernelMemory = sysInfo.KernelMemory
v.KernelMemoryTCP = sysInfo.KernelMemoryTCP
v.OomKillDisable = sysInfo.OomKillDisable
v.CPUCfsPeriod = sysInfo.CPUCfsPeriod
v.CPUCfsQuota = sysInfo.CPUCfsQuota
v.CPUCfsPeriod = sysInfo.CPUCfs
v.CPUCfsQuota = sysInfo.CPUCfs
v.CPUShares = sysInfo.CPUShares
v.CPUSet = sysInfo.Cpuset
v.PidsLimit = sysInfo.PidsLimit

View file

@ -824,15 +824,32 @@ func WithCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts {
cgroupsPath = filepath.Join(parent, c.ID)
}
s.Linux.CgroupsPath = cgroupsPath
// the rest is only needed for CPU RT controller
if daemon.configStore.CPURealtimePeriod == 0 && daemon.configStore.CPURealtimeRuntime == 0 {
return nil
}
if cgroups.IsCgroup2UnifiedMode() {
return errors.New("daemon-scoped cpu-rt-period and cpu-rt-runtime are not implemented for cgroup v2")
}
// FIXME this is very expensive way to check if cpu rt is supported
sysInfo := daemon.RawSysInfo(true)
if !sysInfo.CPURealtime {
return errors.New("daemon-scoped cpu-rt-period and cpu-rt-runtime are not supported by the kernel")
}
p := cgroupsPath
if useSystemd {
initPath, err := cgroups.GetInitCgroup("cpu")
if err != nil {
return err
return errors.Wrap(err, "unable to init CPU RT controller")
}
_, err = cgroups.GetOwnCgroup("cpu")
if err != nil {
return err
return errors.Wrap(err, "unable to init CPU RT controller")
}
p = filepath.Join(initPath, s.Linux.CgroupsPath)
}
@ -843,8 +860,19 @@ func WithCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts {
parentPath = filepath.Clean("/" + parentPath)
}
if err := daemon.initCgroupsPath(parentPath); err != nil {
return fmt.Errorf("linux init cgroups path: %v", err)
mnt, root, err := cgroups.FindCgroupMountpointAndRoot("", "cpu")
if err != nil {
return errors.Wrap(err, "unable to init CPU RT controller")
}
// When docker is run inside docker, the root is based of the host cgroup.
// Should this be handled in runc/libcontainer/cgroups ?
if strings.HasPrefix(root, "/docker/") {
root = "/"
}
mnt = filepath.Join(mnt, root)
if err := daemon.initCPURtController(mnt, parentPath); err != nil {
return errors.Wrap(err, "unable to init CPU RT controller")
}
return nil
}

View file

@ -90,10 +90,8 @@ func applyCPUCgroupInfoV2(info *SysInfo, controllers map[string]struct{}, _ stri
return warnings
}
info.CPUShares = true
info.CPUCfsPeriod = true
info.CPUCfsQuota = true
info.CPURealtimePeriod = false
info.CPURealtimeRuntime = false
info.CPUCfs = true
info.CPURealtime = false
return warnings
}

View file

@ -62,17 +62,11 @@ type cgroupCPUInfo struct {
// Whether CPU shares is supported or not
CPUShares bool
// Whether CPU CFS(Completely Fair Scheduler) period is supported or not
CPUCfsPeriod bool
// Whether CPU CFS (Completely Fair Scheduler) is supported
CPUCfs bool
// Whether CPU CFS(Completely Fair Scheduler) quota is supported or not
CPUCfsQuota bool
// Whether CPU real-time period is supported or not
CPURealtimePeriod bool
// Whether CPU real-time runtime is supported or not
CPURealtimeRuntime bool
// Whether CPU real-time scheduler is supported
CPURealtime bool
}
type cgroupBlkioInfo struct {

View file

@ -144,27 +144,17 @@ func applyCPUCgroupInfo(info *SysInfo, cgMounts map[string]string) []string {
info.CPUShares = cgroupEnabled(mountPoint, "cpu.shares")
if !info.CPUShares {
warnings = append(warnings, "Your kernel does not support cgroup cpu shares")
warnings = append(warnings, "Your kernel does not support CPU shares")
}
info.CPUCfsPeriod = cgroupEnabled(mountPoint, "cpu.cfs_period_us")
if !info.CPUCfsPeriod {
warnings = append(warnings, "Your kernel does not support cgroup cfs period")
info.CPUCfs = cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
if !info.CPUCfs {
warnings = append(warnings, "Your kernel does not support CPU CFS scheduler")
}
info.CPUCfsQuota = cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
if !info.CPUCfsQuota {
warnings = append(warnings, "Your kernel does not support cgroup cfs quotas")
}
info.CPURealtimePeriod = cgroupEnabled(mountPoint, "cpu.rt_period_us")
if !info.CPURealtimePeriod {
warnings = append(warnings, "Your kernel does not support cgroup rt period")
}
info.CPURealtimeRuntime = cgroupEnabled(mountPoint, "cpu.rt_runtime_us")
if !info.CPURealtimeRuntime {
warnings = append(warnings, "Your kernel does not support cgroup rt runtime")
info.CPURealtime = cgroupEnabled(mountPoint, "cpu.rt_period_us")
if !info.CPURealtime {
warnings = append(warnings, "Your kernel does not support CPU realtime scheduler")
}
return warnings

View file

@ -240,46 +240,41 @@ func TestDecodeHostConfig(t *testing.T) {
func TestValidateResources(t *testing.T) {
type resourceTest struct {
ConfigCPURealtimePeriod int64
ConfigCPURealtimeRuntime int64
SysInfoCPURealtimePeriod bool
SysInfoCPURealtimeRuntime bool
ErrorExpected bool
FailureMsg string
ConfigCPURealtimePeriod int64
ConfigCPURealtimeRuntime int64
SysInfoCPURealtime bool
ErrorExpected bool
FailureMsg string
}
tests := []resourceTest{
{
ConfigCPURealtimePeriod: 1000,
ConfigCPURealtimeRuntime: 1000,
SysInfoCPURealtimePeriod: true,
SysInfoCPURealtimeRuntime: true,
ErrorExpected: false,
FailureMsg: "Expected valid configuration",
ConfigCPURealtimePeriod: 1000,
ConfigCPURealtimeRuntime: 1000,
SysInfoCPURealtime: true,
ErrorExpected: false,
FailureMsg: "Expected valid configuration",
},
{
ConfigCPURealtimePeriod: 5000,
ConfigCPURealtimeRuntime: 5000,
SysInfoCPURealtimePeriod: false,
SysInfoCPURealtimeRuntime: true,
ErrorExpected: true,
FailureMsg: "Expected failure when cpu-rt-period is set but kernel doesn't support it",
ConfigCPURealtimePeriod: 5000,
ConfigCPURealtimeRuntime: 5000,
SysInfoCPURealtime: false,
ErrorExpected: true,
FailureMsg: "Expected failure when cpu-rt-period is set but kernel doesn't support it",
},
{
ConfigCPURealtimePeriod: 5000,
ConfigCPURealtimeRuntime: 5000,
SysInfoCPURealtimePeriod: true,
SysInfoCPURealtimeRuntime: false,
ErrorExpected: true,
FailureMsg: "Expected failure when cpu-rt-runtime is set but kernel doesn't support it",
ConfigCPURealtimePeriod: 5000,
ConfigCPURealtimeRuntime: 5000,
SysInfoCPURealtime: false,
ErrorExpected: true,
FailureMsg: "Expected failure when cpu-rt-runtime is set but kernel doesn't support it",
},
{
ConfigCPURealtimePeriod: 5000,
ConfigCPURealtimeRuntime: 10000,
SysInfoCPURealtimePeriod: true,
SysInfoCPURealtimeRuntime: false,
ErrorExpected: true,
FailureMsg: "Expected failure when cpu-rt-runtime is greater than cpu-rt-period",
ConfigCPURealtimePeriod: 5000,
ConfigCPURealtimeRuntime: 10000,
SysInfoCPURealtime: true,
ErrorExpected: true,
FailureMsg: "Expected failure when cpu-rt-runtime is greater than cpu-rt-period",
},
}
@ -289,8 +284,7 @@ func TestValidateResources(t *testing.T) {
hc.Resources.CPURealtimeRuntime = rt.ConfigCPURealtimeRuntime
var si sysinfo.SysInfo
si.CPURealtimePeriod = rt.SysInfoCPURealtimePeriod
si.CPURealtimeRuntime = rt.SysInfoCPURealtimeRuntime
si.CPURealtime = rt.SysInfoCPURealtime
if err := validateResources(&hc, &si); (err != nil) != rt.ErrorExpected {
t.Fatal(rt.FailureMsg, err)

View file

@ -85,12 +85,8 @@ func validateResources(hc *container.HostConfig, si *sysinfo.SysInfo) error {
return nil
}
if hc.Resources.CPURealtimePeriod > 0 && !si.CPURealtimePeriod {
return fmt.Errorf("Your kernel does not support cgroup cpu real-time period")
}
if hc.Resources.CPURealtimeRuntime > 0 && !si.CPURealtimeRuntime {
return fmt.Errorf("Your kernel does not support cgroup cpu real-time runtime")
if (hc.Resources.CPURealtimePeriod != 0 || hc.Resources.CPURealtimeRuntime != 0) && !si.CPURealtime {
return fmt.Errorf("Your kernel does not support CPU real-time scheduler")
}
if hc.Resources.CPURealtimePeriod != 0 && hc.Resources.CPURealtimeRuntime != 0 && hc.Resources.CPURealtimeRuntime > hc.Resources.CPURealtimePeriod {