AI platform với kiến trúc hybrid (Local + Kaggle GPU) sử dụng Prefect, Kafka, Qdrant, Prometheus, Grafana.
Local (Docker Compose):
Kafka → Prefect → Delta Lake → Feast (Redis)
↓ ↓
Qdrant API Gateway (FastAPI)
↓ ↓
Prometheus ← Grafana
↓
LangSmith tracing
Kaggle (GPU T4/P100):
vLLM serving
Embedding service
MLflow tracking
- Docker Desktop đang chạy
- Python 3.10+
- Tài khoản Kaggle với GPU đã bật
- Tunnel service (chọn 1 trong 2):
ngrokđã cài và token configured- HOẶC
cloudflaredđã cài (brew install cloudflare/cloudflare/cloudflared)
cd lab28
docker compose up -d
docker compose ps # Kiểm tra tất cả services UpServices:
- Prefect UI: http://localhost:4200
- Grafana: http://localhost:3000 (admin/admin)
- Qdrant: http://localhost:6333/dashboard
- Prometheus: http://localhost:9090
- API Gateway: http://localhost:8000
Tạo Kaggle Notebook với GPU T4 x2, chọn 1 trong 2 option:
Option A: Single GPU (đơn giản - dùng 1 GPU)
# Cell 1: Install dependencies
!pip install -q vllm fastapi uvicorn pyngrok mlflow sentence-transformers
# Nếu cài vLLM bị lỗi, dùng fallback:
# !pip install transformers==4.46.3 --quiet
# !pip install vllm==0.7.3 --quiet
# Cell 2: Setup ngrok
from pyngrok import ngrok
ngrok.set_auth_token("YOUR_NGROK_TOKEN") # lấy tại ngrok.com
# Cell 3: Start vLLM server (single GPU)
import subprocess, threading, time
def run_vllm():
subprocess.run([
"python", "-m", "vllm.entrypoints.openai.api_server",
"--model", "Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4",
"--port", "8001",
"--max-model-len", "4096",
"--gpu-memory-utilization", "0.5"
])
thread = threading.Thread(target=run_vllm, daemon=True)
thread.start()
time.sleep(60)
print("vLLM server started")
# Cell 4: Create ngrok tunnel
tunnel = ngrok.connect(8001, "http")
print(f"vLLM URL: {tunnel.public_url}")Option B: Multi-GPU (nâng cao - dùng 2 GPUs)
# Cell 1: Install dependencies
!pip install -q vllm fastapi uvicorn pyngrok mlflow sentence-transformers
# Nếu cài vLLM bị lỗi, dùng fallback:
# !pip install transformers==4.46.3 --quiet
# !pip install vllm==0.7.3 --quiet
# Cell 2: Setup ngrok
from pyngrok import ngrok
ngrok.set_auth_token("YOUR_NGROK_TOKEN") # lấy tại ngrok.com
# Cell 3: Start vLLM server (multi-GPU)
import subprocess
import os
import time
import requests
import threading
MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4"
def start_server(gpu_id, port):
env = os.environ.copy()
env["CUDA_VISIBLE_DEVICES"] = str(gpu_id)
proc = subprocess.Popen(
[
"vllm", "serve", MODEL_NAME,
"--dtype", "float16",
"--max-model-len", "8192",
"--host", "0.0.0.0",
"--port", str(port),
],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
env=env
)
def stream_logs():
for line in proc.stdout:
print(f"[GPU {gpu_id}] {line.decode()}", end="")
threading.Thread(target=stream_logs, daemon=True).start()
return proc
print("Starting Server on GPU 0 (Port 8000)")
proc1 = start_server(0, 8000)
print("Starting Server on GPU 1 (Port 8001)")
proc2 = start_server(1, 8001)
def wait_for_server(port):
print(f" Waiting for server on port {port}...")
for _ in range(60):
try:
r = requests.get(f"http://localhost:{port}/health")
if r.status_code == 200:
print(f"Server on port {port} is ready!")
return
except:
time.sleep(5)
raise RuntimeError(f"Server on port {port} failed to start.")
wait_for_server(8000)
wait_for_server(8001)
# Cell 4: Create ngrok tunnel
print("Creating ngrok tunnels...")
tunnel1 = ngrok.connect(8000, "http")
tunnel2 = ngrok.connect(8001, "http")
print(f"GPU 0 URL: {tunnel1.public_url}")
print(f"GPU 1 URL: {tunnel2.public_url}")
# Có thể dùng 1 trong 2 hoặc cả 2 cho load balancingOption C: Dùng cloudflared (Single GPU)
# Cell 1: Install dependencies
!pip install -q vllm fastapi uvicorn cloudflared mlflow sentence-transformers
# Nếu cài vLLM bị lỗi, dùng fallback:
# !pip install transformers==4.46.3 --quiet
# !pip install vllm==0.7.3 --quiet
# Cell 2: Start vLLM server (single GPU)
import subprocess, threading, time
def run_vllm():
subprocess.run([
"python", "-m", "vllm.entrypoints.openai.api_server",
"--model", "Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4",
"--port", "8001",
"--max-model-len", "4096",
"--gpu-memory-utilization", "0.5"
])
thread = threading.Thread(target=run_vllm, daemon=True)
thread.start()
time.sleep(60)
print("vLLM server started")
# Cell 3: Create cloudflare tunnel
import subprocess
tunnel = subprocess.run(["cloudflared", "tunnel", "--url", "http://localhost:8001"], capture_output=True, text=True)
print(tunnel.stdout) # URL sẽ hiển thịOption D: Dùng cloudflared (Multi-GPU)
# Cell 1: Install dependencies
!pip install -q vllm fastapi uvicorn cloudflared mlflow sentence-transformers
# Nếu cài vLLM bị lỗi, dùng fallback:
# !pip install transformers==4.46.3 --quiet
# !pip install vllm==0.7.3 --quiet
# Cell 2: Start vLLM server (multi-GPU)
import subprocess
import os
import time
import requests
import threading
MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4"
def start_server(gpu_id, port):
env = os.environ.copy()
env["CUDA_VISIBLE_DEVICES"] = str(gpu_id)
proc = subprocess.Popen(
[
"vllm", "serve", MODEL_NAME,
"--dtype", "float16",
"--max-model-len", "8192",
"--host", "0.0.0.0",
"--port", str(port),
],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
env=env
)
def stream_logs():
for line in proc.stdout:
print(f"[GPU {gpu_id}] {line.decode()}", end="")
threading.Thread(target=stream_logs, daemon=True).start()
return proc
print("Starting Server on GPU 0 (Port 8000)")
proc1 = start_server(0, 8000)
print("Starting Server on GPU 1 (Port 8001)")
proc2 = start_server(1, 8001)
def wait_for_server(port):
print(f" Waiting for server on port {port}...")
for _ in range(60):
try:
r = requests.get(f"http://localhost:{port}/health")
if r.status_code == 200:
print(f"Server on port {port} is ready!")
return
except:
time.sleep(5)
raise RuntimeError(f"Server on port {port} failed to start.")
wait_for_server(8000)
wait_for_server(8001)
# Cell 3: Create cloudflare tunnel
import subprocess
print("Creating cloudflare tunnels...")
tunnel1 = subprocess.run(["cloudflared", "tunnel", "--url", "http://localhost:8000"], capture_output=True, text=True)
tunnel2 = subprocess.run(["cloudflared", "tunnel", "--url", "http://localhost:8001"], capture_output=True, text=True)
print(f"GPU 0 URL (copy from output):")
print(tunnel1.stdout)
print(f"GPU 1 URL (copy from output):")
print(tunnel2.stdout)
# Có thể dùng 1 trong 2 hoặc cả 2 cho load balancing# Copy và chỉnh sửa file .env
cp .env.example .env
# Thay VLLM_NGROK_URL với URL từ Kaggle (ngrok hoặc cloudflared)
# Thay EMBED_NGROK_URL nếu có embedding service
# Thay LANGCHAIN_API_KEY với key của bạncd prefect/flows
pip install -r requirements.txt
python kafka_to_delta.pycd ../..
python scripts/01_ingest_to_kafka.pypytest smoke-tests/ -vKỳ vọng: 5/5 tests passing
python scripts/production_readiness_check.pyKỳ vọng: Score >80%
| Script | Mô tả |
|---|---|
scripts/01_ingest_to_kafka.py |
Ingest sample data vào Kafka |
scripts/03_delta_to_feast.py |
Load từ Delta Lake và push features vào Feast (Redis) |
scripts/05_embed_to_qdrant.py |
Embed data và lưu vectors vào Qdrant |
scripts/09_verify_observability.py |
Kiểm tra Prometheus metrics và LangSmith traces |
scripts/production_readiness_check.py |
Production readiness checklist |
Health Check:
curl http://localhost:8000/healthChat Endpoint:
curl -X POST http://localhost:8000/api/v1/chat \
-H "Content-Type: application/json" \
-d '{
"query": "What is platform engineering?",
"embedding": [0.1, 0.2, ...]
}'- Grafana Dashboard: http://localhost:3000
- Prometheus: http://localhost:9090
- Prefect UI: http://localhost:4200
Services không start:
docker compose logs <service_name>
docker compose down -v
docker compose up -dPrefect worker không connect:
# Check Prefect UI: http://localhost:4200
# Đảm bảo worker đang chạy:
docker compose logs prefect-workerKafka consumer lag:
# Kiểm tra topic
docker exec lab28-kafka-1 kafka-topics --list --bootstrap-server localhost:9092Xem SUBMISSION.md ở thư mục gốc project.