Docker والحاويات

وحدات التخزين والبيانات الدائمة

18 دقيقة الدرس 6 من 30

وحدات التخزين والبيانات الدائمة

طبقة الكتابة في الحاوية مؤقتة بطبيعتها. في اللحظة التي تُنفّذ فيها docker rm، يختفي كل بايت كُتب داخل تلك الطبقة نهائياً. بالنسبة لخوادم الويب عديمة الحالة، هذا ميزة — حاويات متطابقة بلا انجراف في الحالة. أما لأي شيء يُنتج بيانات دائمة — قواعد بيانات، ملفات مرفوعة، نقاط حفظ نماذج التعلم الآلي، سجلات التدقيق — فهو فخٌّ إنتاجي خطير. هذا الدرس يرسم الخط الفاصل بين وصلات الربط والأحجام المُسمّاة في Docker، ويشرح دورة حياة البيانات خلف كل منهما، ويستعرض الأنماط التي تستخدمها شركات من أمثال Google وNetflix للحفاظ على استقرار الحاويات ذات الحالة على نطاق واسع.

مشكلة طبقة الكتابة

تتألف صور Docker من طبقات للقراءة فقط مُكدَّسة عبر نظام ملفات موحّد (overlay2 على Linux الحديثة). عند بدء تشغيل حاوية، يُضيف Docker طبقة كتابة رفيعة واحدة في الأعلى. تذهب الكتابات إليها وحدها. يترتب على ذلك ثلاث نتائج:

  • فقدان البيانات عند الحذف. يحذف docker rm طبقة الكتابة بلا شرط.
  • لا مشاركة. حاويتان من الصورة ذاتها لكل منهما طبقة كتابة معزولة خاصة — لا يمكنهما رؤية كتابات بعضهما.
  • تأخر في الإدخال/الإخراج. آلية النسخ عند الكتابة في نظام الملفات الموحّد أبطأ من القرص الأصلي؛ تعاني عمليات الإدخال/الإخراج العشوائية المكثّفة كقواعد البيانات بشكل ملحوظ.

الحل هو تركيب تخزين من خارج الحاوية إلى مسار داخلها. يوفر Docker آليتين رئيسيتين: وصلات الربط (bind mounts) والأحجام المُسمّاة (named volumes). آلية ثالثة هي وصلات tmpfs التي تحتفظ بالبيانات في ذاكرة الوصول العشوائي وتُغطّى في نهاية الدرس.

Bind Mount vs Named Volume vs Writable Layer Container Image Layers (read-only) Writable Layer (ephemeral) /data (mount point) /host-files (mount point) Named Volume Managed by Docker Host Directory /srv/app on host Named Volume — Docker managed Bind Mount — host path Writable Layer — ephemeral
أنواع التخزين الثلاثة المتاحة للحاوية قيد التشغيل: طبقة الكتابة المؤقتة، والأحجام المُسمّاة التي يديرها Docker، ووصلات الربط بمسارات المضيف.

الأحجام المُسمّاة — الخيار الافتراضي في الإنتاج

الحجم المُسمّى هو جزء من تخزين المضيف يُنشئه Docker ويديره بنفسه، عادةً تحت /var/lib/docker/volumes/<name>/_data على Linux. تُشير إليه بالاسم لا بالمسار، ويتولى Docker إنشاءه وملكيته ودورة حياته بشكل مستقل عن أي حاوية بعينها.

# إنشاء حجم صريح (اختياري — يُنشئه Docker عند الاستخدام الأول) docker volume create pgdata # تشغيل Postgres مع تركيب الحجم في /var/lib/postgresql/data docker run -d \ --name pg \ -e POSTGRES_PASSWORD=secret \ -v pgdata:/var/lib/postgresql/data \ postgres:16 # فحص مكان البيانات الفعلي على المضيف docker volume inspect pgdata # سرد كل الأحجام docker volume ls # حذف حجم (مدمّر — البيانات تختفي نهائياً) docker volume rm pgdata
تبقى الأحجام المُسمّاة حتى بعد docker stop وdocker rm وحتى ترقيات الصورة. إنها الخيار الصحيح الافتراضي لأي قاعدة بيانات، أو مخزن مؤقت، أو مجلد رفع ملفات في الإنتاج.

تُوسّع برامج تشغيل الأحجام (Volume Drivers) هذا النموذج. يكتب المُشغّل الافتراضي local على قرص المضيف. في بيئات السحابة يمكن استبداله بمُشغّل يُركّب NFS أو AWS EFS أو Azure Files، مما يمنح الحاويات واجهة الحجم المُسمّى ذاتها فيما يكون الخلفية تخزيناً شبكياً مشتركاً:

# إنشاء حجم مدعوم بمشاركة NFS (يتطلب مكوّن مُشغّل NFS) docker volume create \ --driver local \ --opt type=nfs \ --opt o=addr=10.0.0.5,rw \ --opt device=:/exports/appdata \ nfs-appdata

وصلات الربط — للتطوير وحقن الإعدادات

تربط وصلة الربط مساراً مطلقاً على المضيف مباشرةً داخل الحاوية. لا يتولى Docker أي إدارة — يجب أن يوجد المجلد مسبقاً، وتحصل الحاوية على وصول كامل للقراءة والكتابة لما فيه.

# ربط مجلد العمل الحالي بـ /app داخل الحاوية docker run -d \ --name app-dev \ -v "$(pwd)":/app \ -p 3000:3000 \ node:20-alpine \ npm run dev # حقن ملف إعداد للقراءة فقط (العلامة :ro) docker run -d \ --name nginx \ -v /etc/my-nginx/nginx.conf:/etc/nginx/nginx.conf:ro \ -p 80:80 \ nginx:1.27
لا تستخدم وصلات الربط لبيانات قاعدة البيانات في الإنتاج. يختلف تخطيط مسارات المضيف والصلاحيات وتسميات selinux/apparmor بين الأجهزة. وصلة ربط تعمل على جهازك المحلي ستفشل على عامل CI أو VM مختلف. الأحجام المُسمّاة قابلة للنقل؛ وصلات الربط ليست كذلك.

حالات الاستخدام الإنتاجية المقبولة لوصلات الربط هي: حقن شهادات TLS من مدير الأسرار الذي يكتب في مسار مضيف، وتركيب /var/run/docker.sock داخل حاويات عوامل CI (بحذر شديد)، ومشاركة نتائج البناء بين حاويات في خط أنابيب متعدد المراحل يعمل على المضيف ذاته.

دورة حياة البيانات: الحجم مقابل الحاوية

فهم ما الذي يتحكم في دورة حياة البيانات هو النموذج الذهني الأساسي. دورة حياة الحجم المُسمّى مفصولة تماماً عن أي حاوية:

  • حذف الحاوية ← يبقى الحجم.
  • ترقية الحاوية (صورة جديدة) ← تركيب الحجم ذاته ← تنتقل البيانات تلقائياً.
  • يمكن لحاويتين تركيب الحجم ذاته في آنٍ واحد — مفيد للنسخ المتماثلة للقراءة أو ملحقات شحن السجلات، لكنه خطير إن كتبتا الملفات ذاتها.
  • حذف الحجم صراحةً بـ docker volume rm ← البيانات تختفي نهائياً.
شغّل docker volume prune بشكل دوري على خوادم البناء لاستعادة مساحة القرص من الأحجام غير المرتبطة بأي حاوية. على خوادم الإنتاج، لا تُنظّف أبداً بدون نسخة احتياطية مؤكدة — الأحجام المجهولة من الحاويات القديمة تُحذف أيضاً.

الأحجام المجهولة ونمط --volumes-from

عند تصريح Dockerfile بتعليمة VOLUME أو استخدام -v /some/path بدون اسم، يُنشئ Docker حجماً مجهولاً — حجم مُسمّى بـ UUID كاسم له. يعمل مثل الحجم المُسمّى تماماً لكنه أصعب في الإدارة لأن الاسم غير واضح. تجنّب الأحجام المجهولة في أي شيء تُدير عليه عمليات؛ أعطِ الأحجام دائماً أسماء صريحة.

تُركّب العلامة القديمة --volumes-from كل أحجام حاوية واحدة في أخرى. كانت شائعة في نمط حاوية البيانات (قبل عصر Compose) وتظهر أحياناً في ملحقات النسخ الاحتياطي:

# نمط النسخ الاحتياطي: تركيب أحجام من حاوية قيد التشغيل في busybox # وبث أرشيف tar إلى stdout مُضغوطاً بـ gzip على المضيف docker run --rm \ --volumes-from pg \ -v "$(pwd)/backups":/backup \ busybox \ tar czf /backup/pg-backup-$(date +%Y%m%d).tar.gz /var/lib/postgresql/data

وصلات tmpfs — تخزين مؤقت في الذاكرة

وصلة tmpfs موجودة فقط في ذاكرة وصول عشوائي نواة المضيف ولا تُكتب أبداً على القرص. تختفي عند إيقاف الحاوية. استخدمها للبيانات الحساسة المؤقتة (رموز الجلسات، الأسرار المفككة تشفيرها) التي يجب ألا تظهر في السجلات أو على القرص:

docker run -d \ --name secure-app \ --tmpfs /tmp:rw,size=64m,mode=1777 \ myapp:latest

أنماط الإنتاج على نطاق واسع

في مجموعة Kubernetes أو Docker Swarm، لا تعمل الأحجام المُسمّاة المدعومة بالمُشغّل المحلي في حالات النشر متعدد العقد — لكل عقدة قرصها الخاص ولا توجد مزامنة. الحلول المستخدمة عملياً:

  • تخزين الكتل السحابي (AWS EBS، GCP PD). وصل وحيد؛ يتبع الحجم الـ pod/الحاوية إلى أي عقدة جُدولت عليها عبر مُشغّل CSI (Kubernetes) أو مكوّن الحجم (Swarm). أدنى زمن استجابة؛ الأفضل لقواعد البيانات.
  • أنظمة الملفات الشبكية المشتركة (EFS، Azure Files، NFS). وصل متعدد؛ أي عقدة يمكنها تركيب نظام الملفات ذاته في وقتٍ واحد. مناسب للأصول المشتركة كالملفات المرفوعة أو أوزان نماذج التعلم الآلي.
  • تخزين الكائنات (S3، GCS) عبر وصلات FUSE أو SDK. الأفضل للبيانات الضخمة نادرة الوصول. ليس جهاز كتلة — الخيار الخاطئ لقاعدة بيانات ذات معاملات.
على النطاق الواسع كبار التقنية، النمط المفضل هو جعل حاويات التطبيق عديمة الحالة ونقل كل الثبات إلى خدمة مُدارة (RDS، Cloud Spanner، Redis Cluster). عندئذٍ تُستخدم الأحجام في الحاويات فقط للإعدادات والذاكرة التخزينية المؤقتة والتخزين المحلي المؤقت — مما يُبسّط العمليات بشكل جذري.