github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/specgen/generate/validate.go (about) 1 package generate 2 3 import ( 4 "os" 5 "path/filepath" 6 7 "github.com/containers/common/pkg/sysinfo" 8 "github.com/containers/podman/v2/pkg/cgroups" 9 "github.com/containers/podman/v2/pkg/specgen" 10 "github.com/containers/podman/v2/utils" 11 "github.com/pkg/errors" 12 ) 13 14 // Verify resource limits are sanely set when running on cgroup v1. 15 func verifyContainerResourcesCgroupV1(s *specgen.SpecGenerator) ([]string, error) { 16 warnings := []string{} 17 18 sysInfo := sysinfo.New(true) 19 20 if s.ResourceLimits == nil { 21 return warnings, nil 22 } 23 24 if s.ResourceLimits.Unified != nil { 25 return nil, errors.New("Cannot use --cgroup-conf without cgroup v2") 26 } 27 28 // Memory checks 29 if s.ResourceLimits.Memory != nil { 30 memory := s.ResourceLimits.Memory 31 if memory.Limit != nil && !sysInfo.MemoryLimit { 32 warnings = append(warnings, "Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.") 33 memory.Limit = nil 34 memory.Swap = nil 35 } 36 if memory.Limit != nil && memory.Swap != nil && !sysInfo.SwapLimit { 37 warnings = append(warnings, "Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.") 38 memory.Swap = nil 39 } 40 if memory.Limit != nil && memory.Swap != nil && *memory.Swap < *memory.Limit { 41 return warnings, errors.New("minimum memoryswap limit should be larger than memory limit, see usage") 42 } 43 if memory.Limit == nil && memory.Swap != nil { 44 return warnings, errors.New("you should always set a memory limit when using a memoryswap limit, see usage") 45 } 46 if memory.Swappiness != nil { 47 if !sysInfo.MemorySwappiness { 48 warnings = append(warnings, "Your kernel does not support memory swappiness capabilities, or the cgroup is not mounted. Memory swappiness discarded.") 49 memory.Swappiness = nil 50 } else { 51 if *memory.Swappiness < 0 || *memory.Swappiness > 100 { 52 return warnings, errors.Errorf("invalid value: %v, valid memory swappiness range is 0-100", *memory.Swappiness) 53 } 54 } 55 } 56 if memory.Reservation != nil && !sysInfo.MemoryReservation { 57 warnings = append(warnings, "Your kernel does not support memory soft limit capabilities or the cgroup is not mounted. Limitation discarded.") 58 memory.Reservation = nil 59 } 60 if memory.Limit != nil && memory.Reservation != nil && *memory.Limit < *memory.Reservation { 61 return warnings, errors.New("minimum memory limit cannot be less than memory reservation limit, see usage") 62 } 63 if memory.Kernel != nil && !sysInfo.KernelMemory { 64 warnings = append(warnings, "Your kernel does not support kernel memory limit capabilities or the cgroup is not mounted. Limitation discarded.") 65 memory.Kernel = nil 66 } 67 if memory.DisableOOMKiller != nil && *memory.DisableOOMKiller && !sysInfo.OomKillDisable { 68 warnings = append(warnings, "Your kernel does not support OomKillDisable. OomKillDisable discarded.") 69 memory.DisableOOMKiller = nil 70 } 71 } 72 73 // Pids checks 74 if s.ResourceLimits.Pids != nil { 75 pids := s.ResourceLimits.Pids 76 // TODO: Should this be 0, or checking that ResourceLimits.Pids 77 // is set at all? 78 if pids.Limit > 0 && !sysInfo.PidsLimit { 79 warnings = append(warnings, "Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.") 80 s.ResourceLimits.Pids = nil 81 } 82 } 83 84 // CPU Checks 85 if s.ResourceLimits.CPU != nil { 86 cpu := s.ResourceLimits.CPU 87 if cpu.Shares != nil && !sysInfo.CPUShares { 88 warnings = append(warnings, "Your kernel does not support CPU shares or the cgroup is not mounted. Shares discarded.") 89 cpu.Shares = nil 90 } 91 if cpu.Period != nil && !sysInfo.CPUCfsPeriod { 92 warnings = append(warnings, "Your kernel does not support CPU cfs period or the cgroup is not mounted. Period discarded.") 93 cpu.Period = nil 94 } 95 if cpu.Period != nil && (*cpu.Period < 1000 || *cpu.Period > 1000000) { 96 return warnings, errors.New("CPU cfs period cannot be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)") 97 } 98 if cpu.Quota != nil && !sysInfo.CPUCfsQuota { 99 warnings = append(warnings, "Your kernel does not support CPU cfs quota or the cgroup is not mounted. Quota discarded.") 100 cpu.Quota = nil 101 } 102 if cpu.Quota != nil && *cpu.Quota < 1000 { 103 return warnings, errors.New("CPU cfs quota cannot be less than 1ms (i.e. 1000)") 104 } 105 if (cpu.Cpus != "" || cpu.Mems != "") && !sysInfo.Cpuset { 106 warnings = append(warnings, "Your kernel does not support cpuset or the cgroup is not mounted. CPUset discarded.") 107 cpu.Cpus = "" 108 cpu.Mems = "" 109 } 110 111 cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(cpu.Cpus) 112 if err != nil { 113 return warnings, errors.Errorf("invalid value %s for cpuset cpus", cpu.Cpus) 114 } 115 if !cpusAvailable { 116 return warnings, errors.Errorf("requested CPUs are not available - requested %s, available: %s", cpu.Cpus, sysInfo.Cpus) 117 } 118 119 memsAvailable, err := sysInfo.IsCpusetMemsAvailable(cpu.Mems) 120 if err != nil { 121 return warnings, errors.Errorf("invalid value %s for cpuset mems", cpu.Mems) 122 } 123 if !memsAvailable { 124 return warnings, errors.Errorf("requested memory nodes are not available - requested %s, available: %s", cpu.Mems, sysInfo.Mems) 125 } 126 } 127 128 // Blkio checks 129 if s.ResourceLimits.BlockIO != nil { 130 blkio := s.ResourceLimits.BlockIO 131 if blkio.Weight != nil && !sysInfo.BlkioWeight { 132 warnings = append(warnings, "Your kernel does not support Block I/O weight or the cgroup is not mounted. Weight discarded.") 133 blkio.Weight = nil 134 } 135 if blkio.Weight != nil && (*blkio.Weight > 1000 || *blkio.Weight < 10) { 136 return warnings, errors.New("range of blkio weight is from 10 to 1000") 137 } 138 if len(blkio.WeightDevice) > 0 && !sysInfo.BlkioWeightDevice { 139 warnings = append(warnings, "Your kernel does not support Block I/O weight_device or the cgroup is not mounted. Weight-device discarded.") 140 blkio.WeightDevice = nil 141 } 142 if len(blkio.ThrottleReadBpsDevice) > 0 && !sysInfo.BlkioReadBpsDevice { 143 warnings = append(warnings, "Your kernel does not support BPS Block I/O read limit or the cgroup is not mounted. Block I/O BPS read limit discarded") 144 blkio.ThrottleReadBpsDevice = nil 145 } 146 if len(blkio.ThrottleWriteBpsDevice) > 0 && !sysInfo.BlkioWriteBpsDevice { 147 warnings = append(warnings, "Your kernel does not support BPS Block I/O write limit or the cgroup is not mounted. Block I/O BPS write limit discarded.") 148 blkio.ThrottleWriteBpsDevice = nil 149 } 150 if len(blkio.ThrottleReadIOPSDevice) > 0 && !sysInfo.BlkioReadIOpsDevice { 151 warnings = append(warnings, "Your kernel does not support IOPS Block read limit or the cgroup is not mounted. Block I/O IOPS read limit discarded.") 152 blkio.ThrottleReadIOPSDevice = nil 153 } 154 if len(blkio.ThrottleWriteIOPSDevice) > 0 && !sysInfo.BlkioWriteIOpsDevice { 155 warnings = append(warnings, "Your kernel does not support IOPS Block I/O write limit or the cgroup is not mounted. Block I/O IOPS write limit discarded.") 156 blkio.ThrottleWriteIOPSDevice = nil 157 } 158 } 159 160 return warnings, nil 161 } 162 163 // Verify resource limits are sanely set when running on cgroup v2. 164 func verifyContainerResourcesCgroupV2(s *specgen.SpecGenerator) ([]string, error) { 165 warnings := []string{} 166 167 if s.ResourceLimits == nil { 168 return warnings, nil 169 } 170 171 if s.ResourceLimits.Memory != nil && s.ResourceLimits.Memory.Swap != nil { 172 own, err := utils.GetOwnCgroup() 173 if err != nil { 174 return warnings, err 175 } 176 memoryMax := filepath.Join("/sys/fs/cgroup", own, "memory.max") 177 memorySwapMax := filepath.Join("/sys/fs/cgroup", own, "memory.swap.max") 178 _, errMemoryMax := os.Stat(memoryMax) 179 _, errMemorySwapMax := os.Stat(memorySwapMax) 180 // Differently than cgroup v1, the memory.*max files are not present in the 181 // root directory, so we cannot query directly that, so as best effort use 182 // the current cgroup. 183 // Check whether memory.max exists in the current cgroup and memory.swap.max 184 // does not. In this case we can be sure memory swap is not enabled. 185 // If both files don't exist, the memory controller might not be enabled 186 // for the current cgroup. 187 if errMemoryMax == nil && errMemorySwapMax != nil { 188 warnings = append(warnings, "Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.") 189 s.ResourceLimits.Memory.Swap = nil 190 } 191 } 192 return warnings, nil 193 } 194 195 // Verify resource limits are sanely set, removing any limits that are not 196 // possible with the current cgroups config. 197 func verifyContainerResources(s *specgen.SpecGenerator) ([]string, error) { 198 cgroup2, err := cgroups.IsCgroup2UnifiedMode() 199 if err != nil { 200 return []string{}, err 201 } 202 if cgroup2 { 203 return verifyContainerResourcesCgroupV2(s) 204 } 205 return verifyContainerResourcesCgroupV1(s) 206 }