| // +build linux |
| |
| /* |
| Copyright 2016 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 cm |
| |
| import ( |
| "fmt" |
| "io/ioutil" |
| "regexp" |
| "strconv" |
| "time" |
| |
| "github.com/opencontainers/runc/libcontainer/cgroups/fs" |
| "github.com/opencontainers/runc/libcontainer/configs" |
| utilversion "k8s.io/apimachinery/pkg/util/version" |
| "k8s.io/apimachinery/pkg/util/wait" |
| "k8s.io/klog" |
| kubecm "k8s.io/kubernetes/pkg/kubelet/cm" |
| "k8s.io/kubernetes/pkg/kubelet/qos" |
| |
| "k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker" |
| ) |
| |
| const ( |
| // The percent of the machine memory capacity. |
| dockerMemoryLimitThresholdPercent = kubecm.DockerMemoryLimitThresholdPercent |
| |
| // The minimum memory limit allocated to docker container. |
| minDockerMemoryLimit = kubecm.MinDockerMemoryLimit |
| |
| // The Docker OOM score adjustment. |
| dockerOOMScoreAdj = qos.DockerOOMScoreAdj |
| ) |
| |
| var ( |
| memoryCapacityRegexp = regexp.MustCompile(`MemTotal:\s*([0-9]+) kB`) |
| ) |
| |
| func NewContainerManager(cgroupsName string, client libdocker.Interface) ContainerManager { |
| return &containerManager{ |
| cgroupsName: cgroupsName, |
| client: client, |
| } |
| } |
| |
| type containerManager struct { |
| // Docker client. |
| client libdocker.Interface |
| // Name of the cgroups. |
| cgroupsName string |
| // Manager for the cgroups. |
| cgroupsManager *fs.Manager |
| } |
| |
| func (m *containerManager) Start() error { |
| // TODO: check if the required cgroups are mounted. |
| if len(m.cgroupsName) != 0 { |
| manager, err := createCgroupManager(m.cgroupsName) |
| if err != nil { |
| return err |
| } |
| m.cgroupsManager = manager |
| } |
| go wait.Until(m.doWork, 5*time.Minute, wait.NeverStop) |
| return nil |
| } |
| |
| func (m *containerManager) doWork() { |
| v, err := m.client.Version() |
| if err != nil { |
| klog.Errorf("Unable to get docker version: %v", err) |
| return |
| } |
| version, err := utilversion.ParseGeneric(v.APIVersion) |
| if err != nil { |
| klog.Errorf("Unable to parse docker version %q: %v", v.APIVersion, err) |
| return |
| } |
| // EnsureDockerInContainer does two things. |
| // 1. Ensure processes run in the cgroups if m.cgroupsManager is not nil. |
| // 2. Ensure processes have the OOM score applied. |
| if err := kubecm.EnsureDockerInContainer(version, dockerOOMScoreAdj, m.cgroupsManager); err != nil { |
| klog.Errorf("Unable to ensure the docker processes run in the desired containers: %v", err) |
| } |
| } |
| |
| func createCgroupManager(name string) (*fs.Manager, error) { |
| var memoryLimit uint64 |
| |
| memoryCapacity, err := getMemoryCapacity() |
| if err != nil { |
| klog.Errorf("Failed to get the memory capacity on machine: %v", err) |
| } else { |
| memoryLimit = memoryCapacity * dockerMemoryLimitThresholdPercent / 100 |
| } |
| |
| if err != nil || memoryLimit < minDockerMemoryLimit { |
| memoryLimit = minDockerMemoryLimit |
| } |
| klog.V(2).Infof("Configure resource-only container %q with memory limit: %d", name, memoryLimit) |
| |
| allowAllDevices := true |
| cm := &fs.Manager{ |
| Cgroups: &configs.Cgroup{ |
| Parent: "/", |
| Name: name, |
| Resources: &configs.Resources{ |
| Memory: int64(memoryLimit), |
| MemorySwap: -1, |
| AllowAllDevices: &allowAllDevices, |
| }, |
| }, |
| } |
| return cm, nil |
| } |
| |
| // getMemoryCapacity returns the memory capacity on the machine in bytes. |
| func getMemoryCapacity() (uint64, error) { |
| out, err := ioutil.ReadFile("/proc/meminfo") |
| if err != nil { |
| return 0, err |
| } |
| return parseCapacity(out, memoryCapacityRegexp) |
| } |
| |
| // parseCapacity matches a Regexp in a []byte, returning the resulting value in bytes. |
| // Assumes that the value matched by the Regexp is in KB. |
| func parseCapacity(b []byte, r *regexp.Regexp) (uint64, error) { |
| matches := r.FindSubmatch(b) |
| if len(matches) != 2 { |
| return 0, fmt.Errorf("failed to match regexp in output: %q", string(b)) |
| } |
| m, err := strconv.ParseUint(string(matches[1]), 10, 64) |
| if err != nil { |
| return 0, err |
| } |
| |
| // Convert to bytes. |
| return m * 1024, err |
| } |