Bối cảnh & Bài toán thực tế
Khoảng 6 tháng trước, team mình có 35 nhân sự trải từ dev đến marketing. Mỗi tháng, hóa đơn thẻ tín dụng công ty ngốn gần 700 USD cho các tài khoản ChatGPT Plus lẻ tẻ. Đắt đỏ là một chuyện. Nỗi sợ lớn hơn nằm ở việc anh em vô tư dán cả schema database hay source code dự án lên các dịch vụ đám mây công cộng.
Lối thoát khả dĩ nhất lúc đó: dựng một AI Gateway nội bộ ngay trên máy chủ private của công ty. Open WebUI kết hợp Docker giải quyết trọn vẹn 3 bài toán:
- Kiểm soát dữ liệu: Toàn bộ lịch sử chat, file upload hay embeddings nằm gọn trong database nội bộ. Không một dòng prompt nào lọt ra ngoài nếu chưa được kiểm duyệt.
- Linh hoạt switch mô hình: Xử lý tài liệu nội bộ nhạy cảm? Dùng Llama 3.1 8B hoặc Qwen 2.5 chạy qua Ollama nội bộ. Cần viết code phức tạp hay phân tích báo cáo dài? Chuyển sang GPT-4o hoặc Claude 3.5 Sonnet qua API pool chung chỉ bằng một cú click.
- Cắt giảm chi phí và phân quyền: Admin dễ dàng cấp quota theo phòng ban, gán role Admin/User và khóa bớt các model đắt tiền thay vì phát API key bừa bãi.
Cài đặt bằng Docker Compose
Từng hì hục cài bản Python venv trực tiếp và dính lỗi xung đột CUDA lẫn thư viện C++, mình rút ra kinh nghiệm xương máu: cứ Docker Compose mà triển khai. Vừa sạch máy, vừa nâng cấp hay đổi server chỉ mất đúng 5 phút.
Đầu tiên, chuẩn bị thư mục dự án trên server Ubuntu/Debian:
mkdir -p /opt/ai-gateway/data && cd /opt/ai-gateway
Tạo file docker-compose.yml với cấu hình chuẩn cho production:
services:
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
restart: always
ports:
- "127.0.0.1:3000:8080"
environment:
- WEBUI_SECRET_KEY=tao_chuoi_random_that_dai_o_day_32_ky_tu
- DATA_DIR=/app/backend/data
- ENABLE_SIGNUP=true
- DEFAULT_MODELS=llama3.1:8b
- OPENAI_API_BASE_URLS=http://host.docker.internal:11434/v1;https://api.openai.com/v1
- OPENAI_API_KEYS=ollama;sk-proj-your-openai-api-key-here
volumes:
- ./data:/app/backend/data
extra_hosts:
- "host.docker.internal:host-gateway"
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "5"
Khởi chạy container chạy ngầm:
docker compose up -d
Đợi container pull image và migrate database khoảng 1 phút. Sau đó, bạn có thể kiểm tra trạng thái khởi động bằng lệnh docker compose logs -f.
Cấu hình chi tiết sau khi cài đặt
Người đầu tiên đăng ký trên web sẽ tự động nhận quyền Root Admin. Đăng ký xong tài khoản này, bạn hãy vào tinh chỉnh ngay các thiết lập quan trọng sau.
1. Kết nối đa mô hình (Local GPU & Cloud API)
Vào mục Admin Panel > Settings > Connections để kiểm tra kết nối:
- Ollama Local: Mặc định cấu hình dùng
http://host.docker.internal:11434để trỏ ra Ollama chạy trực tiếp trên máy host. Nếu bạn dựng server GPU riêng trong mạng LAN (chẳng hạn máy gắn RTX 4090/3090), hãy đổi thành IP private nhưhttp://192.168.1.100:11434. - OpenAI hoặc LiteLLM: Thêm trực tiếp URL endpoint và key vào danh sách. Nếu team bạn dùng proxy gateway như LiteLLM hoặc vLLM, Open WebUI tương thích 100% với chuẩn format
/v1.
Thực tế vận hành cho thấy, gộp các kết nối về một đầu mối giúp dev và tester không còn phải copy-paste API key thủ công vào từng ứng dụng.
2. Siết quyền đăng ký và phân bổ Model
Đừng để nhân sự bấm đăng ký tự do:
- Tắt ngay mục Enable New Sign Ups trong tab Settings > General (hoặc sửa biến
ENABLE_SIGNUP=falsetrong docker-compose rồi restart). - Tạo tài khoản thủ công cho từng người tại tab Users.
- Gán danh sách model mặc định: Dev thông thường chỉ được gọi model local chạy trên Ollama để tiết kiệm ngân sách. Nhóm Tech Lead hoặc Product Manager mới được mở quyền dùng GPT-4o hay Claude 3.5 Sonnet.
3. Chặn cổng và bảo mật bằng Nginx Reverse Proxy
Tuyệt đối không mở port 3000 trực tiếp ra Internet. Cấu hình Nginx làm lớp đệm SSL và trỏ domain nội bộ:
server {
listen 80;
server_name ai.company.internal;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name ai.company.internal;
ssl_certificate /etc/ssl/certs/company_ai.crt;
ssl_certificate_key /etc/ssl/certs/company_ai.key;
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Bắt buộc để SSE và streaming token không bị nghẽn
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 600s;
proxy_buffering off;
proxy_cache off;
}
}
Dòng proxy_buffering off; đặc biệt quan trọng. Nếu thiếu nó, Nginx sẽ gom đủ buffer mới trả về, khiến chữ không nhảy từng token mượt mà theo thời gian thực.
Kiểm tra & Vận hành thực tế
Trước khi gửi link cho toàn bộ công ty, bạn cần test kỹ 3 kịch bản tải:
1. Benchmark mức ngốn tài nguyên
Kiểm tra container bằng các lệnh quen thuộc:
# Xem log realtime
docker logs -f --tail=100 open-webui
# Soi RAM và CPU
docker stats open-webui
Ở trạng thái rảnh rỗi, Open WebUI chỉ ăn tầm 350MB đến 600MB RAM. Khi có khoảng 10 người cùng lúc upload tài liệu PDF để chạy tính năng hỏi đáp tài liệu (RAG), mức RAM có thể vọt lên 1.8GB – 2.2GB. Hãy chuẩn bị máy chủ tối thiểu 4GB RAM cho riêng service này.
2. Kiểm tra luồng Streaming và File Upload
Đăng nhập bằng một tài khoản thường để kiểm tra trải nghiệm:
- Chat thử với Llama 3.1: Đo độ trễ từ lúc bấm gửi đến khi token đầu tiên hiển thị. Đảm bảo container kết nối tốt với Ollama qua bridge network.
- Đổi sang GPT-4o: Kiểm tra phản hồi stream đều đặn, không xuất hiện lỗi đứt quãng hay timeout sau 60 giây.
- Kéo thả file PDF báo cáo tài chính dày tầm 15 trang: Thử đặt các câu hỏi truy vấn dữ kiện cụ thể trong file để xác nhận backend băm nhỏ đoạn văn bản (chunking) và embedding chuẩn xác.
3. Backup dữ liệu định kỳ
Mọi tin nhắn, danh sách user và file tài liệu upload đều nằm trọn trong thư mục /opt/ai-gateway/data. Bạn chỉ cần đặt một cron job rsync thư mục này sang NAS hoặc S3 bucket mỗi đêm lúc 2h sáng là an tâm vận hành lâu dài.

