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 ...@@ -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. - `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. - `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. - `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. - `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. - `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 ...@@ -39,11 +39,10 @@ and set them before the container is run. This document describes design of the
## Motivation ## Motivation
Since we want to make Kubernetes as simple as possible for its users we don’t want to require setting 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.
[Resources](resource-qos.md#resource-specifications) On the other hand having Resources filled is critical for scheduling decisions.
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.
Current solution to set up Resources to hardcoded value has obvious drawbacks. We need to implement a component We need to implement a component which will set initial Resources to a reasonable value.
which will set initial Resources to a reasonable value.
## Design ## Design
...@@ -51,11 +50,9 @@ InitialResources component will be implemented as an [admission plugin](../../pl ...@@ -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). [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. 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 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) 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).
(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. 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. It will prevent from situation when the pod is rejected due to too low or too high estimation.
......
...@@ -26,20 +26,30 @@ const ( ...@@ -26,20 +26,30 @@ const (
KubeProxyOOMScoreAdj int = -999 KubeProxyOOMScoreAdj int = -999
) )
// isMemoryBestEffort returns true if the container's memory requirements are best-effort. // isBestEffort returns true if the container's resource requirements are best-effort.
func isMemoryBestEffort(container *api.Container) bool { func isBestEffort(container *api.Container) bool {
// A container is memory best-effort if its memory request is unspecified or 0. // A container is best-effort if any of its resource requests is unspecified or 0.
// If a request is specified, then the user expects some kind of resource guarantee. if container.Resources.Requests.Memory().Value() == 0 ||
return 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. // isGuaranteed returns true if the container's resource requirements are Guaranteed.
func isMemoryGuaranteed(container *api.Container) bool { func isGuaranteed(container *api.Container) bool {
// A container is memory guaranteed if its memory request == memory limit. // A container is guaranteed if all its request == limit.
// If memory request == memory limit, the user is very confident of resource consumption. memoryRequest := container.Resources.Requests.Memory().Value()
memoryRequest := container.Resources.Requests.Memory() memoryLimit := container.Resources.Limits.Memory().Value()
memoryLimit := container.Resources.Limits.Memory() cpuRequest := container.Resources.Requests.Cpu().Value()
return (*memoryRequest).Cmp(*memoryLimit) == 0 && memoryRequest.Value() != 0 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 // GetContainerOOMAdjust returns the amount by which the OOM score of all processes in the
...@@ -48,25 +58,25 @@ func isMemoryGuaranteed(container *api.Container) bool { ...@@ -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. // 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. // See https://lwn.net/Articles/391222/ for more information.
func GetContainerOOMScoreAdjust(container *api.Container, memoryCapacity int64) int { func GetContainerOOMScoreAdjust(container *api.Container, memoryCapacity int64) int {
if isMemoryGuaranteed(container) { if isGuaranteed(container) {
// Memory guaranteed containers should be the last to get killed. // Guaranteed containers should be the last to get killed.
return -999 return -999
} else if isMemoryBestEffort(container) { } else if isBestEffort(container) {
// Memory best-effort containers should be the first to be killed. // Best-effort containers should be the first to be killed.
return 1000 return 1000
} else { } else {
// Burstable containers are a middle tier, between Guaranteed and Best-Effort. Ideally, // Burstable containers are a middle tier, between Guaranteed and Best-Effort. Ideally,
// we want to protect Burstable containers that consume less memory than requested. // 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 // 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 // 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 // which use more than their request will have an OOM score of 1000 and will be prime
// targets for OOM kills. // targets for OOM kills.
// Note that this is a heuristic, it won't work if a container has many small processes. // Note that this is a heuristic, it won't work if a container has many small processes.
memoryRequest := container.Resources.Requests.Memory().Value() memoryRequest := container.Resources.Requests.Memory().Value()
oomScoreAdjust := 1000 - (1000*memoryRequest)/memoryCapacity oomScoreAdjust := 1000 - (1000*memoryRequest)/memoryCapacity
// A memory guaranteed container using 100% of memory can have an OOM score of 1. Ensure // A guaranteed container using 100% of memory can have an OOM score of 1. Ensure
// that memory burstable containers have a higher OOM score. // that burstable containers have a higher OOM score.
if oomScoreAdjust < 2 { if oomScoreAdjust < 2 {
return 2 return 2
} }
......
...@@ -29,44 +29,40 @@ const ( ...@@ -29,44 +29,40 @@ const (
) )
var ( var (
zeroRequestMemoryBestEffort = api.Container{ zeroRequestBestEffort = api.Container{
Resources: api.ResourceRequirements{ Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
api.ResourceName(api.ResourceMemory): resource.MustParse("0G"),
},
Limits: api.ResourceList{ Limits: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"), api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
}, },
}, },
} }
edgeMemoryBestEffort = api.Container{ edgeBestEffort = api.Container{
Resources: api.ResourceRequirements{ Resources: api.ResourceRequirements{
Requests: api.ResourceList{ Requests: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("0G"), api.ResourceName(api.ResourceCPU): resource.MustParse("0"),
}, },
Limits: api.ResourceList{ 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{ Resources: api.ResourceRequirements{
Limits: api.ResourceList{ 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{ Resources: api.ResourceRequirements{
Requests: api.ResourceList{ Requests: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
}, },
Limits: api.ResourceList{ Limits: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"), api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
...@@ -75,10 +71,11 @@ var ( ...@@ -75,10 +71,11 @@ var (
}, },
} }
memoryBurstable = api.Container{ burstable = api.Container{
Resources: api.ResourceRequirements{ Resources: api.ResourceRequirements{
Requests: api.ResourceList{ Requests: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse(strconv.Itoa(standardMemoryAmount / 2)), api.ResourceName(api.ResourceMemory): resource.MustParse(strconv.Itoa(standardMemoryAmount / 2)),
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
}, },
Limits: api.ResourceList{ Limits: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
...@@ -86,41 +83,42 @@ var ( ...@@ -86,41 +83,42 @@ var (
}, },
} }
memoryBurstableNoLimit = api.Container{ burstableNoLimit = api.Container{
Resources: api.ResourceRequirements{ Resources: api.ResourceRequirements{
Requests: api.ResourceList{ Requests: api.ResourceList{
api.ResourceName(api.ResourceMemory): resource.MustParse(strconv.Itoa(standardMemoryAmount - 1)), api.ResourceName(api.ResourceMemory): resource.MustParse(strconv.Itoa(standardMemoryAmount - 1)),
api.ResourceName(api.ResourceCPU): resource.MustParse("5m"),
}, },
}, },
} }
) )
func TestIsMemoryBestEffort(t *testing.T) { func TestIsBestEffort(t *testing.T) {
validCases := []api.Container{zeroRequestMemoryBestEffort, noRequestMemoryBestEffort, noLimitMemoryBestEffort, edgeMemoryBestEffort} validCases := []api.Container{zeroRequestBestEffort, noRequestBestEffort, noLimitBestEffort, edgeBestEffort}
for _, container := range validCases { for _, container := range validCases {
if !isMemoryBestEffort(&container) { if !isBestEffort(&container) {
t.Errorf("container %+v is memory best-effort", container) t.Errorf("container %+v is best-effort", container)
} }
} }
invalidCases := []api.Container{memoryGuaranteed, memoryBurstable} invalidCases := []api.Container{guaranteed, burstable}
for _, container := range invalidCases { for _, container := range invalidCases {
if isMemoryBestEffort(&container) { if isBestEffort(&container) {
t.Errorf("container %+v is not memory best-effort", container) t.Errorf("container %+v is not best-effort", container)
} }
} }
} }
func TestIsMemoryGuaranteed(t *testing.T) { func TestIsGuaranteed(t *testing.T) {
validCases := []api.Container{memoryGuaranteed} validCases := []api.Container{guaranteed}
for _, container := range validCases { for _, container := range validCases {
if !isMemoryGuaranteed(&container) { if !isGuaranteed(&container) {
t.Errorf("container %+v is memory guaranteed", 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 { for _, container := range invalidCases {
if isMemoryGuaranteed(&container) { if isGuaranteed(&container) {
t.Errorf("container %+v is not memory guaranteed", container) t.Errorf("container %+v is not guaranteed", container)
} }
} }
} }
...@@ -133,46 +131,45 @@ type oomTest struct { ...@@ -133,46 +131,45 @@ type oomTest struct {
} }
func TestGetContainerOOMScoreAdjust(t *testing.T) { func TestGetContainerOOMScoreAdjust(t *testing.T) {
oomTests := []oomTest{ oomTests := []oomTest{
{ {
container: &zeroRequestMemoryBestEffort, container: &zeroRequestBestEffort,
memoryCapacity: 4000000000, memoryCapacity: 4000000000,
lowOOMScoreAdj: 1000, lowOOMScoreAdj: 1000,
highOOMScoreAdj: 1000, highOOMScoreAdj: 1000,
}, },
{ {
container: &edgeMemoryBestEffort, container: &edgeBestEffort,
memoryCapacity: 8000000000, memoryCapacity: 8000000000,
lowOOMScoreAdj: 1000, lowOOMScoreAdj: 1000,
highOOMScoreAdj: 1000, highOOMScoreAdj: 1000,
}, },
{ {
container: &noRequestMemoryBestEffort, container: &noRequestBestEffort,
memoryCapacity: 7230457451, memoryCapacity: 7230457451,
lowOOMScoreAdj: 1000, lowOOMScoreAdj: 1000,
highOOMScoreAdj: 1000, highOOMScoreAdj: 1000,
}, },
{ {
container: &noLimitMemoryBestEffort, container: &noLimitBestEffort,
memoryCapacity: 4000000000, memoryCapacity: 4000000000,
lowOOMScoreAdj: 1000, lowOOMScoreAdj: 1000,
highOOMScoreAdj: 1000, highOOMScoreAdj: 1000,
}, },
{ {
container: &memoryGuaranteed, container: &guaranteed,
memoryCapacity: 123456789, memoryCapacity: 123456789,
lowOOMScoreAdj: -999, lowOOMScoreAdj: -999,
highOOMScoreAdj: -999, highOOMScoreAdj: -999,
}, },
{ {
container: &memoryBurstable, container: &burstable,
memoryCapacity: standardMemoryAmount, memoryCapacity: standardMemoryAmount,
lowOOMScoreAdj: 495, lowOOMScoreAdj: 495,
highOOMScoreAdj: 505, highOOMScoreAdj: 505,
}, },
{ {
container: &memoryBurstableNoLimit, container: &burstableNoLimit,
memoryCapacity: standardMemoryAmount, memoryCapacity: standardMemoryAmount,
lowOOMScoreAdj: 2, lowOOMScoreAdj: 2,
highOOMScoreAdj: 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