KCNA — 쿠버네티스·클라우드 네이티브 입문 · 배치 예산과 입장 정책을 실제 실패로 구분하기 · 이론
기본값·할당량·입장 거절의 장부
한 줄 요약
LimitRange는 개별 객체의 기본값과 범위를, ResourceQuota는 네임스페이스의 합계를 다룹니다. 할당량에 걸린 요청은 Pod가 Pending으로 기다리는 것이 아니라 생성 단계에서 거절될 수 있습니다.
왜 이게 필요했나
작은 팀 하나가 실수로 수천 개의 객체를 만들면 클러스터의 다른 팀도 영향을 받습니다. 자원 정책은 악의적인 공격뿐 아니라 반복문 하나의 오타가 만드는 피해도 제한합니다. 그러나 제한을 넣었다는 사실만으로 원하는 동작을 보장하지는 못합니다. 어떤 요청에 적용되는지, 기존 객체를 건드리는지, 사용량이 무엇을 세는지 실제로 확인해야 합니다.
새 Pod를 만들려고 했는데 Forbidden이 나왔다고 해 봅시다. 이것을 무조건 계정 권한 문제로 보면 RBAC 권한을 더 주는 잘못된 대응으로 이어집니다. 같은 HTTP 상태라도 응답 본문의 원인과 관련 객체가 중요합니다. 할당량 초과는 권한을 추가한다고 해결되지 않습니다. 제한을 삭제해 성공시키는 것도 정책을 이해한 해결이 아닙니다.
어떻게 동작하나
이번 실험에는 한 컨테이너의 기본 요청 CPU 100m·메모리 32Mi, 기본 제한 CPU 200m·메모리 64Mi를 둡니다. 입력에서 resources를 생략한 새 Pod와 API에 저장된 Pod를 나란히 읽습니다. 저장된 객체에 값이 생겼다면 사용자 파일의 값이 아니라 admission 과정에서 적용된 기본값일 수 있습니다. 이미 CPU 50m를 명시해 만든 기존 Pod를 100m로 바꿨다고 가정하지 않습니다.
ResourceQuota의 requests.cpu는 이 공간에 속한 대상 Pod의 CPU 요청 합계를 제한합니다. 지금 CPU를 실제로 몇 퍼센트 쓰는지를 측정한 숫자가 아닙니다. 이번 예의 장부는 다음처럼 움직입니다.
| 상태 | 저장된 요청 합계 | 허용 상한 | 새 요청의 결과 |
| --- | --- | --- | --- |
| fit 50m + defaulted 100m | 150m | 200m | extra 100m를 더하면 250m이므로 거절 |
| 상한 조정 후 | 150m | 300m | 같은 extra 요청이 들어와 합계 250m |
| 상한을 다시 낮춤 | 250m | 100m | 기존 Pod는 유지, 새 small 요청은 거절 |
| extra 회수·상한 재조정 | 150m | 200m | small 50m가 들어와 합계 200m |
이 표의 상한 변경은 폐기용 네임스페이스에서 정책 의미를 배우는 실험입니다. 운영에서는 사용량·업무 요구·팀 배분을 검토한 권한 있는 담당자가 정책을 바꿉니다. 단순히 에러를 없애려고 할당량을 늘리라는 안내가 아닙니다.
할당량을 현재 사용량 아래로 낮췄다고 기존 Pod를 자동으로 종료시키지는 않습니다. 따라서 status.used가 spec.hard보다 큰 상태를 관측할 수 있습니다. 이것은 정책이 무효라는 증거가 아닙니다. 기존 객체를 유지하면서 추가 생성을 거절하는지 확인해야 합니다. 강제로 사용량을 줄여야 한다면 어떤 작업을 종료할지 별도의 운영 결정을 해야 합니다.
ResourceQuota는 한 축만 제한하지 않을 수 있습니다. requests.cpu, requests.memory, limits.cpu, limits.memory, Pod 개수 등을 함께 검사할 수 있으므로 CPU만 계산해서 성공을 단정하지 않습니다. 이번 실험은 나머지 상한에 여유를 두어 CPU 요청량의 효과를 분리합니다. LimitRange의 개별 상한을 어긴 것인지, ResourceQuota의 합계 상한을 어긴 것인지도 구별합니다.
현장에서 만나는 모습
Pod를 만들기 전 입력, API 응답, 생성 후 조회 결과를 하나의 증거 묶음으로 봅니다. 할당량 거절에서는 실제 HTTP 403과 Kubernetes Status의 Forbidden 이유, team-budget 및 requests.cpu가 나오는 설명을 확인합니다. 이어 같은 이름의 Pod가 존재하지 않는지 확인합니다. 계정 권한 오류나 TLS 오류를 받아 놓고 원하는 정책이 작동했다고 기록하지 않습니다.
반대로 HTTP 201은 객체 생성이 수락됐다는 뜻이지 앱 기동 완료가 아닙니다. 새 Pod UID가 응답과 일치하는지, 노드에 배치되어 Running·Ready인지까지 이어서 봅니다. 스케줄링과 admission은 서로 다른 단계이므로 “이 요청은 quota 안에 있다”와 “이 Pod가 실행될 노드가 있다”는 별개입니다.
kubectl -n kcna-capacity get limitrange defaults -o jsonkubectl -n kcna-capacity get resourcequota team-budget -o jsonkubectl -n kcna-capacity get pods -o json상태 장부는 변경 직후에 아직 이전 값을 보여 줄 수 있습니다. 소유한 extra Pod를 지운 뒤에는 실제 객체 부재와 quota 사용량 감소를 확인하고 다음 요청을 보냅니다. 고정된 짧은 sleep만으로 완료를 가정하지 않습니다. 기다림에는 제한 시간을 두고, 시간이 넘으면 성공으로 덮지 말고 관측을 보존합니다.
다음 실습에서 할 것
자원이 남는 것처럼 보이는 상황에서도 생기는 두 실패를 직접 비교합니다. 배치 실패는 실제 Pod UID와 스케줄러 이벤트로, 입장 거절은 API 상태와 객체 부재로 입증합니다. 할당량 축소 뒤 기존 fit·defaulted·extra의 UID와 컨테이너가 그대로인지 확인한 다음, 조사한 extra만 회수하고 작은 요청을 새로 수락시킵니다. 다른 네임스페이스의 정상 비교군은 바꾸지 않습니다.
공식 근거
- [LimitRange](https://kubernetes.io/docs/concepts/policy/limit-range/)
- [ResourceQuota](https://kubernetes.io/docs/concepts/policy/resource-quotas/)
- [API 응답 상태](https://kubernetes.io/docs/reference/using-api/api-concepts/)