ما هو Docker Compose؟ ولماذا نحتاجه لتشغيل المشاريع المعقدة؟
ماذا سنتعلم اليوم؟ سنتعلم كيفية استخدام Docker Compose لتشغيل تطبيق ويب معقد يتكون من عدة خدمات مترابطة، مما يبسط عملية النشر والإدارة بشكل كبير. سنقوم ببناء تطبيق Flask بسيط يتصل بقاعدة بيانات PostgreSQL.
الخطوة 1: بناء صورة تطبيق الويب (Dockerfile وملفات التطبيق)
لبدء مشروعنا، نحتاج أولاً إلى تطبيق ويب بسيط وصورة Docker خاصة به. سنقوم بإنشاء تطبيق Flask يتصل بقاعدة بيانات PostgreSQL. سيتكون التطبيق من ثلاثة ملفات: app.py (تطبيق Flask)، requirements.txt (التبعيات)، و Dockerfile (لإنشاء صورة Docker للتطبيق).
أولاً: ملف app.py (تطبيق Flask)
هذا هو تطبيق Flask الذي سيحاول الاتصال بقاعدة بيانات PostgreSQL وعرض إصدارها.
from flask import Flask
import os
import psycopg2
app = Flask(__name__)
# الحصول على تفاصيل الاتصال بقاعدة البيانات من متغيرات البيئة
DB_HOST = os.environ.get('DB_HOST', 'db') # 'db' هو اسم خدمة قاعدة البيانات في docker-compose
DB_NAME = os.environ.get('POSTGRES_DB', 'mydatabase')
DB_USER = os.environ.get('POSTGRES_USER', 'user')
DB_PASSWORD = os.environ.get('POSTGRES_PASSWORD', 'password')
@app.route('/')
def hello_world():
try:
# محاولة الاتصال بقاعدة البيانات
conn = psycopg2.connect(host=DB_HOST, database=DB_NAME, user=DB_USER, password=DB_PASSWORD)
cur = conn.cursor()
cur.execute("SELECT version();") # تنفيذ استعلام للحصول على إصدار قاعدة البيانات
db_version = cur.fetchone()[0]
cur.close()
conn.close()
return f"Hello from Flask! Connected to PostgreSQL: {db_version}"
except Exception as e:
# في حالة فشل الاتصال، عرض رسالة خطأ
return f"Hello from Flask! Could not connect to database: {e}"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000) # تشغيل التطبيق على جميع الواجهات والمنفذ 5000
ثانياً: ملف requirements.txt (تبعيات بايثون)
يحتوي هذا الملف على المكتبات التي يحتاجها تطبيق Flask للعمل، بما في ذلك مكتبة الاتصال بـ PostgreSQL.
Flask==2.3.3
psycopg2-binary==2.9.9
ثالثاً: ملف Dockerfile (بناء صورة التطبيق)
يستخدم هذا الملف لإنشاء صورة Docker لتطبيق Flask الخاص بنا. سيتم بناء هذه الصورة بواسطة Docker Compose.
# استخدم صورة بايثون الرسمية كصورة أساسية، وهي نسخة خفيفة من دبيان
FROM python:3.9-slim-buster
# قم بتعيين دليل العمل داخل الحاوية إلى /app
WORKDIR /app
# انسخ ملفات المتطلبات أولاً لتسريع عملية البناء (استفادة من طبقات الكاش في Docker)
COPY requirements.txt .
# قم بتثبيت التبعيات المحددة في requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# انسخ باقي ملفات التطبيق إلى دليل العمل داخل الحاوية
COPY . .
# افتح المنفذ الذي يستمع عليه التطبيق (منفذ Flask الافتراضي هو 5000)
EXPOSE 5000
# الأمر الافتراضي لتشغيل التطبيق عند بدء تشغيل الحاوية
CMD ["python", "app.py"]
ملاحظة تقنية: استخدامWORKDIRيحدد الدليل الافتراضي الذي ستنفذ منه الأوامر اللاحقة. نسخrequirements.txtوتثبيتها قبل نسخ باقي الملفات يسمح لـ Docker بتخزين هذه الخطوة مؤقتًا (caching) إذا لم تتغير التبعيات، مما يسرع عمليات البناء اللاحقة.
الخطوة 2: تعريف الخدمات في Docker Compose (ملف docker-compose.yml - الخدمات الأساسية)
الآن سنبدأ بإنشاء ملف docker-compose.yml، وهو قلب Docker Compose. سنقوم بتعريف خدمتين: web لتطبيق Flask و db لقاعدة بيانات PostgreSQL.
version: '3.8' # تحديد إصدار صيغة Docker Compose المستخدمة
services: # قسم لتعريف الخدمات التي يتكون منها تطبيقنا
web: # تعريف خدمة تطبيق الويب (Flask)
build: . # بناء الصورة لهذه الخدمة من Dockerfile الموجود في الدليل الحالي
ports:
- "5000:5000" # ربط المنفذ 5000 من الحاوية بالمنفذ 5000 على الجهاز المضيف
environment: # متغيرات البيئة التي ستكون متاحة داخل حاوية الويب
POSTGRES_DB: mydatabase # اسم قاعدة البيانات
POSTGRES_USER: user # اسم المستخدم لقاعدة البيانات
POSTGRES_PASSWORD: password # كلمة المرور لقاعدة البيانات
DB_HOST: db # اسم خدمة قاعدة البيانات (سيتعرف Docker Compose على 'db' كاسم مضيف)
depends_on: # تحديد أن خدمة الويب تعتمد على خدمة قاعدة البيانات
- db # هذا يضمن بدء خدمة 'db' قبل خدمة 'web'
db: # تعريف خدمة قاعدة البيانات (PostgreSQL)
image: postgres:13 # استخدام صورة PostgreSQL الرسمية بالإصدار 13 من Docker Hub
environment: # متغيرات البيئة لتهيئة قاعدة بيانات PostgreSQL
POSTGRES_DB: mydatabase
POSTGRES_USER: user
POSTGRES_PASSWORD: password
ملاحظة تقنية: استخدام depends_on في Docker Compose لا يضمن أن الخدمة التابعة قد أصبحت جاهزة تمامًا للاستقبال الاتصالات، بل يضمن فقط بدء تشغيل الحاويات بالترتيب. في بيئات الإنتاج، قد تحتاج إلى آليات تحقق إضافية (مثل health checks) لضمان أن قاعدة البيانات قد بدأت بالفعل وتستقبل الاتصالات قبل أن يحاول تطبيق الويب الاتصال بها.
الخطوة 3: ربط الخدمات وإدارة البيانات (ملف docker-compose.yml - الشبكات والمجلدات)
لضمان استمرارية بيانات قاعدة البيانات حتى بعد إيقاف الحاويات، ولضمان أن الخدمات يمكن أن تتحدث مع بعضها البعض، سنضيف تعريف المجلدات (volumes) للبيانات الدائمة.
version: '3.8'
services:
web:
build: .
ports:
- "5000:5000"
environment:
POSTGRES_DB: mydatabase
POSTGRES_USER: user
POSTGRES_PASSWORD: password
DB_HOST: db
depends_on:
- db
db:
image: postgres:13
environment:
POSTGRES_DB: mydatabase
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes: # تعريف مجلد للحفاظ على بيانات قاعدة البيانات بشكل دائم
- db-data:/var/lib/postgresql/data # ربط المجلد المسمى 'db-data' بالمسار الافتراضي لبيانات PostgreSQL داخل الحاوية
volumes: # قسم لتعريف المجلدات الدائمة (named volumes) التي يمكن للخدمات استخدامها
db-data: # تعريف مجلد باسم 'db-data'
ملاحظة تقنية: المجلدات المسماة (Named Volumes) هي الطريقة المفضلة لإدارة البيانات الدائمة في Docker. يقوم Docker بإنشاء وإدارة هذه المجلدات على نظام الملفات المضيف، مما يضمن أن البيانات لا تزال موجودة حتى لو تم حذف حاوية قاعدة البيانات وإعادة إنشائها. هذا أمر بالغ الأهمية لأي خدمة تتطلب استمرارية البيانات.
الكود النهائي الكامل
فيما يلي جميع الملفات التي نحتاجها لتشغيل مشروعنا بواسطة Docker Compose:
app.py
from flask import Flask
import os
import psycopg2
app = Flask(__name__)
DB_HOST = os.environ.get('DB_HOST', 'db')
DB_NAME = os.environ.get('POSTGRES_DB', 'mydatabase')
DB_USER = os.environ.get('POSTGRES_USER', 'user')
DB_PASSWORD = os.environ.get('POSTGRES_PASSWORD', 'password')
@app.route('/')
def hello_world():
try:
conn = psycopg2.connect(host=DB_HOST, database=DB_NAME, user=DB_USER, password=DB_PASSWORD)
cur = conn.cursor()
cur.execute("SELECT version();")
db_version = cur.fetchone()[0]
cur.close()
conn.close()
return f"Hello from Flask! Connected to PostgreSQL: {db_version}"
except Exception as e:
return f"Hello from Flask! Could not connect to database: {e}"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
requirements.txt
Flask==2.3.3
psycopg2-binary==2.9.9
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"]
docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "5000:5000"
environment:
POSTGRES_DB: mydatabase
POSTGRES_USER: user
POSTGRES_PASSWORD: password
DB_HOST: db
depends_on:
- db
db:
image: postgres:13
environment:
POSTGRES_DB: mydatabase
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
كيفية تشغيل المشروع
لتشغيل هذا المشروع، تأكد من وجود Docker و Docker Compose مثبتين على جهازك. ثم اتبع الخطوات التالية:
- قم بإنشاء مجلد جديد للمشروع.
- داخل هذا المجلد، أنشئ الملفات الأربعة المذكورة أعلاه (
app.py،requirements.txt،Dockerfile،docker-compose.yml) واملأها بالمحتوى الموضح. - افتح سطر الأوامر (Terminal/CMD) وانتقل إلى المجلد الذي يحتوي على هذه الملفات.
- نفذ الأمر التالي لبناء الصور وتشغيل الخدمات في الخلفية (detached mode):
- إذا أردت رؤية سجلات الخدمات، يمكنك استخدام:
- لإيقاف الخدمات وحذف الحاويات والشبكات، نفذ:
- إذا أردت حذف المجلدات الدائمة أيضًا (وبالتالي بيانات قاعدة البيانات)، استخدم:
docker compose up -d
docker compose logs -f
docker compose down
docker compose down --volumes
النتيجة المتوقعة
بعد تنفيذ الأمر docker compose up -d بنجاح، ستتمكن من فتح متصفح الويب الخاص بك والوصول إلى العنوان التالي:
يجب أن تشاهد رسالة مشابهة لهذه:
Hello from Flask! Connected to PostgreSQL: PostgreSQL 13.12 (Debian 13.12-1.pgdg120+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
هذا يؤكد أن تطبيق Flask الخاص بك يعمل بنجاح ويتصل بقاعدة بيانات PostgreSQL التي تم تشغيلها وإدارتها بواسطة Docker Compose. إذا كان هناك خطأ في الاتصال، فستظهر رسالة خطأ توضح المشكلة.