الدليل العملي لتعلّم Docker للمبتدئين: من الحاويات إلى التطبيقات متعددة الخدمات

دقائق القراءة: 9

مقدمة: لماذا يُعد Docker مهارة أساسية اليوم؟

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

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

  • تغليف معظم التطبيقات داخل حاويات قابلة للتشغيل.
  • إنشاء صور مخصصة ورفعها إلى مستودعات صور عبر الإنترنت.
  • إدارة عدة حاويات معاً باستخدام Docker Compose.
  • بناء بيئات تطوير وإنتاج متسقة وسهلة التكرار.

غلاف دليل تعلم Docker للمبتدئين واحتراف أساسيات الحاويات البرمجية

المتطلبات المسبقة قبل البدء

  • إلمام جيد باستخدام الطرفية في Linux.
  • معرفة أساسية بلغة JavaScript لبعض الأمثلة التطبيقية المتقدمة.

ما هي الحاويات البرمجية Containerization؟

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

تخيّل أنك طورت تطبيقاً لإدارة الكتب باستخدام:

  • Node.js
  • Express.js
  • SQLite3

نظرياً يبدو هذا كافياً، لكن عملياً قد تحتاج أيضاً إلى أدوات بناء مثل node-gyp، ووجود Python، وسلسلة أدوات C/C++. هنا تبدأ مشكلات التثبيت واختلاف المنصات، خصوصاً بين Windows وmacOS وLinux.

الحاويات تحل هذه الفوضى عبر ثلاث ركائز أساسية:

  1. تشغيل التطبيق داخل بيئة معزولة تسمى Container.
  2. تجميع التطبيق في ملف قابل للتوزيع يسمى Image.
  3. مشاركة الصور عبر مستودع مركزي يسمى Registry.

ما هو Docker تحديداً؟

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

صحيح أن Docker ليس الأداة الوحيدة في هذا المجال، فهناك أدوات مثل Podman وKaniko وغيرها، لكنه يظل الخيار الأكثر شيوعاً وانتشاراً في بيئات التطوير الحديثة.

تثبيت Docker على الأنظمة المختلفة

تثبيت Docker على macOS

يُعد التثبيت على macOS من أبسط الحالات. يكفي تنزيل التطبيق الرسمي، ثم سحب أيقونته إلى مجلد التطبيقات وتشغيله.

طريقة تثبيت Docker على macOS عبر سحب التطبيق إلى مجلد Applicationsأيقونة Docker بعد تشغيله على شريط القوائم في macOS

بعد التشغيل، تحقق من نجاح التثبيت بالأمرين التاليين:

docker --version
docker-compose --version

تثبيت Docker على Windows

في Windows تحتاج أولاً إلى تفعيل WSL2، ثم تثبيت Docker Desktop. بعد اكتمال العملية، يمكن تشغيله من قائمة ابدأ أو من سطح المكتب.

أيقونة Docker على شريط المهام في نظام ويندوز بعد التثبيتالتحقق من إصدار Docker وDocker Compose على Windows بعد التثبيت

للتحقق من نجاح التثبيت:

docker --version
docker-compose --version

تثبيت Docker على Linux

في Linux يختلف التثبيت باختلاف التوزيعة، لكن الفكرة متشابهة: تثبيت Docker Engine ثم تثبيت Docker Compose بشكل مستقل عند الحاجة.

بعد انتهاء التثبيت، تحقق من الإصدارات:

docker --version
docker-compose --version

التحقق من نجاح تثبيت Docker وDocker Compose على لينكس

أول تجربة: تشغيل Hello World في Docker

بعد تثبيت Docker، جرّب أول حاوية لك بالأمر التالي:

docker run hello-world

هذه الصورة البسيطة تؤكد أن العميل Docker Client استطاع التواصل مع الخدمة الخلفية Docker Daemon، ثم سحب الصورة من Docker Hub وتشغيل الحاوية بنجاح.

ولعرض الحاويات الحالية والسابقة:

docker ps -a

المفاهيم الأساسية في Docker

ما هي الحاوية Container؟

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

هذا الفارق يمنح الحاويات سرعة أعلى واستهلاكاً أقل للموارد، مع الاحتفاظ بدرجة ممتازة من العزل.

مخطط يوضح بنية الآلات الافتراضية مقارنة بطبقة نظام التشغيلمخطط يوضح كيفية عمل الحاويات في Docker مقارنة بالأنظمة الافتراضية

مثال يثبت أن الحاوية تستخدم نواة النظام المضيف:

uname -a
docker run alpine uname -a

ما هي الصورة Docker Image؟

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

ما هو المستودع Docker Registry؟

المستودع هو المكان الذي تُخزَّن فيه الصور وتُشارك مع الآخرين. أشهر مثال هو Docker Hub، كما توجد بدائل مثل Quay، ويمكنك أيضاً إنشاء مستودعك الخاص للصور الداخلية.

واجهة Docker Hub كمستودع مركزي لصور Dockerمثال على صور Docker مرفوعة إلى حساب مستخدم على Docker Hub

معمارية Docker باختصار

تتكون المنصة من ثلاثة أجزاء رئيسية:

  • Docker Daemon: الخدمة الخلفية المسؤولة عن بناء الصور وتشغيل الحاويات وإدارتها.
  • Docker Client: واجهة سطر الأوامر التي يكتب عبرها المستخدم الأوامر.
  • REST API: الجسر الذي يمر عبره التواصل بين العميل والخدمة الخلفية.

عند تنفيذ أمر مثل docker run hello-world، يرسل العميل الطلب إلى الخدمة الخلفية، فتبحث عن الصورة محلياً، وإن لم تجدها تسحبها من المستودع ثم تنشئ الحاوية وتشغلها.

مخطط يشرح ما يحدث عند تنفيذ أمر docker run hello-world

أساسيات التعامل مع الحاويات

تشغيل حاوية

docker container run <image-name>

مثال عملي:

docker container run --publish 8080:80 fhsinchy/hello-dock

نشر المنافذ Port Publishing

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

--publish <host-port>:<container-port>

مثال:

docker container run --publish 8080:80 fhsinchy/hello-dock

ثم افتح العنوان http://127.0.0.1:8080.

تشغيل تطبيق hello-dock داخل حاوية Docker عبر المتصفح

التشغيل في الخلفية Detached Mode

docker container run --detach --publish 8080:80 fhsinchy/hello-dock

يعمل هذا الخيار على إبقاء الحاوية قيد التشغيل في الخلفية دون ربطها بالطرفية الحالية.

عرض الحاويات

docker container ls
docker container ls --all

تسمية الحاويات وإعادة تسميتها

docker container run --detach --publish 8888:80 --name hello-dock-container fhsinchy/hello-dock
docker container rename gifted_sammet hello-dock-container-2

إيقاف الحاويات أو قتلها

docker container stop hello-dock-container
docker container kill hello-dock-container-2

إعادة تشغيل الحاويات

docker container start hello-dock-container
docker container restart hello-dock-container-2

إنشاء حاوية دون تشغيلها

docker container create --publish 8080:80 fhsinchy/hello-dock
docker container start hello-dock

حذف الحاويات المتوقفة

docker container rm <container-id>
docker container prune

التشغيل التفاعلي

docker container run --rm -it ubuntu

يسمح الخيار -it بالتفاعل مع الصدفة داخل الحاوية.

تنفيذ أوامر داخل الحاوية

docker container run alpine uname -a
docker container run --rm busybox sh -c "echo -n my-secret | base64"

الصور في Docker: الإنشاء والإدارة

إنشاء صورة مخصصة

الخطوة الأساسية هي كتابة ملف Dockerfile يصف الحالة النهائية للصورة.

FROM ubuntu:latest
EXPOSE 80
RUN apt-get update && \
    apt-get install nginx -y && \
    apt-get clean && rm -rf /var/lib/apt/lists/*
CMD ["nginx", "-g", "daemon off;"]

لبناء الصورة:

docker image build .

إضافة وسم Tag للصورة

docker image build --tag custom-nginx:packaged .

عرض الصور وحذفها

docker image ls
docker image rm custom-nginx:packaged
docker image prune --force

فهم الطبقات داخل الصورة

docker image history custom-nginx:packaged

كل تعليمة في Dockerfile تنشئ طبقة جديدة. هذه البنية الطبقية تسهّل إعادة الاستخدام والتخزين المؤقت Cache وتقلل التكرار أثناء البناء.

بناء NGINX من المصدر

يمكنك تجاوز تثبيت الحزمة الجاهزة وبناء NGINX من الشيفرة المصدرية باستخدام تعليمات مثل COPY وADD وARG.

FROM ubuntu:latest
RUN apt-get update && \
    apt-get install build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev libssl-dev -y && \
    apt-get clean && rm -rf /var/lib/apt/lists/*
ARG FILENAME="nginx-1.19.2"
ARG EXTENSION="tar.gz"
ADD https://nginx.org/download/${FILENAME}.${EXTENSION} .
RUN tar -xvf ${FILENAME}.${EXTENSION} && rm ${FILENAME}.${EXTENSION}
RUN cd ${FILENAME} && \
    ./configure --sbin-path=/usr/bin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-pcre --pid-path=/var/run/nginx.pid --with-http_ssl_module && \
    make && make install
RUN rm -rf /${FILENAME}
CMD ["nginx", "-g", "daemon off;"]

تحسين الصور وتقليل الحجم

الصورة العاملة ليست بالضرورة صورة جيدة. من أهم مبادئ التحسين:

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

استخدام Alpine Linux مثال ممتاز على ذلك، إذ يمنحك صوراً أخف بكثير من Ubuntu.

FROM alpine:latest
EXPOSE 80
ARG FILENAME="nginx-1.19.2"
ARG EXTENSION="tar.gz"
ADD https://nginx.org/download/${FILENAME}.${EXTENSION} .
RUN apk add --no-cache pcre zlib && \
    apk add --no-cache --virtual .build-deps build-base pcre-dev zlib-dev openssl-dev && \
    tar -xvf ${FILENAME}.${EXTENSION} && rm ${FILENAME}.${EXTENSION} && \
    cd ${FILENAME} && \
    ./configure --sbin-path=/usr/bin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-pcre --pid-path=/var/run/nginx.pid --with-http_ssl_module && \
    make && make install && \
    cd / && rm -rf /${FILENAME} && \
    apk del .build-deps
CMD ["nginx", "-g", "daemon off;"]

إنشاء صور تنفيذية Executable Images

يمكن تحويل الصورة إلى أداة تنفيذية تشبه البرامج الطرفية المعتادة عبر تعليمة ENTRYPOINT.

FROM python:3-alpine
WORKDIR /zone
RUN apk add --no-cache git && \
    pip install git+https://github.com/fhsinchy/rmbyext.git#egg=rmbyext && \
    apk del git
ENTRYPOINT ["rmbyext"]

رفع الصور إلى الإنترنت

لمشاركة صورك عبر Docker Hub، سجّل الدخول أولاً:

docker login

ثم ابنِ الصورة مع وسم يتضمن اسم المستخدم:

docker image build --tag your-username/custom-nginx:latest .

وأخيراً ارفعها:

docker image push your-username/custom-nginx:latest

تغليف تطبيق JavaScript داخل Docker

كتابة Dockerfile للتطوير

FROM node:lts-alpine
EXPOSE 3000
USER node
RUN mkdir -p /home/node/app
WORKDIR /home/node/app
COPY ./package.json .
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]

لبناء الصورة:

docker image build --file Dockerfile.dev --tag hello-dock:dev .

ولتشغيلها:

docker container run --rm --detach --publish 3000:3000 --name hello-dock-dev hello-dock:dev

تشغيل تطبيق JavaScript داخل Docker في وضع التطوير

استخدام Bind Mounts

لتمكين التحديث الفوري Hot Reload أثناء التطوير، اربط مجلد المشروع المحلي بمجلد العمل داخل الحاوية:

docker container run \
  --rm \
  --publish 3000:3000 \
  --name hello-dock-dev \
  --volume $(pwd):/home/node/app \
  hello-dock:dev

لكن هذه الخطوة قد تستبدل مجلد node_modules داخل الحاوية، ما يسبب أخطاء في التشغيل.

استخدام الأحجام المجهولة Anonymous Volumes

لحل مشكلة node_modules:

docker container run \
  --rm \
  --detach \
  --publish 3000:3000 \
  --name hello-dock-dev \
  --volume $(pwd):/home/node/app \
  --volume /home/node/app/node_modules \
  hello-dock:dev

البناء متعدد المراحل Multi-stage Builds

في بيئة الإنتاج، من الأفضل بناء التطبيق باستخدام Node.js ثم نسخ الملفات النهائية إلى صورة NGINX خفيفة:

FROM node:lts-alpine as builder
WORKDIR /app
COPY ./package.json ./
RUN npm install
COPY . .
RUN npm run build

FROM nginx:stable-alpine
EXPOSE 80
COPY --from=builder /app/dist /usr/share/nginx/html

هذه الطريقة تجعل الصورة النهائية أصغر وأكثر أماناً.

نسخة الإنتاج من تطبيق hello-dock بعد البناء متعدد المراحل في Docker

تجاهل الملفات غير الضرورية عبر .dockerignore

.git
*Dockerfile*
*docker-compose*
node_modules

هذا الملف يقلل حجم سياق البناء Build Context ويحسن سرعة الإنشاء.

أساسيات الشبكات في Docker

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

عرض الشبكات

docker network ls

إنشاء شبكة مخصصة

docker network create skynet

ربط حاوية بشبكة

docker network connect skynet hello-dock

أو أثناء التشغيل:

docker container run --network skynet --rm --name alpine-box -it alpine sh

داخل الحاوية، يمكنك اختبار الوصول:

ping hello-dock

فصل الحاوية عن الشبكة

docker network disconnect skynet hello-dock

حذف الشبكة

docker network rm skynet

تغليف تطبيق متعدد الحاويات: API مع قاعدة بيانات

تشغيل قاعدة البيانات

docker container run \
  --detach \
  --name=notes-db \
  --env POSTGRES_DB=notesdb \
  --env POSTGRES_PASSWORD=secret \
  --network=notes-api-network \
  postgres:12

استخدام الأحجام المسماة Named Volumes

docker volume create notes-db-data

ثم ربطه بحاوية قاعدة البيانات:

docker container run \
  --detach \
  --volume notes-db-data:/var/lib/postgresql/data \
  --name=notes-db \
  --env POSTGRES_DB=notesdb \
  --env POSTGRES_PASSWORD=secret \
  --network=notes-api-network \
  postgres:12

قراءة السجلات Logs

docker container logs notes-db

بناء صورة API

FROM node:lts-alpine as builder
RUN apk add --no-cache python make g++
WORKDIR /app
COPY ./package.json .
RUN npm install --only=prod

FROM node:lts-alpine
EXPOSE 3000
ENV NODE_ENV=production
USER node
RUN mkdir -p /home/node/app
WORKDIR /home/node/app
COPY . .
COPY --from=builder /app/node_modules /home/node/app/node_modules
CMD ["node", "bin/www"]

ثم شغّل الخدمة:

docker container run \
  --detach \
  --name=notes-api \
  --env DB_HOST=notes-db \
  --env DB_DATABASE=notesdb \
  --env DB_PASSWORD=secret \
  --publish=3000:3000 \
  --network=notes-api-network \
  notes-api

واجهة تطبيق notes-api بعد تشغيله مع PostgreSQL داخل Docker

تنفيذ أوامر داخل حاوية تعمل

docker container exec notes-api npm run db:migrate

وللدخول التفاعلي إلى الحاوية:

docker container exec -it notes-api sh

إدارة المشاريع متعددة الخدمات باستخدام Docker Compose

بدلاً من كتابة سلسلة طويلة من الأوامر يدوياً، يوفّر Docker Compose طريقة عملية لوصف وتشغيل المشروع كاملًا عبر ملف YAML واحد.

ملف docker-compose.yaml أساسي

version: "3.8"
services:
  db:
    image: postgres:12
    container_name: notes-db-dev
    volumes:
      - notes-db-dev-data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: notesdb
      POSTGRES_PASSWORD: secret

  api:
    build:
      context: ./api
      dockerfile: Dockerfile.dev
    image: notes-api:dev
    container_name: notes-api-dev
    environment:
      DB_HOST: db
      DB_DATABASE: notesdb
      DB_PASSWORD: secret
    volumes:
      - /home/node/app/node_modules
      - ./api:/home/node/app
    ports:
      - 3000:3000

volumes:
  notes-db-dev-data:
    name: notes-db-dev-data

تشغيل الخدمات

docker-compose --file docker-compose.yaml up --detach

عرض الخدمات

docker-compose ps

تنفيذ أمر داخل خدمة تعمل

docker-compose exec api npm run db:migrate

عرض السجلات

docker-compose logs api

إيقاف الخدمات وإزالتها

docker-compose down --volumes

بناء تطبيق Full-stack باستخدام Docker Compose

في المشاريع الأكبر، قد تحتاج إلى خدمات أمامية وخلفية وموجّه طلبات Router مثل NGINX. هنا تصبح الشبكات المتعددة مفيدة جداً للفصل بين الواجهة وقاعدة البيانات والطبقة الوسيطة.

مخطط بنية تطبيق متكامل Full-stack باستخدام Docker Compose وNGINX

مثال على ملف Dockerfile لخدمة NGINX:

FROM nginx:stable-alpine
COPY ./development.conf /etc/nginx/conf.d/default.conf

ومثال على تعريف الشبكات داخل docker-compose.yaml:

networks:
  frontend:
    name: fullstack-notes-application-network-frontend
    driver: bridge
  backend:
    name: fullstack-notes-application-network-backend
    driver: bridge

بهذا الأسلوب يمكن جعل خدمة nginx متصلة بالشبكتين معاً لتوجيه الطلبات بين الواجهة الأمامية والخلفية بكفاءة.

واجهة تطبيق ملاحظات متكامل يعمل باستخدام Docker Compose

روابط الشيفرة المصدرية

  • مشاريع الأمثلة: https://github.com/fhsinchy/docker-handbook-projects/
  • المحتوى المفتوح للكتاب: https://github.com/fhsinchy/the-docker-handbook
  • نسخة محدثة باستمرار: https://docker-handbook.farhan.dev/
  • النسخة المنشورة: https://www.freecodecamp.org/news/the-docker-handbook/

الخلاصة التقنية

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

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *