mirror of https://github.com/k3s-io/k3s
Merge pull request #73651 from RobertKrawitz/node_pids_limit
Support total process ID limiting for nodespull/564/head
commit
888ff4097a
|
@ -68,6 +68,7 @@ go_library(
|
||||||
"//pkg/kubelet/kubeletconfig/configfiles:go_default_library",
|
"//pkg/kubelet/kubeletconfig/configfiles:go_default_library",
|
||||||
"//pkg/kubelet/server:go_default_library",
|
"//pkg/kubelet/server:go_default_library",
|
||||||
"//pkg/kubelet/server/streaming:go_default_library",
|
"//pkg/kubelet/server/streaming:go_default_library",
|
||||||
|
"//pkg/kubelet/stats/pidlimit:go_default_library",
|
||||||
"//pkg/kubelet/types:go_default_library",
|
"//pkg/kubelet/types:go_default_library",
|
||||||
"//pkg/util/configz:go_default_library",
|
"//pkg/util/configz:go_default_library",
|
||||||
"//pkg/util/filesystem:go_default_library",
|
"//pkg/util/filesystem:go_default_library",
|
||||||
|
|
|
@ -590,8 +590,8 @@ func AddKubeletConfigFlags(mainfs *pflag.FlagSet, c *kubeletconfig.KubeletConfig
|
||||||
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.")
|
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.")
|
||||||
|
|
||||||
// Node Allocatable Flags
|
// Node Allocatable Flags
|
||||||
fs.Var(flag.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]")
|
fs.Var(flag.NewMapStringString(&c.SystemReserved), "system-reserved", "A set of ResourceName=ResourceQuantity (e.g. cpu=200m,memory=500Mi,ephemeral-storage=1Gi,pid=100) pairs that describe resources reserved for non-kubernetes components. Currently only cpu, memory, and pid (process IDs) are supported. See http://kubernetes.io/docs/user-guide/compute-resources for more detail. [default=none]")
|
||||||
fs.Var(flag.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]")
|
fs.Var(flag.NewMapStringString(&c.KubeReserved), "kube-reserved", "A set of ResourceName=ResourceQuantity (e.g. cpu=200m,memory=500Mi,ephemeral-storage=1Gi,pid=100) pairs that describe resources reserved for kubernetes system components. Currently cpu, memory, local ephemeral storage for root file system, and pid (process IDs) are supported. See http://kubernetes.io/docs/user-guide/compute-resources for more detail. [default=none]")
|
||||||
fs.StringSliceVar(&c.EnforceNodeAllocatable, "enforce-node-allocatable", c.EnforceNodeAllocatable, "A comma separated list of levels of node allocatable enforcement to be enforced by kubelet. Acceptable options are 'none', 'pods', 'system-reserved', and 'kube-reserved'. If the latter two options are specified, '--system-reserved-cgroup' and '--kube-reserved-cgroup' must also be set, respectively. If 'none' is specified, no additional options should be set. See https://kubernetes.io/docs/tasks/administer-cluster/reserve-compute-resources/ for more details.")
|
fs.StringSliceVar(&c.EnforceNodeAllocatable, "enforce-node-allocatable", c.EnforceNodeAllocatable, "A comma separated list of levels of node allocatable enforcement to be enforced by kubelet. Acceptable options are 'none', 'pods', 'system-reserved', and 'kube-reserved'. If the latter two options are specified, '--system-reserved-cgroup' and '--kube-reserved-cgroup' must also be set, respectively. If 'none' is specified, no additional options should be set. See https://kubernetes.io/docs/tasks/administer-cluster/reserve-compute-resources/ for more details.")
|
||||||
fs.StringVar(&c.SystemReservedCgroup, "system-reserved-cgroup", c.SystemReservedCgroup, "Absolute name of the top level cgroup that is used to manage non-kubernetes components for which compute resources were reserved via '--system-reserved' flag. Ex. '/system-reserved'. [default='']")
|
fs.StringVar(&c.SystemReservedCgroup, "system-reserved-cgroup", c.SystemReservedCgroup, "Absolute name of the top level cgroup that is used to manage non-kubernetes components for which compute resources were reserved via '--system-reserved' flag. Ex. '/system-reserved'. [default='']")
|
||||||
fs.StringVar(&c.KubeReservedCgroup, "kube-reserved-cgroup", c.KubeReservedCgroup, "Absolute name of the top level cgroup that is used to manage kubernetes components for which compute resources were reserved via '--kube-reserved' flag. Ex. '/kube-reserved'. [default='']")
|
fs.StringVar(&c.KubeReservedCgroup, "kube-reserved-cgroup", c.KubeReservedCgroup, "Absolute name of the top level cgroup that is used to manage kubernetes components for which compute resources were reserved via '--kube-reserved' flag. Ex. '/kube-reserved'. [default='']")
|
||||||
|
|
|
@ -82,6 +82,7 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configfiles"
|
"k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configfiles"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/server"
|
"k8s.io/kubernetes/pkg/kubelet/server"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/server/streaming"
|
"k8s.io/kubernetes/pkg/kubelet/server/streaming"
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/stats/pidlimit"
|
||||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||||
"k8s.io/kubernetes/pkg/util/configz"
|
"k8s.io/kubernetes/pkg/util/configz"
|
||||||
utilfs "k8s.io/kubernetes/pkg/util/filesystem"
|
utilfs "k8s.io/kubernetes/pkg/util/filesystem"
|
||||||
|
@ -1152,8 +1153,9 @@ func parseResourceList(m map[string]string) (v1.ResourceList, error) {
|
||||||
rl := make(v1.ResourceList)
|
rl := make(v1.ResourceList)
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
switch v1.ResourceName(k) {
|
switch v1.ResourceName(k) {
|
||||||
// CPU, memory and local storage resources are supported.
|
// CPU, memory, local storage, and PID resources are supported.
|
||||||
case v1.ResourceCPU, v1.ResourceMemory, v1.ResourceEphemeralStorage:
|
case v1.ResourceCPU, v1.ResourceMemory, v1.ResourceEphemeralStorage, pidlimit.PIDs:
|
||||||
|
if v1.ResourceName(k) != pidlimit.PIDs || utilfeature.DefaultFeatureGate.Enabled(features.SupportNodePidsLimit) {
|
||||||
q, err := resource.ParseQuantity(v)
|
q, err := resource.ParseQuantity(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1162,6 +1164,7 @@ func parseResourceList(m map[string]string) (v1.ResourceList, error) {
|
||||||
return nil, fmt.Errorf("resource quantity for %q cannot be negative: %v", k, v)
|
return nil, fmt.Errorf("resource quantity for %q cannot be negative: %v", k, v)
|
||||||
}
|
}
|
||||||
rl[v1.ResourceName(k)] = q
|
rl[v1.ResourceName(k)] = q
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("cannot reserve %q resource", k)
|
return nil, fmt.Errorf("cannot reserve %q resource", k)
|
||||||
}
|
}
|
||||||
|
|
|
@ -405,6 +405,12 @@ const (
|
||||||
//
|
//
|
||||||
// Enables the AWS EBS in-tree driver to AWS EBS CSI Driver migration feature.
|
// Enables the AWS EBS in-tree driver to AWS EBS CSI Driver migration feature.
|
||||||
CSIMigrationAWS utilfeature.Feature = "CSIMigrationAWS"
|
CSIMigrationAWS utilfeature.Feature = "CSIMigrationAWS"
|
||||||
|
|
||||||
|
// owner: @RobertKrawitz
|
||||||
|
// alpha: v1.14
|
||||||
|
//
|
||||||
|
// Implement support for limiting pids in nodes
|
||||||
|
SupportNodePidsLimit utilfeature.Feature = "SupportNodePidsLimit"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -450,6 +456,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
|
||||||
ResourceLimitsPriorityFunction: {Default: false, PreRelease: utilfeature.Alpha},
|
ResourceLimitsPriorityFunction: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
SupportIPVSProxyMode: {Default: true, PreRelease: utilfeature.GA},
|
SupportIPVSProxyMode: {Default: true, PreRelease: utilfeature.GA},
|
||||||
SupportPodPidsLimit: {Default: true, PreRelease: utilfeature.Beta},
|
SupportPodPidsLimit: {Default: true, PreRelease: utilfeature.Beta},
|
||||||
|
SupportNodePidsLimit: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
HyperVContainer: {Default: false, PreRelease: utilfeature.Alpha},
|
HyperVContainer: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
ScheduleDaemonSetPods: {Default: true, PreRelease: utilfeature.Beta},
|
ScheduleDaemonSetPods: {Default: true, PreRelease: utilfeature.Beta},
|
||||||
TokenRequest: {Default: true, PreRelease: utilfeature.Beta},
|
TokenRequest: {Default: true, PreRelease: utilfeature.Beta},
|
||||||
|
|
|
@ -291,12 +291,12 @@ type KubeletConfiguration struct {
|
||||||
|
|
||||||
/* the following fields are meant for Node Allocatable */
|
/* the following fields are meant for Node Allocatable */
|
||||||
|
|
||||||
// A set of ResourceName=ResourceQuantity (e.g. cpu=200m,memory=150G) pairs
|
// A set of ResourceName=ResourceQuantity (e.g. cpu=200m,memory=150G,pids=100) pairs
|
||||||
// that describe resources reserved for non-kubernetes components.
|
// that describe resources reserved for non-kubernetes components.
|
||||||
// Currently only cpu and memory are supported.
|
// Currently only cpu and memory are supported.
|
||||||
// See http://kubernetes.io/docs/user-guide/compute-resources for more detail.
|
// See http://kubernetes.io/docs/user-guide/compute-resources for more detail.
|
||||||
SystemReserved map[string]string
|
SystemReserved map[string]string
|
||||||
// A set of ResourceName=ResourceQuantity (e.g. cpu=200m,memory=150G) pairs
|
// A set of ResourceName=ResourceQuantity (e.g. cpu=200m,memory=150G,pids=100) pairs
|
||||||
// that describe resources reserved for kubernetes system components.
|
// that describe resources reserved for kubernetes system components.
|
||||||
// Currently cpu, memory and local ephemeral storage for root file system are supported.
|
// 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.
|
// See http://kubernetes.io/docs/user-guide/compute-resources for more detail.
|
||||||
|
|
|
@ -73,6 +73,7 @@ go_library(
|
||||||
"//pkg/kubelet/events:go_default_library",
|
"//pkg/kubelet/events:go_default_library",
|
||||||
"//pkg/kubelet/metrics:go_default_library",
|
"//pkg/kubelet/metrics:go_default_library",
|
||||||
"//pkg/kubelet/qos:go_default_library",
|
"//pkg/kubelet/qos:go_default_library",
|
||||||
|
"//pkg/kubelet/stats/pidlimit:go_default_library",
|
||||||
"//pkg/kubelet/types:go_default_library",
|
"//pkg/kubelet/types:go_default_library",
|
||||||
"//pkg/util/mount:go_default_library",
|
"//pkg/util/mount:go_default_library",
|
||||||
"//pkg/util/oom:go_default_library",
|
"//pkg/util/oom:go_default_library",
|
||||||
|
|
|
@ -257,7 +257,7 @@ func (m *cgroupManagerImpl) Exists(name CgroupName) bool {
|
||||||
// in https://github.com/opencontainers/runc/issues/1440
|
// in https://github.com/opencontainers/runc/issues/1440
|
||||||
// once resolved, we can remove this code.
|
// once resolved, we can remove this code.
|
||||||
whitelistControllers := sets.NewString("cpu", "cpuacct", "cpuset", "memory", "systemd")
|
whitelistControllers := sets.NewString("cpu", "cpuacct", "cpuset", "memory", "systemd")
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) {
|
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) || utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportNodePidsLimit) {
|
||||||
whitelistControllers.Insert("pids")
|
whitelistControllers.Insert("pids")
|
||||||
}
|
}
|
||||||
var missingPaths []string
|
var missingPaths []string
|
||||||
|
@ -325,10 +325,11 @@ func getSupportedSubsystems() map[subsystem]bool {
|
||||||
supportedSubsystems := map[subsystem]bool{
|
supportedSubsystems := map[subsystem]bool{
|
||||||
&cgroupfs.MemoryGroup{}: true,
|
&cgroupfs.MemoryGroup{}: true,
|
||||||
&cgroupfs.CpuGroup{}: true,
|
&cgroupfs.CpuGroup{}: true,
|
||||||
|
&cgroupfs.PidsGroup{}: true,
|
||||||
}
|
}
|
||||||
// not all hosts support hugetlb cgroup, and in the absent of hugetlb, we will fail silently by reporting no capacity.
|
// not all hosts support hugetlb cgroup, and in the absent of hugetlb, we will fail silently by reporting no capacity.
|
||||||
supportedSubsystems[&cgroupfs.HugetlbGroup{}] = false
|
supportedSubsystems[&cgroupfs.HugetlbGroup{}] = false
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) {
|
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) || utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportNodePidsLimit) {
|
||||||
supportedSubsystems[&cgroupfs.PidsGroup{}] = true
|
supportedSubsystems[&cgroupfs.PidsGroup{}] = true
|
||||||
}
|
}
|
||||||
return supportedSubsystems
|
return supportedSubsystems
|
||||||
|
@ -377,9 +378,9 @@ func (m *cgroupManagerImpl) toResources(resourceConfig *ResourceConfig) *libcont
|
||||||
if resourceConfig.CpuPeriod != nil {
|
if resourceConfig.CpuPeriod != nil {
|
||||||
resources.CpuPeriod = *resourceConfig.CpuPeriod
|
resources.CpuPeriod = *resourceConfig.CpuPeriod
|
||||||
}
|
}
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) {
|
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) || utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportNodePidsLimit) {
|
||||||
if resourceConfig.PodPidsLimit != nil {
|
if resourceConfig.PidsLimit != nil {
|
||||||
resources.PidsLimit = *resourceConfig.PodPidsLimit
|
resources.PidsLimit = *resourceConfig.PidsLimit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if huge pages are enabled, we set them in libcontainer
|
// if huge pages are enabled, we set them in libcontainer
|
||||||
|
@ -431,8 +432,8 @@ func (m *cgroupManagerImpl) Update(cgroupConfig *CgroupConfig) error {
|
||||||
libcontainerCgroupConfig.Path = cgroupConfig.Name.ToCgroupfs()
|
libcontainerCgroupConfig.Path = cgroupConfig.Name.ToCgroupfs()
|
||||||
}
|
}
|
||||||
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) && cgroupConfig.ResourceParameters != nil && cgroupConfig.ResourceParameters.PodPidsLimit != nil {
|
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) && cgroupConfig.ResourceParameters != nil && cgroupConfig.ResourceParameters.PidsLimit != nil {
|
||||||
libcontainerCgroupConfig.PidsLimit = *cgroupConfig.ResourceParameters.PodPidsLimit
|
libcontainerCgroupConfig.PidsLimit = *cgroupConfig.ResourceParameters.PidsLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := setSupportedSubsystems(libcontainerCgroupConfig); err != nil {
|
if err := setSupportedSubsystems(libcontainerCgroupConfig); err != nil {
|
||||||
|
@ -461,8 +462,8 @@ func (m *cgroupManagerImpl) Create(cgroupConfig *CgroupConfig) error {
|
||||||
libcontainerCgroupConfig.Path = cgroupConfig.Name.ToCgroupfs()
|
libcontainerCgroupConfig.Path = cgroupConfig.Name.ToCgroupfs()
|
||||||
}
|
}
|
||||||
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) && cgroupConfig.ResourceParameters != nil && cgroupConfig.ResourceParameters.PodPidsLimit != nil {
|
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) && cgroupConfig.ResourceParameters != nil && cgroupConfig.ResourceParameters.PidsLimit != nil {
|
||||||
libcontainerCgroupConfig.PidsLimit = *cgroupConfig.ResourceParameters.PodPidsLimit
|
libcontainerCgroupConfig.PidsLimit = *cgroupConfig.ResourceParameters.PidsLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the manager with the specified cgroup configuration
|
// get the manager with the specified cgroup configuration
|
||||||
|
|
|
@ -53,6 +53,7 @@ import (
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/qos"
|
"k8s.io/kubernetes/pkg/kubelet/qos"
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/stats/pidlimit"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/status"
|
"k8s.io/kubernetes/pkg/kubelet/status"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/util/pluginwatcher"
|
"k8s.io/kubernetes/pkg/kubelet/util/pluginwatcher"
|
||||||
schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
|
schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
|
||||||
|
@ -123,6 +124,8 @@ type containerManagerImpl struct {
|
||||||
cgroupManager CgroupManager
|
cgroupManager CgroupManager
|
||||||
// Capacity of this node.
|
// Capacity of this node.
|
||||||
capacity v1.ResourceList
|
capacity v1.ResourceList
|
||||||
|
// Capacity of this node, including internal resources.
|
||||||
|
internalCapacity v1.ResourceList
|
||||||
// Absolute cgroupfs path to a cgroup that Kubelet needs to place all pods under.
|
// Absolute cgroupfs path to a cgroup that Kubelet needs to place all pods under.
|
||||||
// This path include a top level container for enforcing Node Allocatable.
|
// This path include a top level container for enforcing Node Allocatable.
|
||||||
cgroupRoot CgroupName
|
cgroupRoot CgroupName
|
||||||
|
@ -219,6 +222,7 @@ func NewContainerManager(mountUtil mount.Interface, cadvisorInterface cadvisor.I
|
||||||
}
|
}
|
||||||
|
|
||||||
var capacity = v1.ResourceList{}
|
var capacity = v1.ResourceList{}
|
||||||
|
var internalCapacity = v1.ResourceList{}
|
||||||
// It is safe to invoke `MachineInfo` on cAdvisor before logically initializing cAdvisor here because
|
// It is safe to invoke `MachineInfo` on cAdvisor before logically initializing cAdvisor here because
|
||||||
// machine info is computed and cached once as part of cAdvisor object creation.
|
// machine info is computed and cached once as part of cAdvisor object creation.
|
||||||
// But `RootFsInfo` and `ImagesFsInfo` are not available at this moment so they will be called later during manager starts
|
// But `RootFsInfo` and `ImagesFsInfo` are not available at this moment so they will be called later during manager starts
|
||||||
|
@ -227,6 +231,15 @@ func NewContainerManager(mountUtil mount.Interface, cadvisorInterface cadvisor.I
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
capacity = cadvisor.CapacityFromMachineInfo(machineInfo)
|
capacity = cadvisor.CapacityFromMachineInfo(machineInfo)
|
||||||
|
for k, v := range capacity {
|
||||||
|
internalCapacity[k] = v
|
||||||
|
}
|
||||||
|
pidlimits, err := pidlimit.Stats()
|
||||||
|
if err == nil && pidlimits != nil && pidlimits.MaxPID != nil {
|
||||||
|
internalCapacity[pidlimit.PIDs] = *resource.NewQuantity(
|
||||||
|
int64(*pidlimits.MaxPID),
|
||||||
|
resource.DecimalSI)
|
||||||
|
}
|
||||||
|
|
||||||
// Turn CgroupRoot from a string (in cgroupfs path format) to internal CgroupName
|
// Turn CgroupRoot from a string (in cgroupfs path format) to internal CgroupName
|
||||||
cgroupRoot := ParseCgroupfsToCgroupName(nodeConfig.CgroupRoot)
|
cgroupRoot := ParseCgroupfsToCgroupName(nodeConfig.CgroupRoot)
|
||||||
|
@ -264,6 +277,7 @@ func NewContainerManager(mountUtil mount.Interface, cadvisorInterface cadvisor.I
|
||||||
subsystems: subsystems,
|
subsystems: subsystems,
|
||||||
cgroupManager: cgroupManager,
|
cgroupManager: cgroupManager,
|
||||||
capacity: capacity,
|
capacity: capacity,
|
||||||
|
internalCapacity: internalCapacity,
|
||||||
cgroupRoot: cgroupRoot,
|
cgroupRoot: cgroupRoot,
|
||||||
recorder: recorder,
|
recorder: recorder,
|
||||||
qosContainerManager: qosContainerManager,
|
qosContainerManager: qosContainerManager,
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/events"
|
"k8s.io/kubernetes/pkg/kubelet/events"
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/stats/pidlimit"
|
||||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ func (cm *containerManagerImpl) createNodeAllocatableCgroups() error {
|
||||||
cgroupConfig := &CgroupConfig{
|
cgroupConfig := &CgroupConfig{
|
||||||
Name: cm.cgroupRoot,
|
Name: cm.cgroupRoot,
|
||||||
// The default limits for cpu shares can be very low which can lead to CPU starvation for pods.
|
// The default limits for cpu shares can be very low which can lead to CPU starvation for pods.
|
||||||
ResourceParameters: getCgroupConfig(cm.capacity),
|
ResourceParameters: getCgroupConfig(cm.internalCapacity),
|
||||||
}
|
}
|
||||||
if cm.cgroupManager.Exists(cgroupConfig.Name) {
|
if cm.cgroupManager.Exists(cgroupConfig.Name) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -58,10 +59,10 @@ func (cm *containerManagerImpl) enforceNodeAllocatableCgroups() error {
|
||||||
|
|
||||||
// We need to update limits on node allocatable cgroup no matter what because
|
// We need to update limits on node allocatable cgroup no matter what because
|
||||||
// default cpu shares on cgroups are low and can cause cpu starvation.
|
// default cpu shares on cgroups are low and can cause cpu starvation.
|
||||||
nodeAllocatable := cm.capacity
|
nodeAllocatable := cm.internalCapacity
|
||||||
// Use Node Allocatable limits instead of capacity if the user requested enforcing node allocatable.
|
// Use Node Allocatable limits instead of capacity if the user requested enforcing node allocatable.
|
||||||
if cm.CgroupsPerQOS && nc.EnforceNodeAllocatable.Has(kubetypes.NodeAllocatableEnforcementKey) {
|
if cm.CgroupsPerQOS && nc.EnforceNodeAllocatable.Has(kubetypes.NodeAllocatableEnforcementKey) {
|
||||||
nodeAllocatable = cm.getNodeAllocatableAbsolute()
|
nodeAllocatable = cm.getNodeAllocatableInternalAbsolute()
|
||||||
}
|
}
|
||||||
|
|
||||||
klog.V(4).Infof("Attempting to enforce Node Allocatable with config: %+v", nc)
|
klog.V(4).Infof("Attempting to enforce Node Allocatable with config: %+v", nc)
|
||||||
|
@ -130,7 +131,7 @@ func enforceExistingCgroup(cgroupManager CgroupManager, cName CgroupName, rl v1.
|
||||||
if cgroupConfig.ResourceParameters == nil {
|
if cgroupConfig.ResourceParameters == nil {
|
||||||
return fmt.Errorf("%q cgroup is not config properly", cgroupConfig.Name)
|
return fmt.Errorf("%q cgroup is not config properly", cgroupConfig.Name)
|
||||||
}
|
}
|
||||||
klog.V(4).Infof("Enforcing limits on cgroup %q with %d cpu shares and %d bytes of memory", cName, cgroupConfig.ResourceParameters.CpuShares, cgroupConfig.ResourceParameters.Memory)
|
klog.V(4).Infof("Enforcing limits on cgroup %q with %d cpu shares, %d bytes of memory, and %d processes", cName, cgroupConfig.ResourceParameters.CpuShares, cgroupConfig.ResourceParameters.Memory, cgroupConfig.ResourceParameters.PidsLimit)
|
||||||
if !cgroupManager.Exists(cgroupConfig.Name) {
|
if !cgroupManager.Exists(cgroupConfig.Name) {
|
||||||
return fmt.Errorf("%q cgroup does not exist", cgroupConfig.Name)
|
return fmt.Errorf("%q cgroup does not exist", cgroupConfig.Name)
|
||||||
}
|
}
|
||||||
|
@ -157,6 +158,10 @@ func getCgroupConfig(rl v1.ResourceList) *ResourceConfig {
|
||||||
val := MilliCPUToShares(q.MilliValue())
|
val := MilliCPUToShares(q.MilliValue())
|
||||||
rc.CpuShares = &val
|
rc.CpuShares = &val
|
||||||
}
|
}
|
||||||
|
if q, exists := rl[pidlimit.PIDs]; exists {
|
||||||
|
val := q.Value()
|
||||||
|
rc.PidsLimit = &val
|
||||||
|
}
|
||||||
rc.HugePageLimit = HugePageLimits(rl)
|
rc.HugePageLimit = HugePageLimits(rl)
|
||||||
|
|
||||||
return &rc
|
return &rc
|
||||||
|
@ -166,8 +171,12 @@ func getCgroupConfig(rl v1.ResourceList) *ResourceConfig {
|
||||||
// Note that not all resources that are available on the node are included in the returned list of resources.
|
// Note that not all resources that are available on the node are included in the returned list of resources.
|
||||||
// Returns a ResourceList.
|
// Returns a ResourceList.
|
||||||
func (cm *containerManagerImpl) getNodeAllocatableAbsolute() v1.ResourceList {
|
func (cm *containerManagerImpl) getNodeAllocatableAbsolute() v1.ResourceList {
|
||||||
|
return cm.getNodeAllocatableAbsoluteImpl(cm.capacity)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *containerManagerImpl) getNodeAllocatableAbsoluteImpl(capacity v1.ResourceList) v1.ResourceList {
|
||||||
result := make(v1.ResourceList)
|
result := make(v1.ResourceList)
|
||||||
for k, v := range cm.capacity {
|
for k, v := range capacity {
|
||||||
value := *(v.Copy())
|
value := *(v.Copy())
|
||||||
if cm.NodeConfig.SystemReserved != nil {
|
if cm.NodeConfig.SystemReserved != nil {
|
||||||
value.Sub(cm.NodeConfig.SystemReserved[k])
|
value.Sub(cm.NodeConfig.SystemReserved[k])
|
||||||
|
@ -182,7 +191,13 @@ func (cm *containerManagerImpl) getNodeAllocatableAbsolute() v1.ResourceList {
|
||||||
result[k] = value
|
result[k] = value
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// getNodeAllocatableInternalAbsolute is similar to getNodeAllocatableAbsolute except that
|
||||||
|
// it also includes internal resources (currently process IDs). It is intended for setting
|
||||||
|
// up top level cgroups only.
|
||||||
|
func (cm *containerManagerImpl) getNodeAllocatableInternalAbsolute() v1.ResourceList {
|
||||||
|
return cm.getNodeAllocatableAbsoluteImpl(cm.internalCapacity)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNodeAllocatableReservation returns amount of compute or storage resource that have to be reserved on this node from scheduling.
|
// GetNodeAllocatableReservation returns amount of compute or storage resource that have to be reserved on this node from scheduling.
|
||||||
|
|
|
@ -87,7 +87,7 @@ func (m *podContainerManagerImpl) EnsureExists(pod *v1.Pod) error {
|
||||||
ResourceParameters: ResourceConfigForPod(pod, m.enforceCPULimits, m.cpuCFSQuotaPeriod),
|
ResourceParameters: ResourceConfigForPod(pod, m.enforceCPULimits, m.cpuCFSQuotaPeriod),
|
||||||
}
|
}
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) && m.podPidsLimit > 0 {
|
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) && m.podPidsLimit > 0 {
|
||||||
containerConfig.ResourceParameters.PodPidsLimit = &m.podPidsLimit
|
containerConfig.ResourceParameters.PidsLimit = &m.podPidsLimit
|
||||||
}
|
}
|
||||||
if err := m.cgroupManager.Create(containerConfig); err != nil {
|
if err := m.cgroupManager.Create(containerConfig); err != nil {
|
||||||
return fmt.Errorf("failed to create container for %v : %v", podContainerName, err)
|
return fmt.Errorf("failed to create container for %v : %v", podContainerName, err)
|
||||||
|
|
|
@ -34,7 +34,7 @@ type ResourceConfig struct {
|
||||||
// HugePageLimit map from page size (in bytes) to limit (in bytes)
|
// HugePageLimit map from page size (in bytes) to limit (in bytes)
|
||||||
HugePageLimit map[int64]int64
|
HugePageLimit map[int64]int64
|
||||||
// Maximum number of pids
|
// Maximum number of pids
|
||||||
PodPidsLimit *int64
|
PidsLimit *int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// CgroupName is the abstract name of a cgroup prior to any driver specific conversion.
|
// CgroupName is the abstract name of a cgroup prior to any driver specific conversion.
|
||||||
|
|
|
@ -10,8 +10,6 @@ go_library(
|
||||||
"helper.go",
|
"helper.go",
|
||||||
"log_metrics_provider.go",
|
"log_metrics_provider.go",
|
||||||
"stats_provider.go",
|
"stats_provider.go",
|
||||||
"stats_provider_linux.go",
|
|
||||||
"stats_provider_unsupported.go",
|
|
||||||
],
|
],
|
||||||
importpath = "k8s.io/kubernetes/pkg/kubelet/stats",
|
importpath = "k8s.io/kubernetes/pkg/kubelet/stats",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
|
@ -26,6 +24,7 @@ go_library(
|
||||||
"//pkg/kubelet/leaky:go_default_library",
|
"//pkg/kubelet/leaky:go_default_library",
|
||||||
"//pkg/kubelet/pod:go_default_library",
|
"//pkg/kubelet/pod:go_default_library",
|
||||||
"//pkg/kubelet/server/stats:go_default_library",
|
"//pkg/kubelet/server/stats:go_default_library",
|
||||||
|
"//pkg/kubelet/stats/pidlimit:go_default_library",
|
||||||
"//pkg/kubelet/status:go_default_library",
|
"//pkg/kubelet/status:go_default_library",
|
||||||
"//pkg/kubelet/types:go_default_library",
|
"//pkg/kubelet/types:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
|
@ -52,7 +51,10 @@ filegroup(
|
||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
name = "all-srcs",
|
name = "all-srcs",
|
||||||
srcs = [":package-srcs"],
|
srcs = [
|
||||||
|
":package-srcs",
|
||||||
|
"//pkg/kubelet/stats/pidlimit:all-srcs",
|
||||||
|
],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"pidlimit.go",
|
||||||
|
"pidlimit_linux.go",
|
||||||
|
"pidlimit_unsupported.go",
|
||||||
|
],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/kubelet/stats/pidlimit",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
|
] + select({
|
||||||
|
"@io_bazel_rules_go//go/platform:android": [
|
||||||
|
"//pkg/kubelet/apis/stats/v1alpha1:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:darwin": [
|
||||||
|
"//pkg/kubelet/apis/stats/v1alpha1:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:dragonfly": [
|
||||||
|
"//pkg/kubelet/apis/stats/v1alpha1:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:freebsd": [
|
||||||
|
"//pkg/kubelet/apis/stats/v1alpha1:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:linux": [
|
||||||
|
"//pkg/kubelet/apis/stats/v1alpha1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:nacl": [
|
||||||
|
"//pkg/kubelet/apis/stats/v1alpha1:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:netbsd": [
|
||||||
|
"//pkg/kubelet/apis/stats/v1alpha1:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:openbsd": [
|
||||||
|
"//pkg/kubelet/apis/stats/v1alpha1:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:plan9": [
|
||||||
|
"//pkg/kubelet/apis/stats/v1alpha1:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:solaris": [
|
||||||
|
"//pkg/kubelet/apis/stats/v1alpha1:go_default_library",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:windows": [
|
||||||
|
"//pkg/kubelet/apis/stats/v1alpha1:go_default_library",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 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 pidlimit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// PIDs is the (internal) name for this resource
|
||||||
|
PIDs v1.ResourceName = "pid"
|
||||||
|
)
|
|
@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package stats
|
package pidlimit
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -28,7 +28,8 @@ import (
|
||||||
statsapi "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
|
statsapi "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *StatsProvider) RlimitStats() (*statsapi.RlimitStats, error) {
|
// Stats provides basic information about max and current process count
|
||||||
|
func Stats() (*statsapi.RlimitStats, error) {
|
||||||
rlimit := &statsapi.RlimitStats{}
|
rlimit := &statsapi.RlimitStats{}
|
||||||
|
|
||||||
if content, err := ioutil.ReadFile("/proc/sys/kernel/pid_max"); err == nil {
|
if content, err := ioutil.ReadFile("/proc/sys/kernel/pid_max"); err == nil {
|
|
@ -16,12 +16,13 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package stats
|
package pidlimit
|
||||||
|
|
||||||
import (
|
import (
|
||||||
statsapi "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
|
statsapi "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *StatsProvider) RlimitStats() (*statsapi.RlimitStats, error) {
|
// Stats provides basic information about max and current process count
|
||||||
|
func Stats() (*statsapi.RlimitStats, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
|
@ -28,6 +28,7 @@ import (
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
kubepod "k8s.io/kubernetes/pkg/kubelet/pod"
|
kubepod "k8s.io/kubernetes/pkg/kubelet/pod"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/server/stats"
|
"k8s.io/kubernetes/pkg/kubelet/server/stats"
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/stats/pidlimit"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/status"
|
"k8s.io/kubernetes/pkg/kubelet/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -96,6 +97,11 @@ type rlimitStatsProvider interface {
|
||||||
RlimitStats() (*statsapi.RlimitStats, error)
|
RlimitStats() (*statsapi.RlimitStats, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RlimitStats returns base information about process count
|
||||||
|
func (p *StatsProvider) RlimitStats() (*statsapi.RlimitStats, error) {
|
||||||
|
return pidlimit.Stats()
|
||||||
|
}
|
||||||
|
|
||||||
// GetCgroupStats returns the stats of the cgroup with the cgroupName. Note that
|
// GetCgroupStats returns the stats of the cgroup with the cgroupName. Note that
|
||||||
// this function doesn't generate filesystem stats.
|
// this function doesn't generate filesystem stats.
|
||||||
func (p *StatsProvider) GetCgroupStats(cgroupName string, updateStats bool) (*statsapi.ContainerStats, *statsapi.NetworkStats, error) {
|
func (p *StatsProvider) GetCgroupStats(cgroupName string, updateStats bool) (*statsapi.ContainerStats, *statsapi.NetworkStats, error) {
|
||||||
|
|
|
@ -165,6 +165,7 @@ go_test(
|
||||||
] + select({
|
] + select({
|
||||||
"@io_bazel_rules_go//go/platform:linux": [
|
"@io_bazel_rules_go//go/platform:linux": [
|
||||||
"//cmd/kubeadm/app/util/system:go_default_library",
|
"//cmd/kubeadm/app/util/system:go_default_library",
|
||||||
|
"//pkg/kubelet/stats/pidlimit:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
|
||||||
|
|
|
@ -29,8 +29,10 @@ import (
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
|
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cm"
|
"k8s.io/kubernetes/pkg/kubelet/cm"
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/stats/pidlimit"
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
|
@ -38,14 +40,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func setDesiredConfiguration(initialConfig *kubeletconfig.KubeletConfiguration) {
|
func setDesiredConfiguration(initialConfig *kubeletconfig.KubeletConfiguration) {
|
||||||
|
initialConfig.FeatureGates[string(features.SupportNodePidsLimit)] = true
|
||||||
initialConfig.EnforceNodeAllocatable = []string{"pods", kubeReservedCgroup, systemReservedCgroup}
|
initialConfig.EnforceNodeAllocatable = []string{"pods", kubeReservedCgroup, systemReservedCgroup}
|
||||||
initialConfig.SystemReserved = map[string]string{
|
initialConfig.SystemReserved = map[string]string{
|
||||||
string(v1.ResourceCPU): "100m",
|
string(v1.ResourceCPU): "100m",
|
||||||
string(v1.ResourceMemory): "100Mi",
|
string(v1.ResourceMemory): "100Mi",
|
||||||
|
string(pidlimit.PIDs): "1000",
|
||||||
}
|
}
|
||||||
initialConfig.KubeReserved = map[string]string{
|
initialConfig.KubeReserved = map[string]string{
|
||||||
string(v1.ResourceCPU): "100m",
|
string(v1.ResourceCPU): "100m",
|
||||||
string(v1.ResourceMemory): "100Mi",
|
string(v1.ResourceMemory): "100Mi",
|
||||||
|
string(pidlimit.PIDs): "738",
|
||||||
}
|
}
|
||||||
initialConfig.EvictionHard = map[string]string{"memory.available": "100Mi"}
|
initialConfig.EvictionHard = map[string]string{"memory.available": "100Mi"}
|
||||||
// Necessary for allocatable cgroup creation.
|
// Necessary for allocatable cgroup creation.
|
||||||
|
@ -81,8 +86,8 @@ func expectFileValToEqual(filePath string, expectedValue, delta int64) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAllocatableLimits(cpu, memory string, capacity v1.ResourceList) (*resource.Quantity, *resource.Quantity) {
|
func getAllocatableLimits(cpu, memory, pids string, capacity v1.ResourceList) (*resource.Quantity, *resource.Quantity, *resource.Quantity) {
|
||||||
var allocatableCPU, allocatableMemory *resource.Quantity
|
var allocatableCPU, allocatableMemory, allocatablePIDs *resource.Quantity
|
||||||
// Total cpu reservation is 200m.
|
// Total cpu reservation is 200m.
|
||||||
for k, v := range capacity {
|
for k, v := range capacity {
|
||||||
if k == v1.ResourceCPU {
|
if k == v1.ResourceCPU {
|
||||||
|
@ -94,7 +99,13 @@ func getAllocatableLimits(cpu, memory string, capacity v1.ResourceList) (*resour
|
||||||
allocatableMemory.Sub(resource.MustParse(memory))
|
allocatableMemory.Sub(resource.MustParse(memory))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return allocatableCPU, allocatableMemory
|
// Process IDs are not a node allocatable, so we have to do this ad hoc
|
||||||
|
pidlimits, err := pidlimit.Stats()
|
||||||
|
if err == nil && pidlimits != nil && pidlimits.MaxPID != nil {
|
||||||
|
allocatablePIDs = resource.NewQuantity(int64(*pidlimits.MaxPID), resource.DecimalSI)
|
||||||
|
allocatablePIDs.Sub(resource.MustParse(pids))
|
||||||
|
}
|
||||||
|
return allocatableCPU, allocatableMemory, allocatablePIDs
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -189,7 +200,7 @@ func runTest(f *framework.Framework) error {
|
||||||
}
|
}
|
||||||
node := nodeList.Items[0]
|
node := nodeList.Items[0]
|
||||||
capacity := node.Status.Capacity
|
capacity := node.Status.Capacity
|
||||||
allocatableCPU, allocatableMemory := getAllocatableLimits("200m", "200Mi", capacity)
|
allocatableCPU, allocatableMemory, allocatablePIDs := getAllocatableLimits("200m", "200Mi", "1738", capacity)
|
||||||
// Total Memory reservation is 200Mi excluding eviction thresholds.
|
// Total Memory reservation is 200Mi excluding eviction thresholds.
|
||||||
// Expect CPU shares on node allocatable cgroup to equal allocatable.
|
// Expect CPU shares on node allocatable cgroup to equal allocatable.
|
||||||
if err := expectFileValToEqual(filepath.Join(subsystems.MountPoints["cpu"], "kubepods", "cpu.shares"), int64(cm.MilliCPUToShares(allocatableCPU.MilliValue())), 10); err != nil {
|
if err := expectFileValToEqual(filepath.Join(subsystems.MountPoints["cpu"], "kubepods", "cpu.shares"), int64(cm.MilliCPUToShares(allocatableCPU.MilliValue())), 10); err != nil {
|
||||||
|
@ -199,11 +210,16 @@ func runTest(f *framework.Framework) error {
|
||||||
if err := expectFileValToEqual(filepath.Join(subsystems.MountPoints["memory"], "kubepods", "memory.limit_in_bytes"), allocatableMemory.Value(), 0); err != nil {
|
if err := expectFileValToEqual(filepath.Join(subsystems.MountPoints["memory"], "kubepods", "memory.limit_in_bytes"), allocatableMemory.Value(), 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Expect PID limit on node allocatable cgroup to equal allocatable.
|
||||||
|
if err := expectFileValToEqual(filepath.Join(subsystems.MountPoints["pids"], "kubepods", "pids.max"), allocatablePIDs.Value(), 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Check that Allocatable reported to scheduler includes eviction thresholds.
|
// Check that Allocatable reported to scheduler includes eviction thresholds.
|
||||||
schedulerAllocatable := node.Status.Allocatable
|
schedulerAllocatable := node.Status.Allocatable
|
||||||
// Memory allocatable should take into account eviction thresholds.
|
// Memory allocatable should take into account eviction thresholds.
|
||||||
allocatableCPU, allocatableMemory = getAllocatableLimits("200m", "300Mi", capacity)
|
// Process IDs are not a scheduler resource and as such cannot be tested here.
|
||||||
|
allocatableCPU, allocatableMemory, _ = getAllocatableLimits("200m", "300Mi", "1738", capacity)
|
||||||
// Expect allocatable to include all resources in capacity.
|
// Expect allocatable to include all resources in capacity.
|
||||||
if len(schedulerAllocatable) != len(capacity) {
|
if len(schedulerAllocatable) != len(capacity) {
|
||||||
return fmt.Errorf("Expected all resources in capacity to be found in allocatable")
|
return fmt.Errorf("Expected all resources in capacity to be found in allocatable")
|
||||||
|
@ -232,6 +248,11 @@ func runTest(f *framework.Framework) error {
|
||||||
if err := expectFileValToEqual(filepath.Join(subsystems.MountPoints["memory"], cgroupManager.Name(kubeReservedCgroupName), "memory.limit_in_bytes"), kubeReservedMemory.Value(), 0); err != nil {
|
if err := expectFileValToEqual(filepath.Join(subsystems.MountPoints["memory"], cgroupManager.Name(kubeReservedCgroupName), "memory.limit_in_bytes"), kubeReservedMemory.Value(), 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Expect process ID limit kube reserved cgroup to equal configured value `738`.
|
||||||
|
kubeReservedPIDs := resource.MustParse(currentConfig.KubeReserved[string(pidlimit.PIDs)])
|
||||||
|
if err := expectFileValToEqual(filepath.Join(subsystems.MountPoints["pids"], cgroupManager.Name(kubeReservedCgroupName), "pids.max"), kubeReservedPIDs.Value(), 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
systemReservedCgroupName := cm.NewCgroupName(cm.RootCgroupName, systemReservedCgroup)
|
systemReservedCgroupName := cm.NewCgroupName(cm.RootCgroupName, systemReservedCgroup)
|
||||||
if !cgroupManager.Exists(systemReservedCgroupName) {
|
if !cgroupManager.Exists(systemReservedCgroupName) {
|
||||||
return fmt.Errorf("Expected system reserved cgroup Does not exist")
|
return fmt.Errorf("Expected system reserved cgroup Does not exist")
|
||||||
|
@ -246,5 +267,10 @@ func runTest(f *framework.Framework) error {
|
||||||
if err := expectFileValToEqual(filepath.Join(subsystems.MountPoints["memory"], cgroupManager.Name(systemReservedCgroupName), "memory.limit_in_bytes"), systemReservedMemory.Value(), 0); err != nil {
|
if err := expectFileValToEqual(filepath.Join(subsystems.MountPoints["memory"], cgroupManager.Name(systemReservedCgroupName), "memory.limit_in_bytes"), systemReservedMemory.Value(), 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Expect process ID limit system reserved cgroup to equal configured value `1000`.
|
||||||
|
systemReservedPIDs := resource.MustParse(currentConfig.SystemReserved[string(pidlimit.PIDs)])
|
||||||
|
if err := expectFileValToEqual(filepath.Join(subsystems.MountPoints["pids"], cgroupManager.Name(systemReservedCgroupName), "pids.max"), systemReservedPIDs.Value(), 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue