One of the challenges in the containerization world is to use system resources efficiently. You can limit the system resources per pod/container.
Here’s how you can set memory/CPU limits:
apiVersion: apps/v1
kind: Deployment
metadata:
name: blog-test-deployment
spec:
replicas: 1
selector:
matchLabels:
app: blog-test
template:
metadata:
name: blog-test-pod
labels:
app: blog-test
spec:
containers:
- name: blog-test-container
image: ubuntu
command: ["/bin/bash", "-c", "--"]
args: ["while true; do sleep 3000; done;"]
resources:
requests:
memory: 24Mi
cpu: 125m
limits:
memory: 128Mi
cpu: 500m
This approach has two drawbacks:
- If you forget to add a limit, the pod will try to consume all system resources.
- The person who deploys this YAML file may replace the values with higher values and consume more resources than they should.
There should be a higher-order method to limit system resources per pod in a namespace. There is one: LimitRange
LimitRange
LimitRange allows you to set limitations per namespace.
Let’s say you want to have a namespace called development
. The pods in this namespace should consume no more than 1 GB of memory and 1 CPU core.
01-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: development
Create the namespace:
➜ ~ kubectl apply -f 01-namespace.yaml
namespace/development created
02-limitrange.yaml
apiVersion: v1
kind: LimitRange
metadata:
name: blog-limitrange
namespace: development
spec:
limits:
- max:
cpu: 1000m
memory: 1Gi
type: Pod
1000m in Kubernetes signifies 1 CPU core or 1 vCPU on the cloud.
Create:
➜ ~ kubectl create -f 02-limitrange.yml
limitrange/blog-limitrange created
➜ ~ kubectl describe limitranges -n development
Name: blog-limitrange
Namespace: development
Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
---- -------- --- --- --------------- ------------- -----------------------
Pod memory - 1Gi - - -
Pod cpu - 1 - - -
Try deploying a Pod with two CPU Cores:
03-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: cpu-problem-nginx
namespace: development
spec:
containers:
- image: nginx
name: cpu-problem-nginx
resources:
requests:
cpu: 2000m
limits:
cpu: 2000m
Create:
➜ ~ kubectl create -f 03-pod.yaml
Error from server (Forbidden): error when creating "03-pod.yaml": pods "cpu-problem-ubuntu" is forbidden: [maximum cpu usage per Pod is 1, but limit is 2, maximum memory usage per Pod is 1Gi. No limit is specified]
The pod creation request was denied due to CPU limits.
Try deploying a Pod with 2 Gigabytes of memory:
04-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: memory-problem-nginx
namespace: development
spec:
containers:
- image: nginx
name: memory-problem-nginx
resources:
requests:
memory: 2Gi
limits:
memory: 2Gi
Create:
➜ ~ kubectl create -f 04-pod.yaml
Error from server (Forbidden): error when creating "04-pod.yaml": pods "memory-problem-nginx" is forbidden: [maximum cpu usage per Pod is 1. No limit is specified, maximum memory usage per Pod is 1Gi, but limit is 2147483648]
Please notice that there are now two errors:
- The maximum CPU core was never mentioned in the
04-pod.yaml
file. As a result, this pod will attempt to utilise all available CPUs. However, in the Limit Range, we set 1 CPU Core. - The
memory-problem-nginx
pod is attempting to allocate 2 GiB of Memory which is in excess of the memory limit.
Try deploying a Pod with 0.5 CPU cores and 2 GiB of memory:
05-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: memory-problem-nginx
namespace: development
spec:
containers:
- image: nginx
name: memory-problem-nginx
resources:
requests:
cpu: 500m
memory: 2Gi
limits:
cpu: 500m
memory: 2Gi
Create:
➜ ~ kubectl create -f 05-pod.yaml
Error from server (Forbidden): error when creating "05-pod.yaml": pods "memory-problem-nginx" is forbidden: maximum memory usage per Pod is 1Gi, but limit is 2147483648
Please notice that the CPU error has been eliminated.
Try deploying a pod with one CPU core and one gigabyte of memory:
06-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: good-nginx
namespace: development
spec:
containers:
- image: nginx
name: good-nginx
resources:
requests:
cpu: 1000m
memory: 1Gi
limits:
cpu: 1000m
memory: 1Gi
Create:
➜ limits kubectl create -f 06-pod.yaml
pod/good-nginx created
➜ limits kubectl get pods -n development
NAME READY STATUS RESTARTS AGE
good-nginx 1/1 Running 0 6s
It has been successfully deployed.
Multiple Containers in One Pod
Please notice that the limit range type Pod was set in the 02-limitrange.yaml
file.
Let’s try deploying multiple containers, each with 1 CPU Core and 1 GiB of memory:
07-multi-containers-one-pods.yaml
apiVersion: v1
kind: Pod
metadata:
name: bad-nginx-redis
namespace: development
spec:
containers:
- image: nginx
name: bad-nginx
resources:
requests:
cpu: 1000m
memory: 1Gi
limits:
cpu: 1000m
memory: 1Gi
- image: redis
name: bad-redis
resources:
requests:
cpu: 1000m
memory: 1Gi
limits:
cpu: 1000m
memory: 1Gi
Create:
➜ ~ kubectl create -f 07-multi-containers-one-pod.yaml
Error from server (Forbidden): error when creating "07-multi-containers-one-pod.yaml": pods "bad-nginx-redis" is forbidden: [maximum memory usage per Pod is 1Gi, but limit is 2147483648, maximum cpu usage per Pod is 1, but limit is 2]
Kubernetes throws an error when each container wants to allocate 1 GiB of Memory and use 1 CPU Core. Because we have a 1 GiB of memory and 1 CPU core limit per Pod. We require two CPU cores and two gigabytes of memory in the 07-multi-containers-one-pod.yaml
file.
08-multi-containers-one-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: good-nginx-redis
namespace: development
spec:
containers:
- image: nginx
name: good-nginx
resources:
requests:
cpu: 500m
memory: 500Mi
limits:
cpu: 500m
memory: 500Mi
- image: redis
name: good-redis
resources:
requests:
cpu: 500m
memory: 500Mi
limits:
cpu: 500m
memory: 500Mi
Create:
➜ ~ kubectl create -f 08-multi-containers-one-pod.yaml
pod/good-nginx-redis created
➜ ~ kubectl get pods -n development
NAME READY STATUS RESTARTS AGE
good-nginx-redis 2/2 Running 0 23s
Delete the limit range:
➜ ~ kubectl delete -f 02-limitrange.yml
limitrange "blog-limitrange" deleted
Define a limit range with Type: Container
09-limitrange.yaml
apiVersion: v1
kind: LimitRange
metadata:
name: blog-limitrange
namespace: development
spec:
limits:
- max:
cpu: 1000m
memory: 1Gi
type: Container
Create:
➜ ~ kubectl create -f 09-limitrange.yaml
limitrange/blog-limitrange created
➜ ~ kubectl describe limitranges -n development
Name: blog-limitrange
Namespace: development
Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
---- -------- --- --- --------------- ------------- -----------------------
Container cpu - 1 1 1 -
Container memory - 1Gi 1Gi 1Gi -
Let’s try to deploy 07-multi-containers-one-pod.yaml
file again:
➜ ~ kubectl create -f 07-multi-containers-one-pod.yaml
pod/bad-nginx-redis created
➜ ~ kubectl get pods -n development
NAME READY STATUS RESTARTS AGE
bad-nginx-redis 2/2 Running 0 33s
It has been successfully deployed. Because the Limit type is Container, not Pod. Each container is limited to 1 GiB of Memory and 1 CPU Core.
Remove the current limit range:
➜ ~ kubectl delete limitranges blog-limitrange -n dev
limitrange "blog-limitrange" deleted
Assign default limits
It is possible to define default limitations for each container
10-limitrange.yaml
apiVersion: v1
kind: LimitRange
metadata:
name: blog-limitrange
namespace: development
spec:
limits:
- max:
cpu: 1000m
memory: 1Gi
min:
cpu: 100m
memory: 32Mi
default:
cpu: 500m
memory: 128Mi
defaultRequest:
cpu: 250m
memory: 64Mi
type: Container
Create:
➜ ~ kubectl create -f 10-limitrange.yaml
limitrange/blog-limitrange created
➜ ~ kubectl describe limitranges -n development
Name: blog-limitrange
Namespace: development
Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
---- -------- --- --- --------------- ------------- -----------------------
Container cpu 100m 1 250m 500m -
Container memory 32Mi 1Gi 64Mi 128Mi -
Let’s deploy a pod without specifying any limits:
11-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: default-nginx
namespace: development
spec:
containers:
- image: nginx
name: default-nginx
Create:
➜ ~ kubectl create -f 11-pod.yaml
pod/default-nginx created
➜ ~ kubectl describe pod/default-nginx -n development
Name: default-nginx
Namespace: development
...
Limits:
cpu: 500m
memory: 128Mi
Requests:
cpu: 250m
memory: 64Mi
...
As we have previously configured default limits for the development
namespace, Kubernetes sets restrictions for new pods.
P.S.: When using Type Pod, you cannot specify default
or defaultRequest