Devops/Kubernetes & Docker

ResourceQuota 적용기

광진구뚝배기 2025. 6. 22. 18:07

우리 회사에서 운영하는 여러 서비스들은 각자 자신의 config repository 에서  values.yaml에 CPU와 Memory Rrequest 값을 정의하고 있다. 문제sms 개발자들은 인프라 설정을 잘 몰라 그냥 복붙해서 쓰다 보니, 이 값들이 실제 필요량보다 너무 크게 잡히거나 적게 잡히거나 하는 것이다. 

특히 dev 환경에서 이게 심했다. m7g.xlarge 노드 3개를 띄워 놓았음에도 pod들이 memory 부족으로 스케줄링이 안 되고 대기하는 상황이 매번 발생했다. 

 

0/3 nodes are available: 3 Insufficient memory. preemption: 0/3 nodes are available: 3 No preemption victims found for incoming pod

 

실제로 해당 노드들은 CPU 자원은 넉넉했지만, 메모리가 꽉 차서 새로운 팟을 받을 수 없었다.
원인은 명확했다. 각 서비스의 리소스 요청이 실제 사용량보다 훨씬 크게 설정되어, 불필요하게 많은 메모리를 예약하고 있었던 것이다.

 

노드 리소스 현황 확인

k top nodes
NAME                                               CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
[pod 내부 ip 1].ap-northeast-2.compute.internal   1667m        10%    22248Mi         77%
[pod 내부 ip 2].ap-northeast-2.compute.internal   1409m        8%     16378Mi         56%
[pod 내부 ip 3].ap-northeast-2.compute.internal   1282m        8%     16396Mi         56%

 

 

그라파나 모니터링과 리소스 요청 조정

그라파나 대시보드의 memory utilization from request 지표를 확인해보니 메모리 요청 대비 실제 사용률이 50% 미만인 서비스가 다수였다. 예상한대로 서비스들이 필요 이상으로 많은 메모리를 예약하고 있던 것이다.

과도한 자원을 차지하는 서비스들을 선별해 Resource Request 값을 수정했다.

이를 통해 불필요한 자원 예약을 줄이고, 실제 사용량에 맞춰 리소스 요청을 최적화했다.

 

ResourceQuota와 LimitRange

인프라 차원에서는 네임스페이스별로 CPU와 메모리 자원의 상한을 관리하기 위해 ResourceQuota와 LimitRange를 관리했다.


ResourceQuota

네임스페이스 내 전체 자원 요청 상한 설정
LimitRange 

Pod 또는 컨테이너 단위의 기본 요청 및 제한 설정으로 과도한 자원 예약 방지

 

- values.yaml에서 네임스페이스별 CPU, 메모리 요청값을 정의
- Helm 템플릿을 이용해 네임스페이스별로 LimitRange와 ResourceQuota 생성
- 기본 CPU 요청과 한도를 LimitRange로 설정해 Pod의 과다 예약 방지
-ResourceQuota로 네임스페이스 전체 자원 요청 상한 설정

 

적용한 Helm 템플릿 예시

ResourceQuota와 LimitRange 템플릿을 만들고, values.yaml에서 namespace별 CPU, Memory Request 값을 정의하여 적용했다.

 

# resource-quota.yaml

{{- range .Values.namespaces }}
apiVersion: v1
kind: ResourceQuota
metadata:
  name: {{ .name }}-quota
  namespace: {{ .name }}
spec:
  hard:
    requests.cpu: {{ with .cpu }}{{ default $.Values.quota.cpu.request .request }}{{ else }}{{ $.Values.quota.cpu.request }}{{ end }}
{{- with .memory }}
  {{- if .request }}
    requests.memory: {{ .request | quote }}
  {{- end }}
{{- end }}
---
{{- end }}

 

# limit-range.yaml

{{- range .Values.namespaces }}
apiVersion: v1
kind: LimitRange
metadata:
  name: {{ .name }}-limitrange
  namespace: {{ .name }}
spec:
  limits:
  - type: Container
    default:
      cpu: "1"
    defaultRequest:
      # 1/100 core
      cpu: "10m"
---
{{- end }}


# values.yaml 예시

- name: silicon-scope-api-system
- name: silicon-scope-synchronizer-system
  cpu:
    request: "2"
- name: klayswap-front-system
  memory:
    request: "512Mi"
- name: launchpad-front-system
  memory:
    request: "256Mi"

quota:
  cpu:
    request: "1"