EduFlow

الوحدة 3 · بيئات محلية مع Docker Compose

الـ Volumes: بيانات تبقى

16 دقراءة 4 داختر بحكمة

الدرس في لمحة

احفظ بيانات Postgres عبر إعادات التشغيل باستخدام named volume، وجهّز قاعدة البيانات ببيانات أولية عبر سكربت تهيئة مربوط بـ bind mount، واعرف أي أوامر Compose تحفظ بياناتك وأيها يدمّرها.

  1. 1الـ Named volumes لملفات قاعدة البيانات
  2. 2الـ Bind mounts للملفات التي تعدّلها
  3. 3الـ tmpfs للبيانات المؤقتة
  4. 4الـ Volumes وإصدارات قاعدة البيانات
  5. 5أي mount لأي مهمة

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

في الأسبوع الذي أضفنا فيه Compose إلى أحد مشاريع Skylane، شغّل مطوّر docker compose down -v «لينظّف» قبل عرض تقديمي. فنظّف ثلاثة أيام من بيانات الاختبار المُدخلة بعناية. الـ containers مصمَّمة ليُتخلَّص منها، لذا يجب أن يعيش كل ما تريد الاحتفاظ به خارجها، ويجب أن تعرف بالضبط أي أمر يمحوه.

الـ Named volumes لملفات قاعدة البيانات

أضف volume إلى خدمة db وصرّح به في المستوى الأعلى:

compose.yaml
services:
  # api: …as before
  db:
    image: postgres:17
    environment:
      POSTGRES_USER: notes
      POSTGRES_PASSWORD: notes
      POSTGRES_DB: notes
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

الـ named volume (وحدة تخزين مسمّاة) تخزين ينشئه Docker ويديره. يضيف Compose اسم المشروع كبادئة له، فيصبح اسم هذا notes_pgdata. إنه ليس جزءًا من أي container، لذا فإن حذف db وإعادة إنشائه يتركان البيانات في مكانها:

Terminal
docker compose down        # containers and network removed, pgdata kept
docker compose up -d       # notes are still there
docker volume ls           # notes_pgdata
docker volume inspect notes_pgdata

الـ named volumes هي الخيار الافتراضي الصحيح لملفات قاعدة البيانات. يضبط Docker الملكية بشكل صحيح للـ container، ولا تواجه غرائب نظام ملفات المضيف، وعلى Docker Desktop هي أسرع بكثير من الـ bind mounts لأنها تعيش داخل آلة Linux الافتراضية.

الاستعادة هي الأنبوب نفسه في الاتجاه المعاكس: docker compose exec -T db psql -U notes notes < backup.sql. الخيار -T يعطّل الطرفية الوهمية (pseudo-TTY) حتى يمكن تمرير الملف إلى الداخل. جرّب ذلك مرة في وقت هادئ، حتى لا تكون أول استعادة لك في اليوم الذي تحتاج إليها فيه.

الـ Bind mounts للملفات التي تعدّلها

الـ bind mount يربط مسارًا على جهازك بداخل الـ container. التعديلات على أي جهة تظهر على الأخرى فورًا. هذا ما تريده للملفات التي تعيش في مستودعك، مثل سكربت المخطّط:

YAML
  db:
    image: postgres:17
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./db/init:/docker-entrypoint-initdb.d:ro

تشغّل image ‏Postgres كل ملف .sql و.sh في /docker-entrypoint-initdb.d، بترتيب الأسماء، فقط حين يكون مجلد البيانات فارغًا. في أول up على الإطلاق، ينشئ db/init/001-schema.sql الجدول notes. وفي كل تشغيل لاحق يُتجاهل المجلد، لأن الـ volume يحتوي أصلًا على قاعدة بيانات مهيّأة. حين تغيّر المخطّط بعد ذلك، اكتب migration يشغّلها التطبيق، أو امسح volume التطوير عن قصد.

اللاحقة :ro تجعل الـ mount للقراءة فقط داخل الـ container. أعطِ الـ containers أقل صلاحية تكفي.

للـ bind mounts عيبان مزعجان. على Linux تكون الملفات مملوكة لـ UID مستخدم المضيف، الذي قد لا يطابق المستخدم داخل الـ container، فتفشل الكتابة بـ EACCES. وعلى macOS وWindows يعبر الوصول إلى الملفات حدود الآلة الافتراضية فيكون أبطأ، ولهذا يجب ألا تُربط المجلدات الثقيلة مثل node_modules من المضيف أبدًا. ستستخدم أداة أفضل لتعديل الكود الحيّ، Compose watch، في الدرس 3.4.

الـ tmpfs للبيانات المؤقتة

الـ mount من نوع tmpfs يعيش في الذاكرة ويختفي حين يتوقّف الـ container. إنه مفيد للملفات المؤقتة التي لا تريدها على القرص أبدًا، أو لقاعدة بيانات اختبار تُحذف بعد الاستخدام حيث تهمّ السرعة أكثر من البقاء:

YAML
  db-test:
    image: postgres:17
    tmpfs:
      - /var/lib/postgresql/data

الـ Volumes وإصدارات قاعدة البيانات

الـ volume يخزّن صيغة قاعدة البيانات على القرص، وهي تخصّ إصدارًا رئيسيًا واحدًا. إذا رفعت postgres:17 إلى postgres:18 وأبقيت الـ volume نفسه، فسيرفض الخادم الجديد البدء على الملفات القديمة. كما أن image ‏Postgres 18 نقلت موقع البيانات الافتراضي: فهي تتوقّع mount واحدًا في /var/lib/postgresql (وتحفظ البيانات في مجلد فرعي يحمل رقم الإصدار) بدلًا من .../data. وإذا وجدت نقطة دخولها بيانات أو mount في المسار القديم، فإنها تتوقّف برسالة خطأ تشرح هذا التغيير. اقرأ ملاحظات الإصدار الخاصة بالـ image قبل رفع إصدار رئيسي.

أي mount لأي مهمة

الحاجة الاستخدام
ملفات قاعدة بيانات تصمد بعد إعادة التشغيل named volume
سكربتات بيانات أولية أو إعدادات من المستودع ‏bind mount مع :ro
مساحة مؤقتة سريعة تُحذف بعد الاستخدام tmpfs
الكود المصدري أثناء التطوير ‏Compose watch (الدرس 3.4)

بياناتك الآن تعيش أطول من الـ containers. لكن شغّل البيئة من البداية الباردة، وقد ترى الـ API يفشل في أول بضعة طلبات: إنه يبدأ قبل أن يصبح Postgres جاهزًا لقبول الاتصالات. ترتيب الإقلاع هو التالي.

أهم النقاط

  • الـ named volumes يديرها Docker وتعيش أطول من الـ containers؛ استخدمها لملفات قاعدة البيانات.
  • الـ bind mounts تربط مسارًا على المضيف بداخل الـ container؛ استخدمها للملفات التي تعدّلها على المضيف، مثل سكربتات البيانات الأولية، وركّبها للقراءة فقط متى استطعت.
  • الأمر docker compose down يحتفظ بالـ named volumes؛ أما docker compose down -v فيحذفها مع كل بياناتها.
  • السكربتات في /docker-entrypoint-initdb.d لا تعمل إلا حين يكون مجلد بيانات Postgres فارغًا، أي عند أول تشغيل على الإطلاق.
  • صيغة تخزين قاعدة البيانات على القرص مرتبطة بإصدارها الرئيسي؛ والترقية بين إصدارات Postgres الرئيسية تحتاج إلى dump واستعادة أو pg_upgrade، لا إلى tag جديد.

قراءات إضافية

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

تشغّل docker compose down ثم docker compose up -d. خدمة db تركّب الـ named volume ‏pgdata. ماذا يحدث لملاحظاتك؟

السؤال 2 من 4

تعدّل db/init/001-schema.sql لإضافة عمود، وتشغّل docker compose up -d، فلا يظهر العمود. لماذا؟

السؤال 3 من 4

أي mount يجب أن يحفظ بيانات Postgres في بيئة تطوير تريدها أن تصمد بعد إعادة التشغيل؟

السؤال 4 من 4

تغيّر image الـ db من postgres:17 إلى postgres:18 وتُبقي الـ volume ‏pgdata نفسه. ما الذي يجب أن تتوقّعه؟

تدريب

اختر بحكمة

اختر الـ mount المناسب

تراجع خيارات التخزين في بيئة Compose الخاصة بـ notes-api. اختر كل ربط صحيح بين الحاجة ونوع الـ mount.

اختر الـ mount المناسب