Docker للمطورين: الصور والحاويات وCompose
استنسخت مشروعًا، ونفّذت تعليمات الإعداد، واصطدمت بحائط: مكتبة نظام مفقودة، أو قاعدة بيانات لا تبدأ، أو عدم تطابق في إصدار Node. وُجد Docker لإزالة هذه الفئة من المشكلات. فبدلاً من تثبيت الخدمات مباشرة على جهازك، تصفها مرة واحدة وتشغّلها في حاويات معزولة تتصرف بنفس الطريقة في كل مكان.
يشرح هذا الدليل الأفكار الثلاث التي تحتاجها فعلاً: الصور، والحاويات، وCompose. ويبقى عمليًا، بأوامر يمكنك تشغيلها أثناء القراءة.
الصور مقابل الحاويات: النموذج الذهني
الصورة هي قالب للقراءة فقط. تحتوي على لقطة لنظام الملفات بالإضافة إلى بيانات وصفية مثل الأمر الافتراضي والمنافذ المكشوفة. أما الحاوية فهي نسخة قيد التشغيل (أو متوقفة) من تلك الصورة مع طبقة رقيقة قابلة للكتابة فوقها.
إذا كنت قادمًا من البرمجة كائنية التوجه، فالتشبيه قريب: الصورة هي الصنف، والحاوية هي الكائن. يمكنك تشغيل العديد من الحاويات من صورة واحدة، والتغييرات داخل الحاوية لا تعدّل الصورة.
| المفهوم | ما هو | الأمر المعتاد |
|---|---|---|
| الصورة | قالب غير قابل للتغيير | docker build, docker pull |
| الحاوية | نسخة قيد التشغيل | docker run, docker ps |
| وحدة التخزين | تخزين دائم | docker volume create |
| Compose | تعريف متعدد الحاويات | docker compose up |
صورتك الأولى باستخدام Dockerfile
Dockerfile هو وصفة. كل تعليمة تُنشئ طبقة، ويخزّن Docker الطبقات مؤقتًا بحيث تكون عمليات إعادة البناء سريعة عندما لا يتغير شيء. إليك مثال صغير بـ Node.js:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]هناك عادتان مهمتان هنا. أولاً، انسخ ملفات تعريف التبعيات قبل بقية الشيفرة المصدرية حتى تُخزَّن طبقة npm ci مؤقتًا عندما تتغير شيفرة التطبيق فقط. ثانيًا، فضّل صورة أساسية نحيفة مثل alpine لتقليل الحجم وسطح الهجوم.
ابنِها وشغّلها:
docker build -t myapp:1.0 .
docker run --rm -p 3000:3000 myapp:1.0تُعيّن العلامة -p 3000:3000 منفذ المضيف 3000 إلى منفذ الحاوية 3000. و--rm يحذف الحاوية عند خروجها، مما يبقي جهازك مرتبًا أثناء التطوير.
أوامر الحاويات اليومية
تغطي هذه معظم العمل اليومي:
docker ps— اسرد الحاويات قيد التشغيل؛ أضف-aلتضمين المتوقفة.docker logs -f <container>— بثّ مخرجات التطبيق.docker exec -it <container> sh— افتح صدفة داخل حاوية قيد التشغيل.docker stop <container>— إيقاف لطيف.docker images— اسرد الصور المحلية وأحجامها.docker system prune— أزل الحاويات والشبكات والصور المعلّقة غير المستخدمة.
عندما تخرج الحاوية فورًا، اقرأ السجلات أولاً. الأسباب الأكثر شيوعًا هي CMD خاطئ، أو متغير بيئة مفقود، أو ربط العملية بـ 127.0.0.1 بدلاً من 0.0.0.0 داخل الحاوية.
لماذا وُجد Compose
عادةً ما يحتاج التطبيق الحقيقي إلى أكثر من عملية: واجهة برمجية، وقاعدة بيانات، وربما ذاكرة تخزين مؤقت. تشغيل ثلاثة أوامر docker run بقوائم علامات طويلة عرضة للخطأ. يتيح لك Docker Compose تعريف جميع الخدمات في ملف YAML واحد وتشغيلها معًا.
services:
api:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://app:secret@db:5432/app
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: app
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:لاحظ أن الواجهة البرمجية تتصل بـ db، وليس localhost. ينشئ Compose شبكة خاصة يمكن الوصول فيها إلى كل خدمة باسم خدمتها. وهذا هو المصدر الأكثر شيوعًا للارتباك لدى القادمين الجدد.
ابدأ كل شيء بالأمر docker compose up، أو docker compose up -d للتشغيل في الخلفية. أعد البناء بعد تغييرات الشيفرة بـ docker compose up --build. أوقف كل شيء بـ docker compose down، وأضف -v إذا كنت تريد أيضًا حذف وحدات التخزين المسماة.
وحدات التخزين والربط وإعادة التحميل المباشر
الحاويات مؤقتة؛ فالبيانات داخلها تختفي عند إزالتها. هناك آليتان تحلان هذا:
- وحدات التخزين المسماة — يديرها Docker، مثالية لملفات قواعد البيانات. تستمر عبر إعادة تشغيل الحاويات وتُعرَّف في قسم
volumes:. - عمليات الربط (Bind mounts) — تعيّن دليل المضيف داخل الحاوية، مثالية للشيفرة المصدرية أثناء التطوير.
services:
api:
build: .
volumes:
- ./src:/app/srcمع الربط، ينعكس تحرير الملفات على المضيف داخل الحاوية. اجمع هذا مع مراقب ملفات (nodemon، uvicorn --reload، air) وتحصل على إعادة تحميل مباشرة دون إعادة بناء الصورة.
سير عمل تطويري عملي
- اكتب Dockerfile لتطبيقك واجعله بسيطًا.
- أضف
docker-compose.ymlمع التطبيق وتبعياته. - استخدم عمليات الربط للشيفرة المصدرية ووحدات التخزين المسماة للبيانات.
- أبقِ الأسرار خارج الملف؛ استخدم ملف
.envوأشر إلى المتغيرات بـ${VAR}. - أضف
.dockerignoreيحتوي علىnode_modulesو.gitومخرجات البناء لتسريع عمليات البناء. - شغّل
docker compose up --buildوكرّر.
إذا كانت خدمتك تنتج سجلات تحتاج إلى فحصها لاحقًا، فتذكّر أن سجلات الحاويات مجرد نص. الأدوات التي تحلل سجلات الخادم، مثل محلل سجلات nginx، تتكامل جيدًا مع الحاويات لأنك تستطيع تمرير السجلات أو نسخها خارجيًا دون تثبيت أي شيء على المضيف.
مزالق شائعة
- صور ضخمة: استخدم عمليات بناء متعددة المراحل للتصريف في مرحلة واحدة ونسخ النواتج فقط إلى صورة تشغيل نحيفة.
- إعادة بناء كل شيء: رتّب تعليمات Dockerfile من الأقل إلى الأكثر تغيّرًا.
- مضيفين مضمّنين: داخل Compose، استخدم أسماء الخدمات؛ وخارجها، استخدم متغيرات البيئة.
- تجاهل التنظيف: تتراكم الصور المعلّقة والحاويات المتوقفة. شغّل
docker system pruneدوريًا. - التشغيل كـ root: أضف
USERغير جذري في صور الإنتاج.
الأسئلة الشائعة
ما الفرق بين الصورة والحاوية؟
الصورة قالب غير قابل للتغيير يحتوي على نظام ملفات وبيانات وصفية. أما الحاوية فهي نسخة قيد التشغيل من تلك الصورة مع طبقة قابلة للكتابة مضافة. يمكنك تشغيل العديد من الحاويات من صورة واحدة.
هل أحتاج Docker Compose لخدمة واحدة؟
لا. يكفي أمر docker run واحد لحاوية واحدة. يصبح Compose ذا قيمة عندما يكون لديك خدمات متعددة، أو شبكات مشتركة، أو متغيرات بيئة تريد تعريفها في مكان واحد.
كيف تتواصل الحاويات مع بعضها؟
يضع Compose جميع الخدمات على شبكة مشتركة يمكن الوصول فيها إلى كل خدمة باسم خدمتها. استخدم ذلك الاسم كاسم مضيف، مثلاً db:5432، بدلاً من localhost.
يكافئ Docker استثمارًا بسيطًا في البداية. بمجرد أن تتضح الصور والحاويات وCompose، يتوقف ضمّ زميل أو تغيير الأجهزة عن كونه طقس إعداد ويصبح أمرًا واحدًا.