Commit 77be271e authored by Vishnu kannan's avatar Vishnu kannan

Linux container manager initialization will now fail if cpu and memory cgroups are not mounted.

parent a518a273
...@@ -35,7 +35,9 @@ import ( ...@@ -35,7 +35,9 @@ import (
"k8s.io/kubernetes/pkg/kubelet/cadvisor" "k8s.io/kubernetes/pkg/kubelet/cadvisor"
"k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/pkg/util/errors" "k8s.io/kubernetes/pkg/util/errors"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/util/oom" "k8s.io/kubernetes/pkg/util/oom"
"k8s.io/kubernetes/pkg/util/sets"
) )
const ( const (
...@@ -70,23 +72,84 @@ func newSystemContainer(containerName string) *systemContainer { ...@@ -70,23 +72,84 @@ func newSystemContainer(containerName string) *systemContainer {
} }
} }
type nodeConfig struct {
dockerDaemonContainerName string
systemContainerName string
kubeletContainerName string
}
type containerManagerImpl struct { type containerManagerImpl struct {
cadvisorInterface cadvisor.Interface
mountUtil mount.Interface
nodeConfig
// External containers being managed. // External containers being managed.
systemContainers []*systemContainer systemContainers []*systemContainer
} }
var _ containerManager = &containerManagerImpl{} var _ containerManager = &containerManagerImpl{}
// checks if the required cgroups subsystems are mounted.
// As of now, only 'cpu' and 'memory' are required.
func validateSystemRequirements(mountUtil mount.Interface) error {
const (
cgroupMountType = "cgroup"
localErr = "system validation failed"
)
mountPoints, err := mountUtil.List()
if err != nil {
return fmt.Errorf("%s - %v", localErr, err)
}
expectedCgroups := sets.NewString("cpu", "cpuacct", "cpuset", "memory")
for _, mountPoint := range mountPoints {
if mountPoint.Type == cgroupMountType {
for _, opt := range mountPoint.Opts {
if expectedCgroups.Has(opt) {
expectedCgroups.Delete(opt)
}
}
}
}
if expectedCgroups.Len() > 0 {
return fmt.Errorf("%s - Following Cgroup subsystem not mounted: %v", localErr, expectedCgroups.List())
}
return nil
}
// TODO(vmarmol): Add limits to the system containers. // TODO(vmarmol): Add limits to the system containers.
// Takes the absolute name of the specified containers. // Takes the absolute name of the specified containers.
// Empty container name disables use of the specified container. // Empty container name disables use of the specified container.
func newContainerManager(cadvisorInterface cadvisor.Interface, dockerDaemonContainerName, systemContainerName, kubeletContainerName string) (containerManager, error) { func newContainerManager(mountUtil mount.Interface, cadvisorInterface cadvisor.Interface, dockerDaemonContainerName, systemContainerName, kubeletContainerName string) (containerManager, error) {
systemContainers := []*systemContainer{} return &containerManagerImpl{
cadvisorInterface: cadvisorInterface,
mountUtil: mountUtil,
nodeConfig: nodeConfig{
dockerDaemonContainerName: dockerDaemonContainerName,
systemContainerName: systemContainerName,
kubeletContainerName: kubeletContainerName,
},
}, nil
}
if dockerDaemonContainerName != "" { // Create a cgroup container manager.
cont := newSystemContainer(dockerDaemonContainerName) func createManager(containerName string) *fs.Manager {
return &fs.Manager{
Cgroups: &configs.Cgroup{
Name: containerName,
AllowAllDevices: true,
},
}
}
info, err := cadvisorInterface.MachineInfo() func (cm *containerManagerImpl) setupNode() error {
if err := validateSystemRequirements(cm.mountUtil); err != nil {
return err
}
systemContainers := []*systemContainer{}
if cm.dockerDaemonContainerName != "" {
cont := newSystemContainer(cm.dockerDaemonContainerName)
info, err := cm.cadvisorInterface.MachineInfo()
var capacity = api.ResourceList{} var capacity = api.ResourceList{}
if err != nil { if err != nil {
} else { } else {
...@@ -94,29 +157,29 @@ func newContainerManager(cadvisorInterface cadvisor.Interface, dockerDaemonConta ...@@ -94,29 +157,29 @@ func newContainerManager(cadvisorInterface cadvisor.Interface, dockerDaemonConta
} }
memoryLimit := (int64(capacity.Memory().Value() * DockerMemoryLimitThresholdPercent / 100)) memoryLimit := (int64(capacity.Memory().Value() * DockerMemoryLimitThresholdPercent / 100))
if memoryLimit < MinDockerMemoryLimit { if memoryLimit < MinDockerMemoryLimit {
glog.Warningf("Memory limit %d for container %s is too small, reset it to %d", memoryLimit, dockerDaemonContainerName, MinDockerMemoryLimit) glog.Warningf("Memory limit %d for container %s is too small, reset it to %d", memoryLimit, cm.dockerDaemonContainerName, MinDockerMemoryLimit)
memoryLimit = MinDockerMemoryLimit memoryLimit = MinDockerMemoryLimit
} }
glog.V(2).Infof("Configure resource-only container %s with memory limit: %d", dockerDaemonContainerName, memoryLimit) glog.V(2).Infof("Configure resource-only container %s with memory limit: %d", cm.dockerDaemonContainerName, memoryLimit)
dockerContainer := &fs.Manager{ dockerContainer := &fs.Manager{
Cgroups: &configs.Cgroup{ Cgroups: &configs.Cgroup{
Name: dockerDaemonContainerName, Name: cm.dockerDaemonContainerName,
Memory: memoryLimit, Memory: memoryLimit,
MemorySwap: -1, MemorySwap: -1,
AllowAllDevices: true, AllowAllDevices: true,
}, },
} }
cont.ensureStateFunc = func(manager *fs.Manager) error { cont.ensureStateFunc = func(manager *fs.Manager) error {
return ensureDockerInContainer(cadvisorInterface, -900, dockerContainer) return ensureDockerInContainer(cm.cadvisorInterface, -900, dockerContainer)
} }
systemContainers = append(systemContainers, cont) systemContainers = append(systemContainers, cont)
} }
if systemContainerName != "" { if cm.systemContainerName != "" {
if systemContainerName == "/" { if cm.systemContainerName == "/" {
return nil, fmt.Errorf("system container cannot be root (\"/\")") return fmt.Errorf("system container cannot be root (\"/\")")
} }
rootContainer := &fs.Manager{ rootContainer := &fs.Manager{
...@@ -124,35 +187,27 @@ func newContainerManager(cadvisorInterface cadvisor.Interface, dockerDaemonConta ...@@ -124,35 +187,27 @@ func newContainerManager(cadvisorInterface cadvisor.Interface, dockerDaemonConta
Name: "/", Name: "/",
}, },
} }
manager := createManager(systemContainerName) manager := createManager(cm.systemContainerName)
err := ensureSystemContainer(rootContainer, manager) err := ensureSystemContainer(rootContainer, manager)
if err != nil { if err != nil {
return nil, err return err
} }
systemContainers = append(systemContainers, newSystemContainer(systemContainerName)) systemContainers = append(systemContainers, newSystemContainer(cm.systemContainerName))
}
if kubeletContainerName != "" {
systemContainers = append(systemContainers, newSystemContainer(kubeletContainerName))
} }
return &containerManagerImpl{ if cm.kubeletContainerName != "" {
systemContainers: systemContainers, systemContainers = append(systemContainers, newSystemContainer(cm.kubeletContainerName))
}, nil
}
// Create a cgroup container manager.
func createManager(containerName string) *fs.Manager {
return &fs.Manager{
Cgroups: &configs.Cgroup{
Name: containerName,
AllowAllDevices: true,
},
} }
cm.systemContainers = systemContainers
return nil
} }
func (cm *containerManagerImpl) Start() error { func (cm *containerManagerImpl) Start() error {
// Setup the node
if err := cm.setupNode(); err != nil {
return err
}
// Don't run a background thread if there are no ensureStateFuncs. // Don't run a background thread if there are no ensureStateFuncs.
numEnsureStateFuncs := 0 numEnsureStateFuncs := 0
for _, cont := range cm.systemContainers { for _, cont := range cm.systemContainers {
......
// +build linux
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 kubelet
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"k8s.io/kubernetes/pkg/util/mount"
)
type fakeMountInterface struct {
mountPoints []mount.MountPoint
}
func (mi *fakeMountInterface) Mount(source string, target string, fstype string, options []string) error {
return fmt.Errorf("unsupported")
}
func (mi *fakeMountInterface) Unmount(target string) error {
return fmt.Errorf("unsupported")
}
func (mi *fakeMountInterface) List() ([]mount.MountPoint, error) {
return mi.mountPoints, nil
}
func (mi *fakeMountInterface) IsLikelyNotMountPoint(file string) (bool, error) {
return false, fmt.Errorf("unsupported")
}
func fakeContainerMgrMountInt() mount.Interface {
return &fakeMountInterface{
[]mount.MountPoint{
{
Device: "cgroup",
Type: "cgroup",
Opts: []string{"rw", "relatime", "cpuset"},
},
{
Device: "cgroup",
Type: "cgroup",
Opts: []string{"rw", "relatime", "cpu"},
},
{
Device: "cgroup",
Type: "cgroup",
Opts: []string{"rw", "relatime", "cpuacct"},
},
{
Device: "cgroup",
Type: "cgroup",
Opts: []string{"rw", "relatime", "memory"},
},
},
}
}
func TestCgroupMountValidationSuccess(t *testing.T) {
assert.Nil(t, validateSystemRequirements(fakeContainerMgrMountInt()))
}
func TestCgroupMountValidationMemoryMissing(t *testing.T) {
mountInt := &fakeMountInterface{
[]mount.MountPoint{
{
Device: "cgroup",
Type: "cgroup",
Opts: []string{"rw", "relatime", "cpuset"},
},
{
Device: "cgroup",
Type: "cgroup",
Opts: []string{"rw", "relatime", "cpu"},
},
{
Device: "cgroup",
Type: "cgroup",
Opts: []string{"rw", "relatime", "cpuacct"},
},
},
}
assert.Error(t, validateSystemRequirements(mountInt))
}
func TestCgroupMountValidationMultipleSubsytem(t *testing.T) {
mountInt := &fakeMountInterface{
[]mount.MountPoint{
{
Device: "cgroup",
Type: "cgroup",
Opts: []string{"rw", "relatime", "cpuset", "memory"},
},
{
Device: "cgroup",
Type: "cgroup",
Opts: []string{"rw", "relatime", "cpu"},
},
{
Device: "cgroup",
Type: "cgroup",
Opts: []string{"rw", "relatime", "cpuacct"},
},
},
}
assert.Nil(t, validateSystemRequirements(mountInt))
}
...@@ -23,6 +23,7 @@ import ( ...@@ -23,6 +23,7 @@ import (
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubelet/cadvisor" "k8s.io/kubernetes/pkg/kubelet/cadvisor"
"k8s.io/kubernetes/pkg/util/mount"
) )
type unsupportedContainerManager struct { type unsupportedContainerManager struct {
...@@ -38,6 +39,6 @@ func (unsupportedContainerManager) SystemContainersLimit() api.ResourceList { ...@@ -38,6 +39,6 @@ func (unsupportedContainerManager) SystemContainersLimit() api.ResourceList {
return api.ResourceList{} return api.ResourceList{}
} }
func newContainerManager(cadvisorInterface cadvisor.Interface, dockerDaemonContainer, systemContainer, kubeletContainer string) (containerManager, error) { func newContainerManager(mounter mount.Interface, cadvisorInterface cadvisor.Interface, dockerDaemonContainer, systemContainer, kubeletContainer string) (containerManager, error) {
return &unsupportedContainerManager{}, nil return &unsupportedContainerManager{}, nil
} }
...@@ -362,7 +362,7 @@ func NewMainKubelet( ...@@ -362,7 +362,7 @@ func NewMainKubelet(
// Setup container manager, can fail if the devices hierarchy is not mounted // Setup container manager, can fail if the devices hierarchy is not mounted
// (it is required by Docker however). // (it is required by Docker however).
containerManager, err := newContainerManager(cadvisorInterface, dockerDaemonContainer, systemContainer, resourceContainer) containerManager, err := newContainerManager(mounter, cadvisorInterface, dockerDaemonContainer, systemContainer, resourceContainer)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create the Container Manager: %v", err) return nil, fmt.Errorf("failed to create the Container Manager: %v", err)
} }
...@@ -758,6 +758,7 @@ func (kl *Kubelet) Run(updates <-chan PodUpdate) { ...@@ -758,6 +758,7 @@ func (kl *Kubelet) Run(updates <-chan PodUpdate) {
// Move Kubelet to a container. // Move Kubelet to a container.
if kl.resourceContainer != "" { if kl.resourceContainer != "" {
// Fixme: I need to reside inside ContainerManager interface.
err := util.RunInResourceContainer(kl.resourceContainer) err := util.RunInResourceContainer(kl.resourceContainer)
if err != nil { if err != nil {
glog.Warningf("Failed to move Kubelet to container %q: %v", kl.resourceContainer, err) glog.Warningf("Failed to move Kubelet to container %q: %v", kl.resourceContainer, err)
......
...@@ -131,7 +131,7 @@ func newTestKubelet(t *testing.T) *TestKubelet { ...@@ -131,7 +131,7 @@ func newTestKubelet(t *testing.T) *TestKubelet {
t: t, t: t,
} }
kubelet.volumeManager = newVolumeManager() kubelet.volumeManager = newVolumeManager()
kubelet.containerManager, _ = newContainerManager(mockCadvisor, "", "", "") kubelet.containerManager, _ = newContainerManager(fakeContainerMgrMountInt(), mockCadvisor, "", "", "")
kubelet.networkConfigured = true kubelet.networkConfigured = true
fakeClock := &util.FakeClock{Time: time.Now()} fakeClock := &util.FakeClock{Time: time.Now()}
kubelet.backOff = util.NewBackOff(time.Second, time.Minute) kubelet.backOff = util.NewBackOff(time.Second, time.Minute)
......
...@@ -51,7 +51,7 @@ func TestRunOnce(t *testing.T) { ...@@ -51,7 +51,7 @@ func TestRunOnce(t *testing.T) {
diskSpaceManager: diskSpaceManager, diskSpaceManager: diskSpaceManager,
containerRuntime: fakeRuntime, containerRuntime: fakeRuntime,
} }
kb.containerManager, _ = newContainerManager(cadvisor, "", "", "") kb.containerManager, _ = newContainerManager(fakeContainerMgrMountInt(), cadvisor, "", "", "")
kb.networkPlugin, _ = network.InitNetworkPlugin([]network.NetworkPlugin{}, "", network.NewFakeHost(nil)) kb.networkPlugin, _ = network.InitNetworkPlugin([]network.NetworkPlugin{}, "", network.NewFakeHost(nil))
if err := kb.setupDataDirs(); err != nil { if err := kb.setupDataDirs(); err != nil {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment