LiteLLM과 Open WebUI를 사용하여 셀프 호스팅 AI 게이트웨이 구축하는 방법
요약
LiteLLM과 Open WebUI를 활용하여 다양한 AI 제공자를 단일 OpenAI 호환 엔드포인트로 통합하는 셀프 호스팅 AI 게이트웨이 구축 방법을 소개합니다. Docker Compose를 통해 30분 내외로 구축 가능하며, 인프라 복잡성을 줄이고 유지보수 효율을 높일 수 있습니다.
핵심 포인트
- LiteLLM을 통해 OpenAI, Anthropic, Ollama 등을 단일 API로 통합
- Open WebUI를 활용한 통합 채팅 프론트엔드 제공
- Cloudflare Tunnel을 이용한 안전한 원격 접속 환경 구축
- Docker Compose 기반의 간편한 오케스트레이션 및 배포
AI 도구를 셀프 호스팅(self-hosted)해 본 적이 있다면, 상황이 얼마나 빠르게 엉망이 되는지 잘 알 것입니다. 어떤 앱은 OpenAI와 통신하고, 다른 앱은 Anthropic을 사용합니다. 로컬에서 Ollama를 실행하면 이제 관리해야 할 세 번째 엔드포인트(endpoint)가 생깁니다. 인증(Authentication) 방식도 곳곳마다 다릅니다. 모델을 전환하려면 통합 코드를 다시 작성해야 합니다. 그러다 보면 어느덧 실제 무언가를 구축하는 시간보다 글루 코드(glue code)를 유지 관리하는 데 더 많은 시간을 쓰게 됩니다. 저도 정확히 이 문제에 직면했고, 그래서 더 깔끔한 설정을 구축했습니다. 아이디어는 간단합니다. 모든 제공자(provider) 앞에 단일 게이트웨이(gateway)를 두어, 나머지 스택이 오직 하나의 API하고만 통신하게 만드는 것입니다. 전체 작동 구현체를 여기에 오픈 소스로 공개했습니다: 🔗 github.com/dixon400/myllm 이를 클론(Clone)하고 docker compose up을 실행하세요. 30분 안에 작동하는 AI 게이트웨이를 가질 수 있습니다.
이 스택이 하는 일
- 하나의 API → OpenAI, Anthropic, Groq, 그리고 Ollama가 모두 단일 OpenAI 호환 엔드포인트 뒤에 위치
- 하나의 프론트엔드(frontend) → 통합 채팅 인터페이스로서의 Open WebUI
- 안전한 원격 접속 → Cloudflare Tunnel 사용, 포트 노출 없음, 방화벽 규칙 개방 없음
- 유지 관리 용이 → 앱을 건드리지 않고도 하단의 제공자(providers)를 변경 가능
전체 설정에는 Docker 이미지 다운로드 속도와 이미 로컬에 Ollama 모델이 설치되어 있는지 여부에 따라 약 20~45분이 소요됩니다.
스택 구성
특별한 것은 없습니다. 잘 조합된 도구들일 뿐입니다:
| 구성 요소 | 역할 |
|---|---|
| LiteLLM | 게이트웨이(Gateway) / 라우팅(routing) 계층 |
| Open WebUI | 채팅 프론트엔드(Chat frontend) |
| PostgreSQL | 상태(State) + 메타데이터(metadata) |
| Docker Compose | 오케스트레이션(Orchestration) |
| Cloudflare Tunnel | 안전한 원격 노출 |
아키텍처(Architecture)
사용자(User) ↓ Open WebUI ↓ LiteLLM (gateway) ↓ OpenAI / Anthropic / Groq / Ollama
핵심 통찰: 여러분의 앱과 프론트엔드는 오직 LiteLLM하고만 통신합니다. 하단의 제공자(Providers)는 서로 교체 가능한 상태가 됩니다. 새로운 모델을 추가하거나, 제공자를 바꾸거나, 라우팅을 변경하더라도 다른 어떤 것도 알 필요가 없습니다.
대상 독자
- 로컬 AI 인프라를 실험 중인 개발자
- 하나의 API 레이어 뒤로 여러 제공자를 통합하려는 팀
- 내부 AI 툴링을 구축하는 엔지니어
- 별도의 제공자 통합을 유지 관리하는 것에 지친 모든 사람
만약 여러분이 "이 모든 AI 엔드포인트(endpoints)를 관리할 더 간단한 방법이 있을 텐데"라고 생각해 본 적이 있다면 — 이것이 바로 그 간단한 방법입니다.
이 스택이 존재하는 이유
대부분의 셀프 호스팅(self-hosted) AI 환경은 놀라울 정도로 빠르게 관리하기 어려워집니다. 한 애플리케이션은 OpenAI와 직접 통신하고, 다른 애플리케이션은 Anthropic을 별도로 사용합니다. 로컬 Ollama 모델은 자체적인 엔드포인트가 필요합니다. 인증 방식은 일관되지 않으며, 모델 전환은 서서히 인프라 확산(infrastructure sprawl)으로 이어집니다.
모든 제공자 앞에 LiteLLM을 배치함으로써, 시스템의 나머지 부분은 단 하나의 인터페이스만 이해하면 됩니다. 제공자가 바뀌거나, 로컬 모델이 추가되거나, 라우팅 로직이 진화하더라도 — 매번 프론트엔드나 애플리케이션 로직을 다시 작성할 필요가 없습니다.
사전 요구 사항
컨테이너를 시작하기 전에 다음 항목이 있는지 확인하세요:
- Docker Desktop
- Docker Compose
- curl
- cloudflared
- Ollama (선택 사항, 로컬 모델용)
빠른 확인 방법:
docker --version
docker compose version
cloudflared --version
curl --version
로컬 Ollama 모델을 사용하는 경우:
ollama list
설치된 모델이 나타나면 로컬 추론(inference) 준비가 된 것입니다.
저장소 구조
저장소는 의도적으로 가볍게 구성되었습니다:
🔗 github.com/dixon400/myllm
├── Docker-compose.yml
├── litellm-config.yml
└── .env
각 파일은 고유한 역할을 수행합니다:
- Docker Compose는 서비스를 오케스트레이션(orchestrates)하고,
- LiteLLM config는 라우팅(routing) 및 모델 별칭(model aliases)을 처리하며,
- .env는 비밀 정보(secrets) 및 런타임 설정을 저장합니다.
환경 변수 설정
.env 파일은 제공자 자격 증명(credentials)이 저장되는 곳입니다. 프로젝트 루트에서 파일을 생성하거나 업데이트하세요:
LITELLM_MASTER_KEY=sk-very-strong-key
OPENAI_API_KEY=...
ANTHROPIC_API_KEY=...
GROQ_API_KEY=...
OLLAMA_CLOUD_API_BASE=https://<host>/v1
OLLAMA_CLOUD_API_KEY=...
사람들이 예상하는 것보다 더 중요한 몇 가지 사항들이 있습니다:
LITELLM_MASTER_KEY는 Open WebUI와 LiteLLM 사이의 인증 계층 (authentication layer)이 됩니다.- 이 값들은 절대로 Git에 커밋해서는 안 됩니다.
- 원격 접속이 가능해지면 취약한 키는 심각한 보안 문제가 됩니다.
- 설령 개인적인 설정으로 시작하더라도, 첫날부터 환경 설정 (environment config)을 운영 환경 (production)처럼 취급하십시오.
Docker Compose 연결하기
여기서의 목표는 컨테이너들이 서로 통신할 수 있도록 보장하는 것입니다. 대부분의 배포 실패는 Docker 자체보다는 작은 설정 불일치에서 발생합니다.
docker-compose.yml은 Open WebUI가 LiteLLM을 직접 가리키도록 설정해야 합니다:
OPENAI_API_BASE_URL=http://litellm:4000/v1
OPENAI_API_KEY=${LITELLM_MASTER_KEY}
이는 Open WebUI에 게이트웨이가 어디에 있는지, 그리고 어떤 키를 사용할지를 알려줍니다. LiteLLM은 설정 파일을 올바르게 마운트(mount)해야 합니다:
./litellm-config.yml:/app/config.yaml
⚠️ 이 파일 이름은 보기보다 훨씬 중요합니다. 여기서 오타가 발생하면 Docker가 파일을 마운트하는 대신 디렉토리를 생성할 수 있으며, 이는 나중에 혼란스러운 시작 오류 (startup errors)로 이어집니다.
LiteLLM 설정하기
litellm-config.yml 내부에서 LiteLLM은 모델 별칭 (model aliases), 제공자 라우팅 (provider routing), 게이트웨이 동작 및 인증 설정을 정의합니다. 가장 중요한 섹션은 마스터 키 (master key)입니다:
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY
이렇게 하면 비밀 정보 (secrets)를 YAML 파일 외부에 유지할 수 있으며, 나중에 자격 증명 로테이션 (credential rotation)을 더 쉽게 만들 수 있습니다.
model_list 내부에서는 제공자 모델 ID (provider model IDs)가 최신 상태인지 확인하십시오. 모델 이름은 대부분의 사람들이 예상하는 것보다 더 자주 변경됩니다. 특히 Groq 및 최신 OpenAI 릴리스 전반에 걸쳐 그렇습니다.
스택 시작하기
설정이 올바르게 보인다면, 모든 것을 시작하십시오:
docker compose up -d --force-recreate
Docker가 이미지를 풀(pull)하고, PostgreSQL을 초기화하며, 컨테이너 상태를 생성하는 동안 초기 시작에는 1분 정도 소요될 수 있습니다.
컨테이너 상태를 확인하십시오:
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
open-webui, litellm, litellm-db가 모두 실행 중인 것을 확인해야 합니다.
만약 컨테이너가 즉시 종료된다면, 다음 단계로 넘어가기 전에 로그를 확인하세요: docker logs <container-name>
게이트웨이 검증 (Validating the Gateway)
Open WebUI를 건드리기 전에, 먼저 LiteLLM을 검증해야 합니다. /v1/models 엔드포인트를 통해 인증이 작동하는지, 프로바이더(Providers)가 올바르게 로드되었는지, 그리고 모델 라우팅(Model routing)이 초기화되었는지 확인할 수 있습니다.
set -a && source .env && \ curl -s http://localhost:4000/v1/models \ -H "Authorization: Bearer $LITELLM_MASTER_KEY "
읽기 쉬운 출력을 보려면:
set -a && source .env && \ curl -s http://localhost:4000/v1/models \ -H "Authorization: Bearer $LITELLM_MASTER_KEY " \ | python3 -m json.tool | head -n 80
만약 이 엔드포인트가 실패한다면, Open WebUI도 거의 확실히 실패할 것입니다. 따라서 게이트웨이 문제를 먼저 해결하십시오.
Open WebUI 확인 (Verifying Open WebUI)
LiteLLM이 올바르게 응답하면, 인터페이스를 엽니다: http://localhost:3000
채팅을 생성하고, 모델을 선택하며, 프롬프트(Prompts)를 정상적으로 보낼 수 있어야 합니다. 만약 모델 드롭다운 메뉴가 비어 있다면, 대개 LiteLLM 인증 문제일 가능성이 높습니다. 마스터 키(Master keys) 불일치, 오래된 모델 ID, 또는 유효하지 않은 프로바이더 자격 증명(Provider credentials) 등이 원인일 수 있습니다.
프로바이더 모델 최신 상태 유지 (Keeping Provider Models Current)
이 부분에서 많은 사람들이 당황하곤 합니다. 프로바이더의 모델 식별자(Model identifiers)는 생각보다 더 자주 변경됩니다. 몇 달 전까지만 해도 완벽하게 작동하던 배포 환경이, 프로바이더가 특정 모델 이름을 지원 중단(Deprecated)함에 따라 깨질 수 있습니다.
로컬 Ollama 모델 확인:
ollama list
Groq 모델 확인:
set -a && source .env && \ curl -s https://api.groq.com/openai/v1/models \ -H "Authorization: Bearer $GROQ_API_KEY " \ -H "Content-Type: application/json"
litellm-config.yml에서 모델 ID를 업데이트한 후, 스택을 다시 생성하세요:
docker compose up -d --force-recreate
Cloudflare Tunnel을 이용한 안전한 원격 접속 (Secure Remote Access with Cloudflare Tunnel)
이 시점에서 스택은 로컬에만 존재합니다. 다음 단계는 인바운드 포트(Inbound ports)를 열거나, 홈 IP를 노출하거나, 리버스 프록시(Reverse proxies)를 수동으로 관리하지 않고도 Open WebUI를 인터넷에 노출하는 것입니다. Cloudflare Tunnel은 사용자의 머신에서 Cloudflare의 엣지(Edge)로 나가는(Outbound) 암호화된 연결을 생성합니다.
다음 항목들을 얻을 수 있습니다:
- 자동 HTTPS (Automatic HTTPS)
- 숨겨진 오리진 인프라 (Hidden origin infrastructure)
- Cloudflare 프록시 보호 (Cloudflare proxy protection)
- 간편한 DNS 관리 (Simple DNS management)
DNS를 Cloudflare로 이동하기:
- Cloudflare에 도메인 추가
- 도메인 등록업체(Registrar)에서 네임서버(Nameservers) 업데이트
- 전파(Propagation) 대기
cloudflared 인증하기:
cloudflared tunnel login
이 명령을 실행하면 인증을 위한 브라우저 창이 열립니다.
터널 생성하기:
cloudflared tunnel create openwebui
Cloudflare가 터널 UUID와 인증 정보가 담긴 JSON 파일을 생성합니다.
서브도메인 라우팅하기:
도메인이 chat.yourdomain.tech라고 가정할 때:
cloudflared tunnel route dns openwebui chat.yourdomain.tech
터널 설정 생성하기:
~/.cloudflared/config.yml 파일을 생성합니다:
tunnel:
id: openwebui
credentials-file: ~/.cloudflared/<tunnel-uuid>.json
ingress:
- hostname: chat.yourdomain.tech
service: http://localhost:3000
- service: http_status:404
<tunnel-uuid>를 생성된 파일 이름으로 교체하세요.
터널 실행하기:
cloudflared tunnel run openwebui
macos에서 지속적인 시작을 설정하려면:
cloudflared service install
cloudflared service start
터널을 백그라운드 서비스(Background service)로 실행하는 것이 터미널 세션에 띄워두는 것보다 훨씬 더 안정적입니다.
원격 접속 확인하기:
빠른 DNS 및 연결성 확인:
dig +short chat.yourdomain.tech
curl -I https://chat.yourdomain.tech
원격 접속 시에는 항상 HTTPS를 사용하세요. Cloudflare Tunnel은 보안 프록시 트래픽(Secure proxied traffic)을 중심으로 설계되었습니다.
운영 상태 확인 (Operational Health Checks) 스택이 안정화되면, 이 단일 명령어로 모든 것에 대한 빠른 개요를 얻을 수 있습니다:
set -a && source .env &&
echo "== Docker services ==" &&
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' &&
echo " \n == Local Ollama models ==" &&
ollama list &&
echo " \n == Groq model count ==" &&
curl -s https://api.groq.com/openai/v1/models
-H "Authorization: Bearer $GROQ_API_KEY "
-H "Content-Type: application/json"
| python3 -c 'import sys,json; d=json.load(sys.stdin); print(len(d.get("data", [])))' &&
echo " \n == LiteLLM models ==" &&
curl -s http://localhost:4000/v1/models
-H "Authorization: Bearer $LITELLM_MASTER_KEY "
개인적인 배포의 경우에도, 이러한 가시성을 확보하는 것은 많은 디버깅 시간을 절약해 줍니다.
배포가 불안정해지는 문제(Stable deployments drift). 제공업체 API 변경, Docker 마운트 오류, 자격 증명 만료, 모델 ID 사용 중단 등이 발생합니다. 가장 흔한 실패 패턴은 다음과 같습니다.
Open WebUI는 로드되지만 모델이 누락됨: LiteLLM 로그에서 빈 드롭다운 목록, 누락된 제공업체(providers), 또는 인증 오류가 나타납니다.
docker logs --tail 200 litellm
모델 가시성을 직접 확인하세요: set -a && source .env &&
curl -s http://localhost:4000/v1/models
-H "Authorization: Bearer $LITELLM_MASTER_KEY "
일반적인 해결 방법:
OPENAI_API_KEY=${LITELLM_MASTER_KEY}를 확인하세요. 마스터 키가 환경 변수를 사용하는지 확인합니다.
컨테이너 재배포: docker compose up -d --force-recreate
docker compose restart open-webui
LiteLLM이 IsADirectoryError로 실패함: Docker가 YAML 파일을 마운트하는 대신 실수로 디렉터리를 생성했습니다.
ls -la ./litellm-config.yml ./litellm-config.yaml
grep -n "litellm-config" Docker-compose.yml
올바른 마운트 (Correct mount): ./litellm-config.yml:/app/config.yaml
그 다음 재생성: docker compose up -d --force-recreate litellm
로컬에서는 작동하지만 Cloudflare를 통해서는 안 되는 경우
로컬 접속은 가능하지만 퍼블릭 호스트네임 (Public hostname) 접속이 실패한다면, 터널 (Tunnel)에 집중하세요:
cloudflared tunnel list
cloudflared tunnel info openwebui
cat ~/.cloudflared/config.yml
dig +short chat.yourdomain.tech
curl -I https://chat.yourdomain.tech
대부분의 원격 접속 실패는 비활성 터널 커넥터 (Inactive tunnel connectors), 잘못된 인그레스 타겟 (Incorrect ingress targets), 누락된 프록시 DNS 레코드 (Missing proxied DNS records), 또는 임시 터미널 세션에서 cloudflared를 실행한 경우에 발생합니다.
모델은 나타나지만 생성 (Generation)이 실패하는 경우
/v1/models 엔드포인트는 작동하지만 프롬프트 (Prompts)가 실패한다면 — 제공자 자격 증명 (Provider credentials)이 유효하지 않거나, 할당량 (Quotas)이 소진되었거나, 모델 ID (Model IDs)가 더 이상 존재하지 않을 수 있습니다.
set -a && source .env &&
env | grep -E '^(OPENAI_API_KEY|GROQ_API_KEY|ANTHROPIC_API_KEY|LITELLM_MASTER_KEY)='
| sed 's/=.*/=<set>/'
그 다음 LiteLLM 로그를 검사하세요: docker logs --tail 300 litellm
제공자 모델 ID를 새로고침하는 것만으로도 의외로 자주 문제가 해결됩니다.
보안 권장 사항 (Security Recommendations)
원격 접속이 구축되었다면, 기본적인 보안 강화 (Hardening)가 중요합니다:
- 강력한 LITELLM_MASTER_KEY 사용
- LiteLLM을 인터넷에 직접 노출하지 말 것
- 제공자 API 키를 주기적으로 교체 (Rotate)
- CORS 규칙을 제한적으로 유지
개인 또는 팀 단위 사용을 위해, Cloudflare Access를 Open WebUI 앞에 배치하여 ID 기반의 액세스 제어 (Identity-aware access control)를 추가하는 것이 좋습니다 — 활성화할 가치가 있습니다.
정상 작동하는 기준점 (Known-Good Baseline) 확보
시스템이 안정되면 다음 사항들을 저장해 두세요:
docker ps 출력 결과
/v1/models 출력 결과
활성 모델 별칭 (Active model aliases)
터널 상태: cloudflared tunnel info openwebui
나중에 문제가 발생했을 때, 정상 작동하던 스냅샷과 비교하는 것이 처음부터 디버깅하는 것보다 거의 항상 빠릅니다.
직접 시도해 보세요
Docker Compose, LiteLLM 설정, 환경 설정이 포함된 전체 작동 구현체는 모두 여기에 있습니다:
🔗 github.com/dixon400/myllm
이를 클론(Clone)하고, API 키를 추가한 뒤, docker compose up 을 실행하면 작동하는 AI 게이트웨이를 가질 수 있습니다.
이 내용이 유용했다면, ⭐ 레포지토리(repo)에 별을 눌러주세요 — 더 많은 사람들이 찾을 수 있도록 도움이 됩니다. 질문이나 개선 사항이 있으신가요? 이슈(Issue)를 생성하거나 아래에 댓글을 남겨주세요. 저는 이 프로젝트를 활발히 유지 관리하고 있습니다. 이 글은 원래 HackerNoon에 게시되었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기