diff --git a/cmd/kubelet/app/BUILD b/cmd/kubelet/app/BUILD index 569dd2d4f02..bfd511422e2 100644 --- a/cmd/kubelet/app/BUILD +++ b/cmd/kubelet/app/BUILD @@ -135,6 +135,7 @@ go_library( ], "@io_bazel_rules_go//go/platform:windows": [ "//pkg/windows/service:go_default_library", + "//vendor/golang.org/x/sys/windows:go_default_library", ], "//conditions:default": [], }), @@ -143,6 +144,7 @@ go_library( go_test( name = "go_default_test", srcs = [ + "init_windows_test.go", "server_bootstrap_test.go", "server_test.go", ], diff --git a/cmd/kubelet/app/init_others.go b/cmd/kubelet/app/init_others.go index 4b19ac91b28..1fe895f5d0a 100644 --- a/cmd/kubelet/app/init_others.go +++ b/cmd/kubelet/app/init_others.go @@ -18,6 +18,6 @@ limitations under the License. package app -func initForOS(service bool) error { +func initForOS(service bool, priorityClass string) error { return nil } diff --git a/cmd/kubelet/app/init_windows.go b/cmd/kubelet/app/init_windows.go index a2da4cf0498..bed76806701 100644 --- a/cmd/kubelet/app/init_windows.go +++ b/cmd/kubelet/app/init_windows.go @@ -19,6 +19,10 @@ limitations under the License. package app import ( + "fmt" + + "golang.org/x/sys/windows" + "k8s.io/klog/v2" "k8s.io/kubernetes/pkg/windows/service" ) @@ -26,7 +30,33 @@ const ( serviceName = "kubelet" ) -func initForOS(windowsService bool) error { +// getPriorityValue returns the value associated with a Windows process priorityClass +// Ref: https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/setpriority-method-in-class-win32-process +func getPriorityValue(priorityClassName string) uint32 { + var priorityClassMap = map[string]uint32{ + "IDLE_PRIORITY_CLASS": uint32(64), + "BELOW_NORMAL_PRIORITY_CLASS": uint32(16384), + "NORMAL_PRIORITY_CLASS": uint32(32), + "ABOVE_NORMAL_PRIORITY_CLASS": uint32(32768), + "HIGH_PRIORITY_CLASS": uint32(128), + "REALTIME_PRIORITY_CLASS": uint32(256), + } + return priorityClassMap[priorityClassName] +} + +func initForOS(windowsService bool, windowsPriorityClass string) error { + priority := getPriorityValue(windowsPriorityClass) + if priority == 0 { + return fmt.Errorf("unknown priority class %s, valid ones are available at "+ + "https://docs.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities", windowsPriorityClass) + } + kubeletProcessHandle := windows.CurrentProcess() + // Set the priority of the kubelet process to given priority + klog.Infof("Setting the priority of kubelet process to %s", windowsPriorityClass) + if err := windows.SetPriorityClass(kubeletProcessHandle, priority); err != nil { + return err + } + if windowsService { return service.InitService(serviceName) } diff --git a/cmd/kubelet/app/init_windows_test.go b/cmd/kubelet/app/init_windows_test.go new file mode 100644 index 00000000000..2920e8c69c8 --- /dev/null +++ b/cmd/kubelet/app/init_windows_test.go @@ -0,0 +1,44 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package app + +import "testing" + +func TestIsValidPriorityClass(t *testing.T) { + testCases := []struct { + description string + priorityClassName string + expectedPriorityValue uint32 + }{ + { + description: "Invalid Priority Class", + priorityClassName: "myPriorityClass", + expectedPriorityValue: 0, + }, + { + description: "Valid Priority Class", + priorityClassName: "IDLE_PRIORITY_CLASS", + expectedPriorityValue: uint32(64), + }, + } + for _, test := range testCases { + actualPriorityValue := getPriorityValue(test.priorityClassName) + if test.expectedPriorityValue != actualPriorityValue { + t.Fatalf("unexpected error for %s", test.description) + } + } +} diff --git a/cmd/kubelet/app/options/options.go b/cmd/kubelet/app/options/options.go index 37cbb7d6087..f216bc8484d 100644 --- a/cmd/kubelet/app/options/options.go +++ b/cmd/kubelet/app/options/options.go @@ -111,6 +111,13 @@ type KubeletFlags struct { // Its corresponding flag only gets registered in Windows builds. WindowsService bool + // WindowsPriorityClass sets the priority class associated with the Kubelet process + // Its corresponding flag only gets registered in Windows builds + // The default priority class associated with any process in Windows is NORMAL_PRIORITY_CLASS. Keeping it as is + // to maintain backwards compatibility. + // Source: https://docs.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities + WindowsPriorityClass string + // remoteRuntimeEndpoint is the endpoint of remote runtime service RemoteRuntimeEndpoint string // remoteImageEndpoint is the endpoint of remote image service diff --git a/cmd/kubelet/app/options/osflags_windows.go b/cmd/kubelet/app/options/osflags_windows.go index 8923f5d6f1d..95ee23483e6 100644 --- a/cmd/kubelet/app/options/osflags_windows.go +++ b/cmd/kubelet/app/options/osflags_windows.go @@ -24,4 +24,10 @@ import ( func (f *KubeletFlags) addOSFlags(fs *pflag.FlagSet) { fs.BoolVar(&f.WindowsService, "windows-service", f.WindowsService, "Enable Windows Service Control Manager API integration") + // The default priority class associated with any process in Windows is NORMAL_PRIORITY_CLASS. Keeping it as is + // to maintain backwards compatibility. + // Source: https://docs.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities + fs.StringVar(&f.WindowsPriorityClass, "windows-priorityclass", "NORMAL_PRIORITY_CLASS", + "Set the PriorityClass associated with kubelet process, the default ones are available at "+ + "https://docs.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities") } diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index c7690392b57..f920f65c004 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -414,7 +414,7 @@ func Run(ctx context.Context, s *options.KubeletServer, kubeDeps *kubelet.Depend logOption.Apply() // To help debugging, immediately log version klog.Infof("Version: %+v", version.Get()) - if err := initForOS(s.KubeletFlags.WindowsService); err != nil { + if err := initForOS(s.KubeletFlags.WindowsService, s.KubeletFlags.WindowsPriorityClass); err != nil { return fmt.Errorf("failed OS init: %v", err) } if err := run(ctx, s, kubeDeps, featureGate); err != nil {