[Retry, k8s] 28. 리소스 관리, 오토 스케일링 - 리소스 제안
카테고리: KUBERNETES
태그: kubernetes
01. 리소스 제안
(1) CPU/메모리 리소스 제한
| 리소스 유형 | 단위 |
|---|---|
| CPU | 1 = 1000M = 1 vCPU |
| 메모리 | 1G = 1000M = (1Gi = 1024Mi) |
# 예시 1) sample-resources.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample
spec:
replicas: 3
selector:
matchLabels:
app: sample-app
template:
metadata:
labels:
app: sample-app
spec:
containers:
- name: nginx-container
image: nginx:1.16
resources:
# 사용할 리소스 최솟값 지정
requests:
memory: "1024Mi"
cpu: "500M"
# 사용할 리소스 최댓값 지정
limits:
memory: "2048Mi"
cpu: "1000M"
“Limits를 설정하지 않으면 호스트 측의 부하가 최대로 상승할 때까지 리소스를 소비하려 한다.”
# 예시 2) sample-resources-only-requests.yaml
apiVersion: v1
kind: Pod
metadata:
name: sample-resources-only-requests
spec:
containers:
- name: nginx-container
image: nginx:1.16
resources:
# requests만 설정
requests:
memory: 256Mi
cpu: 200M
# 리소스 설정 확인
$ kubectl get pod sample-resources-only-requests -o json | jq ".spec.containers[].resources"
{
"requests": {
"cpu": "200M",
"memory": "256Mi"
}
}
“반대로 Limits만 설정한 경우 Limits와 같은 값이 Requests에 설정되게 되어 있다.”
# 예시 3) sample-resources-only-limits.yaml
apiVersion: v1
kind: Pod
metadata:
name: sample-resources-only-limits
spec:
containers:
- name: nginx-container
image: nginx:1.16
resources:
# Limits만 설정
limits:
memory: 256Mi
cpu: 200M
# 리소스 설정 확인
$ kubectl get pod sample-resources-only-requests -o json | jq ".spec.containers[].resources"
{
"limits": {
"cpu": "200M",
"memory": "256Mi"
},
"requests": {
"cpu": "200M",
"memory": "256Mi"
}
}
(2) Ephemeral 스토리지 리소스 제어
쿠버네티스 노드에서 기동 중인 시스템 구성 요소의 kubelet이 디스크 사용 현황을 정기적으로 모니터링하고 초과한 경우 파드는 축출(Evict)되게 되어 있다. Ephemeral 스토리지 용량을 계산되는 것은 다음과 같다.
| 구분 | 설명 |
|---|---|
| (1) | 컨테이너가 출력하는 로그 |
| (2) | emptyDir에 기록된 데이터(medium: 메모리가 아닌 것) |
| (3) | 컨테이너의 쓰기 가능한 레이어(영구 볼륨/hostPath/nfs 등이 마운트된 영역을 제외한 모든 영역)에 기록된 데이터 |
# 예시 4) sample-ephemeral-storage.yaml
apiVersion: v1
kind: Pod
metadata:
name: sample-ephemeral-storage
spec:
containers:
- name: nginx-container
image: nginx:1.16
resources:
# Ephemeral 스토리지도 마찬가지로 Requests, Limits에 의해 제한을 설정한다.
# 2048Mi를 초과하는 데이터를 쓰면 파드는 자동으로 Evict 된다.
requests:
ephemeral-storage: "1024Mi"
limits:
ephemeral-storage: "2048Mi"
“기본적으로 Evict되지 않게 여분의 리소스가 확보되지만, ephemeral-storage 사용 제한을 하지 않으면 예상치 못한 데이터 쓰기로 인해 쿠버네티스 노드의 디스크 용량을 초과하여 그 노드의 모든 파드에 영향을 줄 가능성도 있으므로 주의해야 한다.”
# 컨테이너의 쓰기 가능한 레이어에 2048MB 데이터 쓰기
$ kubectl exec -it sample-ephemeral-storage -- dd if=/dev/zero of=/dummy bs=1M count=2048
# 데이터 기록 후 파드가 Evict된 것을 확인
$ kubectl describe pod sample-ephemeral-storage
...~(생략)~
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m42s default-scheduler Successfully assigned default/sample-ephemeral-storage to k8s-worker01
Normal Pulled 2m41s kubelet Container image "nginx:1.16" already present on machine
Normal Created 2m41s kubelet Created container nginx-container
Normal Started 2m41s kubelet Started container nginx-container
# Evict 된 것을 확인할 수 있다.
Warning Evicted 68s kubelet Pod ephemeral local storage usage exceeds the total limit of containers 2Gi.
Normal Killing 68s kubelet Stopping container nginx-container
아래 코드는 파드의 각 컨테이너의 /cache 디렉터리에 같은 emptyDir이 마운트되어 있고, 2GiB의 Ephemeral 스토리지 제한이 설정되어 있다.
# 예시 5) sample-ephemeral-storage-multi.yaml
apiVersion: v1
kind: Pod
metadata:
name: sample-ephemeral-storage-multi
spec:
containers:
- name: container-a
image: amsy810/tools:v2.0
resources:
requests:
ephemeral-storage: "1024Mi"
limits:
ephemeral-storage: "2048Mi"
volumeMounts:
- mountPath: /cache
name: cache-volume
- name: container-b
image: amsy810/tools:v2.0
resources:
requests:
ephemeral-storage: "1024Mi"
limits:
ephemeral-storage: "2048Mi"
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
첫 번째 컨테이너의 쓰기 가능한 레이어에 3GB 데이터를 쓰면 일정 시간 후 파드가 Evict된다.
# 컨테이너의 쓰기 가능한 레이어에 3GB 데이터 쓰기
$ kubectl exec -it sample-ephemeral-storage-multi -c container-a -- dd if=/dev/zero of=/dummy bs=1M count=3000
# 데이터 기록 후 파드가 Evict된 것을 확인
$ kubectl describe pod sample-ephemeral-storage-multi
...~(생략)~
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 48s default-scheduler Successfully assigned default/sample-ephemeral-storage-multi to k8s-worker02
Normal Pulled 47s kubelet Container image "amsy810/tools:v2.0" already present on machine
Normal Created 47s kubelet Created container container-a
Normal Started 47s kubelet Started container container-a
Normal Pulled 47s kubelet Container image "amsy810/tools:v2.0" already present on machine
Normal Created 47s kubelet Created container container-b
Normal Started 47s kubelet Started container container-b
Warning Evicted 2s kubelet Container container-a exceeded its local ephemeral storage limit "2Gi".
Normal Killing 2s kubelet Stopping container container-a
Normal Killing 2s kubelet Stopping container container-b
이번에는 첫 번째 컨테이너의 emptyDir 영역에 3GB 데이터를 써도 두 개 파드의 Ephemeral 스토리지 제한인 4GiB에 도달하지 않아 파드는 Evict되지 않는다. (4GB 이상의 데이털를 쓸 때 Evict된다.)
# emptyDir(/cahce) 영역에 3GB 데이터 쓰기
$ kubectl exec -it sample-ephemeral-storage-multi -c container-a -- dd if=/dev/zero of=/cache/dummy bs=1M count=3000
# Evict 되지 않음을 확인
$ kubectl describe pod sample-ephemeral-storage-multi
...~(생략)~
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 3m11s default-scheduler Successfully assigned default/sample-ephemeral-storage-multi to k8s-worker02
Normal Pulled 3m10s kubelet Container image "amsy810/tools:v2.0" already present on machine
Normal Created 3m10s kubelet Created container container-a
Normal Started 3m10s kubelet Started container container-a
Normal Pulled 3m10s kubelet Container image "amsy810/tools:v2.0" already present on machine
Normal Created 3m10s kubelet Created container container-b
Normal Started 3m10s kubelet Started container container-b
(3) 시스템에 할당된 리소스와 Eviction 매니저
리소스(CPU, 메모리, Ephemeral 스토리지)가 고갈되면 쿠버네티스 자체가 동작하지 않거나 그 노드 전체에 영향을 미칠 수 있기 때무에 각 노드에는 “kube-reserved”, “system-reserved”라는 두 가지 리소스가 시스템용으로 확보되어 있다.
| 구분 | 설명 |
|---|---|
| kube-reserved | 쿠버네티스 시스템 구성 요소나 컨테이너 런타임에 확보된 리소스 |
| system-reserved | OS에 깊이 관련된 데몬 등에 확보된 리소스 |
실제 파드에 할당 가능한 리소스(Allocatable)는 쿠버네티스 노드에 존재하는 리소스 총량에서 kube-reserved, system-reserved를 제외한 양이다.
$ kubectl get nodes -o custom-columns="
> NAME:.metadata.name, \
> CPU Capacity:.status.capacity.cpu, \
> CPU Allocatable:.status.allocatable.cpu, \
> Mem Capacity:.status.capacity.memory, \
> Mem Allocatable:.status.allocatable.memory"
NAME CPU Capacity CPU Allocatable Mem Capacity Mem Allocatable
k8s-master01 2 2 3969444Ki 3867044Ki
k8s-worker01 2 2 1974556Ki 1872156Ki
k8s-worker02 2 2 1974540Ki 1872140Ki
Eviction 매니저는 시스템 전체가 과부하되지 않도록 관리하며 Allocatable, System-reserved, kube-reserved에서 실제로 사용되는 리소스 합계가 Eviction Threshold를 초과하지 않는지 정기적으로 확인하고, 초과한 경우 파드를 Evict한다.
Eviction Threshold는 soft와 hard가 있고, soft 제한에 걸리면 SIGTERM 신호를 보내 파드를 정지하려고 하며, hard 제한이 걸리면 SIGKILL이 보내져 파드가 정지된다.
Eviction 매니저가 Evict하는 파드는 다음 우선순에 따라 결정된다.
| 구분 | 설명 |
|---|---|
| (1) | Requests에 할당된 양보다 초과하여 리소스를 소비하고 있는 것 |
| (2) | PodPriority가 더 낮은 것 |
| (3) | Requests에 할당된 양보다 초과하여 소비하고 있는 리소스 양이 더 많은 것 |
(4) GPU 등의 리소스 제한
k8s 1.8 이후 Divice Plugins 기능이 지원되며 GPU(Nvidia/AMD)/FPGA/VPU(머신 비전 액셀러레이터)/QAT(암호 관련 워크로드 액셀러레이터)/TPU(기계 학습용 프로세서) 등의 장치도 제한을 설정할 수 있게 되었다.
# 엔비디아 GPU 제한
resources:
requests:
nvidia.com/gpu: 2
limits:
nvidia.com/gpu: 2
#
클러스터 구축 후 쿠버네티스가 GPU를 사용할 수 있도록 엔비디아의 드라이버를 설치하는 데몬셋을 배포해야 한다.
(5) 오버커밋과 리소스 부족
위 예제의 “sample-resource” 디플로이먼트는 한 개의 파드당 ‘500m CPU/1024Mi 메모리’의 Requests가 설정되어 있으며, 오버커밋 동작을 테스트하기 위해 스케일 아웃을 한다.
# 디플로이먼트 스케일 아웃
$ kubectl scale deployment --replicas 24 sample
deployment.apps/sample scaled
# pending status 상태의 파드가 존재
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/sample-5bb75c5db9-2254h 0/1 Pending 0 2s
pod/sample-5bb75c5db9-4d54d 0/1 Pending 0 2s
pod/sample-5bb75c5db9-4vxf9 0/1 Pending 0 2s
pod/sample-5bb75c5db9-4zrcb 0/1 Pending 0 2s
pod/sample-5bb75c5db9-55msb 0/1 Pending 0 2s
pod/sample-5bb75c5db9-5xbjt 0/1 Pending 0 2s
# 디플로이먼트 상태 확인
$ kubectl get deployments.apps
NAME READY UP-TO-DATE AVAILABLE AGE
sample 5/24 24 5 104s
노드에 할당된 리소스 상태를 확인(kubectl describe node ‘노드명’)해보면, CPU Requests(최소) 제한이 거의 꽉 찬 상태가 되었고 sample-resource 디플로이먼트가 생성하는 파드의 CPU Requests 값 500m를 허용하는 리소스가 없는 것을 알 수 있다.
댓글 남기기