تشغيل تطبيق Python داخل Docker من الصفر
يعمل التطبيق على جهاز المطور ثم يفشل على جهاز آخر بسبب اختلاف إصدار Python أو المكتبات أو إعدادات النظام. يحاول Docker تقليل هذا النوع من المفاجآت بوضع التطبيق واعتماداته داخل صورة يمكن تشغيلها في بيئات مختلفة. سننشئ في المقال تطبيق Flask صغيراً، ونكتب Dockerfile، ونبني الصورة، ثم نشغل الحاوية مع تمرير المنفذ. لن يحل Docker كل مشكلات النشر، لكنه يقدم طريقة واضحة لتثبيت البيئة وتكرارها. كما سنشير إلى ممارسات مهمة مثل عدم وضع الأسرار داخل الصورة وتقليل الملفات التي تدخل في عملية البناء.
الفكرة الأساسية
الصورة في Docker قالب ثابت يحتوي على نظام الملفات والتبعيات اللازمة، بينما الحاوية عملية تعمل من تلك الصورة. يقرأ Dockerfile التعليمات بالتسلسل، ويستفيد من الطبقات التي لم تتغير لتسريع البناء. يحتاج تطبيق Flask داخل الحاوية إلى الاستماع على 0.0.0.0 بدلاً من localhost حتى يصبح متاحاً من خارج الحاوية. يربط الأمر -p منفذ الجهاز بمنفذ التطبيق، لكنه لا يعني أن التطبيق أصبح آمناً أو جاهزاً للإنتاج من تلقاء نفسه.
مثال برمجي عملي
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 5000
CMD ["python", "app.py"]
شرح المثال
يستخدم Dockerfile صورة Python صغيرة نسبياً، ثم يحدد مجلد العمل داخل الحاوية. نسخ requirements.txt قبل بقية الكود يسمح بإعادة استخدام طبقة تثبيت المكتبات عندما يتغير app.py فقط. بعد ذلك ينسخ ملف التطبيق، ويعلن المنفذ المتوقع، ويحدد الأمر الذي يبدأ العملية. في app.py يجب أن يستخدم Flask app.run(host="0.0.0.0", port=5000) حتى يستقبل الاتصالات القادمة من واجهة Docker. لا تعتمد على EXPOSE لفتح المنفذ وحده؛ الربط الفعلي يتم عند التشغيل.
أنشئ requirements.txt يحتوي على Flask، ثم نفذ docker build -t tasks-api . لبناء الصورة. شغلها بالأمر docker run --rm -p 5000:5000 tasks-api، وافتح localhost:5000 من الجهاز. استخدم docker logs لمتابعة الرسائل، وdocker ps لرؤية الحاويات التي تعمل. عند إضافة مكتبة، عدل requirements ثم أعد البناء. في المشاريع الأكبر استخدم compose لتعريف التطبيق وقاعدة البيانات والشبكة في ملف واحد قابل للتكرار.
طريقة العمل قبل كتابة الكود
قبل فتح محرر النصوص، اكتب النتيجة التي تريد الوصول إليها وحدد المدخلات والمخرجات. يساعد هذا التمرين على كشف الحالات الغامضة مبكراً، مثل قيمة ناقصة أو قائمة فارغة أو طلب لا يصل إلى الخادم. لا تحتاج إلى مخطط كبير للمثال التعليمي، لكنك تحتاج إلى أسماء واضحة وخطوات يمكن اختبارها واحدة بعد أخرى. عندما تتغير الفكرة أثناء الكتابة، عدل التصميم قبل إضافة شروط متفرعة يصعب تتبعها.
قسّم المشكلة إلى أجزاء صغيرة، واجعل كل جزء مسؤولاً عن قرار واحد قدر الإمكان. يمكن أن تكون الأجزاء دوال، أو مكونات، أو طبقات منفصلة بحسب التقنية. لا يعني التقسيم إنشاء ملفات كثيرة، بل يعني أن تعرف أين تبحث عندما يحدث الخطأ. سجّل الافتراضات المهمة بجملة قصيرة، مثل أن القائمة مرتبة أو أن السعر غير سالب، ثم تحقق منها في المكان المناسب بدلاً من الاعتماد على الذاكرة.
اختبار المثال في حالات مختلفة
لا تختبر المسار الطبيعي فقط. جرّب مدخلاً فارغاً، وقيمة أكبر من المتوقع، وعنصراً غير موجود، وطلباً يصل من دون البيانات المطلوبة. الحالات الحدية تكشف غالباً مشكلات الفهارس والأنواع وترتيب التنفيذ. اكتب النتيجة المتوقعة قبل تشغيل الكود، ثم قارنها بالنتيجة الفعلية. إذا كان الاختبار يمر بالصدفة، أضف شرطاً أو اختباراً أوضح، ولا تكتفِ بطباعة قيمة تبدو منطقية.
- تحقق من المدخلات قبل استخدامها في الحساب أو التخزين.
- أعد رسالة مفهومة ورمزاً مناسباً عند وقوع الخطأ.
- اجعل الاختبارات قابلة للإعادة من دون اعتماد على شبكة أو وقت متغير.
- راجع أثر التغيير على الأجزاء التي تستدعي الدالة أو المكون.
ملاحظات تتعلق بجودة الكود
تظهر جودة الحل في التفاصيل الصغيرة: اسم يشرح الغرض، ودالة لا تجمع مهاماً بعيدة، ورسالة خطأ لا تترك القارئ في حيرة. لا تحاول اختصار كل سطر، فالكود المقروء أفضل من تعبير قصير يحتاج إلى شرح طويل. وفي الوقت نفسه، لا تكرر القاعدة نفسها في أماكن كثيرة؛ انقلها إلى موضع واحد عندما يكون ذلك أوضح. راجع الملف بعد أن يعمل، لأن أول نسخة تركز عادةً على الوصول إلى النتيجة أكثر من قابلية الصيانة.
احتفظ بالإعدادات التي تختلف بين جهاز وآخر خارج الكود، ولا تضع كلمات مرور أو مفاتيح خاصة في المستودع. استخدم سجلات مناسبة أثناء التطوير، ثم راجع ما ينبغي حجبه في بيئة التشغيل. إذا تعامل البرنامج مع بيانات المستخدم، فافصل بين ما يحتاجه التطبيق وما يمكن الاحتفاظ به. هذه الممارسات لا تخص لغة واحدة، بل تقلل المشكلات عندما يكبر المشروع أو يعمل عليه أكثر من شخص.
متى تعرف أن الحل يحتاج إلى تطوير؟
يحتاج المثال التعليمي إلى طبقات إضافية عندما يدخل في نظام حقيقي: قاعدة بيانات، مستخدمون متعددون، مراقبة، اختبارات، وصلاحيات. لا تضف هذه الأجزاء قبل معرفة المشكلة التي تحلها، لكن لا تنقل الكود التجريبي إلى الإنتاج كما هو. راقب حجم البيانات، وعدد الطلبات، ومصدر المدخلات، وما إذا كان الفشل يجب أن يعيد العملية أو يوقفها. اكتب قرارك في مستند صغير أو تعليق يشرح السبب، حتى لا يضطر الفريق إلى تخمينه لاحقاً.
إذا وجدت أن الخطأ يتكرر في أكثر من مكان، فابحث عن قاعدة مشتركة. وإذا أصبح التعديل في ملف صغير يؤثر في ملفات كثيرة، فراجع حدود المسؤوليات. لا توجد بنية واحدة صحيحة لكل مشروع، لكن توجد أسئلة تساعدك على اختيار بنية مناسبة: من يملك البيانات؟ من يغيرها؟ ماذا يحدث عند الفشل؟ وكيف يمكن اختبار الجزء من دون تشغيل النظام كله؟
أخطاء ينبغي تجنبها
لا تنسخ مجلد المشروع كله إذا كان يحتوي على .venv أو أسرار، واستخدم .dockerignore لتقليل السياق. لا تضع مفاتيح API في Dockerfile لأن الطبقات قد تبقى محفوظة. من الأخطاء أيضاً تشغيل التطبيق بحساب root من دون حاجة، أو ربط قاعدة البيانات بمجلد مؤقت يجعل البيانات تختفي عند حذف الحاوية. افصل الإعدادات عن الصورة باستخدام متغيرات البيئة، وراجع السجلات بدلاً من تخمين سبب فشل التشغيل.
خطوة تالية مناسبة
بعد تشغيل المثال، أضف مستخدماً غير root، واستخدم فحصاً صحياً، وجرّب volume لحفظ البيانات. إذا كان التطبيق يحتاج قاعدة بيانات، اجعل كل خدمة في حاوية منفصلة ولا تضع قاعدة البيانات داخل صورة التطبيق. عند النشر، لا تستخدم خادم التطوير المرفق بـ Flask، بل خادم WSGI مناسباً وإعدادات مراقبة وسجلات واضحة.
فصل إعدادات التشغيل عن الصورة
لا تضع مفاتيح الخدمة أو كلمات المرور داخل Dockerfile، لأن الصورة قد تنتقل إلى سجل عام أو تبقى في تاريخ البناء حتى بعد حذف السطر من النسخة الحالية. استخدم متغيرات البيئة عند التشغيل، واكتب ملفاً نموذجياً يوضح أسماء المتغيرات من دون قيمها الحقيقية. راجع أيضاً المستخدم الذي تعمل به العملية داخل الحاوية؛ فالتشغيل بصلاحيات مرتفعة يزيد أثر أي خطأ في التطبيق. وفي التطبيقات التي تكتب ملفات، حدد مجلداً واضحاً واربطه بمجلد دائم عند الحاجة. هذه التفاصيل تبدو خارج المثال الأول، لكنها تفرق بين حاوية تصلح للتجربة وحاوية يمكن لفريق الاعتماد عليها في بيئة اختبار أو إنتاج.
فحص الحاوية بعد التشغيل
بعد تشغيل الحاوية، راجع السجلات وتأكد من أن المنفذ منشور بالطريقة الصحيحة. جرّب إيقافها ثم تشغيلها من جديد، ولاحظ هل يعود التطبيق إلى حالة سليمة من دون خطوات يدوية. هذه التجربة تكشف الفرق بين إعداد يعمل مرة واحدة وإعداد يمكن تكراره. احتفظ بأوامر البناء والتشغيل في README، وحدد إصدار Python والحزم حتى لا يختلف سلوك الصورة بين جهاز وآخر.
الخلاصة
بعد تشغيل المثال، أضف مستخدماً غير root، واستخدم فحصاً صحياً، وجرّب volume لحفظ البيانات. إذا كان التطبيق يحتاج قاعدة بيانات، اجعل كل خدمة في حاوية منفصلة ولا تضع قاعدة البيانات داخل صورة التطبيق. عند النشر، لا تستخدم خادم التطوير المرفق بـ Flask، بل خادم WSGI مناسباً وإعدادات مراقبة وسجلات واضحة. يوضح هذا الموضوع كيف يتحول مفهوم نظري إلى خطوات يمكن تشغيلها وفحصها.
ابدأ بتطبيق المثال على ملف صغير، ثم غيّر مدخلاً واحداً وراقب النتيجة. بعد ذلك أضف حالة فشل واكتب اختباراً لها، ثم انقل الفكرة إلى مشروعك الحقيقي بحذر. عندما تفهم سبب كل خطوة، ستستطيع تغيير الأدوات أو اللغة من دون فقدان المفهوم. البرمجة تتحسن بالمحاولات القصيرة والمراجعة المستمرة، لا بنسخ كود طويل من دون معرفة ما الذي يحميه أو ما الذي قد يكسره.