||
안녕하세요. 여기어때컴퍼니 공통플랫폼개발팀 플랫폼엔지니어 코플 입니다. 이번 글은 Right-Sizing라는 업무를 진행하면서 기준을 정하고 활용해 얻은 경험을 공유하는 글을 작성했습니다.
Right-Sizing 란 무엇일까?
Right-Sizing이란 Kubernetes 환경에서 각 Pod에 설정된 resources.requests와 limits를 실제 사용 패턴에 맞게 조정하는 작업입니다. 너무 높게 설정된 Request는 클러스터 자원을 불필요하게 점유하고, 너무 낮게 설정된 Request는 OOMKill이나 CPU Throttling으로 이어집니다. 적정한 수준을 찾는 것이 Right-Sizing의 목적입니다.
여기어때컴퍼니는 시스템 복잡도를 파악하고 모니터링하기 위해 Observability를 Self-hosted로 운영하고 있습니다. 수백 개 서비스의 다양한 지표를 계측하기 위해 OpenTelemetry와 Grafana LGTM 스택을 주력으로 활용합니다. LGTM 스택은 로그(Loki), 메트릭(Mimir), 트레이스(Tempo)를 각각 담당하며, 각 역할에 맞는 다수의 컴포넌트가 연계되어 구성됩니다. 그만큼 많은 인프라 리소스를 Observability에 활용 중이기에 각각의 컨퍼넌트 또한 모니터링을 진행하게 됩니다. 옵저버빌리티 인프라의 노드 사용률을 점검하던 중 이런 의문이 들었습니다.
“노드는 꽉 찬 것처럼 보이는데, 실제로 Pod들이 그만큼 쓰고 있는 건가?”
Kubernetes에서 resources.requests는 단순한 설정값이 아닙니다. 스케줄러가 Pod을 노드에 배치할 때 이 값을 기준으로 노드의 가용 용량을 판단합니다. 즉 Pod가 실제로 얼마를 쓰든, requests에 선언된 만큼은 이미 예약된 것으로 처리됩니다.
이 구조에서 over-provisioning은 단순한 낭비를 넘어 클러스터 운영 전반에 영향을 줍니다. 실제 사용률은 낮음에도 requests 합산이 노드 용량에 근접하면 새로운 Pod이 스케줄되지 못하고, 이를 해소하기 위해 노드를 추가해도 실제 활용도는 여전히 낮은 상태가 지속됩니다. 반대로 under-provisioning은 Memory의 경우 OOMKill로, CPU의 경우 Throttling으로 직결됩니다.
초기 구성 당시에는 성능 테스트를 기반으로 Observability의 다양한 컴포넌트의 리소스를 산정했지만, 서비스가 성장하고 트래픽 패턴이 변화하면서 당시 기준이 현재에도 유효한지 검증이 필요한 시점이 되어 있었습니다. 문제는 단순히 “Request가 높다 또는 낮다”는 것이 아니었습니다. 현재 설정값이 적정한지 판단할 수 있는 명확한 정책과 기준이 수립하는 것이 였습니다.
먼저 부딪힌 세 가지 문제
가장 직관적인 접근은 현재 사용량 지표를 기반으로 Request를 조정하는 것입니다. 하지만 이를 실제로 적용하려고 하자 기술적으로 몇가지를 정의해야 했습니다.
첫 번째 문제는 측정 방식에 따라 결론이 달라진다는 것이었습니다. 같은 컴포넌트를 평균(avg)으로 보면 넉넉해 보이고, 최대값(max)으로 보면 위험해 보이는 경우가 있었습니다. 기간을 3개월로 잡으면 안정적으로 보이던 것이 최근 2주로 좁히면 다른 패턴이 나오기도 했습니다. 측정 방식과 기간이 정해지지 않은 상태에서는 같은 데이터를 보고도 전혀 다른 판단을 내릴 수 있었습니다.
두 번째 문제는 컴포넌트마다 리소스 사용 패턴이 전혀 다르다는 것이었습니다. 여기어때에서 운영하고 있는 옵저버빌리티 스택은 역할에 따라 여러 컴포넌트로 구성됩니다. 로그나 메트릭 데이터를 수신하고 저장하는 컴포넌트(Ingester)는 데이터를 일정량 메모리에 쌓았다가 주기적으로 디스크에 내려쓰는 방식으로 동작합니다. 때문에 메모리 사용량이 주기적으로 증가했다가 감소하는 파형 패턴을 보입니다. 오래된 데이터를 정리하고 압축하는 컴포넌트(Compactor)는 대부분의 시간에는 대기 상태이다가 작업이 시작되면 순간적으로 대용량 메모리를 소비하는 버스트 패턴을 보입니다. 반면 데이터를 분산하거나 쿼리 요청을 라우팅하