هذا الموقع في نسخته التجريبية

ما هو دوكر؟ شرح مبسط بالعربية

‐ مدة قراءة المقال ١١ دقيقة مصطلحاتDevOpsدوكر
شخص يضع تطبيقاً واعتمادياته داخل حاوية قابلة للنقل

إذا عمل تطبيقك على جهازك ثم فشل عند زميلك أو على مخدم الإنتاج، فغالباً لا تكون المشكلة في النص البرمجي وحده، بل في اختلاف البيئة المحيطة به: إصدار Node مختلف، مكتبة ناقصة، أو إعداد لنظام التشغيل لا يشبه إعداد جهازك.

دوكر منصة ومجموعة أدوات تساعدك على بناء صور التطبيقات وتشغيل الحاويات وإدارتها، بحيث تنتقل البيئة المطلوبة مع التطبيق وتبقى متسقة عبر المنصات المتوافقة. لا يعني ذلك أن كل جهاز يصبح متطابقاً، فالنواة والمعمارية والبيئة المضيفة ما زالت عوامل مهمة.

لماذا يهمك؟

أشهر جملة في عالم البرمجة: ”لكنه يعمل على جهازي!“. يعود هذا الاختلاف غالباً إلى البيئة التي تحيط بالتطبيق، لا إلى التطبيق وحده. عندما تصف هذه البيئة داخل Dockerfile وتبني منها صورة، يصبح إعدادها جزءاً قابلاً للمراجعة والتكرار ويمكن مشاركته مع بقية الفريق عبر Git.

فبدلاً من إرسال دليل تثبيت طويل لزميلك، ترسل له ملفات المشروع وملف البناء، ثم يحصل على بيئة متسقة مع بيئتك ما دامت المنصة المضيفة والمعمارية مدعومتين.

كيف يعمل؟

ثلاثة مفاهيم أساسية تكفي لتكوين مدخل أولي:

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

قابلية النقل وحدودها

يساعدك دوكر على نقل بيئة التطبيق بين المنصات المتوافقة، لكنه لا يلغي القيود المتعلقة بالمعمارية والنواة ونظام التشغيل الأساسي.

بمعنى آخر، يساعدك دوكر على تثبيت المكتبات والإعدادات والأدوات داخل بيئة قابلة لإعادة البناء عبر المنصات المتوافقة، لكنه لا يجعل العتاد وأنظمة التشغيل المختلفة متطابقة.

كيف يتحقق العزل داخل الحاوية؟

تبدو الحاوية وكأنها جهاز مستقل، لكنها في الحقيقة عملية عادية في نظام التشغيل تطبق عليها آليات من النواة لتقييد الرؤية والموارد. يعتمد العزل أساساً على آليتين:

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

العلاقة بالمحاكاة الافتراضية

الحاويات والأجهزة الافتراضية تستخدمان أسلوبين مختلفين للعزل:

graph TD
    subgraph VM["الجهاز الافتراضي: نظام تشغيل مستقل"]
        APP1["تطبيق أ"] --> OS1["نظام تشغيل كامل<br/>(نواة خاصة)"]
        APP2["تطبيق ب"] --> OS2["نظام تشغيل كامل<br/>(نواة خاصة)"]
        OS1 --> HV["مراقب الأجهزة الافتراضية<br/>(موارد افتراضية)"]
        OS2 --> HV
        HV --> HW1["عتاد الجهاز"]
    end
graph TD
    subgraph CONT["الحاويات: عزل بالنواة المشتركة"]
        C1["حاوية 1<br/>(عملية مقيدة)"] --> NS["Namespaces + cgroups"]
        C2["حاوية 2<br/>(عملية مقيدة)"] --> NS
        NS --> K["نواة لينوكس المشتركة"]
        K --> HW2["عتاد الجهاز"]
    end

يحمل كل جهاز افتراضي نظام تشغيل كاملاً، بينما تعتمد الحاويات على آليات العزل التي توفرها النواة المشتركة. لذلك قد تكون الحاوية أقل استهلاكاً للموارد، في حين يضيف الجهاز الافتراضي حداً آخر عبر مراقب الأجهزة الافتراضية. لا يعني ذلك أن أحد الخيارين أفضل دائماً، فالاختيار يعتمد على درجة العزل المطلوبة وطبيعة التطبيق.

على ماك وويندوز، قد يكون مراقب الأجهزة الافتراضية جزءاً من الطريقة التي يوفر بها تطبيق دوكر لسطح المكتب (Docker Desktop) نواة لينوكس اللازمة لحاويات لينوكس. في هذه الحالة تعمل الحاوية داخل آلة افتراضية تعمل بلينوكس، حتى لو كانت أوامر Docker تُنفذ من نظام التشغيل المضيف.

محرك دوكر مقابل تطبيق دوكر لسطح المكتب

عندما تقول ”دوكر“ فأنت غالباً تعني أحد مكوّنين، والفرق بينهما مهم:

رخصة محرك دوكر لا تعني أن كل مكوّنات تطبيق دوكر لسطح المكتب مفتوحة المصدر أو مجانية لكل أنواع الاستخدام. بحسب الشروط الرسمية لتطبيق دوكر لسطح المكتب، يكون تطبيق دوكر لسطح المكتب مجانياً للاستخدام الشخصي والتعليم والمشروعات المفتوحة المصدر غير التجارية والشركات الصغيرة ضمن الحدود الرسمية، ومن أمثلتها أن يكون لدى الشركة أقل من 250 موظفاً وإيرادات سنوية أقل من 10 ملايين دولار، مع مراعاة بقية الشروط. ويكون تطبيق دوكر لسطح المكتب مدفوعاً للاستخدام المهني في المؤسسات الأكبر وللجهات الحكومية، لذلك لا يكفي تلخيص الأهلية بهذين الرقمين فقط، بل يجب الرجوع إلى التعريفات والشروط الرسمية.

يختلف Podman عن Colima في المستوى الذي يعمل فيه كل منهما. Podman محرك وأدوات لإدارة الحاويات من دون خدمة مركزية دائمة، ويمكن استخدامه بواجهة متوافقة مع أوامر Docker في حالات كثيرة. أما Colima فهو مدير لبيئة افتراضية تعمل بلينوكس محلياً، ويمكنه توفير بيئة تشغيل Docker أو containerd على جهازك، لذلك لا يمثل محرك حاويات من النوع نفسه.

متى تستخدمه ومتى تتجنبه؟

استخدم دوكر عندما:

تجنبه عندما:

مثال عملي

سنفترض أن لدينا تطبيق Node.js بسيطاً يستمع إلى المنفذ 3000 ويعيد نصاً قصيراً. سننشئ له الملفات الأساسية داخل المقال حتى تستطيع تشغيل المثال كما هو، مع التنبيه إلى أن التطبيقات الحقيقية قد تحتاج إلى إعدادات واعتماديات إضافية.

في هذا المثال، يجب أن يستمع التطبيق على 0.0.0.0 داخل الحاوية، لا على localhost فقط، حتى تصل إليه الاتصالات القادمة من منفذ Docker المنشور.

نبدأ بملف package.json بسيط، لأن npm ci يحتاج إلى ملف القفل والبيانات التي يصفها:

{
  "name": "my-app",
  "version": "1.0.0",
  "private": true
}

ثم ننشئ package-lock.json متوافقاً معه:

{
  "name": "my-app",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "my-app",
      "version": "1.0.0"
    }
  }
}

أما server.js فيستمع على 0.0.0.0 كما يلي:

const http = require("node:http");

const port = Number(process.env.PORT || 3000);

const server = http.createServer((_request, response) => {
  response.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
  response.end("مرحبا من داخل Docker\n");
});

server.listen(port, "0.0.0.0", () => {
  console.log(`Listening on ${port}`);
});

إذا لم يكن package-lock.json موجوداً أو لم يكن متوافقاً مع package.json، فسيفشل npm ci. أنشئ ملف القفل من المشروع نفسه، ثم راجعه واحتفظ به مع بقية الملفات.

نبدأ بملف .dockerignore صغير. الهدف هو عدم إرسال الملفات الكبيرة أو الحساسة إلى سياق البناء:

node_modules
.git
.env
dist
build

نكتب Dockerfile تدريجياً، ونشرح سبب كل جزء:

أولاً، نختار صورة Node.js كأساس حتى تتوفر بيئة التشغيل داخل الصورة:

FROM node:22-alpine

ثم نحدد مجلد العمل. يجعل ذلك المسارات التالية واضحة، ويمنع وضع ملفات التطبيق في جذر نظام الملفات:

WORKDIR /app

ننسخ ملفات تعريف الحزم قبل بقية النص البرمجي، لأن Docker يستطيع إعادة استخدام هذه الطبقة عندما لا تتغير الاعتماديات:

COPY package.json package-lock.json ./

بعد ذلك نثبت الاعتماديات من ملف القفل. يستخدم npm ci الحالة المقفلة بدل إعادة حل الإصدارات، وهو سبب حاجته إلى package-lock.json متوافق. نستخدم --omit=dev لأن الصورة هنا مخصصة للتشغيل:

RUN npm ci --omit=dev

الآن ننسخ بقية ملفات التطبيق، ومنها server.js:

COPY . .

يخبر EXPOSE قارئ الملف بأن التطبيق يتوقع المنفذ 3000، لكنه توثيق فقط، ولا ينشر المنفذ ولا ينشئ تحويلاً للشبكة. النشر يحدث لاحقاً عند استخدام -p مع docker run:

EXPOSE 3000

أخيراً، نحدد العملية الافتراضية التي تبدأ عند تشغيل الحاوية:

CMD ["node", "server.js"]

بعد جمع المقاطع، يكون Dockerfile الكامل كما يلي:

FROM node:22-alpine

WORKDIR /app

COPY package.json package-lock.json ./
RUN npm ci --omit=dev

COPY . .

EXPOSE 3000

CMD ["node", "server.js"]

نبني الصورة ثم نشغل الحاوية ونربط المنفذ 3000 في المضيف بالمنفذ نفسه داخل الحاوية:

docker build -t my-app:1.0 .
docker run --rm -d --name my-app -p 3000:3000 my-app:1.0

نتحقق من النتيجة باستخدام curl:

curl http://localhost:3000

المخرج المتوقع:

مرحبا من داخل Docker

بعد التحقق، أوقف الحاوية بالأمر docker stop my-app إذا كانت ما تزال تعمل.

مصطلحات ذات صلة

مقالات ذات صلة

→ ارشيف المقالات