Kubernetes Resource Quotas
By Alwyn Botha, Alibaba Cloud Tech Share Author. Tech Share is Alibaba Cloud’s incentive program to encourage the sharing of technical knowledge and best practices within the cloud community.
When several teams of people share Kubernetes development and production nodes (servers running Kubernetes), it is typically a requirement to divide the computing resources equally (CPU, RAM, various types of disk space).
Kubernetes namespaces help with this via creating logically isolated work environments. But namespaces does not enforce limitations / quotas.
We need to use Kubernetes quotas to precisely specify strict quota limits for around 15 Kubernetes API resources.
All the exercises in this tutorial run in the default namespace. At your work you need to divide your nodes into namespaces and define quotas for each of those … separately for development and for production. That specific topic is outside the scope of this tutorial.
This tutorial focus on how to define quotas.
Defining quotas are easy. Checking to see quotas get enforced is easy too, so this tutorial is quite repetitive. Just reading the Kubernetes docs does not let the facts sink in, you need practice and experience, hence this tutorial.
1) Basic Quota : 2 Pods
Enter this using your favorite Linux editor.
It defines a ResourceQuota that specifies a hard limit of 2 Pods.
nano myQuota.yamlapiVersion: v1
kind: ResourceQuota
metadata:
name: count-quotas
spec:
hard:
pods: "2"
Create the Quota
kubectl create -f myQuota.yamlresourcequota/count-quotas created
Get a list of all quotas on your node.
kubectl get quotaNAME CREATED AT
object-counts 2019-01-22T05:53:02Z
Currently only 1 quota object: the one just created.
Describe how Kubernetes interpreted our YAML spec file.
kubectl describe quota object-countsName: object-counts
Namespace: default
Resource Used Hard
-------- ---- ----
pods 0 2
Quota of 2 Pods available, zero used so far.
Let’s create one Pod to use one of the available Pods.
nano myQuota-Pod.yamlapiVersion: v1
kind: Pod
metadata:
name: quota-pod-1
spec:
containers:
- name: quota-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Pod is Running ; sleep 3600']
restartPolicy: Never
terminationGracePeriodSeconds: 0
Nothing special about this Pod spec. Pods need no special action or spec definition to use quota-limited resource.
Quotas are automatically enforced.
WARNING. All Pod specs in this tutorial specifies terminationGracePeriodSeconds: 0
( By default, all deletes are graceful within 30 seconds. This allows time for shutdown routines to run. )
I do this to speed of the deletion of the many Pods throughout this tutorial. Only adjustterminationGracePeriodSeconds during production after careful consideration.
Create the Pod.
kubectl create -f myQuota-Pod.yamlpod/quota-pod-1 created
We want to exceed quota, so we need more Pods.
Edit just the name of the Pod in myQuota-Pod.yaml
name: quota-pod-2
Create this second Pod.
kubectl create -f myQuota-Pod.yamlpod/quota-pod-2 created
Investigate the status of our quota :
kubectl describe quota object-countsName: object-counts
Namespace: default
Resource Used Hard
-------- ---- ----
pods 2 2
2 Pods created so far, full quota used up.
Here are our Pods:
kubectl get poNAME READY STATUS RESTARTS AGE
quota-pod-1 1/1 Running 0 2m36s
quota-pod-2 1/1 Running 0 90s
We want to exceed quota, so we need 1 more Pod.
Edit just the name of the Pod in myQuota-Pod.yaml
name: quota-pod-3
Create the Pod.
kubectl create -f myQuota-Pod.yamlError from server (Forbidden): error when creating "myQuota-Pod.yaml": pods "quota-pod-3" is forbidden: exceeded quota: object-counts, requested: pods=1, used: pods=2, limited: pods=2
As expected, create failed ( Quota enforced ). Error message easy to interpret.
This ends this demo:
- create a quota to limit usage of a resource
- usage within quota limit allowed
- attempt to exceed quota gets a well-deserved error message
Delete Pods and resourcequota objects.
kubectl delete pod/quota-pod-1
pod "quota-pod-1" deletedkubectl delete pod/quota-pod-2
pod "quota-pod-2" deletedkubectl delete quota/object-counts
resourcequota "object-counts" deletedkubectl get po
No resources found.
2) CPU Quota on Requests and Limits
CPU resources are usually high on list of resources to limit via quotas.
Kubernetes supports 2 CPU quotas:
- requests … a Pod requests an amount of CPU resources
- limit … a Pod defines the limit of CPU resources it will use
Below is our ResourceQuota for :
- all Pods in total may request 1 full CPU = 1000 Millicores
- all Pods in total may limit 2 full CPUs = 2000 Millicores
( One Millicores is 1/1000 of a CPU, therefore 1000m equals 1 CPU. 1000m equals one CPU on all computers. )
A four core server has a CPU capacity of 4000m.
nano myQuota.yamlapiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
spec:
hard:
requests.cpu: "1000m"
limits.cpu: "2000m"
Create the Quota
kubectl create -f myQuota.yamlresourcequota/count-quotas created
Now we define a Pod that will use a part of those resources.
nano myQuota-Pod-1.yamlapiVersion: v1
kind: Pod
metadata:
name: quota-pod-1
spec:
containers:
- name: quota-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Pod is Running ; sleep 3600'] resources:
requests:
cpu: "500m"
limits:
cpu: "1500m"
restartPolicy: Never
terminationGracePeriodSeconds: 0
Define a Pod that will use the remaining part of those resources.
nano myQuota-Pod-2.yamlapiVersion: v1
kind: Pod
metadata:
name: quota-pod-2
spec:
containers:
- name: quota-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Pod is Running ; sleep 3600'] resources:
requests:
cpu: "500m"
limits:
cpu: "500m"
restartPolicy: Never
terminationGracePeriodSeconds: 0
Define a Pod that will exceed the CPU resources quota. ( When running simultaneously with 2 previous Pods )
nano myQuota-Pod-3.yamlapiVersion: v1
kind: Pod
metadata:
name: quota-pod-3
spec:
containers:
- name: quota-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Pod is Running ; sleep 3600'] resources:
requests:
cpu: "10m"
limits:
cpu: "50m"
restartPolicy: Never
terminationGracePeriodSeconds: 0
Create the first 2 Pods.
kubectl create -f myQuota-Pod-1.yaml
pod/quota-pod-1 createdkubectl create -f myQuota-Pod-2.yaml
pod/quota-pod-2 created
Investigate if quota usage is as expected.
kubectl describe quota object-countsName: object-counts
Namespace: default
Resource Used Hard
-------- ---- ----
limits.cpu 2 2
requests.cpu 1 1
Our 2 Pods use exactly the total CPU quotas.
Attempt to use even more CPU resources.
kubectl create -f myQuota-Pod-3.yamlError from server (Forbidden): error when creating "myQuota-Pod-3.yaml": pods "quota-pod-3" is forbidden: exceeded quota: object-counts, requested: limits.cpu=50m,requests.cpu=10m, used: limits.cpu=2,requests.cpu=1, limited: limits.cpu=2,requests.cpu=1
As expected: even exceeding quota by 10% results in error.
Demo done, delete …
kubectl delete -f myQuota-Pod-1.yaml
pod "quota-pod-1" deletedkubectl delete -f myQuota-Pod-2.yaml
pod "quota-pod-2" deletedkubectl delete quota/object-counts
resourcequota "object-counts" deleted
3) Quota Count/ Syntax
We will now create a quota of 2 secret API objects using the previous syntax.
Then we test its enforcement.
Then we define the quota using count/ syntax.
nano myQuota.yamlapiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
spec:
hard:
secrets: "2"
Create the Quota
kubectl create -f myQuota.yamlresourcequota/count-quotas created
Describe quota:
kubectl describe quota object-counts
Name: object-counts
Namespace: default
Resource Used Hard
-------- ---- ----
secrets 1 2
Unexpectedly one secret already used ?
List all current secrets:
kubectl get secretsNAME TYPE DATA AGE
default-token-gs2wt kubernetes.io/service-account-token 3 26d
This is the default secret token automatically created by Kubernetes. It is used by Pods to access the Kubernetes API.
So this secret uses 1 of our 2 quota slots.
One very simple and INSECURE way to create a secret is to use this one-liner command:
kubectl create secret generic my-insecure-secret-1 --from-literal=literalkey1=insecure-secret-value-1
secret/my-insecure-secret-1 created
Run it to create a second secret.
List all secrets:
kubectl get secretsNAME TYPE DATA AGE
default-token-gs2wt kubernetes.io/service-account-token 3 26d
my-insecure-secret-1 Opaque 1 3s
Describe the quota status:
kubectl describe quota object-countsName: object-counts
Namespace: default
Resource Used Hard
-------- ---- ----
secrets 2 2
As expected, 2 secrets using full quota.
Attempting to create another secret results in error.
kubectl create secret generic my-insecure-secret-2 --from-literal=literalkey2=insecure-secret-value-2
Error from server (Forbidden): secrets "my-insecure-secret-2" is forbidden: exceeded quota: object-counts, requested: secrets=1, used: secrets=2, limited: secrets=2
This used the ( by now familiar ) first syntax.
Delete my-insecure-secret-1:
kubectl delete secret/my-insecure-secret-1
secret "my-insecure-secret-1" deleted
Delete quota object:
kubectl delete quota/object-counts
Second syntax for specifying count limit quotas:
Just prefix the API object you want to quota with count/
nano myQuota.yamlapiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
spec:
hard:
count/secrets: "2"
Create the Quota
kubectl create -f myQuota.yamlresourcequota/count-quotas created
Describe the quota.
kubectl describe quota object-countsName: object-counts
Namespace: default
Resource Used Hard
-------- ---- ----
count/secrets 1 2
It works exactly as the previous syntax quota.
It correctly already counts the default secret token as one secret already being used.
kubectl get secretsNAME TYPE DATA AGE
default-token-gs2wt kubernetes.io/service-account-token 3 26d
The effect of syntax 1 and 2 is identical:
Syntax 1:
spec:
hard:
pods: "2"
secrets: "2"
Syntax 2:
spec:
hard:
count/pods: "2"
count/secrets: "2"
Pick one of these as your standard way of defining quotas.
Delete Quota
kubectl delete -f myQuota.yamlresourcequota "counts-quota" deleted
From https://kubernetes.io/docs/concepts/policy/resource-quotas/#object-count-quota
List of resources you can limit via object count quota:
- count/persistentvolumeclaims
- count/services
- count/secrets
- count/configmaps
- count/replicationcontrollers
- count/deployments.apps
- count/replicasets.apps
- count/statefulsets.apps
- count/jobs.batch
- count/cronjobs.batch
- count/deployments.extensions
4) Quota on Quality of Service (QoS) Class
You can limit Pods based on their Quality of Service (QoS) class.
For a Pod to be given a QoS class of BestEffort , the Containers in the Pod must not have any memory or CPU limits or requests.
Let’s define a quota of 2 Pods in the BestEffort QoS class.
Terminology: Pods running in the BestEffort class are in the scope of the quota.
nano myQuota.yamlapiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
spec:
hard:
count/pods: "2"
scopes:
- BestEffort
Create the Quota
kubectl create -f myQuota.yamlresourcequota/count-quotas created
Describe the quota:
kubectl describe quota object-countsName: object-counts
Namespace: default
Scopes: BestEffort
* Matches all pods that do not have resource requirements set. These pods have a best effort quality of service.
Resource Used Hard
-------- ---- ----
count/pods 0 2
It clearly describes the BestEffort scope.
Now we need to define BestEffort Pods: one that does not have resource requirements.
None of the Pods below have such lines in their YAML spec files.
resources:
requests:
cpu: "500m"
limits:
cpu: "500m"
We need 3 Pods since 2 Pods will fall within quota limits, the 3rd will attempt to exceed quota.
( Only Pod names differ below )
nano myQuota-Pod-1.yamlapiVersion: v1
kind: Pod
metadata:
name: quota-pod-1
spec:
containers:
- name: quota-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Pod is Running ; sleep 3600']
restartPolicy: Never
terminationGracePeriodSeconds: 0nano myQuota-Pod-2.yamlapiVersion: v1
kind: Pod
metadata:
name: quota-pod-2
spec:
containers:
- name: quota-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Pod is Running ; sleep 3600']
restartPolicy: Never
terminationGracePeriodSeconds: 0nano myQuota-Pod-3.yamlapiVersion: v1
kind: Pod
metadata:
name: quota-pod-3
spec:
containers:
- name: quota-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Pod is Running ; sleep 3600']
restartPolicy: Never
terminationGracePeriodSeconds: 0
Create the first Pod.
kubectl create -f myQuota-Pod-1.yaml
pod/quota-pod-1 created
Check quota stats are as expected.
kubectl describe quota object-counts
Name: object-counts
Namespace: default
Scopes: BestEffort
* Matches all pods that do not have resource requirements set. These pods have a best effort quality of service.
Resource Used Hard
-------- ---- ----
count/pods 1 2
Our Pod correctly classified as BestEffort. It counts as 1 Pod against the quota.
Create 2nd Pod.
kubectl create -f myQuota-Pod-2.yaml
pod/quota-pod-2 created
Check quota again.
kubectl describe quota object-countsName: object-counts
Namespace: default
Scopes: BestEffort
* Matches all pods that do not have resource requirements set. These pods have a best effort quality of service.
Resource Used Hard
-------- ---- ----
count/pods 2 2
As expected: bot Pod quotas used.
Attempt to create a third BestEffort Pod:
kubectl create -f myQuota-Pod-3.yaml
Error from server (Forbidden): error when creating "myQuota-Pod-3.yaml": pods "quota-pod-3" is forbidden: exceeded quota: object-counts, requested: count/pods=1, used: count/pods=2, limited: count/pods=2
Fails since it exceeds quota.
If we kubectl describe our first 2 Pods we will see their QoS is BestEffort.
kubectl describe pod/quota-pod-1|grep Best
QoS Class: BestEffortkubectl describe pod/quota-pod-2|grep Best
QoS Class: BestEffort
Done, delete …
kubectl delete -f myQuota-Pod-1.yaml
pod "quota-pod-1" deletedkubectl delete -f myQuota-Pod-2.yaml
pod "quota-pod-2" deletedkubectl delete quota/object-counts
resourcequota "object-counts" deleted
5) Scopes: NotTerminating Quotas
Another scope is NotTerminating
Matches all pods that do not have an active deadline. These pods usually include long running pods whose container command is not expected to terminate.
In this exercise we are going to :
- create a NotTerminating quota of 2 Pods
- create 2 running Pods
- attempt to create a third Pod
nano myQuota.yamlapiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
spec:
hard:
count/pods: "2"
scopes:
- NotTerminating
Create NotTerminating quota and describe it:
kubectl create -f myQuota.yaml
resourcequota/object-counts created
kubectl describe quota object-countsName: object-counts
Namespace: default
Scopes: NotTerminating
* Matches all pods that do not have an active deadline. These pods usually include long running pods whose container command is not expected to terminate.
Resource Used Hard
-------- ---- ----
count/pods 0 2
Create one Pod and check it gets counted as NotTerminating / running.
kubectl create -f myQuota-Pod-1.yaml
pod/quota-pod-1 created
kubectl describe quota object-countsName: object-counts
Namespace: default
Scopes: NotTerminating
* Matches all pods that do not have an active deadline. These pods usually include long running pods whose container command is not expected to terminate.
Resource Used Hard
-------- ---- ----
count/pods 1 2
Is Pod running?
kubectl get po
NAME READY STATUS RESTARTS AGE
quota-pod-1 1/1 Running 0 10s
NotTerminating correctly counts this running Pod against NotTerminating quota.
Create second running Pod:
kubectl create -f myQuota-Pod-2.yaml
pod/quota-pod-2 createdkubectl get poNAME READY STATUS RESTARTS AGE
quota-pod-1 1/1 Running 0 20s
quota-pod-2 1/1 Running 0 4s
Show status of quota counters:
kubectl describe quota object-counts
Name: object-counts
Namespace: default
Scopes: NotTerminating
* Matches all pods that do not have an active deadline. These pods usually include long running pods whose container command is not expected to terminate.
Resource Used Hard
-------- ---- ----
count/pods 2 2
Both running Pods count against NotTerminating quota.
Attempt to run a third Pod should result in error:
kubectl create -f myQuota-Pod-3.yaml
Error from server (Forbidden): error when creating "myQuota-Pod-3.yaml": pods "quota-pod-3" is forbidden: exceeded quota: object-counts, requested: count/pods=1, used: count/pods=2, limited: count/pods=2
Delete objects:
kubectl delete quota/object-counts
resourcequota "object-counts" deletedkubectl delete -f myQuota-Pod-1.yaml
pod "quota-pod-1" deletedkubectl delete -f myQuota-Pod-2.yaml
pod "quota-pod-2" deleted
6) Quotas Need Value in YAML Spec
https://kubernetes.io/docs/concepts/policy/resource-quotas/#requests-vs-limits
If the quota has a value specified for requests.cpu or requests.memory, then it requires that every incoming container makes an explicit request for those resources.
If the quota has a value specified for limits.cpu or limits.memory, then it requires that every incoming container specifies an explicit limit for those resources
To see this in action we define a ResourceQuota with requests.cpu and limits.cpu.
nano myQuota.yamlapiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
spec:
hard:
requests.cpu: "1000m"
limits.cpu: "2000m"
count/pods: "2"
Create the Quota
kubectl create -f myQuota.yamlresourcequota/count-quotas created
Describe quota:
kubectl describe quota object-countsName: object-counts
Namespace: default
Resource Used Hard
-------- ---- ----
count/pods 0 2
limits.cpu 0 2
requests.cpu 0 1
Now we attempt to create a Pod without specifying CPU limits and resources:
nano myQuota-Pod-1.yamlapiVersion: v1
kind: Pod
metadata:
name: quota-pod-1
spec:
containers:
- name: quota-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Pod is Running ; sleep 3600']
restartPolicy: Never
terminationGracePeriodSeconds: 0
Create the Pod.
kubectl create -f myQuota-Pod-1.yamlError from server (Forbidden): error when creating "myQuota-Pod-1.yaml": pods "quota-pod-1" is forbidden: failed quota: object-counts: must specify limits.cpu,requests.cpu
Error message is exactly as expected.
Delete Quota
kubectl delete -f myQuota.yamlresourcequota "counts-quota" deleted
7) Resource Quota Per PriorityClass
The Kubernetes documentation provides a complete example of this …
https://kubernetes.io/docs/concepts/policy/resource-quotas/#resource-quota-per-priorityclass
Just follow those instructions.
8) Storage Resource Quota
https://kubernetes.io/docs/concepts/policy/resource-quotas/#storage-resource-quota
You can limit the total sum of storage resources that can be requested in a given namespace.
In addition, you can limit consumption of storage resources based on associated storage-class.
This tutorial gave several repetitively similar exercises for several differences Kubernetes resources.
Once you need quotas on your storage resources you should be able to easily apply your knowledge learnt here.
9) Quota and Cluster Capacity
From https://kubernetes.io/docs/concepts/policy/resource-quotas/#quota-and-cluster-capacity
ResourceQuotas are independent of the cluster capacity. They are expressed in absolute units.
So, if you add nodes to your cluster, this does not automatically give each namespace the ability to consume more resources.
Note that resource quota divides up aggregate cluster resources, but it creates no restrictions around nodes : pods from several namespaces may run on the samenode .
10) Start Using Quotas
If you only start using quotas months after starting to use Kubernetes you will have several different Kubernetes objects that already exists on your nodes.
You may then create quotas that are too small for even your current environment.
One way around this is to create temporary quotas that are 100 or 1000 more than your planned values.
You can then immediately run kubectl describe quota to instantly see accurate current counts for all Kubernetes objects you need quotas for. Then just adjust your quota values downwards as needed.
If you create quotas below your current usage that quota will be created without giving an error message.
This is bad, since if for example Pods are under specified, a Pod delete and recreate will fail since you are over quota already.
Therefore every time before you create a new quota run kubectl describe quota to see current resource usage levels.
After you create that quota run kubectl describe quota immediately to check its values are correct.
Original Source
https://www.alibabacloud.com/blog/kubernetes-resource-quotas_595023?spm=a2c41.13112061.0.0