[GPU] GLM 배포와 DeepSeek 대비 실측 비교
개요
이전 글에서 CVD GPU 인스턴스에 DeepSeek-V4-Flash를 배포하고 systemd로 서비스를 관리하는 과정을 다뤘습니다. 이번 글은 같은 워크스페이스(첫 글에서 세팅한 CVD 환경) 위에 GLM-5.3-Flash를 배포하면서, "API 형태는 비슷한데 실제로 뭐가 다른가"를 직접 확인한 기록입니다.
두 모델 다 OpenAI 호환 /v1/chat/completions 엔드포인트를 쓰고 8000번 포트를 씁니다. 겉으로 보면 같은 API처럼 보이지만, GPU 요구량, 서비스 관리 방식, 인증 설정, 추론 강도를 조절하는 방식까지 실제로는 상당히 다릅니다. 이 글은 그 차이를 하나씩 실측으로 짚어봅니다.
배포 사양부터 다릅니다
GLM-5.3-Flash는 DeepSeek보다 요구하는 GPU 자원이 두 배입니다.
| 항목 | GLM-5.3-Flash | DeepSeek-V4-Flash |
|---|---|---|
| GPU | PRO 5000 72GB × 8 | PRO 5000 72GB × 4 |
| 가중치 | FP8, 약 328GB(62개 파일) | FP4+FP8 혼합, 약 155GB |
| 구조 | MoE 총 320B, 활성 18B, 라우팅 전문가 288개 + 공유 전문가 1개 | - |
| 병렬 처리 | 텐서 병렬 TP=8 | TP4 + EP |
| 서비스 관리 | Docker 컨테이너(glm53-flash) | systemd(deepseek-v4-flash) |
| API 포트 | 8000 | 8000 |
| 기본 컨텍스트 | 131072 token | 131072 token |
가중치 크기(328GB vs 155GB)와 GPU 개수(8 vs 4)가 정확히 비례하지는 않는다는 점이 눈에 띕니다. GLM은 MoE 구조라 전체 파라미터(320B) 중 실제로 활성화되는 건 18B뿐인데도, 라우팅 전문가 288개를 전부 GPU 메모리에 올려두고 있어야 하니 필요한 VRAM 자체는 훨씬 큽니다.
실행 이미지도 범용 vLLM이 아니라 cstechdev/vllm:glm53-flash-nope-sm120-cu130-20260826-r1처럼 모델/GPU 아키텍처(SM120)/CUDA 버전/커널 패치가 정확히 맞춰진 전용 빌드였습니다. 가이드에는 "범용 vLLM 최신 이미지로 교체하면 동일하게 동작한다고 가정하지 말라"는 경고가 명시되어 있었고, 이미지를 바꿔야 한다면 먼저 digest와 실행 인수를 기록하고 별도 환경에서 검증하는 절차를 거쳐야 했습니다.
첫 호출과 상태 확인
DeepSeek와 마찬가지로 CLI 래퍼와 REST API 둘 다 있습니다.
# CLI
glm53 "한 문장으로 자신을 소개해 줘"
# REST API
curl -s http://127.0.0.1:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "glm-5.3-flash",
"messages": [{"role": "user", "content": "안녕"}],
"max_tokens": 512
}'헬스 체크와 모델 목록 조회도 같은 패턴입니다.
# 헬스 체크. 본문뿐 아니라 HTTP 상태 코드도 확인
curl -s http://127.0.0.1:8000/health
# 모델 목록 확인
curl -s http://127.0.0.1:8000/v1/models여기서 DeepSeek와 다른 첫 번째 함정을 만났습니다. REST API는 기본적으로 가장 높은 추론 강도(max)를 쓰는데, glm53 CLI는 낮은 추론 강도를 쓰는 래퍼입니다. 즉 CLI와 기본 API 요청은 같은 질문을 던져도 지연 시간이 다르게 나옵니다. 빠른 확인만 하고 싶다면 아래처럼 reasoning_effort를 low로 명시해야 합니다.
curl -fsS http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"glm-5.3-flash","messages":[{"role":"user","content":"한 문장으로 소개해 줘"}],"max_tokens":2048,"chat_template_kwargs":{"reasoning_effort":"low"}}'Docker 운영: systemd와는 다른 재시작/재생성 규칙
DeepSeek는 systemd 서비스라 systemctl restart 계열 명령으로 다뤘지만, GLM은 Docker 컨테이너라 규칙이 다릅니다.
docker ps | grep glm53-flash # 컨테이너 실행 상태
docker logs glm53-flash -f # 실시간 로그 확인, Ctrl+C로 로그 보기 종료
docker restart glm53-flash # 기존 설정으로 재시작. docker run 인수 변경은 재생성 필요
docker stop glm53-flash # 중지
docker start glm53-flash # 시작
docker inspect glm53-flash --format '{{.Args}}' # 현재 시작 인수 확인여기서 실무적으로 중요한 차이가 하나 있습니다. docker restart는 컨테이너를 만들 때 쓴 docker run 인수를 그대로 유지한 채 재시작할 뿐입니다. --max-model-len이나 --api-key처럼 실행 인수 자체를 바꾸려면 컨테이너를 지우고(docker rm -f) 새 인수로 다시 만들어야 합니다. systemd 서비스처럼 설정 파일 하나 고치고 재시작하는 방식이 아닙니다.
컨테이너 재생성 전에는 기존 설정을 반드시 백업해야 합니다.
docker inspect glm53-flash > glm53-flash.inspect.json
# 이 파일에는 환경변수 등 민감한 값이 포함될 수 있습니다. 외부 공유 금지가중치(/opt/glm-5.3-flash/models/GLM-5.3-Flash)와 Docker 계층(/var/lib/docker)이 물리적으로 분리되어 있다는 점도 확인했습니다. 컨테이너를 지워도 가중치 자체는 남아 있지만, 컨테이너 안에서 만든 상태(쓰기 계층)는 재생성과 함께 사라집니다.
API Key 인증: DeepSeek 예제에는 없던 단계
이번 실측에서 가장 눈에 띈 차이는 인증입니다. 가이드의 GLM 배포 예제에는 API Key를 설정하는 전체 실행 명령이 별도로 준비되어 있었습니다.
# 1) 기존 컨테이너 삭제. 설정 백업과 서비스 중단 승인 후 실행
docker rm -f glm53-flash
# 2) 본인의 강력한 API Key로 교체하고 --api-key를 포함해 재생성
docker run -d --name glm53-flash --restart unless-stopped --init --gpus all --ipc=host --shm-size 32g \
-p 8000:8000 -e VLLM_ENGINE_READY_TIMEOUT_S=3600 \
-v /opt/glm-5.3-flash/models/GLM-5.3-Flash:/model:ro \
cstechdev/vllm:glm53-flash-nope-sm120-cu130-20260826-r1 \
/model --served-model-name glm-5.3-flash --host 0.0.0.0 --port 8000 \
--tensor-parallel-size 8 --max-num-seqs 10 --max-model-len 131072 \
--max-num-batched-tokens 8192 --gpu-memory-utilization 0.90 --kv-cache-dtype fp8 \
--enable-prefix-caching --no-enable-flashinfer-autotune \
--enable-auto-tool-choice --tool-call-parser glm47 --reasoning-parser glm45 \
--api-key REPLACE_WITH_STRONG_API_KEY \
--speculative-config '{"method":"mtp","num_speculative_tokens":5}'-p 8000:8000과 --host 0.0.0.0 조합은 모든 인터페이스에서 접근 가능한 구성이라, 보안 정책으로 출처를 제한하거나 터널 전용이라면 포트 매핑을 -p 127.0.0.1:8000:8000으로 좁혀야 합니다. API Key만으로는 전송 구간이 암호화되지 않으므로, 외부 통신이 필요하다면 TLS도 별도로 얹어야 한다는 점도 명시되어 있었습니다.
인증 적용 여부는 직접 확인했습니다.
# Key 없음: 401 예상
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8000/v1/models
# 올바른 Key 포함: 200 예상
curl -s http://localhost:8000/v1/models -H "Authorization: Bearer REPLACE_WITH_STRONG_API_KEY"인증을 켜면 기존 glm53 CLI 스크립트(/usr/local/bin/glm53)를 포함해 이 엔드포인트를 호출하는 모든 클라이언트가 요청 헤더에 Authorization: Bearer <Key>를 추가해야 합니다. Python SDK도 마찬가지입니다.
from openai import OpenAI
client = OpenAI(
base_url="http://127.0.0.1:8000/v1",
api_key="REPLACE_WITH_STRONG_API_KEY", # 서버에 설정한 Key로 교체
)대화, 스트리밍, 추론 강도 조절
기본적인 대화 요청과 SSE 스트리밍은 DeepSeek와 형태가 같습니다.
curl -s http://127.0.0.1:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "glm-5.3-flash",
"messages": [
{"role": "system", "content": "너는 질문에 도움을 주는 GLM-5.3-Flash야"},
{"role": "user", "content": "Python으로 퀵 정렬을 작성해 줘"}
],
"max_tokens": 1024,
"temperature": 0.6
}'curl -sN http://127.0.0.1:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "glm-5.3-flash",
"messages": [{"role": "user", "content": "농담 하나 해 줘"}],
"max_tokens": 512,
"stream": true
}'다만 추론 강도를 조절하는 파라미터 이름 자체가 다릅니다. GLM은 chat_template_kwargs.reasoning_effort에 low/high/max 세 단계 문자열을 넣습니다.
| 요청 값 | 권장 시작점 |
|---|---|
low | 빠른 질의응답 |
high | 여러 조건을 비교하는 분석 |
max(기본값) | 복잡한 문제, 충분한 출력 예산이 있는 작업 |
{
"model": "glm-5.3-flash",
"messages": [{"role": "user", "content": "이 알고리즘의 시간 복잡도를 단계별로 분석해 줘"}],
"max_tokens": 4096,
"chat_template_kwargs": {"reasoning_effort": "high"}
}DeepSeek 쪽의 enable_thinking 옵션과는 이름도 값의 형태도 다르므로 혼용하면 안 됩니다. 답이 비어 있고 finish_reason이 length로 찍힌다면, 이건 추론 강도가 출력 예산(max_tokens)을 다 써버렸다는 뜻이라 추론 강도를 낮추거나 max_tokens를 늘려야 합니다. 컨텍스트 상한과 출력 예산은 서로 다른 제약이라는 점을 다시 한번 확인했습니다.
도구 호출은 같은 방식, 이미지/영상 입력은 GLM에만
도구(function calling) 정의를 전달하고 결과를 돌려받는 방식은 DeepSeek의 도구 호출과 동일합니다.
{
"model": "glm-5.3-flash",
"messages": [{"role": "user", "content": "서울의 오늘 날씨는 어때?"}],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "지정한 도시의 날씨 조회",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "도시 이름"}
},
"required": ["city"]
}
}
}
],
"max_tokens": 1024
}GLM 쪽에서 눈에 띄는 차이는 멀티모달 입력입니다. 이미지 입력은 바로 시도해볼 수 있었습니다.
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8000/v1", api_key="not-needed")
client.chat.completions.create(
model="glm-5.3-flash",
messages=[{
"role": "user",
"content": [
{"type": "image_url", "image_url": {"url": "https://example.com/image.png"}},
{"type": "text", "text": "이 이미지를 설명해 줘"},
],
}],
max_tokens=512,
)영상 입력은 video_url 형태를 쓰지만, 가이드는 여기에 분명한 경고를 붙여뒀습니다. URL만 이미지에서 영상으로 바꾼다고 끝나는 게 아니라, 현재 모델의 처리기와 vLLM이 영상 입력을 실제로 지원하는지 작은 파일로 먼저 검증해야 하고, URL을 바꾸는 것만으로 영상 길이와 프레임 처리 비용이 사라지지는 않는다는 점입니다.
Python/CLI 사용과 콜드 스타트
Python 클라이언트 사용법은 DeepSeek와 거의 동일한 패턴입니다.
from openai import OpenAI
client = OpenAI(
base_url="http://127.0.0.1:8000/v1",
api_key="not-needed", # 인증 없는 사설 테스트 서버에만 사용하는 SDK 자리표시자
)
resp = client.chat.completions.create(
model="glm-5.3-flash",
messages=[
{"role": "system", "content": "너는 코딩을 돕는 GLM-5.3-Flash야"},
{"role": "user", "content": "버블 정렬을 작성해 줘"},
],
max_tokens=1024,
temperature=0.6,
)
print(resp.choices[0].message.content)glm53 "1+1은 얼마야?" # 예상 답: 2
glm53 "이메일을 찾는 정규식을 작성해 줘"
glm53 "영어로 번역해 줘: 오늘 날씨가 좋아"기본 샘플링 값은 temperature=1.0, top_p=0.95이고 요청에 지정한 값이 우선 적용됩니다. 낮은 temperature가 반복성을 높이는 데는 도움이 되지만 완전한 결정성을 보장하지는 않는다는 점도 명시되어 있었습니다.
콜드 스타트는 DeepSeek보다 부담이 컸습니다. 수백 GB 가중치 로딩에 8 GPU JIT 컴파일까지 겹치면서 서비스가 뜨기까지 수분 이상 걸릴 수 있다는 걸 실측으로 확인했습니다. GPU 4장짜리 DeepSeek보다 초기 기동 시간이 눈에 띄게 길어지는 지점입니다.
환경 구성 빠른 참조
| 항목 | 값 |
|---|---|
| 운영체제 | Ubuntu 22.04 LTS |
| GPU | 8 × NVIDIA RTX PRO 5000 72GB Blackwell(SM120) |
| 서비스 주소 | http://127.0.0.1:8000 |
| 추론 엔진 | vLLM(Docker 이미지, SM120 NoPE sparse-MLA 패치 포함) |
| 이미지 | cstechdev/vllm:glm53-flash-nope-sm120-cu130-20260826-r1 |
| CUDA | 13.0 / 드라이버 580.126.20 |
| 모델 | zai-org/GLM-5.3-Flash |
| 가중치 | FP8, 약 328GB, 62개 파일 |
| 컨텍스트 길이 | 131072 token(128K, 1M 설정은 별도 검증 필요) |
| 병렬 처리 | 텐서 병렬 TP=8 |
정리
- GLM-5.3-Flash는 DeepSeek-V4-Flash보다 GPU 요구량이 두 배(8장 vs 4장)입니다. MoE 구조상 활성 파라미터는 18B로 작지만, 라우팅 전문가 288개를 전부 GPU 메모리에 올려둬야 해서 실제 VRAM 요구량은 훨씬 큽니다.
- 서비스 관리 방식이 근본적으로 다릅니다. DeepSeek는 systemd, GLM은 Docker 컨테이너입니다.
docker restart는 기존 실행 인수를 유지할 뿐이고, 인수를 바꾸려면 컨테이너를 삭제하고 재생성해야 합니다. - GLM 예제에는 API Key 인증을 설정하는 전체 명령이 별도로 준비되어 있었고, 인증을 켜면 CLI 스크립트를 포함한 모든 클라이언트에
Authorization헤더를 추가해야 합니다. API Key만으로는 전송이 암호화되지 않으므로 외부 통신에는 TLS를 함께 검토해야 합니다. - 추론 강도를 조절하는 파라미터가 다릅니다. GLM은
chat_template_kwargs.reasoning_effort(low/high/max), DeepSeek는enable_thinking입니다. 이름과 값 형태가 다르므로 혼용하면 안 됩니다. - 도구 호출 방식은 두 모델이 동일하지만, 이미지/영상 입력은 GLM 쪽에만 있습니다. 다만 영상 입력은 URL 형식만 바꾼다고 되는 게 아니라 실제 지원 여부를 작은 파일로 먼저 검증해야 합니다.
- 콜드 스타트 시간은 GPU 개수와 가중치 크기에 비례해서 GLM 쪽이 더 길었습니다. 수백 GB 가중치 로딩과 8 GPU JIT 컴파일이 겹치면 수분 이상 소요됩니다.
이어지는 실측은 다음 글에서 다룹니다. 텍스트 LLM 두 개를 배포해봤으니, 이번엔 영상 생성 모델(MiniMax H3)과 ComfyUI 워크플로 구성으로 넘어갑니다.