ما الذي يبني هذا
مثيل GPU واحد يقدم نموذجًا مفتوح الأوزان عبر واجهة برمجة تطبيقات HTTP تتحدث مخططًا متوافقًا مع OpenAI، لذا تعمل كل مكتبات العملاء الموجودة لديك معه بتغيير عنوان URL الأساسي واحد فقط. خلف nginx، مع TLS، ومفتاح API، ووحدة systemd تنجو من عمليات إعادة التشغيل.
كُتب على G-L40S: ثمانية وأربعون غيغابايت من ذاكرة VRAM على بطاقة واحدة، تم تمريرها إلى المثيل عبر PCIe بدلاً من تقسيمها زمنيًا بين المستأجرين. هذا التمييز هو الفرق بين زمن استجابة يمكن التنبؤ به والانتظار خلف وظيفة تدريب شخص آخر. البطاقة ملكك حتى تلغيها.
قبل أن تبدأ
- G-L40S مع Debian 13. تعمل G-ADA بسعة عشرين غيغابايت مع النماذج الأصغر؛ الحسابات في الخطوة الرابعة تخبرك أيها.
- تيرابايتان من NVMe في هذه الخطة، وهو المكان المناسب للأوزان. تنزيل أربعين غيغابايت مرتين لأن ذاكرة التخزين المؤقت كانت على نظام الملفات الجذر هي طريقة مزعجة لقضاء ساعة.
- اسم مضيف يشير إلى المثيل،
llm.example.comأدناه.
1. برنامج التشغيل
sed -i "s/ main$/ main contrib non-free-firmware non-free/" /etc/apt/sources.list.d/debian.sources
apt update
apt install -y nvidia-driver firmware-misc-nonfree build-essential
rebootبعد أن يعود:
nvidia-smiتريد أن ترى البطاقة مدرجة، وإصدار برنامج التشغيل مطبوعًا، واستخدام الذاكرة قريبًا من الصفر. البطاقة المفقودة في هذه المرحلة تكون دائمًا تقريبًا بسبب initramfs قديم؛ update-initramfs -u وأعد التشغيل مرة أخرى قبل فتح تذكرة.
nvidia-smi -q -d PERFORMANCE | grep -A3 "Clocks Event Reasons"
nvidia-smi -pm 1وضع الاستمرارية يبقي برنامج التشغيل محملاً بين العمليات، مما يزيل عدة ثوانٍ من التهيئة عند كل إعادة تشغيل.
2. بيئة التشغيل
apt install -y python3-venv python3-pip
install -d -o root -g root /srv/models /opt/vllm
python3 -m venv /opt/vllm/venv
/opt/vllm/venv/bin/pip install --upgrade pip
/opt/vllm/venv/bin/pip install vllmيجلب التثبيت مجموعة عجلات كبيرة مرتبطة بـ CUDA ويستغرق بعض الوقت. في هذه الأثناء، قرر النموذج.
3. اختيار ما يناسب
الأوزان هي الحد الأدنى، وليست الميزانية. يحتاج النموذج بدقة ستة عشر بتًا إلى ما يقرب من غيغابايتين من VRAM لكل مليار معلمة، بالإضافة إلى ذاكرة تخزين مؤقت للمفاتيح والقيم لكل طلب متزامن، بالإضافة إلى غيغابايت أو نحو ذلك من النفقات العامة للتشغيل.
| البطاقة | الأوزان بدقة 16 بت | حجم النموذج الواقعي | المتبقي للذاكرة المؤقتة |
|---|---|---|---|
| L40S، 48 غيغابايت | 2 غيغابايت لكل مليار | حتى حوالي 20 مليار | 6 إلى 8 غيغابايت |
| L40S، 48 غيغابايت، 8 بت | 1 غيغابايت لكل مليار | حتى حوالي 34 مليار | 10 غيغابايت |
| RTX 4000 Ada، 20 غيغابايت | 2 غيغابايت لكل مليار | حتى حوالي 7 مليار | 5 غيغابايت |
| اثنتان × L40S، 96 غيغابايت | 2 غيغابايت لكل مليار | حتى حوالي 40 مليار | 12 غيغابايت |
عمود الذاكرة المؤقتة هو ما يحدد عدد الأشخاص الذين يمكنهم استخدام نقطة النهاية في وقت واحد. املأ البطاقة حتى الحافة بالأوزان وستكون قد بنيت لعبة سريعة جدًا لمستخدم واحد.
4. تقديمه
وجه الذاكرة المؤقتة إلى NVMe وابدأ الخادم. استبدل معرف المستودع لأي نموذج مفتوح الأوزان اخترته؛ تجلبه بيئة التشغيل عند أول تشغيل.
cat > /etc/systemd/system/vllm.service <<EOF
[Unit]
Description=vLLM inference server
After=network-online.target
[Service]
Environment=HF_HOME=/srv/models
Environment=VLLM_API_KEY=<a long random string>
ExecStart=/opt/vllm/venv/bin/vllm serve <org>/<model> \\
--host 127.0.0.1 --port 8000 \\
--served-model-name local \\
--max-model-len 16384 \\
--gpu-memory-utilization 0.92 \\
--max-num-seqs 32
Restart=on-failure
RestartSec=10
TimeoutStartSec=1800
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload && systemctl enable --now vllm
journalctl -fu vllmالربط بـ loopback مقصود. فحص مفتاح API في بيئة التشغيل هو سر مشترك واحد بدون حد معدل خلفه، لذا يقوم nginx بالتعرض الخارجي ولا تلمس بيئة التشغيل الواجهة العامة أبدًا.
مهلة البدء السخية موجودة لأن الإطلاق الأول ينزل عشرات الغيغابايت ثم يجمع النوى. البدءات اللاحقة تستغرق أقل من دقيقة.
5. البروكسي و TLS
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name llm.example.com;
ssl_certificate /etc/letsencrypt/live/llm.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/llm.example.com/privkey.pem;
location /v1/ {
proxy_pass http://127.0.0.1:8000;
proxy_buffering off;
proxy_read_timeout 600s;
proxy_set_header Host $host;
}
}proxy_buffering off هو السطر الذي يجعل الاستجابات المتدفقة تتدفق. اتركه مفعلًا وستصل الرموز في دفعات مهذبة عندما يشعر nginx أن المخزن المؤقت ممتلئ، وهو ما يبدو تمامًا مثل نموذج بطيء وليس كذلك.
تحقق منه
ابدأ بالبطاقة:
nvidia-smi --query-gpu=memory.used,memory.total,utilization.gpu --format=csvيجب أن تكون الذاكرة المستخدمة حوالي تسعين بالمائة من الإجمالي، لأن هذا ما طلبه --gpu-memory-utilization. ثم نقطة النهاية:
curl -s https://llm.example.com/v1/models -H "Authorization: Bearer <the key>" | head -20نموذج واحد، اسمه local. الآن إكمال حقيقي، وقم بتوقيته:
time curl -s https://llm.example.com/v1/chat/completions \
-H "Authorization: Bearer <the key>" \
-H "Content-Type: application/json" \
-d '{"model":"local","messages":[{"role":"user","content":"List three prime numbers."}],"max_tokens":64}'أخيرًا، قم بقياسه تحت الحمل بدلاً من طلب واحد في كل مرة، وهو الرقم الوحيد الذي يستحق الاستشهاد:
/opt/vllm/venv/bin/vllm bench serve \
--backend openai-chat \
--base-url https://llm.example.com \
--endpoint /v1/chat/completions \
--model local --num-prompts 200 --request-rate 8اقرأ ثلاثة أرقام من المخرجات: رموز الإخراج في الثانية كإجمالي، والوسيط للوقت حتى الرمز الأول، والمئين التاسع والتسعين من النهاية إلى النهاية. على L40S واحدة مع نموذج في نطاق عشرة إلى عشرين مليارًا، فإن إجمالي إنتاجية الإخراج في المئات العالية من الرموز في الثانية ووقت حتى الرمز الأول أقل من ثانية هو الشكل المتوقع. الوقت حتى الرمز الأول الذي يتصاعد مع التزامن يعني أن ذاكرة التخزين المؤقت للمفاتيح والقيم صغيرة جدًا، لذا اخفض --max-model-len أو --max-num-seqs وأعد التشغيل.
بعد ذلك
راقب درجة الحرارة وخنق الساعة خلال الأسبوع الأول باستخدام nvidia-smi dmon، لأن البطاقة التي تخنق حراريًا تحت الحمل المستمر تنتج بالضبط نوع البطء المتقطع الذي يُلقى باللوم فيه على الشبكة. من ناحية الفواتير، تذكر أن مثيل GPU يكلف نفس الشيء سواء كانت البطاقة مشغولة أو خاملة، لذا قم بمعالجة الأعمال دفعة واحدة بدلاً من تركه دافئًا. الأسعار على صفحة GPU.