CNPE — 클라우드 네이티브 플랫폼 엔지니어 (전문가) · 플랫폼 아키텍처와 인프라 · 이론
멀티테넌시와 비용을 숫자로 다루기
한 줄 요약
멀티테넌시의 핵심 숫자는 오버커밋 비율입니다. 테넌트에게 약속한 총량을 실제로 가진 총량으로 나눈 값이고, 이 값이 1.0 이면 아무것도 아끼지 못한 것이고 너무 크면 어느 날 다 같이 못 뜹니다.
왜 이 숫자가 필요한가
쿠버네티스의 ResourceQuota 는 네임스페이스마다 따로 걸립니다. 그래서 쿼터를 하나씩 볼 때는 아무 문제가 없어 보이는데, 다 더하면 클러스터가 가진 것보다 훨씬 많은 경우가 흔합니다. 이 합계를 아무도 안 보기 때문입니다.
tenant-red requests.cpu 12tenant-green requests.cpu 9tenant-gold requests.cpu 9────────────────────────────약속한 총량 30 코어실제 allocatable 24 코어 (8 코어 노드 3 대)오버커밋 비율 1.251.25 는 "모두가 동시에 상한까지 쓰지는 않는다" 는 데 25% 를 건 것입니다. 이 내기는 대개 이깁니다. 다만 이긴다는 사실과 얼마를 걸었는지 아는 것은 다른 일이고, 후자를 모르면 그건 설계가 아니라 우연입니다.
어떻게 동작하나
세 층이 각자 다른 것을 막습니다
| 장치 | 막는 것 | 걸리는 시점 |
| --- | --- | --- |
| ResourceQuota | 네임스페이스 전체의 합계 | 객체를 만들 때 |
| LimitRange (max) | 컨테이너 한 개의 크기 | 객체를 만들 때 |
| LimitRange (default·defaultRequest) | 아무것도 안 적은 파드 | 객체를 만들 때 값을 채워 준다 |
셋 중 하나만 쓰면 반드시 구멍이 납니다. 쿼터만 걸면 한 파드가 테넌트 몫을 통째로 먹을 수 있고, LimitRange 만 걸면 작은 파드를 수백 개 만들 수 있습니다.
defaultRequest 를 따로 두는 이유
default 만 적으면 쿠버네티스는 요청을 상한과 같은 값으로 채웁니다. 그러면 모든 파드가 Guaranteed 가 되고, 스케줄러는 상한만큼의 자리를 통째로 예약합니다. 오버커밋으로 벌려던 여유가 그 순간 사라집니다. defaultRequest 를 상한보다 낮게 따로 두어야 파드가 Burstable 이 되고, 그래야 1.25 라는 숫자가 의미를 갖습니다.
커스텀 리소스에도 쿼터를 걸 수 있습니다
count/appclaims.platform.labhub.io 처럼 적으면 CRD 로 만든 리소스의 개수에도 상한이 걸립니다. 셀프서비스 API 를 열 때 이것이 유일한 발급 상한입니다.
여기에 함정이 하나 있습니다. CRD 를 만든 직후에 그 리소스를 겨냥한 쿼터를 걸면, 쿼터 컨트롤러가 아직 그 리소스를 모르는 상태로 굳습니다. 이 상태의 쿼터는 사용량이 비어 있고, 그 네임스페이스에서 그 리소스를 만들려는 요청은 전부 거절됩니다. 화면에는 "status unknown for quota" 만 나옵니다. 고치는 방법은 쿼터를 지웠다가 다시 거는 것 하나뿐입니다.
현장에서 만나는 모습
비용을 줄이라는 요구를 받으면 사람들은 먼저 노드를 줄입니다. 그런데 대부분의 낭비는 노드가 아니라 요청과 실사용의 차이에 있습니다. 요청 4 코어에 실사용 0.3 코어인 워크로드가 스무 개면, 스케줄러는 80 코어를 예약해 두고 실제로는 6 코어를 씁니다. 노드를 줄이면 이 워크로드들이 못 뜨고, 아무것도 해결되지 않습니다.
그래서 순서는 반대입니다. 요청을 실사용에 맞춰 내리고(right-sizing), 그다음에 남은 노드를 정리합니다. 이 순서를 뒤집으면 장애를 먼저 만듭니다.
다음 퀴즈에서 확인할 것
세 장치가 각각 무엇을 막는지, 그리고 오버커밋 비율을 어떻게 읽는지를 확인합니다.