파드와 컨테이너의 리소스 관리
파드와 컨테이너의 리소스 관리 (Resource Management for Pods and Containers)
파드를 지정할 때 각 컨테이너가 필요한 각 리소스의 양을 선택적으로 지정할 수 있어요. 가장 흔히 지정하는 리소스는 CPU와 메모리(RAM)이며, 다른 것들도 있어요.
파드의 컨테이너에 대한 리소스 요청(request)을 지정하면 kube-scheduler가 이 정보를 사용해 파드를 어느 노드에 배치할지 결정해요. 컨테이너에 대한 리소스 한도(limit)를 지정하면 kubelet이 그 한도를 강제해 실행 중인 컨테이너가 설정한 한도보다 더 많은 리소스를 사용하지 못하게 해요. kubelet은 또한 그 시스템 리소스의 요청한 양 이상을 정확히 그 컨테이너가 사용하도록 예약해요.
출처: 문서
본문
Requests와 limits
파드가 실행 중인 노드에 리소스가 충분히 사용 가능하면, 컨테이너가 그 리소스에 대한 요청보다 더 많은 리소스를 사용하는 것이 가능하고(허용돼요).
예를 들어, 컨테이너에 메모리 요청 256 MiB를 설정하고, 그 컨테이너가 8GiB 메모리와 다른 파드가 없는 노드에 스케줄링된 파드에 있다면, 컨테이너는 더 많은 RAM을 사용하려고 시도할 수 있어요.
한도(limits)는 다른 이야기예요. CPU와 메모리 한도 모두 kubelet(과 컨테이너 런타임)에 의해 적용되며, 궁극적으로 커널에 의해 강제돼요. Linux 노드에서 Linux 커널은 cgroups로 한도를 강제해요. CPU와 메모리 한도 강제의 동작은 약간 다르게요.
- CPU 한도는 CPU 스로틀링으로 강제돼요. 컨테이너가 CPU 한도에 가까워지면 커널이 컨테이너의 한도에 해당하는 CPU에 대한 접근을 제한해요. 따라서 CPU 한도는 커널이 강제하는 하드 한도예요. 컨테이너는 CPU 한도에 지정된 것보다 더 많은 CPU를 사용할 수 없어요.
- 메모리 한도는 커널이 OOM(메모리 부족) 킬로 강제해요. 컨테이너가 메모리 한도보다 더 많이 사용하면 커널이 그것을 종료할 수 있어요. 그러나 종료는 커널이 메모리 압력을 감지할 때만 일어나요. 따라서 메모리를 초과 할당한 컨테이너가 즉시 종료되지 않을 수 있어요. 이는 메모리 한도가 반응적으로(reactively) 강제된다는 뜻이에요. 컨테이너는 메모리 한도보다 더 많은 메모리를 사용할 수 있지만, 그렇게 하면 종료될 수 있어요.
리소스 유형
리소스 유형은 기본 단위(base unit)가 있고 요청, 한도 또는 둘 다 가능해요. 쿠버네티스에는 다음과 같은 내장 리소스 유형이 있어요:
| 리소스 유형 | 설명 | 기본 단위 |
|---|---|---|
| cpu | 컴퓨팅 처리 | cpu (core) |
| memory | RAM | Bytes |
| ephemeral-storage | 로컬 임시 저장소 | Bytes |
| hugepages- |
대용량 페이지 (Linux 전용) | Bytes |
클러스터는 확장 리소스(extended resources)(사용자 지정 이름의 리소스, 보통 디바이스 플러그인이 노출)를 제공할 수도 있어요.
대용량 페이지 (Huge pages)
Linux 워크로드의 경우 대용량 페이지 리소스를 지정할 수 있어요. 대용량 페이지는 노드 커널이 기본 페이지 크기보다 훨씬 큰 메모리 블록을 할당하는 Linux 특유의 기능이에요.
예를 들어 기본 페이지 크기가 4KiB인 시스템에서 hugepages-2Mi: 80Mi 한도를 지정할 수 있어요. 컨테이너가 40개가 넘는 2MiB 대용량 페이지(총 80 MiB)를 할당하려고 하면 그 할당이 실패해요.
참고:
CPU와 메모리는 함께 컴퓨팅 리소스(compute resources) 또는 리소스(resources)라고 불러요. 컴퓨팅 리소스는 요청, 할당, 소비할 수 있는 측정 가능한 양이에요. 그것들은 API 리소스(API resources)와 구별돼요. 파드와 서비스 같은 API 리소스는 쿠버네티스 API 서버를 통해 읽고 수정할 수 있는 객체예요.
파드와 컨테이너의 리소스 요청과 한도
각 컨테이너에 대해 다음을 포함한 리소스 한도와 요청을 지정할 수 있어요:
spec.containers[].resources.limits.cpuspec.containers[].resources.limits.memoryspec.containers[].resources.limits.ephemeral-storagespec.containers[].resources.limits.hugepages-<size>spec.containers[].resources.requests.cpuspec.containers[].resources.requests.memoryspec.containers[].resources.requests.ephemeral-storagespec.containers[].resources.requests.hugepages-<size>
개별 컨테이너에 대해서만 requests와 limits를 지정할 수 있지만, 파드의 전체 리소스 요청과 한도에 대해서도 생각하는 것이 유용해요. 특정 리소스에 대해 파드 리소스 요청/한도는 파드의 각 컨테이너에 대한 해당 유형의 리소스 요청/한도의 합이에요.
파드 레벨 리소스 스펙
클러스터에 PodLevelResources 기능 게이트가 활성화되어 있다면 파드 레벨에서 리소스 요청과 한도를 지정할 수 있어요. 파드 레벨에서 쿠버네티스 1.37은 특정 리소스 유형에 대해서만 리소스 요청이나 한도를 지원해요: cpu 및/또는 memory 및/또는 hugepages. 이 기능으로 쿠버네티스는 파드에 대한 전체 리소스 예산을 선언할 수 있게 하며, 개별 리소스 필요량을 정확히 가늠하기 어려운 많은 수의 컨테이너를 다룰 때 특히 도움이 돼요. 또한 파드 내 컨테이너가 서로 유휴 리소스를 공유할 수 있게 하여 리소스 활용률을 개선해요.
파드에 대해 CPU와 메모리에 대한 리소스 한도와 요청을 다음을 포함해 지정할 수 있어요:
spec.resources.limits.cpuspec.resources.limits.memoryspec.resources.limits.hugepages-<size>spec.resources.requests.cpuspec.resources.requests.memoryspec.resources.requests.hugepages-<size>
쿠버네티스의 리소스 단위
CPU 리소스 단위
CPU 리소스에 대한 한도와 요청은 cpu 단위로 측정돼요. 쿠버네티스에서 1 CPU 단위는 물리 호스트인지 물리 머신 안에서 실행되는 가상 머신인지에 따라 1개의 물리 CPU 코어 또는 1개의 가상 코어와 같아요.
분수 요청은 허용돼요. spec.containers[].resources.requests.cpu가 0.5로 설정된 컨테이너를 정의하면, 1.0 CPU를 요청했을 때보다 절반의 CPU 시간을 요청하는 거예요. CPU 리소스 단위에서 수량 표현 0.1은 100m과 동등하며, "백 밀리CPU(one hundred millicpu)"로 읽을 수 있어요. 어떤 사람은 "백 밀리코어"라고 말하며, 이것은 같은 것을 의미하는 것으로 이해돼요.
CPU 리소스는 항상 상대적 양이 아닌 절대적 양으로 지정돼요. 예를 들어 500m CPU는 그 컨테이너가 단일 코어, 듀얼 코어, 또는 48코어 머신에서 실행되는지와 관계없이 거의 같은 양의 컴퓨팅 성능을 나타내요.
참고:
쿠버네티스는 1m 또는 0.001 CPU보다 더 미세한 정밀도로 CPU 리소스를 지정하는 것을 허용하지 않아요. 잘못된 CPU 수량을 실수로 사용하지 않으려면, 1 CPU 단위 미만을 사용할 때 십진법 대신 milliCPU 형태로 CPU 단위를 지정하는 것이 유용해요.
예를 들어 5m 또는 0.005 CPU를 사용하는 파드가 있고 CPU 리소스를 줄이고 싶다고 해요. 십진법을 사용하면 0.0005 CPU가 잘못된 값이라는 것을 알아채기 어렵지만, milliCPU 형태를 사용하면 0.5m이 잘못된 값이라는 것을 알아차리기가 더 쉬워요.
메모리 리소스 단위
메모리에 대한 한도와 요청은 바이트로 측정돼요. 메모리를 평범한 정수나 다음 수량 접미사 중 하나를 사용한 고정 소수점 숫자로 표현할 수 있어요: E, P, T, G, M, k. 2의 거듭제곱 동등물도 사용할 수 있어요: Ei, Pi, Ti, Gi, Mi, Ki. 쿠버네티스 API는 또한 m을 접미사로 허용하지만(밀리바이트: 바이트의 1/1000), 이것은 지정하는 데 유용하지 않아요: 항상 정수의 바이트, 또는 때로 1 기비바이트의 배수 같은 더 큰 덩어리를 할당해야 해요.
거의 같은 값을 나타내는 메모리 수량의 예시는 다음과 같아요:
128974848, 129e6, 129M, 128974848000m, 123Mi
접미사의 대소문자에 주의하세요. "M"은 메가바이트를 뜻하고, "m"은 밀리바이트를 뜻해요. 400m의 메모리를 요청하면 0.4바이트를 요청하는 거예요. 그렇게 입력하는 사람은 아마 400 메비바이트(400Mi) 또는 400 메가바이트(400M)를 요청하려 한 것일 거예요.
컨테이너 리소스 예시
다음 파드는 두 개의 컨테이너를 가져요. 두 컨테이너 모두 0.25 CPU와 64MiB(2^26 바이트)의 메모리 요청으로 정의돼요. 각 컨테이너는 0.5 CPU와 128MiB 메모리의 한도를 가져요. 파드는 0.5 CPU와 128 MiB의 메모리 요청, 그리고 1 CPU와 256MiB의 메모리 한도를 가진다고 말할 수 있어요.
---
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: app
image: images.my-company.example/app:v4
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
- name: log-aggregator
image: images.my-company.example/log-aggregator:v6
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
파드 리소스 예시
이 기능은 PodLevelResources 기능 게이트를 설정해 활성화할 수 있어요. 다음 파드는 1 CPU와 100 MiB 메모리의 명시적 요청, 그리고 1 CPU와 200 MiB 메모리의 명시적 한도를 가져요. pod-resources-demo-ctr-1 컨테이너는 명시적 requests와 limits가 설정되어 있어요. 그러나 pod-resources-demo-ctr-2 컨테이너는 명시적 requests와 limits가 설정되어 있지 않으므로 파드 리소스 경계 내에서 사용 가능한 리소스를 그저 공유할 거예요.
apiVersion: v1
kind: Pod
metadata:
name: pod-resources-demo
namespace: pod-resources-example
spec:
resources:
limits:
cpu: "1"
memory: "200Mi"
requests:
cpu: "1"
memory: "100Mi"
containers:
- name: pod-resources-demo-ctr-1
image: nginx
resources:
limits:
cpu: "0.5"
memory: "100Mi"
requests:
cpu: "0.5"
memory: "50Mi"
- name: pod-resources-demo-ctr-2
image: fedora
command:
- sleep
- inf
리소스 요청이 있는 파드가 스케줄링되는 방법
파드를 만들 때 쿠버네티스 스케줄러가 파드가 실행될 노드를 선택해요. 각 노드는 각 리소스 유형에 대해 최대 용량을 가져요: 파드에 제공할 수 있는 CPU와 메모리의 양. 스케줄러는 각 리소스 유형에 대해 스케줄링된 컨테이너의 리소스 요청 합이 노드의 용량보다 적도록 보장해요. 노드의 실제 메모리나 CPU 리소스 사용량이 매우 낮더라도, 스케줄러는 용량 검사가 실패하면 파드를 노드에 배치하려 하지 않는다는 점에 유의하세요. 이는 예를 들어 요청률의 일일 최고치 동안 리소스 사용량이 나중에 증가할 때 노드의 리소스 부족으로부터 보호해요.
쿠버네티스가 리소스 요청과 한도를 적용하는 방법
kubelet이 파드의 일부로 컨테이너를 시작할 때, kubelet은 그 컨테이너의 메모리와 CPU에 대한 requests와 limits를 컨테이너 런타임에 전달해요.
Linux에서 컨테이너 런타임은 일반적으로 정의한 한도를 적용하고 강제하는 커널 cgroups를 구성해요.
- CPU 한도는 컨테이너가 사용할 수 있는 CPU 시간에 대한 하드 상한을 정의해요. 각 스케줄링 간격(시간 조각) 동안 Linux 커널은 이 한도가 초과되었는지 확인하고; 그렇다면 커널은 그 cgroup이 실행을 재개하도록 허용하기 전에 기다려요.
- CPU 요청은 일반적으로 가중치를 정의해요. 여러 다른 컨테이너(cgroups)가 경합 중인 시스템에서 실행되려 하면, 더 큰 CPU 요청을 가진 워크로드가 작은 요청을 가진 워크로드보다 더 많은 CPU 시간을 할당받아요.
- 메모리 요청은 주로 (쿠버네티스) 파드 스케줄링 중에 사용돼요. cgroups v2를 사용하는 노드에서 컨테이너 런타임은
memory.min과memory.low를 설정하기 위한 힌트로 메모리 요청을 사용할 수 있어요. - 메모리 한도는 그 cgroup에 대한 메모리 한도를 정의해요. 컨테이너가 이 한도보다 더 많은 메모리를 할당하려고 하면 Linux 커널의 OOM(메모리 부족) 하위 시스템이 활성화되고, 일반적으로 메모리를 할당하려고 시도한 컨테이너의 프로세스 중 하나를 중지시켜 개입해요. 그 프로세스가 컨테이너의 PID 1이고 컨테이너가 재시작 가능으로 표시되어 있다면, 쿠버네티스가 컨테이너를 재시작해요.
- 파드나 컨테이너의 메모리 한도는
emptyDir같은 메모리 백업 볼륨의 페이지에도 적용될 수 있어요. kubelet은 tmpfsemptyDir볼륨을 로컬 임시 저장소가 아닌 컨테이너 메모리 사용으로 추적해요. 메모리 백업emptyDir을 사용할 때는 아래의 참고 사항을 확인하세요.
컨테이너가 메모리 요청을 초과하고 실행 중인 노드가 전반적으로 메모리가 부족해지면, 컨테이너가 속한 파드가 퇴거(evicted)될 가능성이 커요.
컨테이너는 장기간 CPU 한도를 초과할 수 있을 수도 있고 없을 수도 있어요. 그러나 컨테이너 런타임은 과도한 CPU 사용 때문에 파드나 컨테이너를 종료하지 않아요.
컨테이너가 리소스 한도 때문에 스케줄링될 수 없는지 또는 종료되고 있는지 확인하려면 문제 해결(Troubleshooting) 섹션을 참조하세요.
컨테이너 리소스 크기 조정 (Resizing container resources)
파드를 만든 후 실제 사용 패턴에 기반해 그 CPU나 메모리 리소스를 조정해야 할 수 있어요. 쿠버네티스는 파드 리소스 크기를 조정하는 두 가지 접근 방식을 제공해요:
제자리 크기 조정 (In-place resize)
이것은 쿠버네티스의 안정적(stable) 기능이며, v1.35 버전부터 그렇게 되어 왔어요. v1.27 릴리스에서 처음 사용 가능해졌어요. 더 이상 이 기능이나 동작을 비활성화하거나 선택 해제할 수 없어요(잠겨 있음); 관련 기능 게이트 InPlacePodVerticalScaling에 값을 명시적으로 설정하면 쿠버네티스는 그것을 무시하지만 오류를 보고하지 않아요.
실행 중인 파드의 컨테이너에 대한 CPU와 메모리 requests와 limits를 재생성 없이 수정할 수 있어요. 이것을 제자리 파드 수직 스케일링(in-place Pod vertical scaling) 또는 제자리 파드 리사이즈(in-place Pod resize)라고 해요. 제자리 리사이즈를 수행하려면 파드의 /resize 하위 리소스를 사용해 컨테이너의 리소스 스펙을 업데이트해요. 컨테이너 스펙에 resizePolicy 필드를 설정해 컨테이너 재시작이 필요한지 제어할 수 있어요.
InPlacePodVerticalScalingSchedulerPreemption 기능 게이트가 활성화되면, 지연된 제자리 리사이즈 요청이 kube-scheduler로 하여금 리사이즈를 위한 공간을 만들도록 할당된 노드에서 낮은 우선순위 파드를 선점하게 할 수 있어요. 자세한 내용은 파드 리사이즈를 위한 선점을 참조하세요.
교체 파드 출시로 크기 조정 (Resizing by launching replacement Pods)
파드의 리소스를 변경하는 클라우드 네이티브 접근 방식은 워크로드 객체(Deployment나 StatefulSet 같은)에서 파드 템플릿을 업데이트하고, 워크로드의 컨트롤러가 업데이트된 리소스를 가진 새 파드로 교체하게 하는 것이에요. 이 접근 방식은 어떤 쿠버네티스 버전에서도 작동하며 어떤 파드 스펙도 변경할 수 있어요.
파드 리사이즈에 대한 더 자세한 내용은 파드 크기 조정하기를 참조하세요. 제자리 리사이즈에 대한 자세한 지침은 컨테이너에 할당된 CPU와 메모리 리소스 크기 조정하기를 참조하세요. 수직 파드 오토스케일러(Vertical Pod Autoscaler)를 사용해 파드 리소스 권장 사항을 자동으로 관리할 수도 있어요.
컴퓨팅 및 메모리 리소스 사용량 모니터링
kubelet은 파드 상태(Pod status)의 일부로 파드의 리소스 사용량을 보고해요.
클러스터에서 모니터링용 선택적 도구를 사용할 수 있다면, 파드 리소스 사용량은 Metrics API에서 직접 또는 모니터링 도구에서 검색할 수 있어요.
메모리 백업 emptyDir 볼륨에 대한 고려 사항
InPlacePodVerticalScalingMemoryBackedVolumes 기능 게이트가 활성화되면, 실행 중인 파드에서 메모리 백업(medium: Memory) emptyDir 볼륨의 sizeLimit을 파드 재생성이나 컨테이너 재시작 없이 동적으로 조정할 수 있어요. 단계별 지침은 컨테이너에 할당된 CPU와 메모리 리소스 크기 조정하기를 참조하세요.
주의:
메모리 관리 관점에서, 프로세스가 메모리를 작업 영역으로 사용할 때와 메모리 백업 emptyDir을 사용할 때 사이에는 몇 가지 유사점이 있어요. 그러나 메모리 백업 emptyDir처럼 메모리를 볼륨으로 사용할 때는 아래의 추가 사항에 주의해야 해요:
- 메모리 백업 볼륨에 저장된 파일은 거의 전적으로 사용자 애플리케이션에 의해 관리돼요. 프로세스의 작업 영역으로 사용될 때와 달리, 언어 수준의 가비지 컬렉션 같은 것에 의존할 수 없어요.
- 볼륨에 파일을 쓰는 목적은 데이터를 저장하거나 애플리케이션 사이에 전달하는 것이에요. 쿠버네티스나 OS가 볼륨에서 파일을 자동으로 삭제하지 않을 수 있으므로, 그 파일들이 사용하는 메모리는 시스템이나 파드가 메모리 압력을 받을 때 회수될 수 없어요.
- 메모리 백업
emptyDir은 성능 때문에 유용하지만, 메모리는 일반적으로 디스크나 SSD 같은 다른 저장 매체에 비해 크기가 훨씬 작고 비용이 훨씬 높아요.emptyDir볼륨에 많은 양의 메모리를 사용하는 것은 파드나 전체 노드의 정상 동작에 영향을 줄 수 있으므로 주의해서 사용해야 해요.
클러스터나 네임스페이스를 관리한다면 메모리 사용을 제한하는 ResourceQuota를 설정할 수도 있어요; 추가 강제를 위한 LimitRange를 정의하고 싶을 수도 있어요. 각 파드에 spec.containers[].resources.limits.memory를 지정하면 emptyDir 볼륨의 최대 크기는 파드의 메모리 한도가 돼요.
대안으로 클러스터 관리자는 ValidatingAdmissionPolicy 같은 정책 메커니즘을 사용해 새 파드에서 emptyDir 볼륨의 크기 한도를 강제할 수 있어요.
로컬 임시 저장소
로컬 임시 저장소에 대한 일반적인 개념과 컨테이너의 임시 저장소 requests 및/또는 limits 구성에 대한 힌트는 로컬 임시 저장소 페이지를 확인하세요.
로컬 임시 저장소 리소스 모니터링
kubelet은 로컬 임시 저장소가 얼마나 사용되고 있는지 측정할 수 있어요. 로컬 임시 저장소 용량 격리를 활성화했다면 그렇게 해요.
쿠버네티스는 파드가 다음에서 사용하는 임시 저장소의 양을 추적해요:
- 컨테이너의 쓰기 가능 레이어(rootfs), 컨테이너 이미지, 또는 둘 다에 쓰기.
- 로컬
emptyDir볼륨에 쓰기. - 파드 자체의 로그(보통
/var/log/pods아래에 저장). /etc/hosts같은 파드에 매핑된 쿠버네티스가 관리하는 시스템 파일.
확장 리소스 (Extended resources)
확장 리소스는 kubernetes.io 도메인 밖의 완전히 정규화된 리소스 이름이에요. 그것들은 클러스터 운영자가 광고하고 사용자가 쿠버네티스 내장이 아닌 리소스를 소비하게 해줘요.
확장 리소스를 사용하려면 두 단계가 필요해요. 첫째, 클러스터 운영자가 확장 리소스를 광고해야 해요. 둘째, 사용자가 파드에서 확장 리소스를 요청해야 해요.
확장 리소스 관리
노드 레벨 확장 리소스
노드 레벨 확장 리소스는 노드에 연결돼요.
각 노드에서 디바이스 플러그인 관리 리소스를 광고하는 방법은 디바이스 플러그인을 참조하세요.
새 노드 레벨 확장 리소스를 광고하려면 클러스터 운영자가 클러스터의 노드에 대한 status.capacity에서 사용 가능한 수량을 지정해 API 서버에 PATCH HTTP 요청을 제출할 수 있어요. 이 작업 후 노드의 status.capacity는 새 리소스를 포함할 거예요. status.allocatable 필드는 kubelet에 의해 새 리소스로 비동기적으로 자동 업데이트돼요.
스케줄러는 파드 적합성을 평가할 때 노드의 status.allocatable 값을 사용하므로, 스케줄러는 그 비동기 업데이트 이후에만 새 값을 고려해요. 새 리소스로 노드 용량을 패치하는 것과 그 리소스를 요청하는 첫 번째 파드가 그 노드에 스케줄링될 수 있는 시간 사이에는 짧은 지연이 있을 수 있어요.
예시:
다음은 master가 k8s-master인 k8s-node-1 노드에 5개의 "example.com/foo" 리소스를 광고하는 HTTP 요청을 curl로 구성하는 방법을 보여 주는 예시예요.
curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "add", "path": "/status/capacity/example.com~1foo", "value": "5"}]' \
http://k8s-master:8080/api/v1/nodes/k8s-node-1/status
참고:
클러스터 레벨 확장 리소스
클러스터 레벨 확장 리소스는 노드에 연결되지 않아요. 그것들은 보통 리소스 소비와 리소스 쿼터를 처리하는 스케줄러 익스텐더(extenders)에 의해 관리돼요.
스케줄러 구성에서 스케줄러 익스텐더가 처리하는 확장 리소스를 지정할 수 있어요.
예시:
스케줄러 정책에 대한 다음 구성은 클러스터 레벨 확장 리소스 "example.com/foo"가 스케줄러 익스텐더에 의해 처리됨을 나타내요.
- 스케줄러는 파드가 "example.com/foo"를 요청할 때만 파드를 스케줄러 익스텐더에 보내요.
ignoredByScheduler필드는 스케줄러가 그PodFitsResources술어에서 "example.com/foo" 리소스를 검사하지 않음을 지정해요.
{
"kind": "Policy",
"apiVersion": "v1",
"extenders": [
{
"urlPrefix": "<extender-endpoint>",
"bindVerb": "bind",
"managedResources": [
{
"name": "example.com/foo",
"ignoredByScheduler": true
}
]
}
]
}
DRA에 의한 확장 리소스 할당
DRA에 의한 확장 리소스 할당을 사용하면 클러스터 관리자가 DeviceClass에 extendedResourceName을 지정할 수 있고, 그러면 DeviceClass와 일치하는 디바이스가 파드의 확장 리소스 요청에서 요청될 수 있어요. 확장 리소스 할당에 대해 자세히 읽어보세요.
확장 리소스 소비
사용자는 CPU와 메모리처럼 파드 스펙에서 확장 리소스를 소비할 수 있어요. 스케줄러가 리소스 회계를 처리해 사용 가능한 양보다 더 많이 파드에 동시에 할당되지 않도록 해요.
API 서버는 확장 리소스의 수량을 정수로 제한해요. 유효한 수량의 예시는 3, 3000m, 3Ki예요. 유효하지 않은 수량의 예시는 0.5와 1500m이에요(1500m은 1.5가 되기 때문).
참고:
파드에서 확장 리소스를 소비하려면 리소스 이름을 컨테이너 스펙의 spec.containers[].resources.limits 맵의 키로 포함해요.
참고:
모든 리소스 요청(CPU, 메모리, 모든 확장 리소스 포함)이 충족될 때만 파드가 스케줄링돼요. 리소스 요청을 충족할 수 없는 한 파드는 PENDING 상태로 남아 있어요.
예시:
아래 파드는 2 CPU와 1 "example.com/foo"(확장 리소스)를 요청해요.
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: myimage
resources:
requests:
cpu: 2
example.com/foo: 1
limits:
example.com/foo: 1
PID 제한
PID(프로세스 ID) 한도는 주어진 파드가 소비할 수 있는 PID 수를 제한하도록 kubelet을 구성할 수 있게 해줘요. 정보는 PID 제한을 참조하세요.
문제 해결
내 파드가 FailedScheduling 이벤트 메시지와 함께 pending 상태예요
스케줄러가 파드가 들어갈 수 있는 어떤 노드도 찾을 수 없으면, 파드는 자리가 생길 때까지 스케줄링되지 않은 채로 남아요. 스케줄러가 파드의 자리를 찾지 못할 때마다 이벤트가 생성돼요. kubectl을 사용해 파드의 이벤트를 볼 수 있어요; 예:
kubectl describe pod frontend | grep -A 9999999999 Events
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 23s default-scheduler 0/42 nodes available: insufficient cpu
앞의 예시에서 "frontend"라는 파드는 어떤 노드에도 충분한 CPU 리소스가 없어 스케줄링에 실패해요. 비슷한 오류 메시지가 충분하지 않은 메모리(PodExceedsFreeMemory)로 인한 실패를 암시할 수도 있어요. 일반적으로 파드가 이런 유형의 메시지로 pending 상태라면 시도해 볼 것이 몇 가지 있어요:
- 클러스터에 더 많은 노드 추가하기.
- pending 파드를 위한 자리를 만들기 위해 불필요한 파드 종료하기.
- 파드가 모든 노드보다 크지 않은지 확인하기. 예를 들어 모든 노드의 용량이
cpu: 1이라면,cpu: 1.1요청이 있는 파드는 절대 스케줄링되지 않을 거예요. - 노드 테인트 확인하기. 대부분의 노드가 테인트되어 있고 새 파드가 그 테인트를 톨러레이션하지 않으면, 스케줄러는 그 테인트가 없는 나머지 노드로의 배치만 고려해요.
kubectl describe nodes 명령으로 노드 용량과 할당된 양을 확인할 수 있어요. 예:
kubectl describe nodes e2e-test-node-pool-4lw4
Name: e2e-test-node-pool-4lw4
[ ... lines removed for clarity ...]
Capacity:
cpu: 2
memory: 7679792Ki
pods: 110
Allocatable:
cpu: 1800m
memory: 7474992Ki
pods: 110
[ ... lines removed for clarity ...]
Non-terminated Pods: (5 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits
--------- ---- ------------ ---------- --------------- -------------
kube-system fluentd-gcp-v1.38-28bv1 100m (5%) 0 (0%) 200Mi (2%) 200Mi (2%)
kube-system coredns-3297075139-61lj3 260m (13%) 0 (0%) 100Mi (1%) 170Mi (2%)
kube-system kube-proxy-e2e-test-... 100m (5%) 0 (0%) 0 (0%) 0 (0%)
kube-system monitoring-influxdb-grafana-v4-z1m12 200m (10%) 200m (10%) 600Mi (8%) 600Mi (8%)
kube-system node-problem-detector-v0.1-fj7m3 20m (1%) 200m (10%) 20Mi (0%) 100Mi (1%)
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
CPU Requests CPU Limits Memory Requests Memory Limits
------------ ---------- --------------- -------------
680m (34%) 400m (20%) 920Mi (11%) 1070Mi (13%)
앞의 출력에서 파드가 1.120 CPU 이상 또는 6.23Gi 메모리 이상을 요청하면 그 노드에 들어가지 못할 것임을 볼 수 있어요.
"Pods" 섹션을 보면 어떤 파드가 노드에서 공간을 차지하고 있는지 볼 수 있어요.
파드에 사용 가능한 리소스 양은 시스템 데몬이 사용 가능한 리소스의 일부를 사용하기 때문에 노드 용량보다 적어요. 쿠버네티스 API에서 각 노드는 .status.allocatable 필드를 가져요 (자세한 내용은 NodeStatus 참조).
.status.allocatable 필드는 그 노드에서 파드에 사용 가능한 리소스 양을 설명해요 (예: 15개의 가상 CPU와 7538 MiB의 메모리). 쿠버네티스의 노드 할당 가능 리소스에 대한 자세한 내용은 시스템 데몬을 위한 컴퓨트 리소스 예약하기를 참조하세요.
리소스 쿼터를 구성해 네임스페이스가 소비할 수 있는 총 리소스 양을 제한할 수 있어요. 쿠버네티스는 해당 네임스페이스에 ResourceQuota가 있을 때 특정 네임스페이스의 객체에 대한 쿼터를 강제해요. 예를 들어 특정 네임스페이스를 다른 팀에 할당하면, 그 네임스페이스에 ResourceQuotas를 추가할 수 있어요. 리소스 쿼터를 설정하면 한 팀이 어떤 리소스를 너무 많이 사용해 그 초과 사용이 다른 팀에 영향을 주는 것을 방지하는 데 도움이 돼요.
그 네임스페이스에 부여하는 접근도 고려해야 해요: 네임스페이스에 대한 전체 쓰기 접근은 그 접근을 가진 사람이 구성된 ResourceQuota를 포함해 어떤 리소스든 제거할 수 있게 해요.
내 컨테이너가 종료됐어요
컨테이너는 리소스가 부족해서 종료될 수 있어요. 컨테이너가 리소스 한도에 부딪혀 종료되고 있는지 확인하려면 관심 있는 파드에 kubectl describe pod을 호출해요:
kubectl describe pod simmemleak-hra99
출력은 다음과 비슷해요:
Name: simmemleak-hra99
Namespace: default
Image(s): saadali/simmemleak
Node: kubernetes-node-tf0f/10.240.216.66
Labels: name=simmemleak
Status: Running
Reason:
Message:
IP: 10.244.2.75
Containers:
simmemleak:
Image: saadali/simmemleak:latest
Limits:
cpu: 100m
memory: 50Mi
State: Running
Started: Tue, 07 Jul 2019 12:54:41 -0700
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
Started: Fri, 07 Jul 2019 12:54:30 -0700
Finished: Fri, 07 Jul 2019 12:54:33 -0700
Ready: False
Restart Count: 5
Conditions:
Type Status
Ready False
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 42s default-scheduler Successfully assigned simmemleak-hra99 to kubernetes-node-tf0f
Normal Pulled 41s kubelet Container image "saadali/simmemleak:latest" already present on machine
Normal Created 41s kubelet Created container simmemleak
Normal Started 40s kubelet Started container simmemleak
Normal Killing 32s kubelet Killing container with id ead3fb35-5cf5-44ed-9ae1-488115be66c6: Need to kill Pod
앞의 예시에서 Restart Count: 5는 파드의 simmemleak 컨테이너가 종료되고 다섯 번 재시작되었음을(지금까지) 나타내요. OOMKilled 이유는 컨테이너가 한도보다 더 많은 메모리를 사용하려고 시도했음을 보여 줘요.
다음 단계는 애플리케이션 코드에서 메모리 누수를 확인하는 것일 수 있어요. 애플리케이션이 기대하는 대로 동작하고 있다는 것을 알게 되면, 그 컨테이너에 더 높은 메모리 한도(및 가능하면 요청)를 설정하는 것을 고려해요.
더 알아보기 (Learn more)
- 컨테이너와 파드에 메모리 리소스를 할당하는 실습 경험 얻기.
- 컨테이너와 파드에 CPU 리소스를 할당하는 실습 경험 얻기.
- API 참조가 컨테이너와 그 리소스 요구 사항을 어떻게 정의하는지 읽기.
- 로컬 임시 저장소에 대해 더 읽기.
- kube-scheduler 구성 참조(v1)에 대해 더 읽기.
- 파드의 서비스 품질(QoS) 클래스에 대해 더 읽기.
- DRA에 의한 확장 리소스 할당에 대해 더 읽기.