mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 21:47:07 +00:00
Merge pull request #22487 from vishh/node-status-cpu-hardcap
Auto commit by PR queue bot
This commit is contained in:
commit
663f7b8a4c
@ -33,6 +33,9 @@ type ContainerManager interface {
|
|||||||
|
|
||||||
// Returns a NodeConfig that is being used by the container manager.
|
// Returns a NodeConfig that is being used by the container manager.
|
||||||
GetNodeConfig() NodeConfig
|
GetNodeConfig() NodeConfig
|
||||||
|
|
||||||
|
// Returns internal Status.
|
||||||
|
Status() Status
|
||||||
}
|
}
|
||||||
|
|
||||||
type NodeConfig struct {
|
type NodeConfig struct {
|
||||||
@ -41,3 +44,8 @@ type NodeConfig struct {
|
|||||||
KubeletCgroupsName string
|
KubeletCgroupsName string
|
||||||
ContainerRuntime string
|
ContainerRuntime string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Status struct {
|
||||||
|
// Any soft requirements that were unsatisfied.
|
||||||
|
SoftRequirements error
|
||||||
|
}
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -34,6 +35,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/resource"
|
"k8s.io/kubernetes/pkg/api/resource"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cadvisor"
|
"k8s.io/kubernetes/pkg/kubelet/cadvisor"
|
||||||
|
"k8s.io/kubernetes/pkg/util"
|
||||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
"k8s.io/kubernetes/pkg/util/oom"
|
"k8s.io/kubernetes/pkg/util/oom"
|
||||||
@ -79,24 +81,35 @@ type containerManagerImpl struct {
|
|||||||
cadvisorInterface cadvisor.Interface
|
cadvisorInterface cadvisor.Interface
|
||||||
mountUtil mount.Interface
|
mountUtil mount.Interface
|
||||||
NodeConfig
|
NodeConfig
|
||||||
|
status Status
|
||||||
// External containers being managed.
|
// External containers being managed.
|
||||||
systemContainers []*systemContainer
|
systemContainers []*systemContainer
|
||||||
periodicTasks []func()
|
periodicTasks []func()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type features struct {
|
||||||
|
cpuHardcapping bool
|
||||||
|
}
|
||||||
|
|
||||||
var _ ContainerManager = &containerManagerImpl{}
|
var _ ContainerManager = &containerManagerImpl{}
|
||||||
|
|
||||||
// checks if the required cgroups subsystems are mounted.
|
// checks if the required cgroups subsystems are mounted.
|
||||||
// As of now, only 'cpu' and 'memory' are required.
|
// As of now, only 'cpu' and 'memory' are required.
|
||||||
func validateSystemRequirements(mountUtil mount.Interface) error {
|
// cpu quota is a soft requirement.
|
||||||
|
func validateSystemRequirements(mountUtil mount.Interface) (features, error) {
|
||||||
const (
|
const (
|
||||||
cgroupMountType = "cgroup"
|
cgroupMountType = "cgroup"
|
||||||
localErr = "system validation failed"
|
localErr = "system validation failed"
|
||||||
)
|
)
|
||||||
|
var (
|
||||||
|
cpuMountPoint string
|
||||||
|
f features
|
||||||
|
)
|
||||||
mountPoints, err := mountUtil.List()
|
mountPoints, err := mountUtil.List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s - %v", localErr, err)
|
return f, fmt.Errorf("%s - %v", localErr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedCgroups := sets.NewString("cpu", "cpuacct", "cpuset", "memory")
|
expectedCgroups := sets.NewString("cpu", "cpuacct", "cpuset", "memory")
|
||||||
for _, mountPoint := range mountPoints {
|
for _, mountPoint := range mountPoints {
|
||||||
if mountPoint.Type == cgroupMountType {
|
if mountPoint.Type == cgroupMountType {
|
||||||
@ -104,14 +117,31 @@ func validateSystemRequirements(mountUtil mount.Interface) error {
|
|||||||
if expectedCgroups.Has(opt) {
|
if expectedCgroups.Has(opt) {
|
||||||
expectedCgroups.Delete(opt)
|
expectedCgroups.Delete(opt)
|
||||||
}
|
}
|
||||||
|
if opt == "cpu" {
|
||||||
|
cpuMountPoint = mountPoint.Path
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if expectedCgroups.Len() > 0 {
|
if expectedCgroups.Len() > 0 {
|
||||||
return fmt.Errorf("%s - Following Cgroup subsystem not mounted: %v", localErr, expectedCgroups.List())
|
return f, fmt.Errorf("%s - Following Cgroup subsystem not mounted: %v", localErr, expectedCgroups.List())
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
// Check if cpu quota is available.
|
||||||
|
// CPU cgroup is required and so it expected to be mounted at this point.
|
||||||
|
periodExists, err := util.FileExists(path.Join(cpuMountPoint, "cpu.cfs_period_us"))
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("failed to detect if CPU cgroup cpu.cfs_period_us is available - %v", err)
|
||||||
|
}
|
||||||
|
quotaExists, err := util.FileExists(path.Join(cpuMountPoint, "cpu.cfs_quota_us"))
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("failed to detect if CPU cgroup cpu.cfs_quota_us is available - %v", err)
|
||||||
|
}
|
||||||
|
if quotaExists && periodExists {
|
||||||
|
f.cpuHardcapping = true
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(vmarmol): Add limits to the system containers.
|
// TODO(vmarmol): Add limits to the system containers.
|
||||||
@ -185,10 +215,13 @@ func setupKernelTunables(option KernelTunableBehavior) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cm *containerManagerImpl) setupNode() error {
|
func (cm *containerManagerImpl) setupNode() error {
|
||||||
if err := validateSystemRequirements(cm.mountUtil); err != nil {
|
f, err := validateSystemRequirements(cm.mountUtil)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if !f.cpuHardcapping {
|
||||||
|
cm.status.SoftRequirements = fmt.Errorf("CPU hardcapping unsupported")
|
||||||
|
}
|
||||||
// TODO: plumb kernel tunable options into container manager, right now, we modify by default
|
// TODO: plumb kernel tunable options into container manager, right now, we modify by default
|
||||||
if err := setupKernelTunables(KernelTunableModify); err != nil {
|
if err := setupKernelTunables(KernelTunableModify); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -312,6 +345,12 @@ func (cm *containerManagerImpl) GetNodeConfig() NodeConfig {
|
|||||||
return cm.NodeConfig
|
return cm.NodeConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cm *containerManagerImpl) Status() Status {
|
||||||
|
cm.RLock()
|
||||||
|
defer cm.RUnlock()
|
||||||
|
return cm.status
|
||||||
|
}
|
||||||
|
|
||||||
func (cm *containerManagerImpl) Start() error {
|
func (cm *containerManagerImpl) Start() error {
|
||||||
// Setup the node
|
// Setup the node
|
||||||
if err := cm.setupNode(); err != nil {
|
if err := cm.setupNode(); err != nil {
|
||||||
|
@ -20,9 +20,13 @@ package cm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
)
|
)
|
||||||
@ -75,7 +79,9 @@ func fakeContainerMgrMountInt() mount.Interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCgroupMountValidationSuccess(t *testing.T) {
|
func TestCgroupMountValidationSuccess(t *testing.T) {
|
||||||
assert.Nil(t, validateSystemRequirements(fakeContainerMgrMountInt()))
|
f, err := validateSystemRequirements(fakeContainerMgrMountInt())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.False(t, f.cpuHardcapping, "cpu hardcapping is expected to be disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCgroupMountValidationMemoryMissing(t *testing.T) {
|
func TestCgroupMountValidationMemoryMissing(t *testing.T) {
|
||||||
@ -98,7 +104,8 @@ func TestCgroupMountValidationMemoryMissing(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
assert.Error(t, validateSystemRequirements(mountInt))
|
_, err := validateSystemRequirements(mountInt)
|
||||||
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCgroupMountValidationMultipleSubsytem(t *testing.T) {
|
func TestCgroupMountValidationMultipleSubsytem(t *testing.T) {
|
||||||
@ -121,5 +128,37 @@ func TestCgroupMountValidationMultipleSubsytem(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
assert.Nil(t, validateSystemRequirements(mountInt))
|
_, err := validateSystemRequirements(mountInt)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSoftRequirementsValidationSuccess(t *testing.T) {
|
||||||
|
req := require.New(t)
|
||||||
|
tempDir, err := ioutil.TempDir("", "")
|
||||||
|
req.NoError(err)
|
||||||
|
req.NoError(ioutil.WriteFile(path.Join(tempDir, "cpu.cfs_period_us"), []byte("0"), os.ModePerm))
|
||||||
|
req.NoError(ioutil.WriteFile(path.Join(tempDir, "cpu.cfs_quota_us"), []byte("0"), os.ModePerm))
|
||||||
|
mountInt := &fakeMountInterface{
|
||||||
|
[]mount.MountPoint{
|
||||||
|
{
|
||||||
|
Device: "cgroup",
|
||||||
|
Type: "cgroup",
|
||||||
|
Opts: []string{"rw", "relatime", "cpuset"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Device: "cgroup",
|
||||||
|
Type: "cgroup",
|
||||||
|
Opts: []string{"rw", "relatime", "cpu"},
|
||||||
|
Path: tempDir,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Device: "cgroup",
|
||||||
|
Type: "cgroup",
|
||||||
|
Opts: []string{"rw", "relatime", "cpuacct", "memory"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
f, err := validateSystemRequirements(mountInt)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, f.cpuHardcapping, "cpu hardcapping is expected to be enabled")
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,10 @@ func (cm *containerManagerStub) GetNodeConfig() NodeConfig {
|
|||||||
return NodeConfig{}
|
return NodeConfig{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cm *containerManagerStub) Status() Status {
|
||||||
|
return Status{}
|
||||||
|
}
|
||||||
|
|
||||||
func NewStubContainerManager() ContainerManager {
|
func NewStubContainerManager() ContainerManager {
|
||||||
return &containerManagerStub{}
|
return &containerManagerStub{}
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,10 @@ func (unsupportedContainerManager) GetNodeConfig() NodeConfig {
|
|||||||
return NodeConfig{}
|
return NodeConfig{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cm *unsupportedContainerManager) Status() Status {
|
||||||
|
return Status{}
|
||||||
|
}
|
||||||
|
|
||||||
func NewContainerManager(_ mount.Interface, _ cadvisor.Interface, _ NodeConfig) (ContainerManager, error) {
|
func NewContainerManager(_ mount.Interface, _ cadvisor.Interface, _ NodeConfig) (ContainerManager, error) {
|
||||||
return &unsupportedContainerManager{}, nil
|
return &unsupportedContainerManager{}, nil
|
||||||
}
|
}
|
||||||
|
@ -2964,6 +2964,12 @@ func (kl *Kubelet) setNodeReadyCondition(node *api.Node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Record any soft requirements that were not met in the container manager.
|
||||||
|
status := kl.containerManager.Status()
|
||||||
|
if status.SoftRequirements != nil {
|
||||||
|
newNodeReadyCondition.Message = fmt.Sprintf("%s. WARNING: %s", newNodeReadyCondition.Message, status.SoftRequirements.Error())
|
||||||
|
}
|
||||||
|
|
||||||
readyConditionUpdated := false
|
readyConditionUpdated := false
|
||||||
needToRecordEvent := false
|
needToRecordEvent := false
|
||||||
for i := range node.Status.Conditions {
|
for i := range node.Status.Conditions {
|
||||||
|
Loading…
Reference in New Issue
Block a user