pod에는 필요로 하는 cpu, memory 자원량이 있는데, 현재 node의 자원 현황을 토대로 이 pod들을 배치시켜주는 것이 kubernetes scheduler의 몫이다.
가령 다음과 같은 예를 보도록 하자.
Pod1: CPU 2, Memory 1
Pod2: CPU 2, Memory 1
Node1: CPU 8, Memory 8
Node2: CPU 8, Memory 8
다음의 상황에서 pod들은 node에 할당될 때, 다음과 같이 할당될 수 있다.
Node1: CPU 4, Memory 6 (Pod1, Pod2)
Node2: CPU 8, Memory 8
이 다음 Pod3이 Node1보다 더 많은 자원량을 필요로 한다면 Node1이 아니라 Node2로 배정될 것이다.
Pod3: CPU: 5, Memory: 7
Node1: CPU 4, Memory 6 (Pod1, Pod2)
Node2: CPU 8, Memory 8
Node1으로는 Pod3가 감당이 되지 못하니 Node2로 배정된다.
Node1: CPU 4, Memory 6 (Pod1, Pod2)
Node2: CPU 3, Memory 1 (Pod3)
그런데, Pod4가 왔는데, 배정될 충분한 자원을 가진 Node가 없다면 어떻게될까?? 그런 경우 Insufficient
event가 발생하여 어느 node에도 pod4가 배정되지 않아 Pending
상태가 되어버리고 만다.
Pod4: CPU 5, Memory 7
Node1: CPU 4, Memory 6 (Pod1, Pod2)
Node2: CPU 3, Memory 1 (Pod3)
Pod4는 Pending
상태에 빠지고 FailedScheduling
event가 발생한다.
이렇게 pod에 최소 자원량을 요구하는 것이 바로 Resource Requests
이다. pod안에 spec.containers.resources
안에 node에 최소로 요구할 자원량을 넣어주면 된다.
apiVersion: v1
kind: Pod
metadata:
name: simple-webapp-color
labels:
name: simple-webapp-color
spec:
containers:
- name: simple-webapp-color
image: simple-webapp-color
ports:
- containerPort: 8080
resources:
requests:
memory: "4Gi"
cpu: 2
스케줄러에 의해 pod가 배정될 node를 찾을 때, 해당 자원량만큼의 여유 공간이 있는 node를 찾아배정해주는 것이다.
그럼 CPU의 숫자값은 무엇을 의미하는 것을까?? 이것은 vCPU 또는 1 CPU core를 말하는 것으로 생각하면 된다.
Memory는 M
, Mi
와 같이 뒤에 접미사로 i
가 붙은 값들이 있는데, 이 경우는 2의 배수로 측정된 값이라고 생각하면 된다.
1G (Gigabyte) = 1,000,000,000 bytes
1M (Megabyte) = 1,000,000 bytes
1K (Kilobyte) = 1,000 bytes
1 Gi(Gibibyte) = 1,073,741,824 bytes
1 Mi(Mebibyte) = 1,048,576 bytes
1 Ki(Kibibyte) = 1,024 bytes
문제는 기본적으로 container에는 자원 제한량이 없기 때문에 실제로 requests
보다 더 많은 양의 자원을 사용할 수도 있다. 그래서 사용하는 것이 바로 Resource Limits
이다.
apiVersion: v1
kind: Pod
metadata:
name: simple-webapp-color
labels:
name: simple-webapp-color
spec:
containers:
- name: simple-webapp-color
image: simple-webapp-color
ports:
- containerPort: 8080
resources:
requests:
memory: "1Gi"
cpu: 1
limits:
memory: "2Gi"
cpu: 2
다음과 같이 최대 제한량을 limits
로 제한 둘 수 있는 것이다. 단, 이는 container단위이기 때문에 pod단위로 생각해서는 안된다.
그런데, 만약 container가 제한된 자원량 이상으로 요구를 하려고 한다면 어떻게 될까?? 가령 memory의 경우 limit보다 더 많은 자원량을 요구하게 되는 경우 OOM(out of memory)
가 발생하며 container가 들어가 있는 pod도 함께 terminate된다. 이 동작을 OOMK(out-of-memory kill)
이라고 한다.
재밌는 것은 requests
만 있을 때, limits
만 있을 때의 default동작들이 있다는 것이다.
먼저 CPU측면에서만 보도록 하자.
Pod1
Pod2
Node1: 6 CPU
다음의 상황에서 두 pod가 모두 requests
도 없고, limits
도 없다고 하자. 그렇다면 다음과 같이 Node1의 자원을 모두 소모할 수도 있게 된다.
Pod2
Node1: 0 CPU (Pod1: 6 CPU)
만약 Limits
를 걸어주면 어떻게 될까? 두 pod에 Limit
로 3 CPU를 걸어주면, 아무리 최대로 pod가 자원을 할당해도 3 CPU를 넘지 않는다.
Node1: 0 CPU (Pod1: 3 CPU, Pod2: 3 CPU)
다음으로 Requests
와 Limits
를 모두 넣어주면 어떻게될까?? Requests
로 두 pod 모두 1 CPU씩 할당해주고 Limits
로 3 CPU
를 넣어주면 다음과 같이 된다.
Node1: 4 (Pod1: 1 CPU, Pod2: 1 CPU)
먼저 pod들이 요청을 적게받아 널널할 때는 다음과 같이 Requests
양만큼만 최소로 자원을 잡게 된다.
그런데, pod들이 요청을 받게되어 부하가 커지면 다음과 같이 된다.
Node1: 0 (Pod1: 3 CPU, Pod2: 3 CPU)
두 pod모두 아무리 많은 요청을 받아도 Limits
인 3을 넘지 않는 것이다.
마지막으로 Requests
만 설정할 때이다. 두 pod 모두 requests
를 1 CPU로 설정하고, 요청이 적은 경우는 다음과 같이 된다.
Node1: 4 (Pod1: 1 CPU, Pod2: 1 CPU)
그런데, 갑자기 Pod1
에 대한 요청이 많아진다고 하자. limit
이 없는데 어떻게될까??
Node1: 0 (Pod1: 5 CPU, Pod2: 1 CPU)
다행이도 Pod2
에 Reuqests
로 1 CPU가 있다보니 Pod2
의 자원까지 소모하진 않게된다. 따라서 Pod1
은 자원이 쭉쭉 올라가서 5 CPU까지 넘보게되지만 Node1
의 모든 자원이 Pod1
에만 한정되는 것이 아니게 되는 것이다.
가장 좋은 방법은 Requests - No Limits
조합이다. 이렇게 하면 유연하 자원 소모율이 변하고, 다른 pod들도 서비스에 크게 영향을 미치지 않게되기 때문이다.
따라서, 모든 pod마다 Requests
를 반드시 설정해주는 것이 좋다.
CPU뿐만 아니라 memory측면도 사실 위의 example과 동작이 동일하다.
그런데 사용자가 Requests
와 Limits
가 없는 pod들을 만드는데, 이를 global하게 default Requests
와 Limits
를 제공하고 싶다면 어떻게 해야할까?? 이를 가능하게 해주는 것이 바로 LimitRange
이다.
apiVersion: v1
kind: LimitRange
metadata:
name: cpu-resource-constraint
spec:
limits:
- default:
cpu: 500m
defaultRequest:
cpu: 500m
max:
cpu: "1"
min:
cpu: 100m
type: Container
참고로 LimitRange
는 namespace 영향을 받으므로 해당 namespace에서 배포되는 pod에만 적용된다고 생각하면 된다.
limits.default.cpu
: pod에 limits
, requests
값이 없다면 자동으로 이 값을 pod의 limits
, requests
로 설정해준다.limits.defaultRequest.cpu
: pod에 requests
가 없다면 해당 값으로 requests
를 설정해준다.limits.max.cpu
: 추가된 pod에 설정된 requests
와 limit
의 최대값으로 이 값을 넘지 않도록 해야한다.limits.min.cpu
: 추가된 pod에 설정된 requests
와 limit
의 최소값으로 이 값보다는 커야한다.메모리 역시도 마찬가지이다. cpu
부분만 memory
로 바꾸면 된다.
LimitRange
는 namespace단위로 namespace안에 배포되는 pod에 대한 requests
, limits
를 설정하고 제한한다면, ResourceQuota
는 namespace 자체에 최대 자원량을 제한한다. 따라서 namespace안에 전체 pod의 자원량을 제한하는 기능을 제공하는 것이다.
apiVersion: v1
kind: ResourceQuota
tadata:
name: my-resource-quota
namespace: target
spec:
hard:
requests.cpu: 4
requests.memory: 4Gi
limits.cpu: 10
limits.memory: 10Gi
위의 ResourceQuota
는 target
namespace에 대해서 자원량 최대를 제한하는 것이다. 참고로 kubernetes object의 갯수, 특정 object의 자원량을 제한하는 기능들이 있다.