حاوية TaskFlow ونشره
التعليمات
الهدف
تعبئة واجهة TaskFlow API للنشر في الإنتاج. ستُنشئ ملف Dockerfile متعدد المراحل وتكوين Docker Compose للإنتاج وخط أنابيب CI/CD مع GitHub Actions وإعدادات تطبيق مُحصنة للإنتاج.
الجزء 1: Dockerfile (25 نقطة)
أنشئ ملف Dockerfile في جذر المشروع ببناء متعدد المراحل.
المتطلبات
مرحلة البناء:
- استخدم
python:3.12-slimكصورة أساسية (إصدار مُثبت، وليسlatest) - ثبّت تبعيات النظام اللازمة لتجميع حزم Python (
build-essential،libpq-dev) - انسخ
requirements.txtأولاً، ثم ثبّت التبعيات مع--no-cache-dirو--prefix=/install - هذا الترتيب يضمن عمل التخزين المؤقت لطبقات Docker -- طبقات التبعيات تُعاد بناؤها فقط عند تغيير
requirements.txt
مرحلة الإنتاج:
- استخدم
python:3.12-slimمرة أخرى كقاعدة نظيفة - ثبّت مكتبات النظام للتشغيل فقط (
libpq5،curl) - أنشئ مستخدماً ومجموعة غير جذريين باسم
taskflow - انسخ حزم Python المُثبتة من مرحلة البناء باستخدام
COPY --from=builder - انسخ كود التطبيق
- عيّن الملكية لمستخدم
taskflowوانتقل إليه بـUSER taskflow - أضف
HEALTHCHECKيفحص نقطة/health - اعرض المنفذ 8000
- استخدم Gunicorn مع عمال Uvicorn كنقطة دخول:
gunicorn app.main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
مثال على الهيكل
# المرحلة 1: البناء
FROM python:3.12-slim AS builder
WORKDIR /app
# تثبيت تبعيات البناء
# نسخ وتثبيت تبعيات Python
# المرحلة 2: الإنتاج
FROM python:3.12-slim AS production
# تثبيت تبعيات التشغيل فقط
# إنشاء مستخدم غير جذري
# النسخ من مرحلة البناء
# نسخ كود التطبيق
# تعيين المستخدم وفحص الصحة والعرض والأمر
الجزء 2: Docker Compose للإنتاج (25 نقطة)
أنشئ docker-compose.prod.yml مع ثلاث خدمات.
المتطلبات
خدمة API:
- البناء من Dockerfile مع استهداف مرحلة
production - ربط المنفذ 8000
- تحميل البيئة من
.env.production - الاعتماد على
dbوredisمعcondition: service_healthy - تعيين حدود الموارد: 1 CPU، 512MB ذاكرة
- تعيين
restart: unless-stopped - الاتصال بشبكتي
externalوinternal
خدمة PostgreSQL:
- استخدام صورة
postgres:18 - حفظ البيانات بحجم مُسمى
pgdataمركب على/var/lib/postgresql/data - تكوين اسم قاعدة البيانات والمستخدم وكلمة المرور عبر متغيرات البيئة
- إضافة فحص صحة باستخدام
pg_isready - الاتصال بشبكة
internalفقط
خدمة Redis:
- استخدام صورة
redis:7-alpine - تفعيل استمرارية AOF مع
--appendonly yes - تعيين الحد الأقصى للذاكرة إلى 128MB مع سياسة إخلاء
allkeys-lru - حفظ البيانات بحجم مُسمى
redisdataمركب على/data - إضافة فحص صحة باستخدام
redis-cli ping - الاتصال بشبكة
internalفقط
الشبكات:
external: شبكة جسر افتراضية (API يمكن الوصول إليه من الخارج)internal: تعيينinternal: trueلعزل db وredis عن المضيف
الأحجام:
pgdataوredisdataكأحجام مُسماة
الجزء 3: GitHub Actions CI/CD (25 نقطة)
أنشئ .github/workflows/ci.yml مع خط أنابيب CI/CD.
المتطلبات
المشغل: عند push إلى فرع main وعند pull_request إلى main
مهمة الاختبار:
- تعمل على
ubuntu-latest - تشغيل PostgreSQL 18 وRedis 7 كحاويات خدمة مع فحوصات صحة
- الخطوات:
actions/checkout@v4actions/setup-python@v5مع Python 3.12- تثبيت التبعيات من
requirements.txt - تشغيل المُدقق:
ruff check . - تشغيل الاختبارات:
pytest --cov=app tests/
- تمرير
DATABASE_URLوREDIS_URLوSECRET_KEYكمتغيرات بيئة لخطوة الاختبار
مهمة البناء:
- تعمل فقط عند الدفع إلى
main(وليس عند طلبات السحب) - تعتمد على نجاح مهمة الاختبار (
needs: test) - الخطوات:
actions/checkout@v4- بناء صورة Docker مُوسمة بـ SHA الخاص بالإيداع
الجزء 4: تكوين الإنتاج (25 نقطة)
أنشئ أو حدّث الملفات التالية في التطبيق.
4أ. تكوين Gunicorn
أنشئ gunicorn.conf.py في جذر المشروع:
import multiprocessing
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "uvicorn.workers.UvicornWorker"
bind = "0.0.0.0:8000"
accesslog = "-"
errorlog = "-"
loglevel = "info"
4ب. وسيط ترويسات الأمان
أنشئ وسيطاً في app/middleware/security.py يُضيف هذه الترويسات
لكل استجابة:
X-Content-Type-Options: nosniffX-Frame-Options: DENYStrict-Transport-Security: max-age=31536000; includeSubDomainsX-XSS-Protection: 1; mode=blockReferrer-Policy: strict-origin-when-cross-origin
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next) -> Response:
response = await call_next(request)
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["Strict-Transport-Security"] = (
"max-age=31536000; includeSubDomains"
)
response.headers["X-XSS-Protection"] = "1; mode=block"
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
return response
4ج. تكوين CORS
في app/main.py، كوّن CORS بأصول صريحة للإنتاج
(لا تستخدم * كأصل شامل أبداً في الإنتاج):
from fastapi.middleware.cors import CORSMiddleware
if settings.is_production:
allowed_origins = ["https://taskflow.example.com"]
else:
allowed_origins = ["http://localhost:3000"]
app.add_middleware(
CORSMiddleware,
allow_origins=allowed_origins,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["Authorization", "Content-Type"],
)
4د. إعدادات مبنية على البيئة
أنشئ app/config.py باستخدام Pydantic Settings لتحميل والتحقق من
جميع التكوينات من متغيرات البيئة:
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
redis_url: str
secret_key: str
environment: str = "development"
api_v1_prefix: str = "/api/v1"
@property
def is_production(self) -> bool:
return self.environment == "production"
model_config = {"env_file": ".env"}
settings = Settings()
4هـ. إصدارات API
يجب تركيب جميع الموجهات تحت بادئة /api/v1/:
from app.config import settings
app.include_router(tasks_router, prefix=settings.api_v1_prefix)
app.include_router(auth_router, prefix=settings.api_v1_prefix)
ما يجب تقديمه
يجب أن يحتوي تقديمك على 7 أقسام ملفات في المحرر أدناه. يبدأ كل قسم بعنوان # FILE N:.
تلميحات
- اختبر Dockerfile محلياً بـ
docker build -t taskflow .قبل التسليم - شغّل
docker compose -f docker-compose.prod.yml upللتحقق من بدء جميع الخدمات ونجاح فحوصات الصحة - ترتيب تعليمات
COPYفي Dockerfile مهم للتخزين المؤقت -- ضع الملفات نادرة التغيير أولاً - لسير عمل CI، استخدم مفتاح
servicesتحت المهمة لتشغيل PostgreSQL وRedis