From 54a6da979c6d85dc81fae364124d5c7dd465bc6c Mon Sep 17 00:00:00 2001 From: "Tim St. Clair" Date: Mon, 7 Dec 2015 16:42:58 -0800 Subject: [PATCH] Node Allocatable resources proposal --- docs/proposals/node-allocatable.md | 184 ++++++++++++++++++++++++++++ docs/proposals/node-allocatable.png | Bin 0 -> 17673 bytes 2 files changed, 184 insertions(+) create mode 100644 docs/proposals/node-allocatable.md create mode 100644 docs/proposals/node-allocatable.png diff --git a/docs/proposals/node-allocatable.md b/docs/proposals/node-allocatable.md new file mode 100644 index 0000000000000..8429eda96494f --- /dev/null +++ b/docs/proposals/node-allocatable.md @@ -0,0 +1,184 @@ + + + + +WARNING +WARNING +WARNING +WARNING +WARNING + +

PLEASE NOTE: This document applies to the HEAD of the source tree

+ +If you are using a released version of Kubernetes, you should +refer to the docs that go with that version. + + +The latest release of this document can be found +[here](http://releases.k8s.io/release-1.1/docs/proposals/node-allocatable.md). + +Documentation for other releases can be found at +[releases.k8s.io](http://releases.k8s.io). + +-- + + + + + +# Node Allocatable Resources + +**Issue:** https://github.com/kubernetes/kubernetes/issues/13984 + +## Overview + +Currently Node.Status has Capacity, but no concept of node Allocatable. We need additional +parameters to serve several purposes: + +1. [Kubernetes metrics](compute-resource-metrics-api.md) provides "/docker-daemon", "/kubelet", + "/kube-proxy", "/system" etc. raw containers for monitoring system component resource usage + patterns and detecting regressions. Eventually we want to cap system component usage to a certain + limit / request. However this is not currently feasible due to a variety of reasons including: + 1. Docker still uses tons of computing resources (See + [#16943](https://github.com/kubernetes/kubernetes/issues/16943)) + 2. We have not yet defined the minimal system requirements, so we cannot control Kubernetes + nodes or know about arbitrary daemons, which can make the system resources + unmanageable. Even with a resource cap we cannot do a full resource management on the + node, but with the proposed parameters we can mitigate really bad resource over commits + 3. Usage scales with the number of pods running on the node +2. For external schedulers (such as mesos, hadoop, etc.) integration, they might want to partition + compute resources on a given node, limiting how much Kubelet can use. We should provide a + mechanism by which they can query kubelet, and reserve some resources for their own purpose. + +### Scope of proposal + +This proposal deals with resource reporting through the [`Allocatable` field](#allocatable) for more +reliable scheduling, and minimizing resource over commitment. This proposal *does not* cover +resource usage enforcement (e.g. limiting kubernetes component usage), pod eviction (e.g. when +reservation grows), or running multiple Kubelets on a single node. + +## Design + +### Definitions + +![image](node-allocatable.png) + +1. **Node Capacity** - Already provided as + [`NodeStatus.Capacity`](https://htmlpreview.github.io/?https://github.com/kubernetes/kubernetes/HEAD/docs/api-reference/v1/definitions.html#_v1_nodestatus), + this is total capacity read from the node instance, and assumed to be constant. +2. **System-Reserved** (proposed) - Compute resources reserved for processes which are not managed by + Kubernetes. Currently this covers all the processes lumped together in the `/system` raw + container. +3. **Kubelet Allocatable** - Compute resources available for scheduling (including scheduled & + unscheduled resources). This value is the focus of this proposal. See [below](#api-changes) for + more details. +4. **Kube-Reserved** (proposed) - Compute resources reserved for Kubernetes components such as the + docker daemon, kubelet, kube proxy, etc. + +### API changes + +#### Allocatable + +Add `Allocatable` (4) to +[`NodeStatus`](https://htmlpreview.github.io/?https://github.com/kubernetes/kubernetes/HEAD/docs/api-reference/v1/definitions.html#_v1_nodestatus): + +``` +type NodeStatus struct { + ... + // Allocatable represents schedulable resources of a node. + Allocatable ResourceList `json:"allocatable,omitempty"` + ... +} +``` + +Allocatable will be computed by the Kubelet and reported to the API server. It is defined to be: + +``` + [Allocatable] = [Node Capacity] - [Kube-Reserved] - [System-Reserved] +``` + +The scheduler will use `Allocatable` in place of `Capacity` when scheduling pods, and the Kubelet +will use it when performing admission checks. + +*Note: Since kernel usage can fluctuate and is out of kubernetes control, it will be reported as a + separate value (probably via the metrics API). Reporting kernel usage is out-of-scope for this + proposal.* + +#### Kube-Reserved + +`KubeReserved` is the parameter specifying resources reserved for kubernetes components (4). It is +provided as a command-line flag to the Kubelet at startup, and therefore cannot be changed during +normal Kubelet operation (this may change in the [future](#future-work)). + +The flag will be specified as a serialized `ResourceList`, with resources defined by the API +`ResourceName` and values specified in `resource.Quantity` format, e.g.: + +``` +--kube-reserved=cpu=500m,memory=5Mi +``` + +Initially we will only support CPU and memory, but will eventually support more resources. See +[#16889](https://github.com/kubernetes/kubernetes/pull/16889) for disk accounting. + +If KubeReserved is not set it defaults to a sane value (TBD) calculated from machine capacity. If it +is explicitly set to 0 (along with `SystemReserved`), then `Allocatable == Capacity`, and the system +behavior is equivalent to the 1.1 behavior with scheduling based on Capacity. + +#### System-Reserved + +In the initial implementation, `SystemReserved` will be functionally equivalent to +[`KubeReserved`](#system-reserved), but with a different semantic meaning. While KubeReserved +designates resources set asside for kubernetes components, SystemReserved designates resources set +asside for non-kubernetes components (currently this is reported as all the processes lumped +together in the `/system` raw container). + +## Issues + +### Kubernetes reservation is smaller than kubernetes component usage + +**Solution**: Initially, do nothing (best effort). Let the kubernetes daemons overflow the reserved +resources and hope for the best. If the node usage is less than Allocatable, there will be some room +for overflow and the node should continue to function. If the node has been scheduled to capacity +(worst-case scenario) it may enter an unstable state, which is the current behavior in this +situation. + +In the [future](#future-work) we may set a parent cgroup for kubernetes components, with limits set +according to `KubeReserved`. + +### Version discrepancy + +**API server / scheduler is not allocatable-resources aware:** If the Kubelet rejects a Pod but the + scheduler expects the Kubelet to accept it, the system could get stuck in an infinite loop + scheduling a Pod onto the node only to have Kubelet repeatedly reject it. To avoid this situation, + we will do a 2-stage rollout of `Allocatable`. In stage 1 (targeted for 1.2), `Allocatable` will + be reported by the Kubelet and the scheduler will be updated to use it, but Kubelet will continue + to do admission checks based on `Capacity` (same as today). In stage 2 of the rollout (targeted + for 1.3 or later), the Kubelet will start doing admission checks based on `Allocatable`. + +**API server expects `Allocatable` but does not receive it:** If the kubelet is older and does not + provide `Allocatable` in the `NodeStatus`, then `Allocatable` will be + [defaulted](../../pkg/api/v1/defaults.go) to + `Capacity` (which will yield todays behavior of scheduling based on capacity). + +### 3rd party schedulers + +The community should be notified that an update to schedulers is recommended, but if a scheduler is +not updated it falls under the above case of "scheduler is not allocatable-resources aware". + +## Future work + +1. Convert kubelet flags to Config API - Prerequisite to (2). See + [#12245](https://github.com/kubernetes/kubernetes/issues/12245). +2. Set cgroup limits according KubeReserved - as described in the [overview](#overview) +3. Report kernel usage to be considered with scheduling decisions. + + + + +[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/proposals/node-allocatable.md?pixel)]() + diff --git a/docs/proposals/node-allocatable.png b/docs/proposals/node-allocatable.png new file mode 100644 index 0000000000000000000000000000000000000000..d6f5383e7adf8f417362c63791d64beb27cee5e9 GIT binary patch literal 17673 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 literal 0 HcmV?d00001