
نشر نموذج LLM باستخدام Modal: من pip install إلى نقطة نهاية الإنتاج
معظم الأدلة المتعلقة باستضافة نماذج LLM ذاتيًا تتجاهل الجزء الأصعب: البنية التحتية. تصارع مع تعريفات CUDA، وإدارة صور Docker، وتهيئة التوسع التلقائي، وفي النهاية لا تزال تدفع مقابل وحدات GPU خاملة في الساعة الثالثة صباحًا. Modal يُزيل كل ذلك. تكتب Python، تنشر، تحصل على رابط URL.
يأخذك هذا الدليل خطوة بخطوة خلال نشر نموذج LLM مفتوح المصدر على Modal مع vLLM كمحرك استنتاج. في النهاية، سيكون لديك نقطة نهاية API متوافقة مع OpenAI تعمل على وحدات GPU H100 وتتوسع إلى الصفر عندما لا يستخدمها أحد.
ما هو Modal (ولماذا تستخدمه لنماذج LLM)؟
Modal هي منصة حوسبة لامركزية مبنية خصيصًا لأعباء عمل الذكاء الاصطناعي. فكر فيها كـ AWS Lambda، لكن مع دعم GPU، وفواتير بالثانية، وتجربة مطور أصيلة في Python. لا YAML، لا Dockerfile، لا Kubernetes — تُعرّف بنيتك التحتية بالكامل في نص Python وتنشرها بأمر واحد.
إليك لماذا أصبح الخيار المُفضَّل لنشر نماذج LLM:
- فواتير scale-to-zero — لا تدفع شيئًا عندما لا تعالج نقطة النهاية الخاصة بك أي طلبات
- أسعار GPU بالثانية — H100 بـ ~$3.95/ساعة، A100 80 GB بـ ~$2.50/ساعة، محسوبة بالثانية
- بدايات باردة دون ثانية — تبدأ الحاويات بسرعة، خاصةً مع لقطات الذاكرة
- $30/شهر كرصيد مجاني — كافٍ للتجربة دون أي رسوم على بطاقة الائتمان
- بدون DevOps — لا بناء Docker، لا Terraform، لا إدارة مجموعات
إذا كنت تشغّل نماذج LLM محليًا وتريد منحها واجهة API حقيقية دون إدارة خوادم، فـ Modal هو أقصر الطرق.
Modal مقابل RunPod مقابل Lambda
| الميزة | Modal | RunPod | Lambda |
|---|---|---|---|
| نموذج الفواتير | بالثانية، scale-to-zero | بالثانية، رسوم دنيا | بالساعة، دائم التشغيل |
| البداية الباردة | 2-4 ثواني | 6-12 ثانية (كبير) | لا ينطبق (دائم) |
| توفر GPU | H100, A100, L40S, T4 | A100, H100, A6000 | H100, A100 |
| البنية التحتية | Python خالص، بلا ملفات تهيئة | Docker، تحكم أكثر | وصول كامل للـ VM |
| المستوى المجاني | $30/شهر كرصيد | لا يوجد | لا يوجد |
| الأنسب لـ | أعباء عمل متقطعة/التطوير | حركة استنتاج ثابتة | تدريب مكثف |
الخلاصة: Modal يفوز بالنسبة لأعباء العمل المتقطعة والتطوير. إذا تجاوز استخدام GPU لديك باستمرار 40%، فإن مثيلًا مخصصًا على RunPod أو Lambda يكون أرخص. لكل شيء آخر — النمذجة الأولية، وAPIs المتقطعة، والعروض التوضيحية — يوفّر نموذج scale-to-zero الخاص بـ Modal أموالًا حقيقية.
المتطلبات المسبقة
قبل أن تبدأ، تحتاج إلى ثلاثة أشياء:
- Python 3.10+ مثبت محليًا
- حساب Modal — سجّل مجانًا على modal.com
- حساب Hugging Face — للوصول إلى النماذج (معظمها مقيّد)
هذا كل شيء. لا تحتاج GPU على جهازك المحلي، ولا CUDA toolkit، ولا Docker.
الخطوة 1: تثبيت Modal والمصادقة
افتح طرفية وثبّت حزمة Modal لـ Python:
pip install modalثم شغّل أمر الإعداد لربط بيئتك المحلية بحساب Modal:
modal setupهذا يفتح نافذة متصفح للمصادقة. بمجرد تأكيدك، يحفظ Modal رمزًا مميزًا محليًا. لن تحتاج إلى فعل هذا مرة أخرى.
الخطوة 2: تعريف صورة الحاوية
تُعرَّف حاويات Modal بالـ Python. تحدد الصورة الأساسية، وتثبّت التبعيات، وتضبط متغيرات البيئة — كلها كأكواد. أنشئ ملفًا اسمه app.py:
import modal
# تعريف صورة الحاوية مع CUDA وPython وvLLM
vllm_image = (
modal.Image.from_registry(
"nvidia/cuda:12.8.0-devel-ubuntu22.04", add_python="3.12"
)
.entrypoint([])
.pip_install(
"vllm==0.13.0",
"huggingface-hub==0.36.0",
)
)
app = modal.App("llm-endpoint", image=vllm_image)بعض الأشياء التي يجب ملاحظتها. لا يوجد Dockerfile — تلك السلسلة modal.Image تستبدله بالكامل. تتضمن الصورة الأساسية NVIDIA CUDA 12.8 مع Ubuntu 22.04، ونثبّت vLLM وعميل Hugging Face Hub فوقها.
الخطوة 3: تهيئة تخزين النموذج باستخدام Volumes
أوزان نماذج LLM كبيرة (نموذج بـ 7 مليار معامل يبلغ حجمه ~14 GB في fp16). لا تريد تنزيلها في كل مرة تبدأ فيها حاوية. تمنحك Modal Volumes تخزينًا دائمًا يُوصَّل مباشرةً إلى حاوياتك:
# حجمات دائمة لتخزين مؤقت لأوزان النموذج
hf_cache = modal.Volume.from_name("huggingface-cache", create_if_missing=True)
vllm_cache = modal.Volume.from_name("vllm-cache", create_if_missing=True)
MODEL_NAME = "Qwen/Qwen3-4B-Thinking-2507-FP8"
MODEL_REVISION = "953532f942706930ec4bb870569932ef63038fdf"نستخدم هنا Qwen3-4B-Thinking (FP8) — نموذج مُكمَّم بـ 4 مليار معامل، سريع وقادر ويتناسب مع وحدة GPU واحدة. يمكنك استبداله بأي نموذج من Hugging Face: Llama 3.1 8B أو Mistral 7B أو أي شيء يدعمه vLLM.
لماذا FP8؟ يُقلل استخدام الذاكرة بنحو النصف مقارنةً بـ fp16، مما يعني إمكانية تشغيل نماذج أكبر على نفس GPU — أو نماذج أصغر على وحدات GPU أرخص. إذا كنت مهتمًا بمقايضات الكَمِّ، فإن دليلنا لتشغيل نماذج LLM محليًا يُغطي صيغ الدقة بالتفصيل.
الخطوة 4: إنشاء دالة خادم vLLM
هنا تحدث سحر Modal. تُزيّن دالة Python بمتطلبات GPU وتهيئة التوسع وتعليق خادم الويب. تتولى Modal الباقي:
N_GPU = 1
MINUTES = 60
VLLM_PORT = 8000
@app.function(
gpu=f"H100:{N_GPU}",
scaledown_window=15 * MINUTES,
timeout=10 * MINUTES,
volumes={
"/root/.cache/huggingface": hf_cache,
"/root/.cache/vllm": vllm_cache,
},
)
@modal.concurrent(max_inputs=32)
@modal.web_server(port=VLLM_PORT, startup_timeout=10 * MINUTES)
def serve():
import subprocess
cmd = [
"vllm", "serve",
MODEL_NAME,
"--revision", MODEL_REVISION,
"--served-model-name", MODEL_NAME,
"--host", "0.0.0.0",
"--port", str(VLLM_PORT),
"--tensor-parallel-size", str(N_GPU),
"--enforce-eager", # بدايات باردة أسرع
]
subprocess.Popen(" ".join(cmd), shell=True)دعنا نُفصّل المزخرفات الرئيسية:
gpu="H100:1"— يطلب وحدة GPU واحدة H100. غيّر إلى"A100-80GB:1"للاستنتاج الأرخص، أو"H100:2"للنماذج الأكبر من 70Bscaledown_window=15 * MINUTES— يُبقي الحاوية دافئة لمدة 15 دقيقة بعد آخر طلب، ثم تتوسع إلى الصفر@modal.concurrent(max_inputs=32)— يسمح بما يصل إلى 32 طلبًا متزامنًا لكل حاوية (يتعامل vLLM مع التجميع داخليًا)@modal.web_server(port=8000)— يكشف خادم HTTP لـ vLLM مباشرةً كنقطة نهاية ويب في Modal--enforce-eager— يتخطى تجميع رسوم CUDA للبدايات الباردة الأسرع (المقايضة: إنتاجية ذروة أقل قليلًا)
scaledown_window هو رافعة التكلفة الرئيسية لديك. اضبطه على 5 دقائق للتطوير، و15-30 دقيقة لواجهات API الإنتاجية ذات حركة المرور المنتظمة.
الخطوة 5: النشر للإنتاج
أمر واحد. هذا كل شيء:
modal deploy app.pyتبني Modal صورة الحاوية، وتدفعها إلى سجلّها، وتعيد رابط URL حيًا:
✓ Created objects.
├── 🔨 Created mount /app.py
├── 🔨 Created volume huggingface-cache
├── 🔨 Created volume vllm-cache
└── 🔨 Created web function serve => https://your-workspace--llm-endpoint-serve.modal.runأول عملية نشر تستغرق بضع دقائق لأنها تُنزّل أوزان النموذج في الحجم. عمليات النشر اللاحقة (والبدايات الباردة) أسرع بكثير لأن الأوزان مخزَّنة مؤقتًا.
للتطوير، استخدم modal serve app.py بدلًا من ذلك — يُعيد التحميل عند تغيير الملفات ويمنحك رابط URL مؤقتًا.
الخطوة 6: استدعاء نقطة النهاية (متوافق مع OpenAI)
خادم vLLM المنشور يُكشف واجهة API متوافقة مع OpenAI على /v1/chat/completions. يمكنك استخدام SDK Python القياسي لـ OpenAI لاستدعائه — فقط وجّه base URL إلى نقطة نهاية Modal الخاصة بك:
from openai import OpenAI
client = OpenAI(
api_key="not-needed", # vLLM لا يتطلب مصادقة افتراضيًا
base_url="https://your-workspace--llm-endpoint-serve.modal.run/v1",
)
response = client.chat.completions.create(
model="Qwen/Qwen3-4B-Thinking-2507-FP8",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain what vLLM is in two sentences."},
],
temperature=0.7,
max_tokens=256,
)
print(response.choices[0].message.content)يعمل هذا أيضًا مع curl:
curl -X POST https://your-workspace--llm-endpoint-serve.modal.run/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen3-4B-Thinking-2507-FP8",
"messages": [{"role": "user", "content": "Hello!"}],
"max_tokens": 128
}'أي أداة تدعم واجهة API متوافقة مع OpenAI ستعمل — LangChain أو LlamaIndex أو تطبيقك الخاص. إذا كنت تُوجّه الطلبات عبر نقاط نهاية متعددة لنماذج LLM، يمكن أن تُساعدك أداة بوابة LLM في إدارة التعافي من الفشل وموازنة التحميل.
نصائح لتحسين التكاليف
الفواتير بالثانية في Modal أكثر كفاءةً بالفعل من الأسعار بالساعة، لكن يمكنك استخراج المزيد منها:
1. استخدام الكَمِّ FP8
تستخدم نماذج FP8 نصف VRAM تقريبًا مقارنةً بنظيراتها fp16. يتناسب Qwen3-8B بـ FP8 مع وحدة H100 واحدة، بينما تحتاج النسخة fp16 إلى معظم الـ 80 GB من تلك وحدة GPU. تعني VRAM الأقل أنك تستطيع استخدام وحدات GPU أرخص (A100 40 GB أو L40S) للنماذج الأصغر.
2. ضبط نافذة التوسع نحو الانخفاض
يتحكم معامل scaledown_window في المدة التي تظل فيها الحاوية دافئة بعد آخر طلب:
| السيناريو | النافذة المُوصَى بها | السبب |
|---|---|---|
| التطوير/الاختبار | 5 دقائق | توفير المال، البدايات الباردة مقبولة |
| API داخلي (متقطع) | 10-15 دقيقة | توازن التكلفة والكمون |
| الإنتاج (حركة منتظمة) | 20-30 دقيقة | تقليل البدايات الباردة |
| الإنتاج عالي الحركة | استخدم min_containers=1 | احتفظ بواحدة دافئة دائمًا |
3. اختيار وحدة GPU المناسبة
لا تختر دائمًا H100. النماذج الأصغر لا تحتاجها:
| حجم النموذج | GPU المُوصَى بها | التكلفة التقريبية/ساعة |
|---|---|---|
| 1-4 مليار معامل | L4 أو T4 | $0.59 – $0.80 |
| 7-8 مليار معامل | A10 أو L40S | $1.10 – $1.95 |
| 13-14 مليار معامل | A100 40 GB | $2.10 |
| 30-70 مليار معامل | A100 80 GB أو H100 | $2.50 – $3.95 |
| أكثر من 70 مليار معامل | H100 x2 | $7.90 |
4. تفعيل التخزين المؤقت للـ Prompt
إذا كانت أعباء عملك تتضمن مطالبات نظام متكررة أو بوادئ مشتركة، فإن التخزين المؤقت التلقائي للبوادئ في vLLM يمكن أن يُقلل الكمون والحوسبة بشكل ملحوظ. فعّله بإضافة --enable-prefix-caching إلى أمر vLLM serve. للتعمق في كيفية عمل التخزين المؤقت عبر مزودين مختلفين، راجع دليلنا حول التخزين المؤقت للـ prompt في LLM.
5. استخدام --enforce-eager لتحسين البدايات الباردة
افتراضيًا، يُجمّع vLLM رسوم CUDA عند التشغيل، مما يستغرق 1-3 دقائق إضافية. يتخطى الإشارة --enforce-eager هذا التجميع. تُقايض ~10-15% من الإنتاجية القصوى ببدايات باردة أسرع بكثير. بالنسبة لأعباء العمل المتقطعة حيث الكمون أهم من الإنتاجية الخام، يكون هذا الاختيار الصحيح دائمًا تقريبًا.
ما هو أبعد: النماذج المُضبَّطة دقيقًا
بمجرد ارتياحك لنشر النماذج الأساسية، فإن الخطوة الطبيعية التالية هي نشر نسختك المُضبَّطة دقيقًا الخاصة. سير العمل متطابق — تُشير فقط MODEL_NAME إلى مستودع Hugging Face الخاص بك أو حجم Modal يحتوي على أوزانك المُضبَّطة دقيقًا.
تدعم Modal أيضًا تشغيل مهام الضبط الدقيق مباشرةً على وحدات GPU الخاصة بها. يمكنك تدريب محوّل LoRA على Modal وحفظه في حجم ونشر النموذج المُدمج — كل ذلك دون مغادرة المنصة. يُغطي دليلنا للضبط الدقيق لنماذج LLM جانب التدريب بعمق.
ملف app.py الكامل
إليك نص النشر الكامل في كتلة واحدة جاهزة للنسخ واللصق:
import modal
# --- تعريف الصورة ---
vllm_image = (
modal.Image.from_registry(
"nvidia/cuda:12.8.0-devel-ubuntu22.04", add_python="3.12"
)
.entrypoint([])
.pip_install("vllm==0.13.0", "huggingface-hub==0.36.0")
)
# --- الحجمات لتخزين النموذج مؤقتًا ---
hf_cache = modal.Volume.from_name("huggingface-cache", create_if_missing=True)
vllm_cache = modal.Volume.from_name("vllm-cache", create_if_missing=True)
# --- تهيئة النموذج ---
MODEL_NAME = "Qwen/Qwen3-4B-Thinking-2507-FP8"
MODEL_REVISION = "953532f942706930ec4bb870569932ef63038fdf"
app = modal.App("llm-endpoint", image=vllm_image)
N_GPU = 1
MINUTES = 60
VLLM_PORT = 8000
@app.function(
gpu=f"H100:{N_GPU}",
scaledown_window=15 * MINUTES,
timeout=10 * MINUTES,
volumes={
"/root/.cache/huggingface": hf_cache,
"/root/.cache/vllm": vllm_cache,
},
)
@modal.concurrent(max_inputs=32)
@modal.web_server(port=VLLM_PORT, startup_timeout=10 * MINUTES)
def serve():
import subprocess
cmd = [
"vllm", "serve",
MODEL_NAME,
"--revision", MODEL_REVISION,
"--served-model-name", MODEL_NAME,
"--host", "0.0.0.0",
"--port", str(VLLM_PORT),
"--tensor-parallel-size", str(N_GPU),
"--enforce-eager",
]
subprocess.Popen(" ".join(cmd), shell=True)انشر بـ modal deploy app.py، واستبدل MODEL_NAME بأي نموذج من Hugging Face، وستكون مباشرًا.
الأسئلة الشائعة
كم يكلف تشغيل نموذج LLM على Modal؟
يعتمد على وحدة GPU ومدة بقاء نقطة النهاية دافئة. Qwen3-4B على H100 يكلف ~$3.95/ساعة من الاستخدام النشط. مع scale-to-zero ونافذة توسع انخفاض مدتها 15 دقيقة، قد تكلف نقطة النهاية قليلة الاستخدام $5-15/شهر. يغطي الرصيد المجاني البالغ $30/شهر كثيرًا من التجارب.
هل يتوسع Modal إلى الصفر؟
نعم — هذه إحدى نقاط بيعه الرئيسية. عندما لا تصل أي طلبات خلال مدة scaledown_window، تُغلق الحاوية وتتوقف عن الدفع. الطلب التالي يُشغّل بداية باردة (عادةً 2-10 ثوانٍ حسب حجم النموذج وما إذا كنت تستخدم --enforce-eager).
هل يمكنني نشر Llama 3.1 أو Mistral على Modal؟
بالتأكيد. استبدل الثابت MODEL_NAME بأي نموذج يدعمه vLLM: meta-llama/Llama-3.1-8B-Instruct، أو mistralai/Mistral-7B-Instruct-v0.3، أو مئات أخرى على Hugging Face. للنماذج الأكبر من 70B، غيّر N_GPU إلى 2 واستخدم gpu="H100:2".
كيف تقارن البدايات الباردة مع RunPod؟
البدايات الباردة في Modal عادةً 2-4 ثوانٍ للحاوية نفسها، بالإضافة إلى وقت تحميل النموذج. مع أوزان النموذج المخزَّنة مؤقتًا في Volume و--enforce-eager مُفعَّلًا، تتوقع 10-30 ثانية إجمالًا لنموذج 7-8B. تتراوح البدايات الباردة اللامركزية في RunPod من أقل من 200 ميلي ثانية (مخزَّنة) إلى 6-12 ثانية للحاويات الأكبر — رغم أن نموذج always-on يتجنب البدايات الباردة كليًا.
هل نقطة نهاية vLLM في Modal متوافقة فعلًا مع OpenAI؟
نعم. ينفّذ vLLM نفس نقاط النهاية /v1/chat/completions، و/v1/completions، و/v1/models التي تستخدمها OpenAI. يمكنك توجيه SDK Python الرسمي openai إلى رابط Modal الخاص بك وسيعمل فورًا. البث والاستدعاء الوظيفي ووضع JSON يعملون جميعًا.
هل أحتاج إلى GPU على جهازي المحلي؟
لا. جهازك المحلي يُشغّل Modal CLI فقط. كل عمل GPU يحدث على البنية التحتية السحابية لـ Modal. يمكنك النشر من Chromebook إذا أردت.
كيف أضيف المصادقة إلى نقطة النهاية الخاصة بي؟
نقاط نهاية الويب في Modal عامة افتراضيًا. للإنتاج، أضف فحص مفتاح API بسيط في كود تطبيقك، أو استخدم ميزات مصادقة الويب المدمجة في Modal. يمكنك أيضًا إعداد طبقة proxy باستخدام بوابة LLM تتعامل مع المصادقة وتحديد المعدل والتوجيه.
ما الفرق بين modal serve وmodal deploy؟
modal serve ينشئ نقطة نهاية مؤقتة تُعاد تحميلها عند تعديل الكود — مثالية للتطوير. modal deploy ينشئ نقطة نهاية دائمة جاهزة للإنتاج برابط URL ثابت. استخدم serve أثناء التكرار، وdeploy عندما تكون مستعدًا للشحن.
هل يمكنني استخدام SGLang بدلًا من vLLM؟
نعم. توثيق Modal يتضمن أمثلة SGLang إلى جانب vLLM. يميل SGLang إلى وجود عبء أقل لأعباء العمل الثقيلة على جانب فك الترميز والنماذج الأصغر. vLLM أفضل عمومًا لأعباء العمل المختلطة ذات التحميل المسبق الثقيل. كلاهما ينتج نقاط نهاية متوافقة مع OpenAI.
كيف يقارن هذا بالنشر على Railway أو Render؟
منصات مثل Railway وRender وFly.io رائعة لتطبيقات الويب، لكنها لا تُقدّم مثيلات GPU. Modal مُصمَّمة خصيصًا لأعباء عمل GPU بفواتير بالثانية والتوسع التلقائي. إذا كنت بحاجة إلى تقديم نموذج LLM، فإن Modal (أو RunPod) هي الأداة الصحيحة — منصات PaaS التقليدية لا تستطيع فعل ذلك.