Skip to content

Commit 89dfd24

Browse files
committed
added --reserved-cpus kubelet command option
1 parent 76d04cf commit 89dfd24

File tree

18 files changed

+409
-28
lines changed

18 files changed

+409
-28
lines changed

cmd/kubeadm/.import-restrictions

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"k8s.io/kubernetes/pkg/features",
6565
"k8s.io/kubernetes/pkg/fieldpath",
6666
"k8s.io/kubernetes/pkg/kubelet/apis",
67+
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset",
6768
"k8s.io/kubernetes/pkg/kubelet/qos",
6869
"k8s.io/kubernetes/pkg/kubelet/types",
6970
"k8s.io/kubernetes/pkg/master/ports",

cmd/kubelet/app/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ go_library(
5757
"//pkg/kubelet/certificate:go_default_library",
5858
"//pkg/kubelet/certificate/bootstrap:go_default_library",
5959
"//pkg/kubelet/cm:go_default_library",
60+
"//pkg/kubelet/cm/cpuset:go_default_library",
6061
"//pkg/kubelet/config:go_default_library",
6162
"//pkg/kubelet/container:go_default_library",
6263
"//pkg/kubelet/dockershim:go_default_library",

cmd/kubelet/app/options/options.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ func AddKubeletConfigFlags(mainfs *pflag.FlagSet, c *kubeletconfig.KubeletConfig
553553
fs.Var(cliflag.NewMapStringString(&c.EvictionMinimumReclaim), "eviction-minimum-reclaim", "A set of minimum reclaims (e.g. imagefs.available=2Gi) that describes the minimum amount of resource the kubelet will reclaim when performing a pod eviction if that resource is under pressure.")
554554
fs.Int32Var(&c.PodsPerCore, "pods-per-core", c.PodsPerCore, "Number of Pods per core that can run on this Kubelet. The total number of Pods on this Kubelet cannot exceed max-pods, so max-pods will be used if this calculation results in a larger number of Pods allowed on the Kubelet. A value of 0 disables this limit.")
555555
fs.BoolVar(&c.ProtectKernelDefaults, "protect-kernel-defaults", c.ProtectKernelDefaults, "Default kubelet behaviour for kernel tuning. If set, kubelet errors if any of kernel tunables is different than kubelet defaults.")
556-
556+
fs.StringVar(&c.ReservedSystemCPUs, "reserved-cpus", c.ReservedSystemCPUs, "A comma-separated list of CPUs or CPU ranges that are reserved for system and kubernetes usage. This specific list will supersede cpu counts in --system-reserved and --kube-reserved.")
557557
// Node Allocatable Flags
558558
fs.Var(cliflag.NewMapStringString(&c.SystemReserved), "system-reserved", "A set of ResourceName=ResourceQuantity (e.g. cpu=200m,memory=500Mi,ephemeral-storage=1Gi) pairs that describe resources reserved for non-kubernetes components. Currently only cpu and memory are supported. See http://kubernetes.io/docs/user-guide/compute-resources for more detail. [default=none]")
559559
fs.Var(cliflag.NewMapStringString(&c.KubeReserved), "kube-reserved", "A set of ResourceName=ResourceQuantity (e.g. cpu=200m,memory=500Mi,ephemeral-storage=1Gi) pairs that describe resources reserved for kubernetes system components. Currently cpu, memory and local ephemeral storage for root file system are supported. See http://kubernetes.io/docs/user-guide/compute-resources for more detail. [default=none]")

cmd/kubelet/app/server.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ import (
7777
kubeletcertificate "k8s.io/kubernetes/pkg/kubelet/certificate"
7878
"k8s.io/kubernetes/pkg/kubelet/certificate/bootstrap"
7979
"k8s.io/kubernetes/pkg/kubelet/cm"
80+
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
8081
"k8s.io/kubernetes/pkg/kubelet/config"
8182
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
8283
"k8s.io/kubernetes/pkg/kubelet/dockershim"
@@ -636,6 +637,48 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, stopCh <-chan
636637
klog.Info("--cgroups-per-qos enabled, but --cgroup-root was not specified. defaulting to /")
637638
s.CgroupRoot = "/"
638639
}
640+
641+
var reservedSystemCPUs cpuset.CPUSet
642+
var errParse error
643+
if s.ReservedSystemCPUs != "" {
644+
reservedSystemCPUs, errParse = cpuset.Parse(s.ReservedSystemCPUs)
645+
if errParse != nil {
646+
// invalid cpu list is provided, set reservedSystemCPUs to empty, so it won't overwrite kubeReserved/systemReserved
647+
klog.Infof("Invalid ReservedSystemCPUs \"%s\"", s.ReservedSystemCPUs)
648+
return errParse
649+
}
650+
// is it safe do use CAdvisor here ??
651+
machineInfo, err := kubeDeps.CAdvisorInterface.MachineInfo()
652+
if err != nil {
653+
// if can't use CAdvisor here, fall back to non-explicit cpu list behavor
654+
klog.Warning("Failed to get MachineInfo, set reservedSystemCPUs to empty")
655+
reservedSystemCPUs = cpuset.NewCPUSet()
656+
} else {
657+
reservedList := reservedSystemCPUs.ToSlice()
658+
first := reservedList[0]
659+
last := reservedList[len(reservedList)-1]
660+
if first < 0 || last >= machineInfo.NumCores {
661+
// the specified cpuset is outside of the range of what the machine has
662+
klog.Infof("Invalid cpuset specified by --reserved-cpus")
663+
return fmt.Errorf("Invalid cpuset %q specified by --reserved-cpus", s.ReservedSystemCPUs)
664+
}
665+
}
666+
} else {
667+
reservedSystemCPUs = cpuset.NewCPUSet()
668+
}
669+
670+
if reservedSystemCPUs.Size() > 0 {
671+
// at cmd option valication phase it is tested either --system-reserved-cgroup or --kube-reserved-cgroup is specified, so overwrite should be ok
672+
klog.Infof("Option --reserved-cpus is specified, it will overwrite the cpu setting in KubeReserved=\"%v\", SystemReserved=\"%v\".", s.KubeReserved, s.SystemReserved)
673+
if s.KubeReserved != nil {
674+
delete(s.KubeReserved, "cpu")
675+
}
676+
if s.SystemReserved == nil {
677+
s.SystemReserved = make(map[string]string)
678+
}
679+
s.SystemReserved["cpu"] = strconv.Itoa(reservedSystemCPUs.Size())
680+
klog.Infof("After cpu setting is overwritten, KubeReserved=\"%v\", SystemReserved=\"%v\"", s.KubeReserved, s.SystemReserved)
681+
}
639682
kubeReserved, err := parseResourceList(s.KubeReserved)
640683
if err != nil {
641684
return err
@@ -678,6 +721,7 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, stopCh <-chan
678721
EnforceNodeAllocatable: sets.NewString(s.EnforceNodeAllocatable...),
679722
KubeReserved: kubeReserved,
680723
SystemReserved: systemReserved,
724+
ReservedSystemCPUs: reservedSystemCPUs,
681725
HardEvictionThresholds: hardEvictionThresholds,
682726
},
683727
QOSReserved: *experimentalQOSReserved,

pkg/kubelet/apis/config/helpers_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ var (
212212
"ReadOnlyPort",
213213
"RegistryBurst",
214214
"RegistryPullQPS",
215+
"ReservedSystemCPUs",
215216
"RuntimeRequestTimeout.Duration",
216217
"SerializeImagePulls",
217218
"StreamingConnectionIdleTimeout.Duration",

pkg/kubelet/apis/config/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,10 @@ type KubeletConfiguration struct {
331331
// This flag accepts a list of options. Acceptable options are `pods`, `system-reserved` & `kube-reserved`.
332332
// Refer to [Node Allocatable](https://git.k8s.io/community/contributors/design-proposals/node/node-allocatable.md) doc for more information.
333333
EnforceNodeAllocatable []string
334+
// This option specifies the cpu list reserved for the host level system threads and kubernetes related threads.
335+
// This provide a "static" CPU list rather than the "dynamic" list by system-reserved and kube-reserved.
336+
// This option overwrites CPUs provided by system-reserved and kube-reserved.
337+
ReservedSystemCPUs string
334338
}
335339

336340
type KubeletAuthorizationMode string

pkg/kubelet/apis/config/v1beta1/zz_generated.conversion.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/kubelet/apis/config/validation/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ go_library(
1717
deps = [
1818
"//pkg/features:go_default_library",
1919
"//pkg/kubelet/apis/config:go_default_library",
20+
"//pkg/kubelet/cm/cpuset:go_default_library",
2021
"//pkg/kubelet/types:go_default_library",
2122
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
2223
"//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library",

pkg/kubelet/apis/config/validation/validation.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
utilfeature "k8s.io/apiserver/pkg/util/feature"
2626
"k8s.io/kubernetes/pkg/features"
2727
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
28+
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
2829
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
2930
)
3031

@@ -142,6 +143,15 @@ func ValidateKubeletConfiguration(kc *kubeletconfig.KubeletConfiguration) error
142143
allErrors = append(allErrors, fmt.Errorf("invalid configuration: option %q specified for HairpinMode (--hairpin-mode). Valid options are %q, %q or %q",
143144
kc.HairpinMode, kubeletconfig.HairpinNone, kubeletconfig.HairpinVeth, kubeletconfig.PromiscuousBridge))
144145
}
146+
if kc.ReservedSystemCPUs != "" {
147+
// --reserved-cpus does not support --system-reserved-cgroup or --kube-reserved-cgroup
148+
if kc.SystemReservedCgroup != "" || kc.KubeReservedCgroup != "" {
149+
allErrors = append(allErrors, fmt.Errorf("can't use --reserved-cpus with --system-reserved-cgroup or --kube-reserved-cgroup"))
150+
}
151+
if _, err := cpuset.Parse(kc.ReservedSystemCPUs); err != nil {
152+
allErrors = append(allErrors, fmt.Errorf("unable to parse --reserved-cpus, error: %v", err))
153+
}
154+
}
145155

146156
if err := validateKubeletOSConfiguration(kc); err != nil {
147157
allErrors = append(allErrors, err)

pkg/kubelet/apis/config/validation/validation_test.go

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
)
2727

2828
func TestValidateKubeletConfiguration(t *testing.T) {
29-
successCase := &kubeletconfig.KubeletConfiguration{
29+
successCase1 := &kubeletconfig.KubeletConfiguration{
3030
CgroupsPerQOS: true,
3131
EnforceNodeAllocatable: []string{"pods", "system-reserved", "kube-reserved"},
3232
SystemReservedCgroup: "/system.slice",
@@ -55,11 +55,45 @@ func TestValidateKubeletConfiguration(t *testing.T) {
5555
CPUCFSQuotaPeriod: metav1.Duration{Duration: 100 * time.Millisecond},
5656
TopologyManagerPolicy: "none",
5757
}
58-
if allErrors := ValidateKubeletConfiguration(successCase); allErrors != nil {
58+
if allErrors := ValidateKubeletConfiguration(successCase1); allErrors != nil {
5959
t.Errorf("expect no errors, got %v", allErrors)
6060
}
6161

62-
errorCase := &kubeletconfig.KubeletConfiguration{
62+
successCase2 := &kubeletconfig.KubeletConfiguration{
63+
CgroupsPerQOS: true,
64+
EnforceNodeAllocatable: []string{"pods"},
65+
SystemReservedCgroup: "",
66+
KubeReservedCgroup: "",
67+
SystemCgroups: "",
68+
CgroupRoot: "",
69+
EventBurst: 10,
70+
EventRecordQPS: 5,
71+
HealthzPort: 10248,
72+
ImageGCHighThresholdPercent: 85,
73+
ImageGCLowThresholdPercent: 80,
74+
IPTablesDropBit: 15,
75+
IPTablesMasqueradeBit: 14,
76+
KubeAPIBurst: 10,
77+
KubeAPIQPS: 5,
78+
MaxOpenFiles: 1000000,
79+
MaxPods: 110,
80+
OOMScoreAdj: -999,
81+
PodsPerCore: 100,
82+
Port: 65535,
83+
ReadOnlyPort: 0,
84+
RegistryBurst: 10,
85+
RegistryPullQPS: 5,
86+
HairpinMode: kubeletconfig.PromiscuousBridge,
87+
NodeLeaseDurationSeconds: 1,
88+
CPUCFSQuotaPeriod: metav1.Duration{Duration: 100 * time.Millisecond},
89+
TopologyManagerPolicy: "none",
90+
ReservedSystemCPUs: "0-3",
91+
}
92+
if allErrors := ValidateKubeletConfiguration(successCase2); allErrors != nil {
93+
t.Errorf("expect no errors, got %v", allErrors)
94+
}
95+
96+
errorCase1 := &kubeletconfig.KubeletConfiguration{
6397
CgroupsPerQOS: false,
6498
EnforceNodeAllocatable: []string{"pods", "system-reserved", "kube-reserved", "illegal-key"},
6599
SystemCgroups: "/",
@@ -86,8 +120,43 @@ func TestValidateKubeletConfiguration(t *testing.T) {
86120
CPUCFSQuotaPeriod: metav1.Duration{Duration: 0},
87121
TopologyManagerPolicy: "",
88122
}
89-
const numErrs = 26
90-
if allErrors := ValidateKubeletConfiguration(errorCase); len(allErrors.(utilerrors.Aggregate).Errors()) != numErrs {
91-
t.Errorf("expect %d errors, got %v", numErrs, len(allErrors.(utilerrors.Aggregate).Errors()))
123+
const numErrsErrorCase1 = 26
124+
if allErrors := ValidateKubeletConfiguration(errorCase1); len(allErrors.(utilerrors.Aggregate).Errors()) != numErrsErrorCase1 {
125+
t.Errorf("expect %d errors, got %v", numErrsErrorCase1, len(allErrors.(utilerrors.Aggregate).Errors()))
126+
}
127+
128+
errorCase2 := &kubeletconfig.KubeletConfiguration{
129+
CgroupsPerQOS: true,
130+
EnforceNodeAllocatable: []string{"pods", "system-reserved", "kube-reserved"},
131+
SystemReservedCgroup: "/system.slice",
132+
KubeReservedCgroup: "/kubelet.service",
133+
SystemCgroups: "",
134+
CgroupRoot: "",
135+
EventBurst: 10,
136+
EventRecordQPS: 5,
137+
HealthzPort: 10248,
138+
ImageGCHighThresholdPercent: 85,
139+
ImageGCLowThresholdPercent: 80,
140+
IPTablesDropBit: 15,
141+
IPTablesMasqueradeBit: 14,
142+
KubeAPIBurst: 10,
143+
KubeAPIQPS: 5,
144+
MaxOpenFiles: 1000000,
145+
MaxPods: 110,
146+
OOMScoreAdj: -999,
147+
PodsPerCore: 100,
148+
Port: 65535,
149+
ReadOnlyPort: 0,
150+
RegistryBurst: 10,
151+
RegistryPullQPS: 5,
152+
HairpinMode: kubeletconfig.PromiscuousBridge,
153+
NodeLeaseDurationSeconds: 1,
154+
CPUCFSQuotaPeriod: metav1.Duration{Duration: 100 * time.Millisecond},
155+
TopologyManagerPolicy: "none",
156+
ReservedSystemCPUs: "0-3",
157+
}
158+
const numErrsErrorCase2 = 1
159+
if allErrors := ValidateKubeletConfiguration(errorCase2); len(allErrors.(utilerrors.Aggregate).Errors()) != numErrsErrorCase2 {
160+
t.Errorf("expect %d errors, got %v", numErrsErrorCase2, len(allErrors.(utilerrors.Aggregate).Errors()))
92161
}
93162
}

0 commit comments

Comments
 (0)