AzLearn

النشر للإنتاج

Deploying to Production

مشروع ~30 دقيقة

النشر للإنتاج — Deploying to Production

كتابة كود يعمل على جهازك الشخصي شيء — ونشره بشكل موثوق يخدم آلاف المستخدمين شيء آخر. في هذا الدرس ستتعلم الأدوات والأنماط التي يستخدمها المطورون المحترفون لنقل تطبيقات Python من بيئة التطوير إلى الإنتاج.

الخطوة الأولى: requirements.txt

requirements.txt هو ملف نصي يُدرج فيه كل الحزم التي يحتاجها تطبيقك مع إصداراتها. هذا ما يضمن أن بيئة الإنتاج (أو جهاز زميلك) تُثبّت نفس الإصدارات تماماً التي طوّرت عليها.

# requirements.txt
flask==3.0.2
gunicorn==21.2.0
python-dotenv==1.0.1
sqlalchemy==2.0.28
requests==2.31.0

أوامر أساسية:

# تثبيت كل الحزم من الملف
pip install -r requirements.txt

# توليد الملف من البيئة الحالية
pip freeze > requirements.txt

# تثبيت حزمة واحدة
pip install flask==3.0.2

نصيحة متقدمة: افصل بين requirements للتطوير والإنتاج:

requirements.txt         # الإنتاج فقط
requirements-dev.txt     # التطوير: pytest، black، flake8...
# requirements-dev.txt
-r requirements.txt      # يشمل الإنتاج
pytest==8.1.0
black==24.3.0
flake8==7.0.0

متغيرات البيئة — Environment Variables

أسوأ خطأ يمكن أن يرتكبه مطور هو وضع كلمات المرور أو مفاتيح API مباشرة في الكود. إذا رفعت الكود على GitHub، الجميع يقرأ أسرارك.

الحل: متغيرات البيئة

import os

# ❌ لا تفعل هذا أبداً — Never do this
DATABASE_URL = "postgresql://user:password123@localhost/mydb"
SECRET_KEY = "super-secret-key-12345"
API_KEY = "sk-abc123..."

# ✅ اقرأ من البيئة — Read from environment
DATABASE_URL = os.environ.get("DATABASE_URL")
SECRET_KEY = os.environ.get("SECRET_KEY")
API_KEY = os.environ.get("API_KEY")

قيم افتراضية للتطوير:

import os

# قيمة افتراضية للتطوير، يجب تعيينها في الإنتاج
# Default for dev, must be set in production
DATABASE_URL = os.environ.get("DATABASE_URL", "sqlite:///local.db")
DEBUG = os.environ.get("DEBUG", "false").lower() == "true"
PORT = int(os.environ.get("PORT", "8000"))

ملف .env للتطوير المحلي:

# .env (لا تضعه في git! — do not commit!)
DATABASE_URL=postgresql://user:pass@localhost/mydb
SECRET_KEY=dev-secret-key
DEBUG=true
PORT=8000
# قراءة .env في التطوير — Load .env in development
from dotenv import load_dotenv  # pip install python-dotenv
load_dotenv()

import os
DATABASE_URL = os.environ.get("DATABASE_URL")

تذكر: .env في ملف .gitignore دائماً.

أنماط الإعداد الإنتاجي — Config Patterns

النمط الأفضل هو فصل الإعداد في كلاس مستقل:

import os
from dataclasses import dataclass

@dataclass
class Config:
    """إعداد التطبيق — Application configuration"""

    # قاعدة البيانات — Database
    database_url: str
    
    # الأمان — Security
    secret_key: str
    
    # الخادم — Server
    port: int
    debug: bool
    
    @classmethod
    def from_env(cls) -> "Config":
        """اقرأ الإعداد من متغيرات البيئة — Read config from environment"""
        return cls(
            database_url=os.environ["DATABASE_URL"],  # مطلوب — Required
            secret_key=os.environ["SECRET_KEY"],       # مطلوب — Required
            port=int(os.environ.get("PORT", "8000")),
            debug=os.environ.get("DEBUG", "false").lower() == "true",
        )
    
    @property
    def is_production(self) -> bool:
        """هل نحن في الإنتاج؟ — Are we in production?"""
        return not self.debug


# الاستخدام — Usage
config = Config.from_env()

if config.is_production:
    print("وضع الإنتاج")
else:
    print("وضع التطوير")

gunicorn — خادم الإنتاج

gunicorn (Green Unicorn) هو خادم HTTP للإنتاج. Flask وFastAPI مدمجان بخوادم تطوير فقط — لا تُستخدم في الإنتاج.

# تثبيت — Install
pip install gunicorn

# تشغيل بسيط — Simple run
gunicorn app:app

# بأربعة عمال — With 4 workers
gunicorn app:app -w 4

# مع خيارات كاملة — With full options
gunicorn app:app \
    --workers 4 \
    --bind 0.0.0.0:8000 \
    --timeout 30 \
    --access-logfile /var/log/gunicorn/access.log \
    --error-logfile /var/log/gunicorn/error.log

قاعدة عدد العمال: (2 × عدد الأنوية) + 1

import multiprocessing

# 4 أنوية → 9 عمال
workers = (2 * multiprocessing.cpu_count()) + 1

لماذا عمال متعددة؟ كل عامل هو عملية Python مستقلة. عامل واحد يعمل على طلب واحد في كل مرة. 4 عمال = 4 طلبات متزامنة. بهذا تتجاوز قيود GIL لأن كل عامل مترجم Python منفصل.

Dockerfile — حاوية التطبيق

Docker يُغلّف تطبيقك مع كل تبعياته في وحدة يمكن تشغيلها في أي مكان:

# Dockerfile

# بيئة Python رسمية خفيفة — Official lightweight Python base
FROM python:3.12-slim

# تعيين مجلد العمل — Set working directory
WORKDIR /app

# نسخ ملف المتطلبات أولاً (استغلال cache) — Copy requirements first (cache optimization)
COPY requirements.txt .

# تثبيت المتطلبات — Install dependencies
RUN pip install --no-cache-dir -r requirements.txt

# نسخ الكود — Copy application code
COPY . .

# فتح المنفذ — Expose port
EXPOSE 8000

# أمر التشغيل — Run command
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:8000", "--workers", "4"]

بناء وتشغيل الصورة:

# بناء — Build
docker build -t myapp:latest .

# تشغيل — Run
docker run -p 8000:8000 \
    -e DATABASE_URL=postgresql://... \
    -e SECRET_KEY=production-key \
    myapp:latest

ملف .dockerignore لتسريع البناء:

.git
.env
__pycache__
*.pyc
*.pyo
venv/
.venv/
*.egg-info/

قائمة جاهزية الإنتاج — Production Checklist

قبل أي نشر، تحقق من هذه النقاط:

☐ متغيرات البيئة جاهزة (لا أسرار في الكود)
☐ requirements.txt مُحدَّث بإصدارات محددة
☐ DEBUG=False في الإنتاج
☐ قاعدة بيانات الإنتاج معزولة عن التطوير
☐ gunicorn أو uvicorn بدلاً من خادم التطوير
☐ HTTPS مُفعَّل
☐ تسجيل الأخطاء (logging) مُعَدّ
☐ اختبارات تجتاز 100%
☐ نسخة احتياطية لقاعدة البيانات
☐ مراقبة الخادم (monitoring) مُفعّلة

التحديات العملية

التحدي الأول: قراءة متغير بيئة بقيمة افتراضية

تحدي — Challenge

التحدي الثاني: كلاس الإعداد

تحدي — Challenge

التحدي الثالث: كشف وضع التشغيل

تحدي — Challenge

التحدي الرابع: توليد محتوى requirements.txt

تحدي — Challenge

التحدي الخامس: دالة فحص الجاهزية

تحدي — Challenge