
처음엔 Open WebUI만 붙이면 끝일 줄 알았다. 실제로 UI는 금방 떴는데, Ollama 모델은 잘 보이고 vLLM은 안 보이고, 번역 모델은 일반 질문을 거부하고, ollama stop은 멈추는 척만 하더라. 하루 종일 어디가 진짜 문제인지 헷갈렸다.
특히 한 군데에서만 꼬인 게 아니라, Open WebUI 설정, vLLM 모델명, Ollama keep alive, 번역 모델 포맷이 각각 따로 문제를 만들고 있었다. 한꺼번에 보려다 더 꼬였다. 결국 문서 안 읽고 삽질한 거였다.
왜 Open WebUI에는 Ollama만 보이고 vLLM은 안보였나
처음엔 Open WebUI가 vLLM 연결 자체를 못 하는 줄 알았다. 근데 로그를 보면 서버가 죽은 건 아니었다.
GET /health HTTP/1.1" 200 OK
GET /v1/models HTTP/1.1" 200 OK
Error with model error=ErrorInfo(message='The model `vllm_gemma4` does not exist.', type='NotFoundError', param='model', code=404)
POST /v1/chat/completions HTTP/1.1" 404 Not Found
여기서 핵심은 두 가지였다.
나는 Open WebUI에서 연결만 붙으면 끝인 줄 알았는데, 실제로는 Open WebUI가 보내는 model 값과 vLLM이 실제로 등록한 served-model-name 이 같아야 했다.
예를 들면 vLLM 쪽은 이렇게 떠 있었다.
services:
vllm_qwen35:
image: vllm/vllm-openai:cu130-nightly
container_name: vllm_qwen35
restart: unless-stopped
ipc: host
network_mode: host
shm_size: "64gb"
environment:
- NVIDIA_VISIBLE_DEVICES=all
- HUGGING_FACE_HUB_TOKEN=${HF_TOKEN:-}
volumes:
- ${HF_CACHE_DIR:-[내부경로]}:/root/.cache/huggingface
command: >
Qwen/Qwen3.5-35B-A3B
--served-model-name qwen3.5-35b
--port 8001
--host 0.0.0.0
--max-model-len 65536
--gpu-memory-utilization 0.80
--kv-cache-memory-bytes 5G
--max-num-seqs 64
--default-chat-template-kwargs '{"enable_thinking": false}'
--enable-prefix-caching
--trust-remote-code
그러면 Open WebUI에서도 qwen3.5-35b 로 요청해야 한다. 다른 임의 이름으로 저장해두면 안된다.
확인은 이게 제일 빨랐다.
curl -s [내부경로] \
-H 'Authorization: Bearer [API_KEY]'
왜 host network 로 바꾸고도 포트가 안 보인다고 떴나
IDE 쪽에서는 이런 문구가 떴다.
No valid ports are available.
이건 컨테이너가 죽어서가 아니었다. network_mode: host 를 쓰면 Docker 입장에서 publish 된 포트가 없어서 그렇게 보인다. 브라우저 접속은 그냥 된다.
Open WebUI compose 는 이렇게 바꿨다.
services:
open_webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open_webui
restart: unless-stopped
network_mode: host
environment:
- OPENAI_API_BASE_URL=[내부경로]
- OPENAI_API_KEY=[API_KEY]
- OLLAMA_BASE_URL=[내부경로]
volumes:
- [내부경로]:/app/backend/data
그리고 접속은 publish 포트가 아니라 호스트 포트로 바로 붙었다.
[내부경로]
처음엔 compose 를 잘못 쓴 줄 알았는데 그냥 host network 특성이었다. 허무하긴 한데 맞다.
왜 TranslateGemma는 일반 질문을 거부했나
이건 더 단순했다. 나는 얘를 일반 채팅 모델처럼 붙여놓고 코드 질문을 던졌는데, 모델이 바로 거부했다.
User user role must match one of this format:
<<<source>>><lang_code><<<target>>><lang_code><<<text>>><text_to_translate>
Or:
<<<custom>>><your custom prompt>
즉 이 모델은 채팅 모델이 아니라 번역 전용 포맷 모델이었다.
작동하는 요청은 이런 식이었다.
curl -s [내부경로] \
-H 'Content-Type: application/json' \
-d '{
"model": "translategemma-4b-it",
"messages": [
{
"role": "user",
"content": "<<<source>>>en<<<target>>>ko<<<text>>>The meeting starts at 3 PM."
}
],
"max_tokens": 256,
"temperature": 0.0
}'
짧은 번역은 잘 됐다. 근데 긴 문서를 자연스럽게 번역하려면 청킹을 클라이언트 쪽에서 해야 하고, 그럴 거면 내가 원래 쓰던 Ollama 번역 모델이 더 편했다.
왜 Gemma 4 는 버렸나
Gemma 4 도 한 번 붙여봤다. NVFP4 까지 올려봤는데 체감이 너무 느렸다.
정확히 말하면 안 돌아가는 건 아니었다. 응답은 뜬다. 근데 Open WebUI에서 왔다갔다 쓰기에는 너무 답답했다. 수치보다 UX가 별로였다.
반대로 qwen3.5:35b 는 속도, 범용성, Open WebUI에서 쓰는 느낌이 다 그럭저럭 균형이 맞았다. 메인 모델은 결국 그쪽으로 기울었다.
내가 최종적으로 정리한 역할은 이랬다.
메인 채팅: qwen3.5:35b
긴 번역: Ollama trans
짧은 번역 실험: translategemma
프론트: Open WebUI
Gemma 4 는 여기선 그냥 안 맞았다
왜 ollama stop 이 바로 안 먹었나
이 부분이 좀 웃겼다. 분명 stop 을 날렸는데 안 죽었다.
ollama stop qwen3.5:35b-a3b-q8_0
ollama ps
출력은 이런 식이었다.
qwen3.5:35b-a3b-q8_0 ... Stopping...
근데 GPU 는 계속 잡고 있었다. 알고보니 Open WebUI가 백그라운드에서 계속 /api/chat, /api/show, /api/generate, /api/ps 를 치고 있었다. 모델이 내려가는 중에 다시 깨운 거다.
그래서 keep alive 도 줄였다.
sudo sed -i 's/OLLAMA_KEEP_ALIVE=10m/OLLAMA_KEEP_ALIVE=5m/' [내부경로]
sudo systemctl daemon-reload
sudo systemctl restart ollama
이후엔 안 쓰는 모델이 빨리 내려가서 훨씬 덜 답답했다. Open WebUI가 있으니까 다시 깨우는 것도 예전보다 훨씬 덜 귀찮았고.
확인은 이렇게 했다.
ollama ps
nvidia-smi
journalctl -u ollama -n 50 --no-pager
왜 웹검색은 또 따로 꼬였나
이것도 처음엔 Open WebUI 기본 웹검색이 실패한 줄 알았다. 근데 로그를 보니까 설치된 툴이 따로 있었다.
Error in native search: 403
Failed to resolve 'crawl4ai'
DB 에 등록된 툴 이름도 아예 따로였다.
즉 Open WebUI에는 두 층이 있었다.
툴 쪽 기본값이 crawl4ai 같은 내부 호스트를 기대하고 있어서, 그 서비스가 없으면 당연히 실패한다. 이걸 모르고 Open WebUI 기본 설정만 보고 있으면 계속 헤맨다.
내가 만든 검색 코드로는 못 붙이나
된다. 이건 확인했다.
Open WebUI 쪽에 외부 검색 훅이 있다. 정해진 JSON 형식만 맞추면 내 검색 서버를 그대로 붙일 수 있다. 이 부분은 오히려 정해진 검색 공급자보다 더 깔끔할 수도 있다.
검색 API 는 이런 입력을 받으면 된다.
{
"query": "옵션 전략",
"count": 5
}
응답은 이렇게 주면 된다.
[
{
"link": "[도메인]",
"title": "옵션 전략 정리",
"snippet": "콜옵션과 풋옵션의 기본"
},
{
"link": "[도메인]",
"title": "커버드콜 사례",
"snippet": "배당주 포트폴리오에서 쓰는 방식"
}
]
이 구조를 맞춰서 외부 검색 URL 로 붙이면 된다.
이건 나중에 따로 붙여볼 생각이다.
자꾸 헷갈렸던 포인트는 뭐였나
Open WebUI에서 모델이 안보인다고 해서 연결이 죽은 건 아닐 수 있다
/v1/models 가 되는데 채팅이 안 되면 모델 ID 부터 의심해야 한다
번역 전용 모델은 일반 챗봇처럼 쓰면 안된다
host network 는 IDE 포트 표시가 이상하게 보일 수 있다
ollama stop 이 안 먹는 게 아니라 누가 계속 깨우고 있는 걸 수도 있다
웹검색은 기본 기능과 설치된 툴이 분리돼 있을 수 있다
다음에 다시 하면 뭘 먼저 볼 건가
curl 로 /health, /v1/models 먼저 확인
Open WebUI가 보내는 실제 모델명 확인
번역 모델인지 일반 채팅 모델인지 먼저 구분
ollama ps 와 nvidia-smi 를 같이 보기
웹검색은 기본 기능 하나만 먼저 살리고, 툴은 나중에 붙이기
이번엔 UI 하나 붙였을 뿐인데 오히려 구조가 더 선명하게 보였다.
CLI 로만 만질 때는 감으로 넘기던 것들이, Open WebUI 붙이고 나니까 어디가 죽었는지 바로 드러나더라. 그게 제일 컸다.