العودة إلى المدونة
أغسطس 19, 2026الأدلة

كيفية تشغيل تطبيقات Python بـ Gunicorn وNginx على VPS

انشر تطبيق Flask أو FastAPI على Ubuntu مع virtualenv وخدمة Gunicorn عبر systemd ووكيل Nginx عكسي وTLS وملفات بيئة وتسجيل وقائمة تحقق لأخطاء 502 وحجم العمال.

كيفية تشغيل تطبيقات Python بـ Gunicorn وNginx على VPS

خادم Flask المدمج وuvicorn --reload للتطوير. على VPS عام تريد مدير عمليات يعيد تشغيل العمال ويربط بـ localhost ويجلس خلف وكيل عكسي يعالج TLS والعملاء البطيئين. Gunicorn هو خيار WSGI المعتاد لـ Flask وDjango. يستطيع FastAPI العمل تحت Gunicorn بفئة عامل Uvicorn. ينهي Nginx (أو Caddy) HTTPS ويحوّل إلى 127.0.0.1:8000.

يمر هذا الدليل على تخطيط يبقى بعد إعادة التشغيل: المشروع في /srv/app، وvirtualenv، و.env بأسرار، ووحدة gunicorn.service، وكتلة خادم Nginx، وLet's Encrypt، وملفات سجل تستطيع grep عليها فعلًا. سنتحدث أيضًا عن عدد العمال والمهل ولماذا 502 Bad Gateway ليست تقريبًا أبدًا «Nginx معطوب» — هي تقريبًا دائمًا Gunicorn غير شغال أو مربوط بالمقبس الخطأ أو ينهار عند الاستيراد. المثال يستخدم Flask مع ملاحظات لـ FastAPI حيث يختلف الأمر.

لماذا هذه الحزمة

يفرّع Gunicorn عمليات عمال مسبقًا. يعالج كل عامل طلبًا واحدًا في كل مرة ما لم تستخدم فئة عامل مختلفة. يخزّن Nginx العملاء البطيئين حتى لا يعلق العمال وهم يرسلون بايتات إلى شبكة جوال. يعيد systemd تشغيل التطبيق إن مات. معًا هذا ممل، وهو ما تريده في الثالثة صباحًا.

  • Gunicorn: نموذج عمليات WSGI/ASGI مستقر
  • systemd: بدء عند الإقلاع، إعادة تشغيل عند الانهيار، سجلات journald
  • Nginx: TLS وملفات ثابتة وحدود حجم الطلب وgzip
  • venv: يبقى Python النظام نظيفًا
  • ربط localhost: التطبيق غير قابل للوصول إلا عبر Nginx

المتطلبات

Python 3.10+ على Ubuntu 22.04/24.04 كافٍ. لا تشغّل pip كـ root إلى site-packages للنظام. تحتاج نطاقًا لـ TLS. إن فضّلت Caddy كوكيل فوحدة Gunicorn في هذه المقالة تبقى نفسها — يتغير إعداد الواجهة فقط (انظر مقال Caddy).

  • VPS بنظام Ubuntu 22.04 أو 24.04
  • تطبيقك مع requirements.txt أو ما يعادله
  • نقطة دخول WSGI (لـ Flask: app:app) أو ASGI (لـ FastAPI: app:app مع عمال uvicorn)
  • سجل A للنطاق لـ HTTPS

الخطوة 1: حزم النظام والمستخدم ودليل المشروع

أنشئ مستخدم نظام لا يستطيع تسجيل الدخول تفاعليًا، ويملك الشفرة، ويشغّل Gunicorn. تثبيت python3-venv وأدوات البناء يتجنب إخفاقات pip على حزم ما زالت تصرّف امتدادات C.

bash
ssh root@YOUR_VPS_IP
apt update && apt -y upgrade
apt -y install python3 python3-venv python3-pip python3-dev build-essential nginx curl

adduser --system --group --home /srv/app appuser
mkdir -p /srv/app
chown appuser:appuser /srv/app

# Copy your code (example):
# rsync -a --delete ./myproject/ appuser@YOUR_VPS_IP:/srv/app/

الخطوة 2: Virtualenv والتبعيات

أنشئ venv كـ appuser حتى تكون ملكية الملفات صحيحة. ثبّت الإصدارات في الإنتاج. بعد التثبيت تأكد أنك تستطيع استيراد التطبيق في gunicorn --check-config لمرة واحدة أو استيراد Python. أخطاء الاستيراد هنا هي الأخطاء نفسها التي تصبح 502 لاحقًا.

bash
sudo -u appuser -H bash -lc '
cd /srv/app
python3 -m venv /srv/app/venv
/srv/app/venv/bin/pip install --upgrade pip
/srv/app/venv/bin/pip install -r /srv/app/requirements.txt gunicorn
'

# Flask example check:
sudo -u appuser -H /srv/app/venv/bin/python -c "from app import app; print('import ok')"

الخطوة 3: ملف البيئة

لا تُضمّن SECRET_KEY أو عناوين قواعد البيانات في وحدة systemd بطريقة تنتهي في git مقروء للعالم. استخدم EnvironmentFile. chmod 640، المالك root، المجموعة appuser (أو المالك appuser إن فضّلت).

bash
cat >/srv/app/.env <<'EOF'
FLASK_ENV=production
SECRET_KEY=replace-with-openssl-rand-hex-32
DATABASE_URL=postgresql://app:password@127.0.0.1:5432/app
EOF
chown appuser:appuser /srv/app/.env
chmod 600 /srv/app/.env

الخطوة 4: خدمة Gunicorn عبر systemd

اربط بـ 127.0.0.1:8000 لا 0.0.0.0 ما لم يكن لديك سبب لتجاوز Nginx. عدد العمال غالبًا (2 × CPU) + 1 للعمال المتزامنين؛ على VPS بـ 2 vCPU هذا 5، وقد يكون أكثر من اللازم إن حمّل كل عامل نموذج تعلم آلي ثقيلًا — استخدم حينها 2–3. لـ FastAPI اضبط --worker-class uvicorn.workers.UvicornWorker وثبّت uvicorn. يجب أن تتجاوز المهل أبطأ طلب صادق لديك، لا 30 ثانية إن كان لديك تصديرات بدقيقتين.

bash
cat >/etc/systemd/system/gunicorn.service <<'EOF'
[Unit]
Description=Gunicorn for the web app
After=network.target

[Service]
User=appuser
Group=appuser
WorkingDirectory=/srv/app
EnvironmentFile=/srv/app/.env
ExecStart=/srv/app/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 --timeout 60 --access-logfile - --error-logfile - app:app
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now gunicorn
systemctl status gunicorn --no-pager
ss -tulpn | grep 8000

الخطوة 5: وكيل Nginx العكسي وTLS

يستمع Nginx على 80/443 ويوكّل إلى Gunicorn. client_max_body_size مهم للرفع. يجب أن يطابق proxy_read_timeout مهلة Gunicorn أو يتجاوزها. بعد عمل كتلة الخادم على HTTP أصدر شهادة بـ Certbot (أو حوّل الواجهة إلى Caddy). المقتطف أدناه HTTP فقط حتى تختبر؛ ثم شغّل Certbot الذي يستطيع تحرير الملف.

bash
cat >/etc/nginx/sites-available/app <<'EOF'
server {
    listen 80;
    server_name app.example.com;
    client_max_body_size 20m;

    location /static/ {
        alias /srv/app/static/;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 90s;
    }
}
EOF

ln -sf /etc/nginx/sites-available/app /etc/nginx/sites-enabled/app
nginx -t && systemctl reload nginx

ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw enable

apt -y install certbot python3-certbot-nginx
certbot --nginx -d app.example.com

الخطوة 6: الملفات الثابتة والأذونات وتفاصيل Flask/FastAPI

يجب أن يخدم Nginx الملفات الثابتة إن استطعت؛ أسرع من Gunicorn. Django collectstatic، أو Flask send_from_directory للتطبيقات الصغيرة، أو شبكة توصيل محتوى لاحقًا. يجب أن يستطيع appuser قراءة الشجرة. إن استخدمت FastAPI فثبّت uvicorn[standard] في venv وغيّر ExecStart لاستخدام UvicornWorker. إن استخدمت مقابس Unix بدل TCP فوجّه proxy_pass إلى المقبس وطابق الأذونات حتى يستطيع www-data الكتابة إليه.

bash
# FastAPI ExecStart example:
# ExecStart=/srv/app/venv/bin/gunicorn -k uvicorn.workers.UvicornWorker --workers 2 --bind 127.0.0.1:8000 app:app

# Unix socket variant:
# --bind unix:/run/gunicorn/app.sock
# Nginx: proxy_pass http://unix:/run/gunicorn/app.sock:

chown -R appuser:appuser /srv/app

العمال والذاكرة وإعادة التحميل دون توقف

عمال Gunicorn المتزامنون بسيطون ويكفيون لواجهات طلب/استجابة خفيفة على المعالج. إن احتجت كثيرًا من انتظار إدخال/إخراج بطيء متزامن ففكر في gevent أو عامل ASGI — قِس ولا تخمّن. يحمّل كل عامل تطبيقك؛ RAM ~= العمال مضروبًا في RSS التطبيق. VPS بـ 2 غيغابايت مع 8 عمال لتطبيق 300 ميغابايت سيتبادل ويشعر «ببطء عشوائي». يستطيع systemctl reload gunicorn (HUP) إعادة تشغيل العمال بالشفرة الجديدة إن نشرت الملفات في المكان؛ إعادة تشغيل كاملة أوضح عندما تتغير التبعيات.

bash
# After git pull / rsync of new code:
sudo -u appuser -H /srv/app/venv/bin/pip install -r /srv/app/requirements.txt
systemctl restart gunicorn
curl -I https://app.example.com/health

استكشاف 502 والانهيارات الصامتة

يعني 502 أن Nginx لم يستطع الحصول على استجابة صالحة من الأعلى. journalctl -u gunicorn -e هو الأمر الأول. الأسباب الشائعة: اسم module:app خاطئ، مفتاح .env مفقود، Postgres غير شغال، مربوط بـ 127.0.0.1 لكن Nginx على مضيف آخر، SELinux (نادر على Ubuntu)، أو التطبيق يستمع على IPv6 فقط. 504 مهلة. تحدث حلقات 301 عندما يعيد التطبيق التوجيه إلى HTTP بينما يُتجاهل X-Forwarded-Proto.

  • systemctl status gunicorn — هل هو نشط؟
  • journalctl -u gunicorn -n 100 — ImportError، بيئة مفقودة، قاعدة بيانات
  • curl -v http://127.0.0.1:8000/ من VPS — إن فشل هذا فـ Nginx بريء
  • nginx -t وerror.log — upstream prematurely closed connection
  • ss -tulpn | grep 8000 — لا شيء يستمع
  • القرص ممتلئ — ينهار العمال بطرق غامضة

الأمان

التطبيق لا يرتبط علنًا أبدًا. الأسرار تبقى في .env. أبقِ venv ونظام التشغيل مرقَّعين. لا تشغّل Gunicorn كـ root. إن عالجت تسجيلات دخول فاضبط ملفات تعريف الجلسة Secure وSameSite، وهيئ الإطار ليثق بـ X-Forwarded-Proto من Nginx فقط. حدّ معدل مسارات تسجيل الدخول في Nginx أو في التطبيق.

  • اربط 127.0.0.1 أو مقبس unix
  • chmod 600 .env
  • User= غير root في systemd
  • TLS عبر Certbot أو Caddy
  • عطّل وضع التصحيح وإعادة التحميل التلقائي في الإنتاج

نصائح

  • أضف مسار /health يتحقق من اتصال قاعدة البيانات لـ Compose وموازنات الحمل
  • اشحن الأصول الثابتة بترويسة cache-control في Nginx
  • استخدم عاملًا أو طابورًا منفصلًا (Redis + systemd) للبريد والمهام الثقيلة
  • ثبّت إصدارات gunicorn وuvicorn في requirements.txt
  • خذ لقطة قبل أول انتقال إنتاج

تطبيق Python على VPS جاهز للإنتاج عندما يعمل تحت Gunicorn كخدمة systemd، ويرتبط بـ localhost فقط، ويُوصل إليه عبر Nginx أو Caddy مع TLS. ضع الأسرار في EnvironmentFile، وحجّم العمال حسب RAM لا صيغة مقال مدونة، وصحّح 502 من سجل Gunicorn أولًا. بمجرد توثيق هذا المسار لمستودعك يصبح كل نشر لاحق rsync أو git pull وpip install وsystemctl restart gunicorn.