لماذا نجمع هذا المكدس على خادم VPS
GitHub Actions وواجهات برمجة نماذج اللغة السحابية يشتركان في نفس العيب: نقل المعالجة إلى الخارج. في الحالة الأولى، يعمل الكود المصدري على عُمَّال مشتركين؛ وفي الثانية، يُرسل سياق طلباتك إلى طرف ثالث. بالنسبة لوكالة أو مطور مستقل يدير كودًا لعملاء بموجب اتفاقية سرية، لا يكفي إصلاح أحدهما دون الآخر.
Gitea يتضمن محرك إجراءات متوافقًا مع GitHub Actions بدءًا من الإصدار 1.19. Ollama يعرض واجهة REST محلية على شبكة Docker الداخلية. حاوية act_runner تقرأ ملفات .yml الخاصة بك تمامًا كما يفعل GitHub — وتستدعي Ollama بدلًا من واجهة برمجة سحابية. يعمل المكدس بأكمله بأمر docker compose up -d ولا يُنشئ أي حركة مرور خارجية نحو واجهات النماذج الكبيرة.
الفوائد العملية لهذه البنية
- سرية الكود التامة: تعمل عُمَّال التشغيل على خادم VPS الخاص بك، ولا يغادر الكود المستنسخ شبكة Docker الداخلية قط.
- لا يُرسَل أي توكن للخارج: يُقدّم Ollama الاستدلال محليًا؛ لا توجد أي طلبات نحو openai.com أو anthropic.com.
- تكلفة ثابتة وقابلة للتنبؤ: لا فوترة لكل تشغيل للـworkflow، ولا فوترة لكل توكن — سطر ميزانية شهري واحد بغض النظر عن الحمل.
- إعادة استخدام workflows GitHub Actions: Gitea Actions متوافق مع صيغة
.github/workflows/؛ خطوط أنابيبك تنتقل دون إعادة كتابة. - تبديل النماذج بحرية: Qwen2.5-Coder أو DeepSeek-Coder أو Llama 3.1 أو Mistral — أمر
ollama pullواحد لتغيير النموذج دون لمس خط الأنابيب. - تدقيق وتتبع كاملان: سجلات عُمَّال التشغيل، وسجل النماذج المُحمَّلة، وسجلات nginx تبقى على بنيتك التحتية وتعود إليك.
المتطلبات المسبقة للأجهزة والبرامج
القيد الحاسم هو ذاكرة الوصول العشوائي: يجب أن يتسع النموذج بالكامل في الذاكرة ليبقى الاستدلال سريع الاستجابة. يتطلب نموذج 7B المكمَّم بصيغة Q4_K_M حوالي 5 إلى 6 غيغابايت من الذاكرة؛ بإضافة Gitea (أقل من 100 ميغابايت عند الخمول) والـrunner، احسب 8 غيغابايت كحد أدنى لنموذج 7B و16 غيغابايت موصى بها لنموذج 13B.
بالنسبة للمعالج، يكفي اثنان من vCPU لـGitea والـrunners؛ الاستدلال على المعالج فقط لنموذج 7B يستغرق بضع ثوانٍ لكل رد، وهو مقبول لمراجعة الكود الآلية. يُقلل وحدة معالجة الرسومات المخصصة هذا التأخير إلى أقل من ثانية، لكنها غير مطلوبة للاستخدام في خط أنابيب CI.
البرامج المطلوبة على الخادم: Docker Engine وDocker Compose v2، واسم نطاق يشير إلى خادمك (لشهادات TLS لـGitea)، والمنافذ 3000 (Gitea) و11434 (Ollama، الشبكة الداخلية فقط) متاحة.
النماذج الموصى بها حسب ذاكرة RAM المتاحة
- Qwen2.5-Coder:7B (صيغة Q4_K_M، ~5 GB من RAM) — نسبة جودة/موارد متوازنة لمراجعات الكود في 2026؛ يفهم السياق الممتد وأعراف الأسلوب البرمجي.
- DeepSeek-Coder:6.7B (صيغة Q4، ~4.5 GB من RAM) — بديل مدمج حين تكون الذاكرة محدودة؛ دقيق في Python وJavaScript والـdiffs الأقل من 200 سطر.
- Llama 3.1:8B (صيغة Q4_K_M، ~5.5 GB من RAM) — نموذج متعدد اللغات للاستخدام العام، مفيد حين تمزج مشاريعك الكود بالتوثيق بلغات متعددة.
- Mistral:7B (صيغة Q4_K_M، ~4.5 GB من RAM) — ردود قصيرة ومباشرة، مثالي لملخص الـdiff بدلًا من تحليل مفصّل.
- الانتقال إلى نموذج 13B — على خادم VPS بـ16 GB أو أكثر، تُحسّن
codellama:13bأوqwen2.5-coder:14bالمراجعات على الـdiffs الكبيرة؛ يرتفع وقت الاستدلال على المعالج من ~5 ث إلى ~15 ث لكل استدعاء.
نشر مكدس Gitea + Ollama + runner
إنشاء هيكل الملفات
أنشئ دليل مشروع وملف docker-compose.yml يُعلن عن ثلاثة خدمات: gitea وollama وrunner. ضع جميع volumes في مجلد فرعي data/ لتسهيل النسخ الاحتياطي.
mkdir -p ~/gitea-stack/data/{gitea,ollama,runner}
cd ~/gitea-stackكتابة ملف docker-compose.yml
يُعلن الملف عن شبكة داخلية ai-net تتواصل عبرها الخدمات الثلاث. لا يُعرض Ollama على المضيف: فقط الـrunner يمكنه الوصول إليه عبر شبكة Docker.
cat > docker-compose.yml << 'EOF'
version: "3.8"
networks:
ai-net:
driver: bridge
volumes:
gitea-data:
ollama-data:
runner-data:
services:
gitea:
image: gitea/gitea:latest
restart: unless-stopped
networks: [ai-net]
ports:
- "3000:3000"
- "2222:22"
volumes:
- gitea-data:/data
environment:
- GITEA__server__DOMAIN=git.yourdomain.com
- GITEA__server__ROOT_URL=https://git.yourdomain.com
- GITEA__actions__ENABLED=true
ollama:
image: ollama/ollama:latest
restart: unless-stopped
networks: [ai-net]
volumes:
- ollama-data:/root/.ollama
runner:
image: gitea/act_runner:latest
restart: unless-stopped
networks: [ai-net]
volumes:
- runner-data:/data
- /var/run/docker.sock:/var/run/docker.sock
environment:
- GITEA_INSTANCE_URL=http://gitea:3000
- GITEA_RUNNER_REGISTRATION_TOKEN=${RUNNER_TOKEN}
- GITEA_RUNNER_NAME=local-runner
- OLLAMA_URL=http://ollama:11434
EOFتشغيل Gitea واسترداد توكن الـrunner
شغِّل Gitea أولًا فقط لإتمام الإعداد الأولي وتوليد رمز تسجيل الـrunner.
docker compose up -d giteaافتح http://<ip-الخادم>:3000 في متصفحك، أكمل معالج الإعداد، ثم اذهب إلى إعدادات الموقع ← الإجراءات ← عُمَّال التشغيل لإنشاء رمز التسجيل. دوِّن هذا الرمز — ستحتاجه في الخطوة التالية.
تشغيل Ollama وتنزيل نموذج
شغِّل Ollama ونزِّل نموذجك. لخط أنابيب مراجعة الكود، يُقدم qwen2.5-coder:7b أو deepseek-coder:6.7b نسبة جيدة بين الجودة والموارد.
docker compose up -d ollama
# تنزيل نموذج للكود
docker exec gitea-stack-ollama-1 ollama pull qwen2.5-coder:7b
# التحقق من أن الـAPI يستجيب على الشبكة الداخلية
docker run --rm --network gitea-stack_ai-net curlimages/curl \
http://ollama:11434/api/tagsاستجابة JSON تُدرج النماذج المتاحة — تأكيد أن API الخاص بـOllama في متناول شبكة Docker الداخلية.
تسجيل الـrunner وتشغيل المكدس بالكامل
أنشئ ملف .env بالرمز المسترد في الخطوة 3، ثم شغِّل الـrunner.
echo "RUNNER_TOKEN=رمزك_هنا" > .env
docker compose up -d runner
# التحقق من تسجيل الـrunner بنجاح
docker compose logs runner | tail -20تحقق في إعدادات الموقع ← الإجراءات ← عُمَّال التشغيل من ظهور عامل التشغيل بحالة نشط.
إنشاء workflow يستدعي Ollama
في مستودع Gitea، أنشئ .gitea/workflows/review.yml. يستنسخ الـworkflow الكود، ويستدعي نقطة نهاية /api/chat الخاصة بـOllama عبر curl، وينشر النتيجة كتعليق على طلب السحب.
cat > .gitea/workflows/review.yml << 'EOF'
name: AI Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: مراجعة الكود عبر Ollama
run: |
DIFF=$(git diff HEAD~1 --unified=5 | head -200)
curl -s ${OLLAMA_URL:-http://ollama:11434}/api/chat \
-H 'Content-Type: application/json' \
-d "{\"model\": \"qwen2.5-coder:7b\", \"stream\": false,
\"messages\": [{\"role\": \"user\",
\"content\": \"راجع هذا الـdiff: $DIFF\"}]}" \
| jq -r '.message.content'
EOFادفع هذا الملف إلى forge Gitea الخاص بك — يكتشفه الـrunner ويُشغِّل المهمة على الشبكة الداخلية، دون أي وصول إلى الإنترنت.
وضع Gitea خلف وكيل عكسي HTTPS
ضع Gitea خلف nginx أو Caddy مع شهادة Let's Encrypt لعرض الـforge على git.yourdomain.com. يجب ألا يكون المنفذ 3000 في متناول الخارج مباشرةً بعد الآن.
# مثال nginx مبسط
cat > /etc/nginx/sites-available/gitea.conf << 'EOF'
server {
listen 443 ssl;
server_name git.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/git.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/git.yourdomain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
EOF
nginx -t && systemctl reload nginxيبقى Ollama حصريًا على شبكة Docker الداخلية ولا يُعرض أبدًا على الإنترنت.
التحقق من عدم خروج أي توكن من خادمك
للتأكد من أن استدعاءات النماذج تبقى على خادم VPS الخاص بك، التقط حركة الشبكة الصادرة أثناء تشغيل workflow: tcpdump -i eth0 -n 'dst port 443 and dst host openai.com'. صفر حزم مُلتقطة يُؤكد العزل. يمكنك أيضًا فحص سجلات Ollama (docker compose logs ollama): يُسجَّل كل طلب استدلال مع عنوان مصدره — يجب أن يكون دائمًا عنوان IP لشبكة Docker الداخلية، وليس عنوانًا خارجيًا.
مقارنة التكلفة: VPS ذاتي مقابل API سحابي
يعالج نموذج 7B ما بين 30 000 و50 000 توكن في الدقيقة على اثنين من vCPU. تستهلك مراجعة diff من 150 سطرًا نحو 800 توكن (الطلب + الرد). في 1 000 مراجعة شهرية — حجم واقعي لفريق مؤلف من خمسة مطورين — يبلغ الإجمالي 800 000 توكن.
على صعيد السحابة، يُفاتر gpt-4o-mini بـ0.15 دولار لكل مليون توكن مُدخَل و0.60 دولار لكل مليون توكن مُخرَج (أسعار OpenAI في سبتمبر 2026). في 800 000 توكن: نحو 0.70 دولار شهريًا. Claude Haiku في النطاق ذاته. الحجة المالية إذن ضعيفة عند الأحجام المعتدلة.
ما يصمد هو حجة السرية: diffs كود العملاء، ومفاتيح API الظاهرة في رسائل الخطأ، وأسماء المتغيرات التجارية — كل ذلك يغادر شبكتك مع كل استدعاء لواجهة خارجية. على خادم ServOrbit VPS بـ8 GB من RAM (نحو 15 يورو شهريًا)، لا يُضاف أي تكلفة إضافية للاستدلال المحلي، ولا يغادر أي توكن خادمك.
المزيد: مراقبة خط الأنابيب وإثراؤه
بمجرد تشغيل المكدس الأساسي، يُعدّ توسيعان أمرًا طبيعيًا. الأول هو إضافة Langfuse كخدمة خامسة: تتبع كل استدعاء للنموذج (النموذج، المدة، التوكنات المستهلكة، النتيجة)، مما يتيح قياس جودة المراجعات، واكتشاف تراجعات النموذج، ومقارنة النتائج على مجموعة بياناتك الحقيقية قبل تغيير الإصدار. الثاني هو توازي عُمَّال التشغيل: يمكن لـact_runner ثانٍ بتسمية مختلفة (مثلًا gpu) استهداف عقدة ذات وصول لـGPU للمهام الثقيلة، بينما تستمر المهام الخفيفة (الفحص، اختبارات الوحدة) على الـrunner الأساسي المعتمد على المعالج.
للنسخ الاحتياطي، تحتوي volumes الخاصة بـgitea-data وollama-data على المستودعات والنماذج المُنزَّلة على التوالي. يكفي لقطة يومية من هذين الـvolume لاستعادة المكدس بالكامل في أقل من عشر دقائق. بما أن نماذج Ollama يمكن إعادة تنزيلها عند الطلب من السجل العام، فإن gitea-data فقط هو الحيوي فعلًا لاستمرارية الخدمة والحفاظ على سجل Git.