لماذا تستضيف Qdrant ذاتيًا على خادم VPS
تخزّن Qdrant المتجهات (embedding vectors) وتستعلم عنها للعثور على العناصر الأقرب دلاليًا: وهي لبنة البحث في صميم روبوتات المحادثة القائمة على RAG ومحركات التوصية والبحث باللغة الطبيعية. ولأنها مكتوبة بلغة Rust، فهي سريعة ومقتصدة في الموارد، ما يجعلها مثالية للاستضافة على خادم VPS بدلًا من دفع اشتراك خدمة متجهية مُدارة تُحتسب تكلفتها لكل متجه مُخزَّن.
الاستضافة الذاتية بالغة الأهمية لتطبيقات الذكاء الاصطناعي التي تعالج بيانات حساسة: فمتجهاتك، المشتقة غالبًا من مستندات داخلية سرية، لا تغادر بنيتك التحتية أبدًا. كما تتحكم في زمن الاستجابة، وهو أمر حاسم عندما يُطلق كل طلب مستخدم عملية بحث متجهي، وتتجنّب حدود المعدل الخاصة بواجهات API الخارجية عند ارتفاع الأحمال.
المتطلبات والموارد الموصى بها لخادم VPS
- RAM: كحدٍّ أدنى 1 غيغابايت للاختبار، و2 غيغابايت لبضع مئات الآلاف من المتجهات، و4–8 غيغابايت فوق المليون متجه ذي أبعاد عالية (فهرس HNSW يقيم في الذاكرة).
- التخزين: من 20 إلى 50 غيغابايت من قرص SSD NVMe لاستمرارية المجموعات واللقطات.
- CPU: معالج افتراضي واحد إلى اثنين (vCPU) يكفيان لحركة مرور معتدلة؛ أضِف أنوية إن كنت تُفهرس باستمرار.
- Docker وDocker Compose مثبَّتان على الخادم.
- المنفذ 6333 (REST) والمنفذ 6334 (gRPC) في متناول تطبيقك؛ و80/443 للوكيل العكسي العام.
- سجل DNS A (مثل
vectors.mydomain.com) إن كنت ستعرض واجهة API عبر HTTPS.
تثبيت Qdrant بـDocker Compose
إنشاء المجلد وملف compose.yml
على الخادم، أنشئ مجلدًا مخصصًا:
mkdir -p /opt/qdrant && cd /opt/qdrant. أنشئ ملف compose.yml التالي:services: qdrant: image: qdrant/qdrant:latest restart: unless-stopped ports: - "127.0.0.1:6333:6333" - "127.0.0.1:6334:6334" volumes: - ./qdrant_storage:/qdrant/storage environment: - QDRANT__SERVICE__API_KEY=${QDRANT_API_KEY}ربط المنافذ بـ
127.0.0.1 يمنع أي تعرّض مباشر على الإنترنت؛ ولا يصل إليها سوى الوكيل العكسي.توليد مفتاح API وتشغيل الحاوية
أنشئ ملف
.env بمفتاحك:echo "QDRANT_API_KEY=$(openssl rand -hex 32)" > .envشغّل Qdrant:
docker compose up -dالتحقق من صحة النسخة
انتظر بضع ثوانٍ ثم تحقق من نقطة نهاية الصحة:
curl -s http://localhost:6333/healthz # المتوقع: {"title":"qdrant - vector search engine","version":"..."}إنشاء أول مجموعة
أنشئ مجموعة اختبارية بمتجهات من البُعد 384:
curl -s -X PUT http://localhost:6333/collections/test \ -H 'Content-Type: application/json' \ -H "api-key: $(grep QDRANT_API_KEY .env | cut -d= -f2)" \ -d '{"vectors":{"size":384,"distance":"Cosine"}}'
تأمين الوصول: مفتاح API وTLS
لا تُفعّل Qdrant أي مصادقة افتراضيًا: نسخة مكشوفة بلا مفتاح API تتيح لأي شخص قراءة مجموعاتك وتعديلها وحذفها. متغير البيئة QDRANT__SERVICE__API_KEY هو خط الدفاع الأول؛ والوكيل العكسي مع TLS هو الثاني.
مع Caddy، الإعداد الأدنى هو:
vectors.mydomain.com {
reverse_proxy localhost:6333
}يحصل Caddy تلقائيًا على شهادة Let's Encrypt ويجدّدها. مع nginx، أضِف داخل كتلة server:
location / {
proxy_pass http://127.0.0.1:6333;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}فعّل TLS بـCertbot: certbot --nginx -d vectors.mydomain.com.
اختبار المصادقة والوكيل العكسي
التحقق من رفض الوصول بلا مفتاح
curl -s https://vectors.mydomain.com/collections # المتوقع: {"status":{"error":"Unauthorized",...}}استدعاء مُصادَق عليه بمفتاح API
curl -s https://vectors.mydomain.com/collections \ -H "api-key: YOUR_KEY" # المتوقع: {"result":{"collections":[]},"status":"ok",...}التحقق من شهادة TLS
curl -sv https://vectors.mydomain.com/healthz 2>&1 | grep 'SSL connection\|issuer' # المتوقع: سطور تُؤكّد Let's Encrypt وTLS 1.3
إنشاء مجموعة وإدراج المتجهات بعميل Python v1
تثبيت العميل الرسمي
pip install qdrant-client>=1.7الاتصال بمفتاح API
from qdrant_client import QdrantClient client = QdrantClient( url="https://vectors.mydomain.com", api_key="YOUR_KEY", )إنشاء مجموعة بـVectorParams
from qdrant_client.models import Distance, VectorParams client.recreate_collection( collection_name="documents", vectors_config=VectorParams(size=384, distance=Distance.COSINE), )إدراج النقاط مع الحمولة والاستعلام
from qdrant_client.models import PointStruct client.upsert( collection_name="documents", points=[ PointStruct( id=1, vector=[0.1, 0.2, ...], payload={"title": "دليل Qdrant", "lang": "ar"}, ), ], ) results = client.search( collection_name="documents", query_vector=[0.1, 0.21, ...], limit=5, with_payload=True, ) for r in results: print(r.score, r.payload)
تحسين الأداء: ضبط HNSW
خوارزمية HNSW (Hierarchical Navigable Small World) هي قلب البحث السريع في Qdrant. ثلاثة معاملات تحكم مقايضة السرعة/الدقة/الذاكرة.
معاملات HNSW الرئيسية
مرّر الجدول أفقيًا
| المعامل | القيمة الافتراضية | النطاق الموصى به | التأثير |
|---|---|---|---|
| m | 16 | 16–64 | الاتصالات لكل عقدة — أعلى = استرجاع أفضل، ذاكرة أكثر |
| ef_construct | 100 | 100–400 | جودة بناء الفهرس — أعلى = فهرس أفضل، بناء أبطأ |
| ef | 128 (عند الاستعلام) | 64–512 | جودة البحث — أعلى = استرجاع أفضل، استعلام أبطأ |
ضبط HNSW عند إنشاء المجموعة
مرّر معاملات HNSW مباشرةً في recreate_collection:
from qdrant_client.models import HnswConfigDiff
client.recreate_collection(
collection_name="documents",
vectors_config=VectorParams(size=384, distance=Distance.COSINE),
hnsw_config=HnswConfigDiff(m=32, ef_construct=200),
)للبحث المتوازن على مجموعة بيانات من 500 000 متجه، يُوفّر m=32 وef_construct=200 استرجاعًا جيدًا (أكثر من 0.97) دون إثقال RAM. رفع ef عند الاستعلام يُفيد حين تعلو الدقة على زمن الاستجابة.
تقليل بصمة الذاكرة: التكميم القياسي والثنائي
- التكميم القياسي (uint8): يضغط كل إحداثية float32 (4 بايت) إلى uint8 (بايت واحد)، بتخفيض RAM نحو 4 أضعاف. خسارة طفيفة في الدقة تُعوَّض بالإفراط في العيّنات (oversampling).
- التكميم الثنائي: تخفيض يصل إلى 32× ببينرة كل بُعد. مثالي للنماذج المدرَّبة للتكميم الثنائي.
- المعامل
on_disk: يخزّن المتجهات الأصلية على القرص ولا يُبقي في RAM سوى الفهرس المُكمَّم. ضروري على خادم VPS محدود الذاكرة. - Oversampling: مع
oversampling=2.0 عند الاستعلام، تسترجع Qdrant ضعف المرشحين من الفهرس المُكمَّم ثم تعيد ترتيبهم بالمتجهات الأصلية.
النسخ الاحتياطي والاستعادة بلقطات Qdrant
إنشاء لقطة لمجموعة
curl -s -X POST https://vectors.mydomain.com/collections/documents/snapshots \ -H "api-key: YOUR_KEY"سرد اللقطات المتاحة
curl -s https://vectors.mydomain.com/collections/documents/snapshots \ -H "api-key: YOUR_KEY"تنزيل اللقطة
curl -O https://vectors.mydomain.com/collections/documents/snapshots/SNAPSHOT_NAME \ -H "api-key: YOUR_KEY"انسخ ملف
.snapshot إلى تخزين خارجي للنسخ الاحتياطي خارج الموقع.الاستعادة من لقطة
curl -s -X PUT https://vectors.mydomain.com/collections/documents/snapshots/upload \ -H "api-key: YOUR_KEY" \ -H 'Content-Type: multipart/form-data' \ -F [email protected]
المراقبة بـPrometheus وGrafana
تعرض Qdrant نقطة نهاية /metrics بتنسيق Prometheus على المنفذ 6333. أضِف الهدف في prometheus.yml:
scrape_configs:
- job_name: qdrant
static_configs:
- targets: ['localhost:6333']
metrics_path: /metrics
bearer_token: YOUR_KEYفي Grafana، استورد لوحة تحكم مجتمع Qdrant (المعرف 18278) لتصوير زمن استجابة p99 ومعدل الأخطاء في الوقت الفعلي.
استكشاف الأخطاء الشائعة وإصلاحها
- Connection refused على المنفذ 6333: تحقق من تشغيل الحاوية (
docker compose ps) وعدم حجب المنفذ بواسطة ufw. - خطأ OOM / إيقاف الحاوية قسرًا: قلّل
ef_construct أو فعّل التكميم القياسي لتخفيض بصمة RAM للفهرس. - بطء عند الإقلاع أو التحميل الأول: فهرس HNSW يُحمَّل من القرص عند الوصول الأول؛ استخدم SSD NVMe وأجرِ استعلامًا تجريبيًا بعد التشغيل.
- Unauthorized 403: المفتاح في ترويسة
api-key لا يطابق QDRANT__SERVICE__API_KEY؛ تحقق من غياب المسافات أو أحرف السطر الجديد. - تراجع الاسترجاع بعد تفعيل التكميم الثنائي: رفع
oversampling إلى 3.0 أو 4.0 وتفعيل rescore=True.
اقتران Qdrant بـOllama لمنظومة RAG قائمة بذاتها بالكامل
شغّل Ollama على الخادم نفسه لتوليد التضمينات (ollama pull mxbai-embed-large) وللاستدلال (ollama pull llama3.1:8b). عندئذٍ يعمل خط أنابيب RAG لديك من طرف إلى طرف دون أي استدعاء لواجهة خارجية: سؤال المستخدم ← تضمين Ollama ← بحث Qdrant ← أفضل المقاطع ← توليد Ollama ← إجابة مؤسَّسة.
أضِف LiteLLM في المقدّمة لكشف نقطة نهاية موحّدة متوافقة مع OpenAI لتطبيقك. خادم VPS بذاكرة 8 غيغابايت يستضيف بيُسر Qdrant + Ollama (نموذج 7B مُكمَّم) + LiteLLM لمجموعة بيانات بضع مئات الآلاف من المتجهات.