DGX Spark MLOps 스택 — 듀얼 노드 LLM 파이프라인
NVIDIA DGX Spark 2대(Production + R&D) 위에 사내에서 쓸 LLM 한 사이클을 세웠습니다. ClearML 기반 10단계 파이프라인, NGC 학습 컨테이너, AWQ 양자화, vLLM 추론, OpenAI 호환 자체 라우터, 그리고 그 스택을 채팅으로 운전하는 운영 에이전트까지입니다.
Architecture
동기
DGX Spark 노드 두 대가 들어왔을 때 목표는 하나였습니다. 데이터셋을 가져오는 것부터 파인튜닝, 양자화, 평가, 추론까지 한 사이클을 사내에서 끝까지 굴릴 수 있는 형태로 묶는 것입니다. 모델을 만드는 일과 그것을 서비스하는 일이 따로 놀면 실험은 되는데 아무도 쓰지 못하는 상태가 됩니다.
내가 담당한 트랙
두 노드 구성과 파이프라인, 양자화·평가, 추론 스택과 라우터, 그리고 DGX1 의 운영 에이전트가 제 쪽입니다. 항목별 내역은 오른쪽 「주요 업무」에 있습니다.
| 트랙 | 경계 |
|---|---|
| DGX2 쪽 R&D 에이전트와 그 스킬 | 동료가 만들었습니다. 이 카드는 다루지 않습니다 |
두 노드를 가른 이유
한 대는 추론이 흔들리면 안 되는 노드이고, 다른 한 대는 질러 볼 수 있는 노드입니다. 학습과 양자화, 긴 문맥 실험은 뒤쪽에서 하고 검증이 끝난 모델만 앞쪽에서 서빙합니다.
가르기만 해서는 지켜지지 않습니다. 저장소를 정본으로 두고 거기서 production 노드로, 다시 R&D 노드로만 흐르게 방향을 못박아서 실험 쪽 변경이 서빙 쪽으로 거슬러 올라가지 못하게 했습니다.
파이프라인을 열 단계로 쪼갠 이유
학습 한 번은 데이터를 가져오고, 검증하고, 서빙을 내리고, 학습 컨테이너를 띄우고, 학습하고, 변환하고, 양자화하고, 배포하고, 평가하고, 보고하는 일련의 과정입니다. 이걸 한 덩어리로 두면 어디서 깨졌든 처음부터 다시 돌려야 합니다.
단계를 나눠 두면 깨진 단계만 다시 돌립니다. 그리고 각 단계가 무엇을 받아 무엇을 내놓는지가 고정되므로, 앞 단계가 형식을 바꿨을 때 그 사실이 다음 단계에서 바로 드러납니다.
양자화가 만드는 차이
같은 모델을 같은 하드웨어에서 같은 프롬프트로, 같은 길이의 답을 받아 재 봤습니다. BF16 은 요청당 28.5초에 초당 8.96 토큰이었고, FP8 은 9.8초에 26.05 토큰이었습니다.
계산량은 거의 같습니다. 달라진 것은 가중치가 차지하는 메모리 크기(52GB → 25.7GB)뿐이고, 그만큼 매 토큰마다 읽어야 하는 바이트가 줄어듭니다. 이 작업이 계산이 아니라 메모리 대역폭에 묶여 있다는 뜻이고, 자가 호스팅에서 같은 장비로 몇 명을 받을 수 있는지가 여기서 갈립니다.
대신 공짜는 아닙니다. 양자화는 정확도를 조용히 깎을 수 있어서 파이프라인 끝에 평가 단계를 붙여 놨습니다.
사람과 에이전트가 같은 문으로 들어온다
추론 앞에 OpenAI 호환 라우터를 하나 두고, 사람이 쓰는 채팅 화면과 운영 에이전트가 같은 문을 지나가게 했습니다. 라우터가 범용 모델과 도메인 파인튜닝 모델 중 어디로 보낼지 정합니다.
DGX1 의 운영 에이전트(Huginn)는 그 스택을 채팅에서 운전합니다. 배포하고, 상태를 확인하고, 문제가 있으면 되돌립니다. 사람이 붙어서 해야 하던 절차를 줄이는 것이 목적입니다.
배운 점
- 어려운 것은 모델이 아니라 단계 사이의 약속이었습니다. 앞 단계가 만든 데이터 형식이 바뀌면 학습도 양자화도 평가도 한 줄을 못 가고 멈춥니다. 각 단계가 무엇을 받고 무엇을 내놓는지를 명시적으로 고정하는 데 시간이 제일 많이 갔습니다.
- 양자화는 공짜가 아닙니다. 보정 데이터가 실제 쓰임을 대표하지 못하면 답변 품질이 조용히 떨어지는데, 속도만 보고 있으면 그게 안 보입니다. 그래서 평가를 파이프라인 안에 넣어 매번 지나가게 했습니다.
- 에이전트가 추론의 첫 사용자였습니다. 라우터가 에이전트의 호출 패턴을 못 받아내면 사람도 못 씁니다. 둘이 같은 문을 쓰게 만들어 두니 한쪽을 고치면 다른 쪽도 같이 좋아졌습니다.