AI 기능을 붙이면서 GPU 서버를 들이는 조직이 늘었습니다. 노드 몇 대에 GPU 카드를 꽂아 쿠버네티스 클러스터에 붙여 두면, 모델을 학습하는 팀과 추론을 서비스하는 팀이 같은 장비를 나눠 쓰는 구조가 됩니다.
여러 팀이 GPU를 나눠 쓰는 클러스터를 맡은 운영자라면 분기마다 두 가지 요청을 같이 받습니다. 한쪽에서는 GPU가 모자라니 더 사자고 하고, 다른 쪽에서는 이미 산 것부터 얼마나 쓰고 있는지 묻습니다. 저는 두 요청에 대시보드를 열어 두고도 답하지 못한 적이 있습니다. 화면에 있던 것은 카드별 사용률 그래프였고, 그 그래프로는 청구서를 설명할 수 없었습니다.
GPU 비용 관리는 세 가지 시간을 숫자로 만드는 일입니다. 산 GPU가 실제로 일한 시간(가동률), 잡아만 두고 놀린 시간(유휴), 그리고 그 시간이 누구 몫인지(비용 배분)입니다.
이 글에서 사용률은 짧은 표본 구간에서 측정한 백분율을 가리킵니다. 가동률은 청구된 시간 가운데 정해 둔 임계값 이상으로 쓴 구간이 차지하는 비율입니다. 비용을 계산하려고 편의상 붙인 이름이고 업계 표준 용어는 아닙니다. 두 숫자를 섞으면 계산 결과가 달라집니다. 카드는 GPU 카드를 줄인 말이고, 카드 한 장은 물리 GPU 하나를 가리킵니다.
저는 이 순서로 확인합니다.
GPU 상태를 볼 때 가장 먼저 확인하는 숫자는 사용률입니다. 기본 점검 명령어인 nvidia-smi를 실행하면 나오는 백분율이고, 대시보드에도 대개 사용률이 큰 그래프로 올라가 있습니다. 이 값은 표본 구간 동안 하나 이상의 커널이 실행된 시간의 비율입니다. 장비가 일하고 있는지 보려고 만든 숫자라, GPU 연산 자원을 얼마나 촘촘하게 썼는지까지는 알려 주지 않습니다.
DCGM(Data Center GPU Manager)이 따로 제공하는 SM(Streaming Multiprocessor) Active(DCGM_FI_PROF_SM_ACTIVE)를 함께 봐도 사정은 비슷합니다. NVIDIA 문서는 이 값을 "멀티프로세서에서 적어도 하나의 워프가 활성이었던 시간의 비율을 모든 멀티프로세서에 대해 평균한 값"으로 정의합니다. 워프(warp)는 GPU가 한 번에 묶어서 실행하는 스레드 다발이고, NVIDIA GPU에서는 스레드 32개가 한 워프입니다. 이어지는 설명에는 "여기서 활성이라는 말이 워프가 실제로 컴퓨팅을 하고 있다는 뜻은 아니며, 메모리 요청을 기다리는 워프도 활성으로 계산한다"는 단서가 붙어 있습니다.
같은 문서는 값 0.8 이상이 GPU를 효과적으로 쓰기 위한 필요조건이지 충분조건은 아니라고도 밝힙니다. 0.8을 넘겨야 하지만, 넘겼다고 해서 잘 쓰고 있다고 단정할 수는 없다는 뜻입니다.
사용률이 높다는 것은 그 구간에 무언가 돌고 있었다는 사실까지만 알려 줍니다. 그 시간 동안 끝낸 작업의 양, 곧 처리량이 그에 걸맞았는지는 사용률만으로 알 수 없습니다.
비용은 다른 단위로 나갑니다. 클라우드에서 빌리면 인스턴스 시간으로 청구되고, 직접 사서 쓰면 장비 값을 쓰기로 한 기간, 곧 상각 기간으로 나눈 금액에 전력과 냉방 비용이 시간 단위로 발생합니다. 어느 쪽이든 비용의 단위는 GPU 시간입니다. 그래서 가동률은 청구된 GPU 시간 가운데 작업이 실제로 실행된 시간으로 계산합니다.
가동률은 순간 사용률의 단순 평균으로 대신하지 않는 편이 좋습니다. 평균을 내면 짧게 치솟은 구간과 길게 낮았던 구간이 한 값으로 합쳐집니다. 그래서 임계값을 하나 정해 시간을 구간으로 나눕니다. 예를 들어 30%로 잡고, 5분 평균 사용률이 그 미만인 구간은 유휴로, 그 이상인 구간은 가동으로 분류하면 결과가 시간으로 나옵니다. 가동으로 분류한 시간에 시간당 단가를 곱하면 금액이 나옵니다.
여기서 30%는 계산 방법을 보여 주기 위한 예시일 뿐 모든 워크로드에 적용할 권장값이 아닙니다. 모니터링 도구가 화면에 표시하는 유휴·정상 상태 기준도 대개 이 임계값과 다릅니다. 할당된 GPU 가운데 사용률 1% 미만을 유휴로 잡는 도구도 있습니다. 화면 상태 값과 비용 계산용 임계값은 목적이 다르므로, 같은 말로 부르더라도 따로 두는 편이 혼선이 적습니다.

임계값은 워크로드 종류마다 다릅니다. 학습이나 배치 작업은 사용률이 계속 높은 것이 정상이지만, 추론 서비스에서는 최대 트래픽을 감당하려고 여유를 남겨 두므로 평균이 낮은 것이 정상입니다. 목표치 하나를 클러스터 전체에 걸면 추론을 맡은 팀만 매달 지적을 받습니다.
GPU 사용률이 낮은 장비를 원인 구분 없이 유휴 하나로 집계하면 총량은 나오지만 무엇을 해야 할지는 알기 어렵습니다. 놀고 있는 이유가 다르면 회수하는 방법도 다르기 때문입니다. 유휴는 원인에 따라 네 종류로 나눠 집계합니다.
메모리만 점유하는 종류는 대시보드에서 알아채기가 가장 어렵습니다. 카드가 잡혀 있으니 대시보드에는 할당된 카드로 집계되고, 사용률만 낮게 나옵니다. 메모리 점유는 대체로 개발용 주피터 노트북 Pod, 중단된 채 남은 학습 작업, 트래픽이 없는 시간대의 추론 서버에서 나옵니다. 메모리 점유량만 보면 정상으로 읽히므로 사용률과 GPU 프로세스 수를 같이 봐야 구분할 수 있습니다.
GPU fragmentation은 남은 카드가 노드마다 한두 장씩 흩어져 큰 요청이 들어가지 못하는 상태를 말합니다. 합계만 보여 주는 총량 지표로는 이런 상태가 드러나지 않습니다. GPU 20장 가운데 16장이 할당됐다고 나오면 4장이 남은 것처럼 읽히는데, 그 4장이 노드 네 곳에 한 장씩 있으면 4장을 한 번에 요청하는 학습 작업은 배정되지 않습니다. 저는 증설 요청을 검토할 때 노드마다 몇 장이 남아 있는지부터 확인합니다. GPU fragmentation이 원인이라면 카드를 더 사도 몇 달 뒤에 같은 요청을 다시 받습니다.
네 종류를 구분해 집계하려면 카드 단위 사용률만으로는 부족합니다. 어느 카드를 어떤 워크로드가 잡고 있었는지가 같은 시간축에 남아 있어야 합니다. 쿠버네티스라면 노드와 GPU, Pod의 연결 관계를 함께 기록해 두는 것이 시작입니다. 노드와 GPU, Pod의 연결 기록은 유휴 집계에만 쓰는 것이 아닙니다. 어느 팀이 얼마를 낼지 정할 때도 같은 기록이 필요합니다.
쿠버네티스 공식 문서에 따르면 GPU는 limits에만 지정합니다. requests를 함께 쓸 수는 있지만 두 값이 같아야 합니다. 여기에 GPU는 확장 리소스라 수량을 정수 단위로만 요청할 수 있습니다. 그래서 기본 설정에서는 컨테이너 하나가 GPU 한 장 전체를 점유합니다. 전용으로 할당한 GPU라면 배분 기준은 사용률이 아니라 점유 시간입니다.
사용률 비례로 나누면 뒤집힌 결과가 나옵니다. 한 팀이 카드 한 장을 통째로 점유하고 있으면, 그 팀의 사용률이 낮아도 다른 팀은 그 카드를 쓰지 못합니다. 사용률에 비례해 청구하면 배치 크기를 키워 카드를 최대한 채워 쓴 팀이 더 많이 내고, 잡아만 두고 쓰지 않은 팀이 덜 냅니다. 낭비를 줄일수록 청구서가 늘어나는 구조라, 아무도 카드를 채워 쓸 이유가 없어집니다.

사용률은 배분 대신 회수 판단에 씁니다. 누가 얼마를 내는지는 점유 시간으로 정하고, 어느 할당을 거둬들일지는 사용률로 정합니다. 두 숫자의 역할을 섞지 않는 것만으로 정산 회의에서 되묻는 일이 줄었습니다.
배분하려면 점유 시간에 주인 이름이 붙어 있어야 합니다. 저는 정산 요청을 받은 뒤에 라벨 체계를 만들려 한 적이 있는데, 이미 끝난 작업의 주인을 확인할 방법이 없었습니다. 라벨은 스케줄링하는 순간에 붙어야 남습니다. 제 경우 네임스페이스 하나로는 부족해서 팀과 프로젝트, 환경, 모델까지 필수 라벨로 두고, 라벨이 없으면 배정되지 않게 막았습니다.
카드를 나눠 쓰는 환경이라면 공유 방식에 따라 배분할 수 있는지가 달라집니다. MIG(Multi-Instance GPU)는 카드 한 장을 여러 인스턴스로 나누는 기능입니다. 인스턴스가 나뉘어 있으니 인스턴스 단위로 배분할 수 있습니다.
타임 슬라이싱(time-slicing)은 카드 한 장을 시간으로 쪼개 여러 워크로드가 번갈아 쓰는 방식입니다. NVIDIA GPU Operator 문서에 따르면 이 방식은 MIG와 달리 복제본 사이를 메모리와 오류로 격리하지 않습니다. 격리가 없으면 복제본별 사용량을 따로 집계할 근거도 약해집니다. 그래서 타임 슬라이싱 구간은 팀별로 청구하기보다 공용 풀로 두고 정액으로 나누는 쪽이 설명하기 쉽습니다.
남는 문제는 유휴분입니다. 리눅스 재단 산하에서 기술 지출 관리 프레임워크를 정리하는 FinOps Foundation은 공유 비용을 비례·고정·균등 분할 가운데 어느 방식으로 나눌지 정해 문서로 남겨 두라고 권합니다. 유휴분을 어느 팀에도 붙이지 않으면 아무도 줄일 이유가 없고, 전부 균등하게 나누면 적게 쓴 팀이 항의합니다. 저는 유휴분을 플랫폼 몫으로 남기고 그 금액을 매달 공개했습니다. 이렇게 두니 논쟁이 가장 적었습니다.
줄지 않습니다. 카드를 절반만 쓰면서 하루를 잡고 있으면 하루치가 그대로 청구됩니다. 사용률이 낮았다고 해서 금액이 달라지지는 않습니다.
비용이 줄어드는 시점은 사용률이 올라갈 때가 아니라 할당을 거둬들여 청구 시간이 줄어들 때입니다. 그래서 사용률만 보지 않고 점유 시간과 유휴 시간을 함께 봅니다.
정상 범위를 숫자 하나로 정해 둔 기준은 없습니다. 같은 작업을 조건만 바꿔 두 번 돌려 비교하는 편이 실용적입니다. 배치 크기나 데이터 로더 설정을 손봤을 때 사용률과 처리량이 함께 올라갔다면 개선입니다. 사용률만 올라가고 처리량이 그대로라면 대기 시간이 늘었을 가능성을 봅니다.
온프레미스로 산 장비도 마찬가지입니다. 이미 지출이 끝난 장비라 배분이 의미 없어 보이지만, 시간당 단가를 만들어 두면 비교 대상이 생깁니다. 취득가를 상각 기간으로 나눈 뒤 전력과 냉방, 랙 비용을 더하고 가동 가능 시간으로 나누면 됩니다. 이 단가가 있어야 증설과 클라우드 임대를 같은 단위로 놓고 판단할 수 있습니다. 저는 이 숫자가 없던 동안 두 선택지를 나란히 놓지 못했습니다.
두 말이 동시에 맞을 수 있습니다. 큰 요청이 GPU fragmentation 때문에 들어가지 못할 수도 있고, 특정 시간대에만 수요가 몰릴 수도 있고, 잡아만 두고 쓰지 않는 할당이 쌓였을 수도 있습니다.
세 경우는 대응이 전부 다릅니다. 그래서 요청을 받으면 노드마다 카드가 몇 장 남았는지와 시간대별 할당 추이부터 확인합니다.
가동률과 유휴 시간, 비용 배분 세 숫자를 만들어 두면 증설 회의에서 할 수 있는 말이 달라집니다. 카드가 부족하다는 말에는 지금 유휴로 잡히는 시간이 몇 시간인지로 답합니다. 비용을 누가 냈느냐는 물음에는 어느 팀의 어떤 작업이 얼마를 썼는지로 답합니다. 저는 이 숫자를 만들기 전까지 증설 여부를 회의 분위기에 밀려 결정했고, 그 결정을 나중에 설명하지 못했습니다.
출발점은 수집입니다. 카드별 사용률과 메모리 사용량, 그리고 그 카드를 어떤 워크로드가 점유했는지가 함께 기록돼 있어야 나중에 가동 시간과 유휴 시간을 집계할 수 있습니다.
와탭 서버 모니터링은 리눅스 서버 에이전트로 GPU 지표를 수집하고, 물리 GPU와 MIG 인스턴스를 나눠 보여 줍니다. 리눅스 에이전트에서 GPU와 프로세스 수집 옵션을 켜면 GPU를 점유한 프로세스 현황도 볼 수 있어, 앞에서 나눈 네 종류 가운데 '할당됐지만 프로세스 없음'과 '메모리만 점유'를 구분하는 데 쓸 수 있습니다. 다만 할당·사용 상태 값은 물리 GPU에만 표시되고 MIG 인스턴스는 N/A로 나오므로, MIG 단위로 배분할 생각이라면 이 점을 먼저 확인하는 편이 좋습니다.
쿠버네티스 환경에서는 GPU 트렌드로 장비별 사용량을 일간·주간·월간 단위로 확인해 어느 시기에 어떤 장비가 놀았는지 볼 수 있습니다. GPU 서비스 그룹핑을 켜면 Pod에 붙인 라벨이 GPU 메트릭으로 전파돼 팀이나 프로젝트 단위로 사용량을 묶어 볼 수 있습니다. 앞에서 라벨을 스케줄링 시점에 붙여 두라고 한 이유가 여기서 드러납니다. 다만 GPU 비용을 금액으로 계산해 주는 전용 기능은 없어, 앞에서 만든 시간당 단가는 따로 곱해야 합니다.