Commit f48c8360 authored by Vishnu kannan's avatar Vishnu kannan

Updating QoS policy to be per-pod instead of per-resource.

parent 96259268
......@@ -42,7 +42,7 @@ The purpose of filtering the nodes is to filter out the nodes that do not meet c
- `NoDiskConflict`: Evaluate if a pod can fit due to the volumes it requests, and those that are already mounted.
- `NoVolumeZoneConflict`: Evaluate if the volumes a pod requests are available on the node, given the Zone restrictions.
- `PodFitsResources`: Check if the free resource (CPU and Memory) meets the requirement of the Pod. The free resource is measured by the capacity minus the sum of requests of all Pods on the node. To learn more about the resource QoS in Kubernetes, please check [QoS proposal](../proposals/resource-qos.md).
- `PodFitsResources`: Check if the free resource (CPU and Memory) meets the requirement of the Pod. The free resource is measured by the capacity minus the sum of requests of all Pods on the node. To learn more about the resource QoS in Kubernetes, please check [QoS proposal](../design/resource-qos.md).
- `PodFitsHostPorts`: Check if any HostPort required by the Pod is already occupied on the node.
- `HostName`: Filter out all nodes except the one specified in the PodSpec's NodeName field.
- `MatchNodeSelector`: Check if the labels of the node match the labels specified in the Pod's `nodeSelector` field and, as of Kubernetes v1.2, also match the `scheduler.alpha.kubernetes.io/affinity` pod annotation if present. See [here](../user-guide/node-selection/) for more details on both.
......
......@@ -39,11 +39,10 @@ and set them before the container is run. This document describes design of the
## Motivation
Since we want to make Kubernetes as simple as possible for its users we don’t want to require setting
[Resources](resource-qos.md#resource-specifications)
for container by its owner. On the other hand having Resources filled is critical for scheduling decisions.
Current solution to set up Resources to hardcoded value has obvious drawbacks. We need to implement a component
which will set initial Resources to a reasonable value.
Since we want to make Kubernetes as simple as possible for its users we don’t want to require setting [Resources](../design/resource-qos.md) for container by its owner.
On the other hand having Resources filled is critical for scheduling decisions.
Current solution to set up Resources to hardcoded value has obvious drawbacks.
We need to implement a component which will set initial Resources to a reasonable value.
## Design
......@@ -51,11 +50,9 @@ InitialResources component will be implemented as an [admission plugin](../../pl
[LimitRanger](https://github.com/kubernetes/kubernetes/blob/7c9bbef96ed7f2a192a1318aa312919b861aee00/cluster/gce/config-default.sh#L91).
For every container without Resources specified it will try to predict amount of resources that should be sufficient for it.
So that a pod without specified resources will be treated as
[Burstable](resource-qos.md#qos-classes).
.
InitialResources will set only [request](resource-qos.md#resource-specifications)
(independently for each resource type: cpu, memory)
field in the first version to avoid killing containers due to OOM (however the container still may be killed if exceeds requested resources).
InitialResources will set only [request](../design/resource-qos.md#requests-and-limits) (independently for each resource type: cpu, memory) field in the first version to avoid killing containers due to OOM (however the container still may be killed if exceeds requested resources).
To make the component work with LimitRanger the estimated value will be capped by min and max possible values if defined.
It will prevent from situation when the pod is rejected due to too low or too high estimation.
......
......@@ -26,20 +26,30 @@ const (
KubeProxyOOMScoreAdj int = -999
)
// isMemoryBestEffort returns true if the container's memory requirements are best-effort.
func isMemoryBestEffort(container *api.Container) bool {
// A container is memory best-effort if its memory request is unspecified or 0.
// If a request is specified, then the user expects some kind of resource guarantee.
return container.Resources.Requests.Memory().Value() == 0
// isBestEffort returns true if the container's resource requirements are best-effort.
func isBestEffort(container *api.Container) bool {
// A container is best-effort if any of its resource requests is unspecified or 0.
if container.Resources.Requests.Memory().Value() == 0 ||
container.Resources.Requests.Cpu().Value() == 0 {
return true
}
return false
}
// isMemoryGuaranteed returns true if the container's memory requirements are Guaranteed.
func isMemoryGuaranteed(container *api.Container) bool {
// A container is memory guaranteed if its memory request == memory limit.
// If memory request == memory limit, the user is very confident of resource consumption.
memoryRequest := container.Resources.Requests.Memory()
memoryLimit := container.Resources.Limits.Memory()
return (*memoryRequest).Cmp(*memoryLimit) == 0 && memoryRequest.Value() != 0
// isGuaranteed returns true if the container's resource requirements are Guaranteed.
func isGuaranteed(container *api.Container) bool {
// A container is guaranteed if all its request == limit.
memoryRequest := container.Resources.Requests.Memory().Value()
memoryLimit := container.Resources.Limits.Memory().Value()
cpuRequest := container.Resources.Requests.Cpu().Value()
cpuLimit := container.Resources.Limits.Cpu().Value()
if memoryRequest != 0 &&
cpuRequest != 0 &&
cpuRequest == cpuLimit &&
memoryRequest == memoryLimit {
return true
}
return false
}
// GetContainerOOMAdjust returns the amount by which the OOM score of all processes in the
......@@ -48,25 +58,25 @@ func isMemoryGuaranteed(container *api.Container) bool {
// and 1000. Containers with higher OOM scores are killed if the system runs out of memory.
// See https://lwn.net/Articles/391222/ for more information.
func GetContainerOOMScoreAdjust(container *api.Container, memoryCapacity int64) int {
if isMemoryGuaranteed(container) {
// Memory guaranteed containers should be the last to get killed.
if isGuaranteed(container) {
// Guaranteed containers should be the last to get killed.
return -999
} else if isMemoryBestEffort(container) {
// Memory best-effort containers should be the first to be killed.
} else if isBestEffort(container) {
// Best-effort containers should be the first to be killed.
return 1000
} else {
// Burstable containers are a middle tier, between Guaranteed and Best-Effort. Ideally,
// we want to protect Burstable containers that consume less memory than requested.
// The formula below is a heuristic. A container requesting for 10% of a system's
// memory will have an oom score adjust of 900. If a process in container Y
// memory will have an OOM score adjust of 900. If a process in container Y
// uses over 10% of memory, its OOM score will be 1000. The idea is that containers
// which use more than their request will have an OOM score of 1000 and will be prime
// targets for OOM kills.
// Note that this is a heuristic, it won't work if a container has many small processes.
memoryRequest := container.Resources.Requests.Memory().Value()
oomScoreAdjust := 1000 - (1000*memoryRequest)/memoryCapacity
// A memory guaranteed container using 100% of memory can have an OOM score of 1. Ensure
// that memory burstable containers have a higher OOM score.
// A guaranteed container using 100% of memory can have an OOM score of 1. Ensure
// that burstable containers have a higher OOM score.
if oomScoreAdjust < 2 {
return 2
}
......
......@@ -29,44 +29,40 @@ const (
)
var (
zeroRequestMemoryBestEffort = api.Container{
zeroRequestBestEffort = api.Container{
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
api.ResourceName(api.ResourceMemory): resource.MustParse("0G"),
},
Limits: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
},
},
}
edgeMemoryBestEffort = api.Container{
edgeBestEffort = api.Container{
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("0G"),
api.ResourceName(api.ResourceCPU): resource.MustParse("0"),
},
Limits: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("0G"),
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
},
},
}
noRequestMemoryBestEffort = api.Container{
noRequestBestEffort = api.Container{
Resources: api.ResourceRequirements{
Limits: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
api.ResourceName(api.ResourceMemory): resource.MustParse("0"),
},
},
}
noLimitMemoryBestEffort = api.Container{}
noLimitBestEffort = api.Container{}
memoryGuaranteed = api.Container{
guaranteed = api.Container{
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
},
Limits: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
......@@ -75,10 +71,11 @@ var (
},
}
memoryBurstable = api.Container{
burstable = api.Container{
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse(strconv.Itoa(standardMemoryAmount / 2)),
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
},
Limits: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
......@@ -86,41 +83,42 @@ var (
},
}
memoryBurstableNoLimit = api.Container{
burstableNoLimit = api.Container{
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse(strconv.Itoa(standardMemoryAmount - 1)),
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
},
},
}
)
func TestIsMemoryBestEffort(t *testing.T) {
validCases := []api.Container{zeroRequestMemoryBestEffort, noRequestMemoryBestEffort, noLimitMemoryBestEffort, edgeMemoryBestEffort}
func TestIsBestEffort(t *testing.T) {
validCases := []api.Container{zeroRequestBestEffort, noRequestBestEffort, noLimitBestEffort, edgeBestEffort}
for _, container := range validCases {
if !isMemoryBestEffort(&container) {
t.Errorf("container %+v is memory best-effort", container)
if !isBestEffort(&container) {
t.Errorf("container %+v is best-effort", container)
}
}
invalidCases := []api.Container{memoryGuaranteed, memoryBurstable}
invalidCases := []api.Container{guaranteed, burstable}
for _, container := range invalidCases {
if isMemoryBestEffort(&container) {
t.Errorf("container %+v is not memory best-effort", container)
if isBestEffort(&container) {
t.Errorf("container %+v is not best-effort", container)
}
}
}
func TestIsMemoryGuaranteed(t *testing.T) {
validCases := []api.Container{memoryGuaranteed}
func TestIsGuaranteed(t *testing.T) {
validCases := []api.Container{guaranteed}
for _, container := range validCases {
if !isMemoryGuaranteed(&container) {
t.Errorf("container %+v is memory guaranteed", container)
if !isGuaranteed(&container) {
t.Errorf("container %+v is guaranteed", container)
}
}
invalidCases := []api.Container{zeroRequestMemoryBestEffort, noRequestMemoryBestEffort, noLimitMemoryBestEffort, edgeMemoryBestEffort, memoryBurstable}
invalidCases := []api.Container{zeroRequestBestEffort, noRequestBestEffort, noLimitBestEffort, edgeBestEffort, burstable}
for _, container := range invalidCases {
if isMemoryGuaranteed(&container) {
t.Errorf("container %+v is not memory guaranteed", container)
if isGuaranteed(&container) {
t.Errorf("container %+v is not guaranteed", container)
}
}
}
......@@ -133,46 +131,45 @@ type oomTest struct {
}
func TestGetContainerOOMScoreAdjust(t *testing.T) {
oomTests := []oomTest{
{
container: &zeroRequestMemoryBestEffort,
container: &zeroRequestBestEffort,
memoryCapacity: 4000000000,
lowOOMScoreAdj: 1000,
highOOMScoreAdj: 1000,
},
{
container: &edgeMemoryBestEffort,
container: &edgeBestEffort,
memoryCapacity: 8000000000,
lowOOMScoreAdj: 1000,
highOOMScoreAdj: 1000,
},
{
container: &noRequestMemoryBestEffort,
container: &noRequestBestEffort,
memoryCapacity: 7230457451,
lowOOMScoreAdj: 1000,
highOOMScoreAdj: 1000,
},
{
container: &noLimitMemoryBestEffort,
container: &noLimitBestEffort,
memoryCapacity: 4000000000,
lowOOMScoreAdj: 1000,
highOOMScoreAdj: 1000,
},
{
container: &memoryGuaranteed,
container: &guaranteed,
memoryCapacity: 123456789,
lowOOMScoreAdj: -999,
highOOMScoreAdj: -999,
},
{
container: &memoryBurstable,
container: &burstable,
memoryCapacity: standardMemoryAmount,
lowOOMScoreAdj: 495,
highOOMScoreAdj: 505,
},
{
container: &memoryBurstableNoLimit,
container: &burstableNoLimit,
memoryCapacity: standardMemoryAmount,
lowOOMScoreAdj: 2,
highOOMScoreAdj: 2,
......
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