DGX Spark급 박스 두 대, 하나의 모델: 각 대에 사본을 두고 분할을 중단
운영
7주 동안 로컬 언어 모델은 텐서 병렬 처리를 통해 NVIDIA GB10 머신 두 대에 분할되어 실행되었습니다. 10월 22일, 이를 각 머신에 모델의 전체 사본을 두고 그 앞에 최소 연결 수 기준 프록시를 배치하는 방식으로 교체했습니다. 처리량은 초당 약 180에서 210 토큰으로 증가했으며, 이제 어느 한 머신을 재시작해도 모델이 중단되지 않습니다.
분할했던 이유
각 머신은 CPU와 GPU가 공유하는 128 GB 메모리를 갖추고 있습니다. 9월에는 모델이 한 대의 머신에 탑재되었지만, 캐시는 간신히 들어갔습니다. 사용했던 vLLM 빌드가 이 모델의 KV 캐시를 FP8으로 유지하지 못해 캐시가 BF16으로 저장되었고, 한 대의 머신에는 499,110개 토큰의 캐시를 위한 공간만 있었습니다. 이는 모델의 전체 컨텍스트인 262,144개 토큰 기준 요청 두 개보다도 적은 양입니다. 두 머신 간 직접 네트워크 링크를 통해 텐서 병렬 처리로 분할하면 캐시는 300만 개 토큰 이상으로 늘어났습니다. 우리는 속도가 아닌 공간을 위해 모델을 분할했습니다.
변경 사항
동일 모델의 최신 4비트 빌드(NVFP4, 양자화 인식 증류로 생성)는 약 71 GiB의 메모리를 사용하며, GB10용 커뮤니티 vLLM 빌드(eugr/spark-vllm-b12x)는 캐시를 FP8로 실행합니다. 이제 한 머신에 전체 모델과 0.76에서 1.08백만 토큰의 캐시를 저장할 수 있으며, 머신에 할당된 메모리 양에 따라 전체 컨텍스트 기준 2.9에서 4.1개의 요청을 처리할 수 있는 공간이 확보됩니다. 두 사본 모두 모델 자체의 추측 디코딩(MTP, 4개의 드래프트 토큰)을 사용합니다. 전환 전에 새로운 빌드로 도구 호출 테스트를 실행한 결과 86점을 기록했으며, 교체된 이전 빌드는 75점이었습니다.
측정 결과
| 두 머신에 분할 | 각 머신에 사본 | |
|---|---|---|
| KV 캐시 | 300만 개 토큰 이상 | 76만 개 및 108만 개 토큰 |
| 전체 컨텍스트 기준 요청 수 | 약 12개 | 2.9 및 4.1개 |
| 부하 하의 처리량 | 초당 약 180 토큰 | 초당 210 토큰 (111 + 99) |
| 시작 시간 | 약 20분 | 사본당 5에서 13분 |
| 한 머신 재시작 | 모델 중단 | 다른 사본이 서비스 |
복제본 한 개만 사용할 때, 동시에 처리하는 요청 수에 따른 결과입니다. 짧은 프롬프트에서는 요청 1개가 초당 36개 토큰, 4개가 82개, 8개가 109개, 16개가 150개를 생성했습니다. 이메일 에이전트의 긴 프롬프트에서는 4개가 81개, 8개가 113개를 생성했지만 16개는 87개에 그쳤습니다. 8개 이상의 요청이 동시에 들어오면 오히려 속도가 느려졌습니다.
오후 동안 한 복제본은 최대 12개, 다른 복제본은 8개의 요청을 동시에 처리했습니다. 첫 번째 복제본은 초당 91~93개 토큰을 생성하며 분당 26~27개 요청을 완료했고, 두 번째 복제본은 초당 81~83개 토큰을 생성하며 분당 22개 요청을 완료했습니다. 현재 두 복제본 모두 12개를 처리합니다.
포기한 것
공간. 분할된 모델은 300만 개 토큰 이상의 캐시 하나를 가졌지만, 복제본은 더 작은 캐시 두 개를 가지며 요청은 이를 처리하는 복제본의 캐시만 사용할 수 있습니다. 우리의 에이전트는 짧고 중간 길이의 요청을 많이 보내므로, 이들에게는 처리량이 더 중요했습니다. 매우 긴 컨텍스트를 몇 개만 동시에 처리한다면 분할이 여전히 더 나은 선택일 수 있습니다. 우리는 분할 설정을 유지했으며, 되돌리는 작업은 스크립트 하나면 됩니다.
전환 중 발생한 문제
- 모델 없이 11분, 그 후 502 오류 30초. 분할된 모델은 같은 머신에서 복제본이 로드되기 전에 중단되어야 했으므로, 에이전트는 약 11분간 대기해야 했습니다. 첫 번째 복제본이 올라왔을 때 프록시는 약 30초 동안 "502 no live upstreams" 응답을 계속 반환했습니다. 백엔드가 다운된 동안 이를 실패로 표시(
max_fails=3,fail_timeout=30s)했고, 해당 시간이 지나기 전까지는 재시도하지 않았기 때문입니다. - 프록시를 거치지 않은 클라이언트. 배치 작업 중 하나가 첫 번째 머신의 주소를 설정에 포함하고 있어, 모든 부하가 한 복제본으로만 전달되고 다른 복제본은 여유 용량이 남아 있었습니다. 이제 모든 클라이언트는 프록시를 거치며, 머신의 자체 주소는 헬스 체크에만 사용합니다.
- 급변한 지표. 처리량 모니터링 도구는 프록시를 통해 vLLM의
/metrics를 읽었습니다. 두 개의 백엔드가 있을 때 연속된 읽기 작업이 서로 다른 복제본에서 발생하여 카운터가 앞뒤로 급변했고, 모니터링 도구는 모델이 고갈된 것으로 보고했습니다. 이제 각 복제본에서 직접 읽어 합산합니다. - 새 복제본을 중단시켰을 오래된 워치독. 분할 설정에는 모델이 응답을 멈추면 서비스를 재시작하는 워치독이 있었습니다. 새 서비스는 이전 서비스와
Conflicts=를 선언하므로, 이 재시작은 새 복제본을 중단시켰을 것입니다. 이는 단순히 중지하는 것이 아니라systemctl disable --now로 꺼야 했습니다. 우리의 운영 계층은 활성화되었으나 실행 중이지 않은 유닛을 재시작하기 때문입니다. - 다른 작업을 수행하는 머신에 메모리를 할당합니다. 한 머신에서 데이터베이스와 사이트 빌드도 함께 실행됩니다. vLLM의
gpu-memory-utilization을 0.78로 설정하면 나머지 작업을 위해 5 GiB만 남았으므로, 이를 0.74로 낮추고 다른 인스턴스가 부하를 담당하는 동안 해당 인스턴스만 재시작했습니다. 이러한 머신에서는 vLLM에 할당하는 메모리 용량이 다른 모든 작업과 동일한 메모리에서 차감됩니다.
이러한 머신이 두 대 있는 경우
- 필요한 모델과 캐시가 한 머신에 들어간다면, 최소 연결 수 기준 프록시 뒤에 각 머신에 복사본을 하나씩 실행하십시오. 한 머신으로 필요한 캐시를 감당할 수 없을 때만 모델을 분할하십시오.
- 배치 작업과 기존 설정을 포함해 모든 클라이언트를 프록시를 향하도록 지정하십시오.
- 각 복사본에서 직접 메트릭을 읽으십시오.
- 각 머신에 다른 작업이 무엇인지에 따라 자체 메모리 할당량을 부여하십시오.
- 자체 트래픽에 따라 한 복사본이 동시에 처리할 요청 수를 설정하십시오. 저희 트래픽 조합에서는 8개보다 12개가 더 나았습니다.
- 복사본이 시작되는 시간만큼 모델이 중단될 것으로 예상하고, 이에 대비하십시오.
Factory Floor는 한 대 또는 두 대의 머신용 프록시 및 실행 설정, 모델 전환 전에 실행하는 상태 확인 및 도구 사용 테스트, 그리고 머신의 나머지 부분을 위한 워치독과 메모리 가드를 제공합니다.