مشكلة "الكود يعمل على جهازي فقط" وكيف يحلها Docker نهائياً؟
سنتعلم اليوم كيف يحل Docker مشكلة "الكود يعمل على جهازي فقط" من خلال توحيد بيئات التشغيل. سنقوم ببناء تطبيق ويب بسيط باستخدام Dockerfile ثم نوسعه باستخدام Docker Compose لإدارة خدمات متعددة.
فهم المشكلة: "يعمل على جهازي فقط"
تنشأ مشكلة "يعمل على جهازي فقط" عندما لا يتمكن تطبيق برمجي من العمل في بيئات مختلفة عن تلك التي تم تطويره فيها. غالبًا ما يكون السبب هو اختلاف إصدارات نظام التشغيل، المكتبات، التبعيات، أو حتى متغيرات البيئة. هذه المشكلة تؤدي إلى إهدار الوقت في تصحيح الأخطاء وتعيق عمليات النشر.
الحل الذي يقدمه Docker
يقوم Docker بحل هذه المشكلة عن طريق تغليف التطبيق وجميع تبعياته (مثل المكتبات، ملفات الإعداد، وحتى نظام التشغيل المصغر) في وحدة مستقلة تسمى "الحاوية" (Container). تضمن هذه الحاويات أن التطبيق سيعمل بنفس الطريقة تمامًا بغض النظر عن البيئة الأساسية، مما يوفر بيئة تشغيل موحدة ومتسقة.
الخطوة 1: إعداد تطبيق Python بسيط (المشكلة المحتملة)
لنبدأ بتطبيق Flask بسيط. سنحتاج إلى ملفين: app.py و requirements.txt.
app.py:
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "مرحباً بك في تطبيق Flask!"
if __name__ == '__main__':
# تشغيل التطبيق على جميع الواجهات المتاحة والمنفذ 5000
app.run(host='0.0.0.0', port=5000)
requirements.txt:
Flask==2.3.2
ملاحظة تقنية: لتشغيل هذا التطبيق تقليدياً، ستحتاج إلى تثبيت Python و Flask يدوياً. قد تواجه مشكلات إذا كان لديك إصدارات أخرى من Python أو إذا لم يتم تثبيت Flask بشكل صحيح، وهذا هو جوهر مشكلة "يعمل على جهازي فقط".
الخطوة 2: بناء Dockerfile - توحيد بيئة التطبيق
الآن، لنقم بإنشاء Dockerfile. هذا الملف يصف كيفية بناء صورة Docker التي ستحتوي على تطبيقنا وبيئته.
Dockerfile:
# Dockerfile
# استخدام صورة بايثون رسمية كقاعدة (إصدار 3.9 خفيف مبني على Debian Buster)
FROM python:3.9-slim-buster
# تعيين مجلد العمل داخل الحاوية ليكون /app
WORKDIR /app
# نسخ ملف المتطلبات إلى مجلد العمل داخل الحاوية
COPY requirements.txt .
# تثبيت التبعيات المحددة في requirements.txt
# --no-cache-dir يقلل من حجم الصورة النهائية عن طريق عدم تخزين الكاش
RUN pip install --no-cache-dir -r requirements.txt
# نسخ باقي ملفات التطبيق (في هذه الحالة، app.py) إلى مجلد العمل
COPY . .
# تعريف المنفذ الذي يستمع عليه التطبيق داخل الحاوية
EXPOSE 5000
# الأمر الذي يتم تنفيذه عند بدء تشغيل الحاوية
CMD ["python", "app.py"]
شرح: كل سطر في Dockerfile يمثل طبقة يتم بناؤها فوق الطبقة السابقة. هذا يضمن بيئة متسقة وقابلة للتكرار.
FROMيحدد الصورة الأساسية،WORKDIRيحدد المجلد الرئيسي،COPYينسخ الملفات،RUNينفذ الأوامر،EXPOSEيعلن عن المنافذ، وCMDيحدد الأمر الافتراضي لتشغيل التطبيق.
الخطوة 3: بناء صورة Docker وتشغيل الحاوية
الآن بعد أن أصبح لدينا Dockerfile، يمكننا بناء صورة Docker ثم تشغيل حاوية منها.
أوامر البناء والتشغيل:
# بناء صورة Docker وتسميتها 'my-flask-app'
docker build -t my-flask-app .
# تشغيل حاوية من الصورة 'my-flask-app'
# -p 5000:5000 يربط المنفذ 5000 على جهازك بالمنفذ 5000 داخل الحاوية
docker run -p 5000:5000 my-flask-app
ملاحظة: الأمر
docker build -t my-flask-app .يخبر Docker ببناء صورة من Dockerfile في المجلد الحالي (.) وتسميتهاmy-flask-app. الأمرdocker run -p 5000:5000 my-flask-appيقوم بإنشاء وتشغيل حاوية جديدة من الصورة التي بنيناها، مع توجيه حركة المرور من المنفذ 5000 على جهازك إلى المنفذ 5000 داخل الحاوية.
الخطوة 4: Docker Compose - إدارة تطبيقات متعددة الخدمات
في التطبيقات الحقيقية، غالبًا ما نحتاج إلى أكثر من خدمة واحدة (مثل تطبيق ويب وقاعدة بيانات أو نظام تخزين مؤقت). Docker Compose يسمح لنا بتعريف وتشغيل تطبيقات Docker متعددة الحاويات.
لنقم بتعديل تطبيق Flask الخاص بنا ليتصل بخدمة Redis لتتبع عدد الزيارات.
app.py (محدث):
# app.py (محدث)
from flask import Flask
from redis import Redis
app = Flask(__name__)
# الاتصال بخدمة Redis. اسم المضيف 'redis' هو اسم الخدمة في docker-compose.yml
redis = Redis(host='redis', port=6379)
@app.route('/')
def hello():
# زيادة عداد الزيارات في Redis
visits = redis.incr('visits')
return f"مرحباً بك! لقد قمت بزيارة هذه الصفحة {visits} مرة."
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
requirements.txt (محدث):
Flask==2.3.2
redis==4.5.1
الآن سنقوم بإنشاء ملف docker-compose.yml لتعريف خدمتي الويب و Redis.
docker-compose.yml:
# docker-compose.yml
version: '3.8' # تحديد إصدار Docker Compose
services:
web: # تعريف خدمة الويب (تطبيق Flask)
build: . # بناء الصورة من Dockerfile الموجود في نفس المجلد
ports:
- "5000:5000" # ربط المنفذ 5000 على المضيف بالمنفذ 5000 في الحاوية
depends_on:
- redis # تحديد أن خدمة الويب تعتمد على خدمة Redis (تضمن بدء Redis أولاً)
redis: # تعريف خدمة Redis
image: "redis:alpine" # استخدام صورة Redis الرسمية والخفيفة
أوامر التشغيل باستخدام Docker Compose:
# تشغيل جميع الخدمات المعرفة في docker-compose.yml في الخلفية
docker-compose up -d
# لإيقاف وإزالة الخدمات
docker-compose down
شرح:
docker-compose up -dيقوم ببناء الصور (إذا لزم الأمر)، وإنشاء الحاويات، وربطها بالشبكة، وتشغيلها جميعًا في الخلفية. هذا يضمن أن تطبيقك بالكامل، بجميع خدماته، يعمل كبيئة موحدة.
الكود النهائي الكامل
Dockerfile:
# Dockerfile
FROM python:3.9-slim-buster
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
app.py (النهائي):
# app.py
from flask import Flask
from redis import Redis
app = Flask(__name__)
redis = Redis(host='redis', port=6379)
@app.route('/')
def hello():
visits = redis.incr('visits')
return f"مرحباً بك! لقد قمت بزيارة هذه الصفحة {visits} مرة."
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
requirements.txt (النهائي):
Flask==2.3.2
redis==4.5.1
docker-compose.yml (النهائي):
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "5000:5000"
depends_on:
- redis
redis:
image: "redis:alpine"
النتيجة المتوقعة
بعد تشغيل docker-compose up -d، يمكنك فتح متصفح الويب الخاص بك والوصول إلى العنوان http://localhost:5000.
- في المرة الأولى التي تزور فيها الصفحة، سترى:
مرحباً بك! لقد قمت بزيارة هذه الصفحة 1 مرة. - عند تحديث الصفحة، سيزداد العدد:
مرحباً بك! لقد قمت بزيارة هذه الصفحة 2 مرة.وهكذا.
هذا يثبت أن تطبيق Flask وخدمة Redis يعملان معًا بسلاسة داخل بيئة Docker الموحدة، بغض النظر عن نظام التشغيل أو التبعيات المثبتة على جهازك المضيف. لقد قمت بحل مشكلة "يعمل على جهازي فقط" بشكل نهائي!