11 Working With Compute Resource Requests and Limits

A long time ago, in a blog post far, far away…

…we talked about Resource Quotas and set Object limits to our new Namespace called Playpen. In this post, we will look at Compute Resource Requests and Limits.

Compute Resource Requests and Limits

Before we proceed it is good to understand the difference between a request and a limit. As the name suggests, both are used to control resources, such as CPU and Memory, and are set on a contrainer level.

A Request defines how many resources is a container quaranteed to get

A Limit defines the maximum amount of resources a container can consume

Given these definitions, a request can be equal, but never larger than the limit, otherwise the container would never get scheduled. If you are running multiple containers in a Pod, all container values will be combined to calculate the total values.

CPU

CPU resources are defined in millicores. If you would like a container to use half a core, you would specify 500m. It is generally advised to keep the number of CPUs under 1 to prevent issues with scheduling and provide greater flexibility. Multiple smaller replicas can be deployed to improve performance.

Another imporant thing to note about CPU is that it can be throttled. This means that if your application starts hitting the limits, Kubernetes will prevent it, even if it decreases your application perfomance.

Memory

Memory resources are defined in bytes. You will often see it as mebibytes but you can use other values. For example, if you want your application to use 0.5GB of RAM, you would define it as 512Mi.

As Memory cannot be throttled, containers that surpass their limits will be terminated.

Namespace Settings

While having resources defined at the container level is a good thing, it allows the creators to define whatever they want. To control this, we can set limits on the Namespace level. This may be a good idea if you are using multiple namespaces, for different environments or used by different teams.

Resource Quotas

We have used a Resource Quota in the previous post to define our object count limits, we can use the same quota to define cpu and memory requests or limits. There are 4 settings we can use:

requests.cpu defines the maximum combined CPU requests in the namespace

requests.memory defines the maximum combined Memory requests in the namespace

limits.cpu defines the maximum combined CPU limits in the namespace

limits.memory defines the maximum combined Memory limits in the namespace

Note: For the ease of operation I will switch my context to playpen by running kubectl config set-context - -current - -namespace=playpen

Let’s change our Resource Quota that we have previously defined. As this is set on the Namespace level, we’ll list namespaces first:

kubectl get ns
NAME              STATUS   AGE
default           Active   176d
kube-node-lease   Active   176d
kube-public       Active   176d
kube-system       Active   176d
playpen           Active   93d

To see the quotas defined on our Playpen Namespace we can run the following command:

kubectl describe ns playpen
Name:         playpen
Labels:       <none>
Annotations:  <none>
Status:       Active

Resource Quotas
Name:     object-counts
Resource  Used  Hard
--------  ---   ---
pods      2     2

We can also use the following to list all existing quotas:

kubectl get quota
NAME            AGE   REQUEST     LIMIT
object-counts   93d   pods: 2/2

I am going to copy the exising quota and then modify it:

cp object-quota.yml resource-quota.yml
vi resource-quota.yaml

Add the following to the new file:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: resource-quota
  namespace: playpen
spec:
  hard:
    requests.cpu: 500m
    requests.memory: 512Mi
    limits.cpu: 500m
    limits.memory: 512Mi

My nodes have 1 CPU core and 2GB of Memory, so I have decided to allocate half a core and 512MB of Memory to this namespace.

Next I’m going to apply the new Resource quota and make sure it’s assigned:

kubectl apply -f resource-quota.yml
resourcequota/rosource-quota created

hugo-site % kubectl describe ns playpen
Name:         playpen
Labels:       <none>
Annotations:  <none>
Status:       Active

Resource Quotas
 Name:     object-counts
 Resource  Used  Hard
 --------  ---   ---
 pods      2     2

 Name:            resource-quota
 Resource         Used  Hard
 --------         ---   ---
 limits.cpu       0     500m
 limits.memory    0     512Mi
 requests.cpu     0     500m
 requests.memory  0     512Mi

No LimitRange resource.

We can also use kubectl get quota to see the quotas and their usage:

kubectl get quota
NAME             AGE   REQUEST                                          LIMIT
object-counts    93d   pods: 2/2
resource-quota   3s    requests.cpu: 0/500m, requests.memory: 0/512Mi   limits.cpu: 0/500m, limits.memory: 0/512Mi

As I have my Pod quota set to 2, I will list existing pods and delete one of them:

kubectl get pods
NAME        READY   STATUS    RESTARTS   AGE
lily-pod    1/1     Running   0          93d
lily-pod2   1/1     Running   0          93d

kubectl delete pod lily-pod2
pod "lily-pod2" deleted

Now I’m going to add my requests and limits to my pod definition file lilypod.yml:

apiVersion: v1
kind: Pod
metadata:
  name: lily-pod2
  namespace: playpen
spec:
  containers:
  - name: busybox
    image: busybox
    command: ['sh', '-c', 'while true; do sleep 3600; done']
    resources:
      requests:
        memory: 256Mi
        cpu: 250m
      limits:
        memory: 512Mi 
        cpu: 250m

I will now recreate the pod:

kubectl apply -f lilypod.yml
pod/lily-pod2 created

kubectl get quota
NAME             AGE     REQUEST                                                 LIMIT
object-counts    93d     pods: 2/2
resource-quota   4m44s   requests.cpu: 250m/500m, requests.memory: 256Mi/512Mi   limits.cpu: 250m/500m, limits.memory: 512Mi/512Mi

To test the Quota, I am going to delete the second pod and try to recreate it with the same values:

kubectl delete pod lily-pod
pod "lily-pod" deleted

I will just change the name of the pod to lily-pod (removing2) and try to create it again:

kubectl apply -f lilypod.yml
Error from server (Forbidden): error when creating "lilypod.yml": pods "lily-pod" is forbidden: exceeded quota: resource-quota, requested: limits.memory=512Mi, used: limits.memory=512Mi, limited: limits.memory=512Mi

As you can see, this has failed because the combined value of limits has exceeded our specification.

Limit Range

Last thing we’re going to talk about in this section is called a Limit Range. Limit Range specifies default values that will be applied to a Container at creation, unless they are explicitely defined. Limit Range has 4 sections:

default defines default limits for a container

default.Requests defines default requests for a container

max defines maximum limits that can be set for a container

min defines minimum limits that can be set for a container

To test this, we will create a Limit Range definition file called limitrange.yml:

apiVersion: v1
kind: LimitRange
metadata:
  name: limit-range
  namespace: playpen
spec:
  limits:
  - default:
      memory: 512Mi
      cpu: 500m
    defaultRequest:
      memory: 256Mi
      cpu: 250m
    max:
      memory: 1024Mi
      cpu: 1000m
    min:
      memory: 128Mi
      cpu: 50m
    type: Container

Before applying it, we will delete any existing pods in the namespace. Then we will create the Limit Range:

kubectl apply -f limitrange.yml
limitrange/limit-range created

kubectl describe ns playpen
Name:         playpen
Labels:       <none>
Annotations:  <none>
Status:       Active

Resource Quotas
 Name:     object-counts
 Resource  Used  Hard
 --------  ---   ---
 pods      0     2

 Name:            resource-quota
 Resource         Used  Hard
 --------         ---   ---
 limits.cpu       0     500m
 limits.memory    0     512Mi
 requests.cpu     0     500m
 requests.memory  0     512Mi

Resource Limits
 Type       Resource  Min    Max  Default Request  Default Limit  Max Limit/Request Ratio
 ----       --------  ---    ---  ---------------  -------------  -----------------------
 Container  cpu       50m    1    250m             500m           -
 Container  memory    128Mi  1Gi  256Mi            512Mi          -

Limit range is in place and resources used are show as zero. We will now modify our pod definition by removing the resources we’ve previously defined:

apiVersion: v1
kind: Pod
metadata:
  name: lily-pod2
  namespace: playpen
spec:
  containers:
  - name: busybox
    image: busybox
    command: ['sh', '-c', 'while true; do sleep 3600; done']

Deploy the pod and check the namespace again:

kubectl apply -f lilypod.yml
pod/lily-pod created

kubectl describe ns playpen
Name:         playpen
Labels:       <none>
Annotations:  <none>
Status:       Active

Resource Quotas
 Name:     object-counts
 Resource  Used  Hard
 --------  ---   ---
 pods      1     2

 Name:            resource-quota
 Resource         Used   Hard
 --------         ---    ---
 limits.cpu       500m   500m
 limits.memory    512Mi  512Mi
 requests.cpu     250m   500m
 requests.memory  256Mi  512Mi

Resource Limits
 Type       Resource  Min    Max  Default Request  Default Limit  Max Limit/Request Ratio
 ----       --------  ---    ---  ---------------  -------------  -----------------------
 Container  memory    128Mi  1Gi  256Mi            512Mi          -
 Container  cpu       50m    1    250m             500m           -

You can see the resources are now used. If we try to create another pod, we should exceed our quota:

kubectl apply -f lilypod.yml
Error from server (Forbidden): error when creating "lilypod.yml": pods "lily-pod2" is forbidden: exceeded quota: resource-quota, requested: limits.cpu=500m,limits.memory=512Mi, used: limits.cpu=500m,limits.memory=512Mi, limited: limits.cpu=500m,limits.memory=512Mi

Cleanup

As I don’t want to keep these settings in place I will delete both quotas and limitrange:

kubectl get limitrange
NAME          CREATED AT
limit-range   2021-11-12T13:54:51Z

kubectl delete limitrange limit-range
limitrange "limit-range" deleted

kubectl delete quota object-counts
resourcequota "object-counts" deleted

kubectl delete quota resource-quota
resourcequota "resource-quota" deleted

kubectl describe ns playpen
Name:         playpen
Labels:       <none>
Annotations:  <none>
Status:       Active

No resource quota.

No LimitRange resource.

Last thing I’m going to do is to set my context back to default:

kubectl config set-context --current --namespace=default

This concludes today’s post, see you next time!