1
16
17 package cm
18
19 import (
20 "bufio"
21 "fmt"
22 "os"
23 "path/filepath"
24 "strconv"
25
26 libcontainercgroups "github.com/opencontainers/runc/libcontainer/cgroups"
27 v1 "k8s.io/api/core/v1"
28 "k8s.io/apimachinery/pkg/types"
29 utilfeature "k8s.io/apiserver/pkg/util/feature"
30
31 podutil "k8s.io/kubernetes/pkg/api/v1/pod"
32 "k8s.io/kubernetes/pkg/api/v1/resource"
33 v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
34 v1qos "k8s.io/kubernetes/pkg/apis/core/v1/helper/qos"
35 kubefeatures "k8s.io/kubernetes/pkg/features"
36 "k8s.io/kubernetes/pkg/kubelet/cm/util"
37 )
38
39 const (
40
41
42 MinShares = 2
43 MaxShares = 262144
44
45 SharesPerCPU = 1024
46 MilliCPUToCPU = 1000
47
48
49 QuotaPeriod = 100000
50
51
52
53 MinQuotaPeriod = 1000
54 )
55
56
57
58 func MilliCPUToQuota(milliCPU int64, period int64) (quota int64) {
59
60
61
62
63
64
65
66 if milliCPU == 0 {
67 return
68 }
69
70 if !utilfeature.DefaultFeatureGate.Enabled(kubefeatures.CPUCFSQuotaPeriod) {
71 period = QuotaPeriod
72 }
73
74
75 quota = (milliCPU * period) / MilliCPUToCPU
76
77
78 if quota < MinQuotaPeriod {
79 quota = MinQuotaPeriod
80 }
81 return
82 }
83
84
85 func MilliCPUToShares(milliCPU int64) uint64 {
86 if milliCPU == 0 {
87
88
89
90 return MinShares
91 }
92
93 shares := (milliCPU * SharesPerCPU) / MilliCPUToCPU
94 if shares < MinShares {
95 return MinShares
96 }
97 if shares > MaxShares {
98 return MaxShares
99 }
100 return uint64(shares)
101 }
102
103
104
105 func HugePageLimits(resourceList v1.ResourceList) map[int64]int64 {
106 hugePageLimits := map[int64]int64{}
107 for k, v := range resourceList {
108 if v1helper.IsHugePageResourceName(k) {
109 pageSize, _ := v1helper.HugePageSizeFromResourceName(k)
110 if value, exists := hugePageLimits[pageSize.Value()]; exists {
111 hugePageLimits[pageSize.Value()] = value + v.Value()
112 } else {
113 hugePageLimits[pageSize.Value()] = v.Value()
114 }
115 }
116 }
117 return hugePageLimits
118 }
119
120
121 func ResourceConfigForPod(pod *v1.Pod, enforceCPULimits bool, cpuPeriod uint64, enforceMemoryQoS bool) *ResourceConfig {
122 inPlacePodVerticalScalingEnabled := utilfeature.DefaultFeatureGate.Enabled(kubefeatures.InPlacePodVerticalScaling)
123
124 reqs := resource.PodRequests(pod, resource.PodResourcesOptions{
125 InPlacePodVerticalScalingEnabled: inPlacePodVerticalScalingEnabled,
126 })
127
128 memoryLimitsDeclared := true
129 cpuLimitsDeclared := true
130
131 limits := resource.PodLimits(pod, resource.PodResourcesOptions{
132 InPlacePodVerticalScalingEnabled: inPlacePodVerticalScalingEnabled,
133 ContainerFn: func(res v1.ResourceList, containerType podutil.ContainerType) {
134 if res.Cpu().IsZero() {
135 cpuLimitsDeclared = false
136 }
137 if res.Memory().IsZero() {
138 memoryLimitsDeclared = false
139 }
140 },
141 })
142
143 hugePageLimits := HugePageLimits(reqs)
144
145 cpuRequests := int64(0)
146 cpuLimits := int64(0)
147 memoryLimits := int64(0)
148 if request, found := reqs[v1.ResourceCPU]; found {
149 cpuRequests = request.MilliValue()
150 }
151 if limit, found := limits[v1.ResourceCPU]; found {
152 cpuLimits = limit.MilliValue()
153 }
154 if limit, found := limits[v1.ResourceMemory]; found {
155 memoryLimits = limit.Value()
156 }
157
158
159 cpuShares := MilliCPUToShares(cpuRequests)
160 cpuQuota := MilliCPUToQuota(cpuLimits, int64(cpuPeriod))
161
162
163 if !enforceCPULimits {
164 cpuQuota = int64(-1)
165 }
166
167
168 qosClass := v1qos.GetPodQOS(pod)
169
170
171 result := &ResourceConfig{}
172 if qosClass == v1.PodQOSGuaranteed {
173 result.CPUShares = &cpuShares
174 result.CPUQuota = &cpuQuota
175 result.CPUPeriod = &cpuPeriod
176 result.Memory = &memoryLimits
177 } else if qosClass == v1.PodQOSBurstable {
178 result.CPUShares = &cpuShares
179 if cpuLimitsDeclared {
180 result.CPUQuota = &cpuQuota
181 result.CPUPeriod = &cpuPeriod
182 }
183 if memoryLimitsDeclared {
184 result.Memory = &memoryLimits
185 }
186 } else {
187 shares := uint64(MinShares)
188 result.CPUShares = &shares
189 }
190 result.HugePageLimit = hugePageLimits
191
192 if enforceMemoryQoS {
193 memoryMin := int64(0)
194 if request, found := reqs[v1.ResourceMemory]; found {
195 memoryMin = request.Value()
196 }
197 if memoryMin > 0 {
198 result.Unified = map[string]string{
199 Cgroup2MemoryMin: strconv.FormatInt(memoryMin, 10),
200 }
201 }
202 }
203
204 return result
205 }
206
207
208 func getCgroupSubsystemsV1() (*CgroupSubsystems, error) {
209
210 allCgroups, err := libcontainercgroups.GetCgroupMounts(true)
211 if err != nil {
212 return &CgroupSubsystems{}, err
213 }
214 if len(allCgroups) == 0 {
215 return &CgroupSubsystems{}, fmt.Errorf("failed to find cgroup mounts")
216 }
217 mountPoints := make(map[string]string, len(allCgroups))
218 for _, mount := range allCgroups {
219
220
221
222
223
224 for _, subsystem := range mount.Subsystems {
225 previous := mountPoints[subsystem]
226 if previous == "" || len(mount.Mountpoint) < len(previous) {
227 mountPoints[subsystem] = mount.Mountpoint
228 }
229 }
230 }
231 return &CgroupSubsystems{
232 Mounts: allCgroups,
233 MountPoints: mountPoints,
234 }, nil
235 }
236
237
238 func getCgroupSubsystemsV2() (*CgroupSubsystems, error) {
239 controllers, err := libcontainercgroups.GetAllSubsystems()
240 if err != nil {
241 return nil, err
242 }
243
244 mounts := []libcontainercgroups.Mount{}
245 mountPoints := make(map[string]string, len(controllers))
246 for _, controller := range controllers {
247 mountPoints[controller] = util.CgroupRoot
248 m := libcontainercgroups.Mount{
249 Mountpoint: util.CgroupRoot,
250 Root: util.CgroupRoot,
251 Subsystems: []string{controller},
252 }
253 mounts = append(mounts, m)
254 }
255
256 return &CgroupSubsystems{
257 Mounts: mounts,
258 MountPoints: mountPoints,
259 }, nil
260 }
261
262
263 func GetCgroupSubsystems() (*CgroupSubsystems, error) {
264 if libcontainercgroups.IsCgroup2UnifiedMode() {
265 return getCgroupSubsystemsV2()
266 }
267
268 return getCgroupSubsystemsV1()
269 }
270
271
272
273
274 func getCgroupProcs(dir string) ([]int, error) {
275 procsFile := filepath.Join(dir, "cgroup.procs")
276 f, err := os.Open(procsFile)
277 if err != nil {
278 if os.IsNotExist(err) {
279
280 return []int{}, nil
281 }
282 return nil, err
283 }
284 defer f.Close()
285
286 s := bufio.NewScanner(f)
287 out := []int{}
288 for s.Scan() {
289 if t := s.Text(); t != "" {
290 pid, err := strconv.Atoi(t)
291 if err != nil {
292 return nil, fmt.Errorf("unexpected line in %v; could not convert to pid: %v", procsFile, err)
293 }
294 out = append(out, pid)
295 }
296 }
297 return out, nil
298 }
299
300
301 func GetPodCgroupNameSuffix(podUID types.UID) string {
302 return podCgroupNamePrefix + string(podUID)
303 }
304
305
306 func NodeAllocatableRoot(cgroupRoot string, cgroupsPerQOS bool, cgroupDriver string) string {
307 nodeAllocatableRoot := ParseCgroupfsToCgroupName(cgroupRoot)
308 if cgroupsPerQOS {
309 nodeAllocatableRoot = NewCgroupName(nodeAllocatableRoot, defaultNodeAllocatableCgroupName)
310 }
311 if cgroupDriver == "systemd" {
312 return nodeAllocatableRoot.ToSystemd()
313 }
314 return nodeAllocatableRoot.ToCgroupfs()
315 }
316
317
318 func GetKubeletContainer(kubeletCgroups string) (string, error) {
319 if kubeletCgroups == "" {
320 cont, err := getContainer(os.Getpid())
321 if err != nil {
322 return "", err
323 }
324 return cont, nil
325 }
326 return kubeletCgroups, nil
327 }
328
View as plain text