diff --git a/cmd/kubelet/app/init_windows.go b/cmd/kubelet/app/init_windows.go index 3b8ef098cdb..1696f0431b6 100644 --- a/cmd/kubelet/app/init_windows.go +++ b/cmd/kubelet/app/init_windows.go @@ -21,7 +21,9 @@ package app import ( "fmt" + "unsafe" + "github.com/pkg/errors" "golang.org/x/sys/windows" "k8s.io/klog/v2" "k8s.io/kubernetes/pkg/windows/service" @@ -35,28 +37,56 @@ const ( // 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), + "IDLE_PRIORITY_CLASS": uint32(windows.IDLE_PRIORITY_CLASS), + "BELOW_NORMAL_PRIORITY_CLASS": uint32(windows.BELOW_NORMAL_PRIORITY_CLASS), + "NORMAL_PRIORITY_CLASS": uint32(windows.NORMAL_PRIORITY_CLASS), + "ABOVE_NORMAL_PRIORITY_CLASS": uint32(windows.ABOVE_NORMAL_PRIORITY_CLASS), + "HIGH_PRIORITY_CLASS": uint32(windows.HIGH_PRIORITY_CLASS), + "REALTIME_PRIORITY_CLASS": uint32(windows.REALTIME_PRIORITY_CLASS), } return priorityClassMap[priorityClassName] } +// createWindowsJobObject creates a new Job Object +// (https://docs.microsoft.com/en-us/windows/win32/procthread/job-objects), +// and specifies the priority class for the job object to the specified value. +// A job object is used here so that any spawned processes such as powershell or +// wmic are created at the specified thread priority class. +// Running kubelet with above normal / high priority can help improve +// responsiveness on machines with high CPU utilization. +func createWindowsJobObject(pc uint32) (windows.Handle, error) { + job, err := windows.CreateJobObject(nil, nil) + if err != nil { + return windows.InvalidHandle, errors.Wrap(err, "windows.CreateJobObject failed") + } + limitInfo := windows.JOBOBJECT_BASIC_LIMIT_INFORMATION{ + LimitFlags: windows.JOB_OBJECT_LIMIT_PRIORITY_CLASS, + PriorityClass: pc, + } + if _, err := windows.SetInformationJobObject( + job, + windows.JobObjectBasicLimitInformation, + uintptr(unsafe.Pointer(&limitInfo)), + uint32(unsafe.Sizeof(limitInfo))); err != nil { + return windows.InvalidHandle, errors.Wrap(err, "windows.SetInformationJobObject failed") + } + return job, nil +} + 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.InfoS("Setting the priority of kubelet process", "windowsPriorityClass", windowsPriorityClass) - if err := windows.SetPriorityClass(kubeletProcessHandle, priority); err != nil { + klog.InfoS("Creating a Windows job object and adding kubelet process to it", "windowsPriorityClass", windowsPriorityClass) + job, err := createWindowsJobObject(priority) + if err != nil { return err } + if err := windows.AssignProcessToJobObject(job, windows.CurrentProcess()); err != nil { + return errors.Wrap(err, "windows.AssignProcessToJobObject failed") + } if windowsService { return service.InitService(serviceName)